diff --git a/config/hscal/hscal_hkettherm.yaml b/config/hscal/hscal_hkettherm.yaml index af50f90..8463cdb 100644 --- a/config/hscal/hscal_hkettherm.yaml +++ b/config/hscal/hscal_hkettherm.yaml @@ -1,7 +1,7 @@ # CAL RED (HK) Etalon SRS PTC10 daemon config # # Usage: -# daemons/generic/srstherm -c config/hscal/hscal_hkettherm.yaml +# daemons/hscal/srs_thermal -c config/hscal/hscal_hkettherm.yaml peer_id: hkettherm group_id: hscal diff --git a/config/hscal/hscal_yjettherm.yaml b/config/hscal/hscal_yjettherm.yaml index 9d0839b..e6ee59c 100644 --- a/config/hscal/hscal_yjettherm.yaml +++ b/config/hscal/hscal_yjettherm.yaml @@ -1,7 +1,7 @@ # CAL BLUE (YJ) Etalon SRS PTC10 daemon config # # Usage: -# daemons/generic/srstherm -c config/hscal/hscal_yjettherm.yaml +# daemons/hscal/srs_thermal -c config/hscal/hscal_yjettherm.yaml peer_id: yjettherm group_id: hscal diff --git a/daemons/generic/srsthermal b/daemons/hscal/srs_thermal similarity index 69% rename from daemons/generic/srsthermal rename to daemons/hscal/srs_thermal index 348d1df..4c9e957 100755 --- a/daemons/generic/srsthermal +++ b/daemons/hscal/srs_thermal @@ -1,8 +1,8 @@ #!/usr/bin/env python3 """ -Generic Stanford Research Systems PTC10 Thermal control Daemon +Generic Stanford Research Systems model PTC10 Daemon -Exposes the SRS PTC10 Temperature controller commands as libby keywords. +Exposes the SRS PTC10 etalon temperature controller commands as libby keywords. """ import argparse @@ -12,15 +12,16 @@ from hispec import HispecDaemon from hispec.driver.srs.ptc10 import PTC10 -class SRSThermalDaemon(HispecDaemon): # pylint: disable=W0223,too-many-instance-attributes - """Daemon for controlling the SRS PTC10 via keywords.""" +class SrsDaemon(HispecDaemon): # pylint: disable=W0223,too-many-instance-attributes + """Daemon for controlling the SRS via keywords.""" def __init__(self): super().__init__() self.ip_address = None self.tcp_port = None - self.units = None + self.units = "C" + self.srs_model = "PTC10" self.sensors = None self.heaters = None self.error = None @@ -41,7 +42,8 @@ class SRSThermalDaemon(HispecDaemon): # pylint: disable=W0223,too-many-instance self.controller = PTC10(log=True) - self.logger.info("Starting %s daemon", self.peer_id) + self.logger.info("Starting %s daemon with for model %s in degrees C", + self.peer_id, self.srs_model) self.logger.info("Using sensors: %s", self.sensors) self.logger.info("Using heaters: %s", self.heaters) @@ -51,10 +53,13 @@ class SRSThermalDaemon(HispecDaemon): # pylint: disable=W0223,too-many-instance self.keyword_registry.bool("isconnected", getter=self.controller.is_connected, setter=self._set_connected, - description="SRS PTC10 controller is connected.") + description="SRS controller is connected.") self.keyword_registry.string("error", getter=self._error_string, - description="Last error from the SRS PTC10 controller.") + description="Last error from the SRS controller.") + self.keyword_registry.string("status", + getter=self._status, + description="Status of the SRS as a (code, message) string.") self.keyword_registry.trigger("shutdown", action=self._shutdown, description="Shutdown the daemon now.") @@ -66,6 +71,10 @@ class SRSThermalDaemon(HispecDaemon): # pylint: disable=W0223,too-many-instance try: self._connect_hardware() self.logger.info("Daemon started successfully and connected to hardware") + if self.controller.initialize(): + self.logger.info("Hardware successfully initialized") + else: + self.logger.error("Hardware failed to initialize") except Exception as e: # pylint: disable=W0718 self.logger.error("Failed to connect to hardware: %s", e) self.error = str(e) @@ -97,9 +106,15 @@ class SRSThermalDaemon(HispecDaemon): # pylint: disable=W0223,too-many-instance self.keyword_registry.float( keyword, + getter=self._make_setpoint_getter(output), + setter=self._make_setpoint_setter(output), + units=self.units, + description=f"Setpoint for heater output {output}.") + self.keyword_registry.float( + f"{keyword}_output", getter=self._make_output_getter(output), units="Amps", - description=f"Output amps of heater output {output}.") + description=f"Heater output in amps: {output}.") def _make_temperature_getter(self, channel): """Return a zero-argument getter reading the temperature at `channel`.""" @@ -107,6 +122,19 @@ class SRSThermalDaemon(HispecDaemon): # pylint: disable=W0223,too-many-instance return self.controller.get_atomic_value(channel) return getter + def _make_setpoint_getter(self, output): + """Return a zero-argument getter reading the setpoint of heater `output`.""" + def getter(): + return self.controller.get_setpoint(output) + return getter + + def _make_setpoint_setter(self, output): + """Return a one-argument setter writing the setpoint of heater `output`.""" + def setter(value): + if not self.controller.set_setpoint(output, value): + raise RuntimeError(f"Failed to set setpoint for heater output {output}") + return setter + def _make_output_getter(self, output): """Return a zero-argument getter reading the power of heater `output`.""" def getter(): @@ -120,6 +148,16 @@ class SRSThermalDaemon(HispecDaemon): # pylint: disable=W0223,too-many-instance self.controller.disconnect() self.request_stop() + def _connected(self) -> bool: + """True when the driver holds an open session to the SRS controller.""" + return self.controller is not None and bool(self.controller.is_connected()) + + def _status(self): + """Handles status.""" + if not self._connected(): + return str((-1, "ERROR: Not connected to hardware")) + return str(self.controller.get_status()) + def on_stop(self, libby=None): """Cleanup when the daemon shuts down.""" self.logger.info("Shutting down %s daemon", self.peer_id) @@ -127,13 +165,13 @@ class SRSThermalDaemon(HispecDaemon): # pylint: disable=W0223,too-many-instance return try: self.controller.disconnect() - self.logger.info("Disconnected from SRS PTC10 controller") + self.logger.info("Disconnected from SRS 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 SRS PTC10 controller at %s:%s", + self.logger.info("Connecting to SRS 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 @@ -155,7 +193,7 @@ class SRSThermalDaemon(HispecDaemon): # pylint: disable=W0223,too-many-instance def main(): """Main entry point for the daemon.""" - parser = argparse.ArgumentParser(description='Generic SRS PTC10 Daemon') + parser = argparse.ArgumentParser(description='Generic SRS Daemon') parser.add_argument('-c', '--config', type=str, help='Path to config file (YAML or JSON)') @@ -166,7 +204,7 @@ def main(): sys.exit(2) try: - daemon = SRSThermalDaemon.from_config_file(args.config) + daemon = SrsDaemon.from_config_file(args.config) daemon.serve() except KeyboardInterrupt: print("\nDaemon interrupted by user") diff --git a/docs/architecture/daemons.md b/docs/architecture/daemons.md index a706acb..3e8a9e7 100644 --- a/docs/architecture/daemons.md +++ b/docs/architecture/daemons.md @@ -19,7 +19,7 @@ their shared `pdu_base.py` base class. |---|---|---|---| | `generic/filterwheel` | `thorlabs.fw102c` | Thorlabs FW102C filter wheel | `hsfei_atcfw`, `hscal_hkcalfwheel1/2`, `hscal_hkgcellfwheel`, `hscal_yjcalfwheel1/2` | | `generic/lakeshore` | `lakeshore.lakeshore` | Lakeshore 336/224 temperature controller | `hsfei_atctherm`, `hscal_gcellheater1/2` | -| `generic/srsthermal` | `srs.ptc10` | SRS PTC10 thermal controller | `hscal_hkettherm`, `hscal_yjettherm` | +| `hscal/srs_thermal` | `srs.ptc10` | SRS PTC10 thermal controller | `hscal_hkettherm`, `hscal_yjettherm` | | `generic/inficon` | `inficon.inficonvgc502` | Inficon VGC502 vacuum gauge controller | `hsfei_atcpress` | | `hsfei/pi-daemon` | `pi.PIControllerBase` | PI C-663 / C-863 / E-754 motion controllers | `hsfei_atcl`, `hsfei_atcp`, `hsfei_feipo`, `hsfei_lsm`, `hsfei_ms` | | `hsfei/adc` | `newport.smc100pp` | Newport SMC100PP ADC prism rotators | `hsfei_adc` | @@ -170,10 +170,10 @@ aggregates likewise and one write closes every loop. A single-stage config registers the bare names directly, so a client that only cares about the mechanism as a whole uses the same keyword either way. -**Thermal daemons** (`lakeshore`, `srsthermal`) generate their keywords from +**Thermal daemons** (`lakeshore`, `srs_thermal`) generate their keywords from config. Each entry in `sensors:` produces one read-only temperature keyword under the name the config chose; each entry in `heaters:` produces a writable -setpoint plus, for the Lakeshore, `_output`, `_status` and one +setpoint and `_output` plus, for the Lakeshore, `_status` and one keyword per PID term. **`atccryo`** exposes cryocooler state — `cold_head_temp`, `reject_temp`, diff --git a/docs/operations/systemd.md b/docs/operations/systemd.md index 2a9a889..c068fdd 100644 --- a/docs/operations/systemd.md +++ b/docs/operations/systemd.md @@ -432,8 +432,8 @@ dependencies or `hispec@.service` changed. `/opt/hispec` belongs to | `hscal_gcellheater1` | `hispecserver` | `generic/lakeshore` | `config/hscal/hscal_gcellheater1.yaml` | | `hscal_gcellheater2` | `hispecserver` | `generic/lakeshore` | `config/hscal/hscal_gcellheater2.yaml` | | `hsfei_atctherm` | `hispecserver` | `generic/lakeshore` | `config/hsfei/hsfei_atctherm.yaml` | -| `hscal_hkettherm` | `hispecserver` | `generic/srsthermal` | `config/hscal/hscal_hkettherm.yaml` | -| `hscal_yjettherm` | `hispecserver` | `generic/srsthermal` | `config/hscal/hscal_yjettherm.yaml` | +| `hscal_hkettherm` | `hispecserver` | `hscal/srs_thermal` | `config/hscal/hscal_hkettherm.yaml` | +| `hscal_yjettherm` | `hispecserver` | `hscal/srs_thermal` | `config/hscal/hscal_yjettherm.yaml` | | `hscal_hketatten` | `hispecserver` | `hscal/smc8_attenuator` | `config/hscal/hscal_hketatten.yaml` | | `hsfei_adc` | `hispecserver` | `hsfei/adc` | `config/hsfei/hsfei_adc.yaml` | | `hsfei_atccryo` | `hispecserver` | `hsfei/atccryo` | `config/hsfei/hsfei_atccryo.yaml` | diff --git a/src/hispec/driver/srs b/src/hispec/driver/srs index 565e833..de51351 160000 --- a/src/hispec/driver/srs +++ b/src/hispec/driver/srs @@ -1 +1 @@ -Subproject commit 565e8333b8e526b85131776ee23f6feefa058c89 +Subproject commit de51351fce764fa82b9b9e4fec59debcd4aafcec diff --git a/systemd/instances/hscal_hkettherm.env b/systemd/instances/hscal_hkettherm.env index e62be57..21054ad 100644 --- a/systemd/instances/hscal_hkettherm.env +++ b/systemd/instances/hscal_hkettherm.env @@ -5,5 +5,5 @@ # copies this file to /etc/hispec/instances/hscal_hkettherm.env and # config/hscal/hscal_hkettherm.yaml to /etc/hispec/hscal_hkettherm.yaml, then # enables and starts the daemon. -HISPEC_DAEMON=generic/srsthermal +HISPEC_DAEMON=hscal/srs_thermal HISPEC_HOST=hispecserver diff --git a/systemd/instances/hscal_yjettherm.env b/systemd/instances/hscal_yjettherm.env index 20e4bf3..f1addea 100644 --- a/systemd/instances/hscal_yjettherm.env +++ b/systemd/instances/hscal_yjettherm.env @@ -5,5 +5,5 @@ # copies this file to /etc/hispec/instances/hscal_yjettherm.env and # config/hscal/hscal_yjettherm.yaml to /etc/hispec/hscal_yjettherm.yaml, then # enables and starts the daemon. -HISPEC_DAEMON=generic/srsthermal +HISPEC_DAEMON=hscal/srs_thermal HISPEC_HOST=hispecserver