diff --git a/.gitmodules b/.gitmodules index 17056d9..215c114 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,15 +1,15 @@ [submodule "src/driver/sunpower"] path = src/driver/sunpower - url = git@github.com:COO-Utilities/sunpower.git + url = https://github.com/COO-Utilities/sunpower.git [submodule "src/driver/coo_ethercat"] path = src/driver/coo_ethercat - url = git@github.com:CaltechOpticalObservatories/coo-ethercat.git + url = https://github.com/CaltechOpticalObservatories/coo-ethercat.git [submodule "src/driver/lris2_csu"] path = src/driver/lris2_csu - url = git@github.com:CaltechOpticalObservatories/lris2-csu.git + url = https://github.com/CaltechOpticalObservatories/lris2-csu.git [submodule "src/driver/inficon"] path = src/driver/inficon - url = git@github.com:COO-Utilities/inficon.git + url = https://github.com/COO-Utilities/inficon.git [submodule "src/driver/pdu"] path = src/driver/pdu - url = git@github.com:COO-Utilities/pdu.git + url = https://github.com/COO-Utilities/pdu.git diff --git a/README.md b/README.md index 3a03d56..a79cb7c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ LRIS2 Instrument Control Software ## Structure -- `daemons/` – mKTL service daemons +- `daemons/` – libby hardware daemons, one script per device type +- `config/` – one YAML per daemon instance, named for its `peer_id` +- `systemd/` – per-instance env files and the host setup script - `gui/` – Graphical interfaces (`slitmaskgui`, `demo`) - `scripts/` – AIT and utility scripts - `src/driver/` – Hardware driver submodules @@ -36,9 +38,12 @@ in-process rather than over a socket: ```bash pip install -e ".[camera]" pip install # builds the camera_interface module -daemons/camera -c config/lris2_rcam.yaml +daemons/camera -c config/r/rcam.yaml ``` +On a deployed host it runs as a service and both checkouts live under +`/opt/lris2`; see [`systemd/README.md`](systemd/README.md). + Reading does not need the detector powered. Opening the link is enough: ```bash @@ -51,7 +56,7 @@ configuration camerad parsed; a write patches a single configuration line and the line numbers have to agree. The controller also refuses to apply a bias change while the detector is unpowered. -`config/lris2_rcam.yaml` names every channel that gets a keyword, and carries +`config/r/rcam.yaml` names every channel that gets a keyword, and carries the voltage limits for the four module 4 rails that have them. ## Submodules @@ -84,6 +89,26 @@ git add .gitmodules . git commit -m "Update submodules to latest on main" ``` +## Running the daemons + +Each daemon takes its config with `-c` and serves keywords over libby +(RabbitMQ by default), addressed as `lris2..`: + +```bash +daemons/vacuum-gauge -c config/lris2_rpress.yaml +libby show lris2.rpress.pressure1 +``` + +The `libby` CLI reads `~/.libby/cli_config.yaml`; on the instrument host it +needs only: + +```yaml +transport: rabbitmq +rabbitmq_url: amqp://localhost +``` + +For running daemons as services, see [`systemd/README.md`](systemd/README.md). + ## Testing Tests mirror the `src/` layout under `tests/`. diff --git a/config/common/keygrabber.yaml b/config/common/keygrabber.yaml new file mode 100644 index 0000000..d962b27 --- /dev/null +++ b/config/common/keygrabber.yaml @@ -0,0 +1,40 @@ +# Keyword archiver: polls the LRIS2 daemons and writes to InfluxDB for Grafana +# +# Usage: +# daemons/keygrabber -c config/lris2_keygrabber.yaml +# +# The InfluxDB token comes from the environment variable named in token_env. + +peer_id: keygrabber +group_id: lris2 + +sink: + type: influxdb + url: http://meridian.caltech.edu:8086 + org: coo + bucket: lris2 + token_env: LRIS2_INFLUX_TOKEN + +# Reads run concurrently across peers; a single daemon still answers one at a time +workers: 4 + +defaults: + interval_s: 30.0 + timeout_s: 2.0 + refresh_s: 300.0 + +# "%" takes everything readable except uptime and lasterror, which libby excludes +collections: + + # Vacuum and cryo: slow-moving, the trend over a cooldown is the point + rpress: + peer: lris2.rpress + keywords: ["%"] + interval_s: 60.0 + rcryo: + peer: lris2.rcryo + keywords: ["%"] + interval_s: 60.0 + +logging: + level: INFO diff --git a/config/lris2_rcam.yaml b/config/r/rcam.yaml similarity index 97% rename from config/lris2_rcam.yaml rename to config/r/rcam.yaml index 9bcb19f..1a379bd 100644 --- a/config/lris2_rcam.yaml +++ b/config/r/rcam.yaml @@ -1,7 +1,7 @@ # RCam - Archon detector controller, Red channel # # Usage: -# daemons/camera -c config/lris2_rcam.yaml +# daemons/camera -c config/r/rcam.yaml # # Addressed as lris2.rcam (group_id.peer_id), e.g. # libby show lris2.rcam.xvbiasbackbias @@ -14,7 +14,7 @@ group_id: lris2 description: LRIS2 Red channel Archon controller camera: - config_file: /usr/local/share/lris2/lris2.cfg + config_file: /opt/lris2/camerad/config/lris2.cfg nframes: 1 # Every labelled channel the three bias boards carry, taken from "bias list" diff --git a/config/lris2_cryocooler.yaml b/config/r/rcryo.yaml similarity index 96% rename from config/lris2_cryocooler.yaml rename to config/r/rcryo.yaml index 51fdd0d..8a88208 100644 --- a/config/lris2_cryocooler.yaml +++ b/config/r/rcryo.yaml @@ -2,7 +2,7 @@ # Usage: # daemons/cryocooler -c config/lris2_cryocooler.yaml -peer_id: lris2_cryocooler +peer_id: rcryo group_id: lris2 hardware: diff --git a/config/lris2_vacuum.yaml b/config/r/rpress.yaml similarity index 92% rename from config/lris2_vacuum.yaml rename to config/r/rpress.yaml index 3cb52cd..fc8b821 100644 --- a/config/lris2_vacuum.yaml +++ b/config/r/rpress.yaml @@ -2,7 +2,7 @@ # Usage: # daemons/vacuum-gauge -c config/lris2_vacuum.yaml -peer_id: lris2_vacuum +peer_id: rpress group_id: lris2 hardware: diff --git a/daemons/keygrabber b/daemons/keygrabber new file mode 100755 index 0000000..0690c47 --- /dev/null +++ b/daemons/keygrabber @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +"""Keyword archiver: polls the LRIS2 daemons into InfluxDB for Grafana. + +The daemon lives in libby; this shim lets the systemd template launch it like +any other LRIS2 daemon. +""" + +from libby.keygrabber.main import main + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/pyproject.toml b/pyproject.toml index 0e7b681..8c22319 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,10 +13,18 @@ authors = [ license = {text = "MIT"} readme = "README.md" dependencies = [ + "instrumentctl@git+https://github.com/CaltechOpticalObservatories/instrumentctl.git@v0.1.0", + "libby@git+https://github.com/CaltechOpticalObservatories/libby.git", + # The driver submodules are vendored rather than installed, so what they + # import has to be declared here. The pin is the one they ask for + "hardware_device_base@git+https://github.com/COO-Utilities/hardware_device_base@v1.0.0", "pyserial", "pyyaml", ] +[project.scripts] +lris2 = "lris2.cli:main" + [project.optional-dependencies] dev = [ "pytest", @@ -32,7 +40,10 @@ camera = [ lris2 = "src" [tool.setuptools] -packages = ["lris2", "lris2.driver"] +packages = ["lris2", "lris2.cli", "lris2.driver"] + +[tool.setuptools.package-data] +"lris2.cli" = ["instrument.toml"] [tool.pytest.ini_options] testpaths = ["tests"] diff --git a/src/cli/__init__.py b/src/cli/__init__.py new file mode 100644 index 0000000..721be22 --- /dev/null +++ b/src/cli/__init__.py @@ -0,0 +1,20 @@ +"""``lris2``: deploy, run and inspect the LRIS2 daemons on this host. + +Every command comes from instrumentctl, which is shared with the other +instruments. All that is specific to LRIS2 is ``instrument.toml`` beside this +file. +""" + +from importlib import resources +from pathlib import Path +from typing import List, Optional + +from instrumentctl import run + + +def main(argv: Optional[List[str]] = None) -> int: + """Entry point for the ``lris2`` console script.""" + config = resources.files(__package__).joinpath("instrument.toml") + # Locates the checkout under an editable install, which is what lets the + # deployed CLI follow a git pull + return run(config, repo_hint=Path(__file__).resolve().parents[2], argv=argv) diff --git a/src/cli/instrument.toml b/src/cli/instrument.toml new file mode 100644 index 0000000..466ca69 --- /dev/null +++ b/src/cli/instrument.toml @@ -0,0 +1,9 @@ +[instrument] +name = "lris2" +display_name = "LRIS2" +ops_group = "lris2-ops" +env_prefix = "LRIS2" + +[help] +example_instance = "rpress" +example_subsystem = "r" diff --git a/systemd/README.md b/systemd/README.md new file mode 100644 index 0000000..a8525e3 --- /dev/null +++ b/systemd/README.md @@ -0,0 +1,96 @@ +# Running daemons under systemd + +Operated with the `lris2` command, which comes from +[instrumentctl](https://github.com/CaltechOpticalObservatories/instrumentctl) +and is shared with the other instruments. All that is specific to LRIS2 is +`src/cli/instrument.toml`. + +One template unit, `lris2@.service`, runs any `daemons/