Use the Launch Framework for CLI Applications#
This guide shows how to use launch() to create deployable FastCS drivers with
automatic CLI generation and YAML configuration.
Basic Setup#
The launch() function generates a CLI from the controller’s type hints:
from fastcs.controllers import Controller
from fastcs.launch import launch
class MyController(Controller):
pass
if __name__ == "__main__":
launch(MyController)
This creates a CLI with:
--version- Display version informationschema- Output JSON schema for configurationrun <config.yaml>- Start the controller with a YAML config file
Adding Configuration Options#
It is recommended to use a dataclass or Pydantic model for the controller’s
configuration, as these provide schema generation and IDE support. The launch()
function checks that __init__ has at most one argument (besides self) and that the
argument has a type hint, which is required to infer the schema:
from dataclasses import dataclass
from fastcs.controllers import Controller
from fastcs.launch import launch
@dataclass
class DeviceSettings:
ip_address: str
port: int = 25565
timeout: float = 5.0
class DeviceController(Controller):
def __init__(self, settings: DeviceSettings):
super().__init__()
self.settings = settings
if __name__ == "__main__":
launch(DeviceController, version="1.0.0")
YAML Configuration Files#
Create a YAML configuration file matching the schema:
# device_config.yaml
controller:
ip_address: "192.168.1.100"
port: 25565
timeout: 10.0
transport:
- epicsca:
pv_prefix: "DEVICE"
Run with:
python my_driver.py run device_config.yaml
Schema Generation#
Generate JSON schema for the configuration yaml:
python my_driver.py schema > schema.json
Use this schema for IDE autocompletion in YAML files:
# yaml-language-server: $schema=schema.json
controller:
ip_address: "192.168.1.100"
# ... IDE will provide autocompletion
Transport Configuration#
Transports are configured in the transport section as a list:
transport:
# EPICS Channel Access
- epicsca:
pv_prefix: "DEVICE"
gui:
output_path: "opis/device.bob"
title: "Device Control"
# REST API
- rest:
host: "0.0.0.0"
port: 8080
# GraphQL
- graphql:
host: "localhost"
port: 8081
Logging Options#
The run command includes logging options:
# Set log level
python my_driver.py run config.yaml --log-level debug
# Send logs to Graylog
python my_driver.py run config.yaml \
--graylog-endpoint "graylog.example.com:12201" \
--graylog-static-fields "app=my_driver,env=prod"
Available log levels: TRACE, DEBUG, INFO, WARNING, ERROR, CRITICAL
Version Information#
Pass a version string to display the driver version:
launch(DeviceController, version="1.2.3")
$ python my_driver.py --version
DeviceController: 1.2.3
FastCS: 0.12.0
Constraints#
The launch() function requires:
Controller
__init__must have at most 2 arguments (includingself)If a configuration argument exists, it must have a type hint
Using a dataclass or Pydantic model is recommended for the configuration type, as it enables JSON schema generation. Other type-hinted types will work, but will not produce a useful schema.
# Valid - no config
class SimpleController(Controller):
def __init__(self):
super().__init__()
# Valid - with config
class ConfiguredController(Controller):
def __init__(self, settings: MySettings):
super().__init__()
# Invalid - missing type hint
class BadController(Controller):
def __init__(self, settings): # Error: no type hint
super().__init__()
# Invalid - too many arguments
class TooManyArgs(Controller):
def __init__(self, settings: MySettings, extra: str): # Error
super().__init__()