Skip to main content

PANOSETI gRPC CI PyPI Version

PANOSETI gRPC Services

This repository contains the microservice architecture for the PANOSETI observatory. It provides gRPC interfaces for real-time data access, observatory control, and general telemetry logging. See here for the main software repo.

Service Directory

Each service operates independently. Click the links below for detailed API documentation and configuration guides.

Service Description Status Documentation
DAQ Data Streams real-time science data from Hashpipe via headnode gateway. 🟢 Production Read Docs
DAQ Control Manages Hashpipe lifecycle on DAQ nodes (start/stop/status). 🟢 Production Read Docs
Telemetry Device status → Redis/InfluxDB; log shipping → Grafana Alloy → Loki. 🟡 Beta Read Docs
ML Inference Real-time cloud-detection scores from the streaming pipeline; pub-sub for predictions and alerts. 🟡 Beta Read Docs
U-blox Control Controls and configures GNSS chips (F9T/F9P). 🔴 Deprecated Read Docs

📜 Changelog

Keep track of the latest changes, modernization efforts, and breaking changes in our Changelog.


🖥️ Installation (Server Mode)

If you're deploying the unified gRPC server (pseti-grpc server) on the head node or a DAQ node, install it as a standalone CLI tool with uv — this puts pseti-grpc on your PATH, isolated in its own environment, without needing a full dev setup:

uv tool install panoseti-grpc

See the "🚦 Unified Server" section below for how to configure and run it.


📦 Installation (Client Mode)

If you only need to write scripts to control the observatory or analyze data, install the package from PyPI:

pip install panoseti-grpc

Example Usage:

# Stream real-time science images from the headnode gateway
import asyncio
from panoseti_grpc.daq_data.client import AioDaqDataClient

async def main():
    async with AioDaqDataClient(host="headnode", port=50051) as client:
        async for image in client.stream_images(stream_movie_data=True):
            print(f"Module {image['module_id']}  {image['type']}")

asyncio.run(main())
# Upload device telemetry
from panoseti_grpc.telemetry.client import TelemetryClient
client = TelemetryClient("headnode", 50051)
client.log_flexible("DEV_weather", "station-01", {"status": "Online", "is-raining": True})

🛠️ Development & Contribution

Environment Setup

If you are deploying the servers on the head node or contributing to the codebase, we recommend installing miniconda (link), then following these steps to setup your environment:

# 0. Clone this repo and go to the repo root
git clone https://github.com/panoseti/panoseti_grpc.git
cd panoseti_grpc

# Option 1. Install with pip
conda create -n grpc-py314 python=3.14
conda activate grpc-py314
pip install -e ".[dev]"

# Option 2. Install with uv
uv tool install -e .

🚦 Unified Server

All three active services (daq_data, daq_control, telemetry) can run on a single port under a unified process. gRPC routes RPCs by proto package name automatically — no collision between services.

Quick Start

# See the options
pseti-grpc -h

# Copy the packaged server config templates (server*.toml) and a .env
# template to the current directory, to customize
pseti-grpc --config-template
pseti-grpc --env-template

# Simplest single-node setup: set PSETI_GRPC_PORT (and PSETI_GRPC_HOST for
# clients not on the same host) once -- in a .env file or the environment --
# and `pseti-grpc server` plus every client command (stat, reflect,
# telemetry, daq-data, daq-control, ...) agree on the same host:port with
# no flags needed.
pseti-grpc server --profile default

# DAQ node: daq_data (edge role) + daq_control
pseti-grpc server --profile daq_node

# Head node: telemetry + daq_data (gateway role, fans in every edge node)
pseti-grpc server --profile headnode

# Custom config file
pseti-grpc server --config /etc/panoseti/server.toml

# List all registered services
pseti-grpc server --list-services

# Check gRPC service health, Alloy liveness, and log disk usage
pseti-grpc daqnode --log-dir /var/log/panoseti

Deployment Profiles

Profile Services Machine
default telemetry + daq_data + daq_control Single-machine dev / test
daq_node daq_data (edge) + daq_control Each DAQ compute node
headnode telemetry + daq_data (gateway) Observatory head node
gateway telemetry + daq_data (gateway) Same shape as headnode; kept separate for sites that want a telemetry-only vs. gateway split later

None of these profiles hardcode [server].port — it resolves at startup from (highest priority first) --port (hidden, deployment/debug-only), the env var named by --port-env (also hidden — role-scoped fleet deployments, e.g. HEADNODE_GRPC_PORT/DAQNODE_GRPC_PORT), PSETI_GRPC_PORT (the simple single-node knob — read by both pseti-grpc server's own default and every pseti-grpc client command's --port default), the legacy GRPC_PORT env var, then the built-in 50051 default (PanosetiServerConfig.port / unified_main.resolve_bind_port()). An explicit port = line in a custom --config TOML always wins over every env var, so don't add one if you want the deployment's .env to control it — this is exactly how a hardcoded port = 50052 in an earlier version of the headnode profile silently desynced from every client still assuming 50051.

PSETI_GRPC_HOST is the client-side counterpart to PSETI_GRPC_PORT: the default --host for every pseti-grpc command. Both are unrelated to HEADNODE_IP/HEADNODE_GRPC_PORT (below) — see .env_example (or pseti-grpc --env-template) for the full rundown.

On DAQ nodes (telemetry = false), services configured with grpc_logging = true automatically forward logs to the head node's telemetry endpoint via the HEADNODE_IP / HEADNODE_GRPC_PORT environment variables.

Environment Configuration

pseti-grpc (and python -m panoseti_grpc) auto-loads a .env file from the current working directory at startup (or a specific file via PSETI_GRPC_ENV_FILE) — plain KEY=value lines, no export needed. Run pseti-grpc --env-template to copy the packaged .env_example to ./.env_grpc_<timestamp> as a starting point, and pseti-grpc --config-template for the bundled server*.toml profiles.

Config File Structure

Bundled profiles live in src/panoseti_grpc/config/. A custom server.toml follows this structure:

[server]
port = 50051
shutdown_grace_period = 5.0
log_dir = "/var/log/panoseti"
grpc_logging = true

[server.services]
telemetry   = true
daq_data    = true
daq_control = true

[telemetry]
redis_host = "localhost"
redis_port = 6379

[daq_data]
# ... DaqDataServerConfig fields ...

[daq_control]
log_dir = "/var/log/panoseti"

🧪 Testing

We use a comprehensive CI pipeline (GitHub Actions) to verify every commit. You can—and should—run these same tests locally before pushing code.

Unified QA Runner (Recommended)

The most efficient way to run quality checks and tests is via the unified QA runner:

# Run all linters and test suites
python tests/qa.py all

# Run specific tasks
python tests/qa.py lint
python tests/qa.py telemetry

Run CI Tests Locally via Bash Scripts

Alternatively, you can use the individual scripts in scripts/run-ci-tests/. Each service has an associated script which builds the Docker containers and runs the appropriate test suites.

Examples:

# Run DAQ Data Service tests
./scripts/run-ci-tests/run-daq-data-ci-test.sh

# Run U-blox Control Service tests
./scripts/run-ci-tests/run-ublox-control-ci-test.sh

🚀 Adding New Services

The PANOSETI gRPC architecture is designed to be extensible. New services slot into the unified server without modifying PanosetiServer itself — only server.py registration and config additions are needed.

0. Branching Strategy

Always create a new feature branch off the development branch:

git checkout dev
git checkout -b feature/daq-control-service

1. Define the Interface (.proto)

Create a new Protocol Buffer definition file in the protos/ directory. This defines the contract between your client and server.

  • File: protos/daq_control.proto
  • Example:
syntax = "proto3";
package panoseti.daq_control;

service DaqControl {
  rpc SetHighVoltage (VoltageRequest) returns (StatusResponse) {}
}

message VoltageRequest { float voltage = 1; }
message StatusResponse { bool success = 1; }

2. Compile the Protos

Run the compilation script to generate the Python gRPC code.

python scripts/compile_protos.py

This will automatically generate two files in src/panoseti_grpc/generated/:

  • daq_control_pb2.py (Message definitions)
  • daq_control_pb2_grpc.py (Client/Server stubs)

3. Create the Service Module

Create a new directory for your service source code. You must include an __init__.py file for Python to recognize it as a package.

mkdir -p src/panoseti_grpc/daq_control
touch src/panoseti_grpc/daq_control/__init__.py

4. Implement Client & Server

Develop your application logic. You can now import your generated protobuf code using the package path.

Example src/panoseti_grpc/daq_control/server.py:

import grpc
from panoseti_grpc.generated import daq_control_pb2, daq_control_pb2_grpc

class DaqControlServicer(daq_control_pb2_grpc.DaqControlServicer):
    def SetHighVoltage(self, request, context):
        print(f"Setting voltage to {request.voltage}")
        return daq_control_pb2.StatusResponse(success=True)

5. Register with the Unified Server

To make the service available via pseti-grpc server, add it to src/panoseti_grpc/server.py:

  1. Write async def _make_<name>_servicer(cfg, shutdown_event) that returns (servicer, [post_start_coros])
  2. Add <name>: NewServiceConfig = Field(default_factory=NewServiceConfig) to PanosetiServerConfig
  3. Add <name>: bool = False to ServiceToggles
  4. Call ServiceRegistry.register(ServiceDescriptor("<name>", ...)) at module level
  5. Add a [<name>] section to the relevant server*.toml profile files

6. Add CI Tests

Finally, ensure your new service is robust by adding a test suite.

  1. Create a test directory: tests/<name>/
  2. Add a BuildKit stage to the root Dockerfile.ci for your test environment.
  3. Add a runner script in scripts/run-ci-tests/run-<name>-ci-test.sh.
  4. Create unit and integration tests with pytest.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

panoseti_grpc-0.4.21.tar.gz (16.2 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

panoseti_grpc-0.4.21-py3-none-any.whl (16.3 MB view details)

Uploaded Python 3

File details

Details for the file panoseti_grpc-0.4.21.tar.gz.

File metadata

  • Download URL: panoseti_grpc-0.4.21.tar.gz
  • Upload date:
  • Size: 16.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for panoseti_grpc-0.4.21.tar.gz
Algorithm Hash digest
SHA256 3473f530cacaab15c34ebb6d70e03bb91246da116475bb17961988428b125713
MD5 0b1332517c66f8c6baa5cd5a57e5cf73
BLAKE2b-256 179bd959b05e5deb7512840bf654ac2848cafa8a9f8d6cbf2b0b496f4cb7c9ea

See more details on using hashes here.

File details

Details for the file panoseti_grpc-0.4.21-py3-none-any.whl.

File metadata

  • Download URL: panoseti_grpc-0.4.21-py3-none-any.whl
  • Upload date:
  • Size: 16.3 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for panoseti_grpc-0.4.21-py3-none-any.whl
Algorithm Hash digest
SHA256 c5065d26adb5e5fe7cca92fc70feb611e8ceef1c36498ca706a8b35e7cc47aee
MD5 2e77547d19e299de4a4ef4bf9ae7a560
BLAKE2b-256 72151d897b401e1c24b51a1cc91a907be86c486c45440c57d0571a9ba5e5da6d

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.4.21 This release

2 files

0.4.20

2 files

0.4.19

2 files

0.4.18

2 files

0.4.17

2 files

0.4.16

2 files

0.4.15

2 files

0.4.14

2 files

0.4.13

2 files

0.4.12

2 files

0.4.11

2 files

0.4.10

2 files

0.4.9

2 files

0.4.8

2 files

0.4.7

2 files

0.4.6

2 files

0.4.5

2 files

0.4.4

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.5

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.14

2 files

0.2.13

2 files

0.2.12

2 files

0.2.11

2 files

0.2.10

2 files

0.2.9

2 files

0.2.7

2 files

0.2.6

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.1.1

2 files

0.1.1

2 files

0.1.0.9

2 files

0.1.0.8

2 files

0.1.0.7

2 files

0.1.0.6

2 files

0.1.0.5

2 files

0.1.0.4

2 files

0.1.0.3

2 files

0.1.0.2

2 files

0.1.0.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page