Parameter System
The parameter system is the metadata and validation layer used by device classes in PlestyLib.
It defines what can be queried or written, validates user input, stores cached values, and supports typed parsing of device responses.
Core implementation: ConfigSystem in plesty.lib.device.params.
What It Does
ConfigSystem provides:
Parameter registration through code (
register_config) or schema (register_params_from_schema).Optional grouping of parameters (
register_config_group).Validation for type, range, and categorical options.
Read/write permission flags (
read_only,write_only).Current value cache per parameter (
value).Automatic response casting through
AutoResponseParser.Response parsers named from the schema (
parser), including a device’s own.Human-readable summaries (
param_summary).
Main Data Structures
ConfigParameter
Represents one parameter, including:
namedtypedefaultand currentvalueunitread_only,write_onlymin_value,max_valueoptionscommand(string orCommandobject)parser(optional custom response parser)description
ConfigGroup
Represents a parameter group with:
Group name
Group parameter dictionary
Optional command prefix
Optional list of group-level allowed commands
Group description
Command
Command metadata object used in grouped schemas:
namecommand# some devices may provide command to query or set parameters in this grouprequired_params# defines required parameters for the group commandoptional_params# defines optional paramters for the group commandmode(writeorquery)description
Register Parameters in Code
from plesty.lib.device.base_visa_scpi_device import BaseVisaScpiDevice
class PowermeterDevice(BaseVisaScpiDevice):
def __init__(self, address: str):
super().__init__(address)
self.register_config("POWER", dtype=float, unit="watt", read_only=True, command="MEAS:SCAL:POW")
self.register_config("WAVELENGTH", dtype=int, unit="nm", min_value=400, max_value=1100, command="SENS:CORR:WAV")
self.register_config("AUTO_RANGE", dtype=bool, default=True, command="SENS:POW:RANG:AUTO")
Register Parameters from Schema
register_params_from_schema supports loading from a JSON file in the following shape:
{
"ROSCillatorFrequency": {
"type": "float",
"command": "SOUR:ROSC:FREQ",
"description": "Set or query the frequency of the internal reference oscillator."
},
"SyncClockoutValue": {
"type": "integer",
"command": "SYNC:VAL",
"read_only": true
}
}
Load schema from file path or dictionary:
from plesty.lib.device.params import ConfigSystem
cfg = ConfigSystem(param_schema="param_schema.json")
# or
# cfg = ConfigSystem(param_schema=schema_dict)
Load schema directly in a base-device constructor (BaseDeviceSyncModel):
from plesty.lib.device.base_device_sync import BaseDeviceSyncModel
class MyDevice(BaseDeviceSyncModel):
def __init__(self, id: str, param_schema):
super().__init__(id=id, param_schema=param_schema)
def connect(self):
...
def disconnect(self):
...
def check_errors(self) -> list[str]:
return []
def check_operatability(self) -> bool:
return True
def _write_(self, key, value) -> bool:
return True
def _query_(self, key) -> str:
return "0"
dev_a = MyDevice(id="dev-1", param_schema="param_schema.json")
# or
# dev_b = MyDevice(id="dev-2", param_schema=schema_dict)
Validation Rules
Validation happens at registration and at write-time checks.
Type checks: values must match
dtype.Numeric range checks:
min_value <= value <= max_value.Option checks: categorical values must be in
options.Permission checks: cannot write
read_onlyparameters.
Important behavior:
Registration errors raise exceptions.
check_write_configraises on invalid values.set_config_valueattempts auto-casting before range/options validation.
Query/Write Flow Integration
In synchronized devices, high-level methods in BaseDeviceSyncModel use ConfigSystem as follows:
write(key, value)check_write_config(key, value)validates the requestDevice-specific
_write_sends command to hardwareOn success,
set_config_valueupdates cached value
For queries:
query(key)check_query_config(key)validates accessDevice-specific
_query_gets raw responseresponse_to_target_typeparses/casts responseset_config_valuestores parsed value
Response Parsing
By default, AutoResponseParser tries to cast response text to the parameter dtype.
Examples:
"42" -> int"3.14" -> float"true"/"1" -> bool(via basic cast helper)Delimited strings for typed list/tuple annotations
A reply the auto parser cannot cast is returned unchanged. When the value has to
be pulled out of the reply first, name a parser in the parameter’s parser key.
Naming a parser from a schema
parser takes a kind name, or a mapping of a kind and its configuration:
{
"wavelength": {
"type": "float",
"unit": "nm",
"command": "?NM",
"parser": {"kind": "regex", "pattern": "(-?\\d+\\.\\d+) nm"}
}
}
The kind is resolved to a ResponseParser when the parameter is registered, so a
misspelled kind, an invalid pattern or an out-of-range capture group fails while
the schema is being loaded rather than on the first query.
Built-in kinds:
kind |
configuration |
reply → value |
|---|---|---|
|
— |
|
|
|
|
|
|
|
Both parsers hand their result to AutoResponseParser, so the parameter’s type
still performs the conversion — the pattern or the separator only decides which
characters are the value. A bytes reply is decoded with encoding (default
utf-8) first.
Use delimited when the reply carries several fields and the parameter is one of
them. When the parameter’s dtype is itself a list or tuple, auto already splits
the reply and no parser is needed.
Registering a device’s own parser
Binary frames and replies keyed by a mnemonic are the device’s own business. The module registers a parser once and every parameter then names it from the schema:
from plesty.lib.device.device_utils import ResponseParser
from plesty.lib.device.params import register_parser_kind
class AptStatusParser(ResponseParser):
def __init__(self, field: str):
self.field = field
def parse(self, response, param=None, **kwargs):
return decode_apt_status(response)[self.field]
register_parser_kind("apt_status", AptStatusParser)
{
"position": {
"type": "float",
"command": "MGMSG_MOT_REQ_STATUSUPDATE",
"parser": {"kind": "apt_status", "field": "position"}
}
}
Schema keys other than kind are passed to the factory as keyword arguments.
Registering the same name twice with the same factory is fine; a different
factory under a name already in use raises. parser_kinds() lists what is
currently nameable.
Passing a ResponseParser instance directly to register_config(parser=...)
from Python keeps working unchanged.
Useful API Methods
register_config(...)register_config_group(...)register_params_from_schema(...)get_config(key, group="default")get_config_list(group="default")get_group_list()set_config_value(key, value)get_config_value(key)set_config_min_max(key, min_value, max_value)param_summary(constraints=True, description=False)register_parser_kind(name, factory)/parser_kinds()(module-level)
Minimal End-to-End Example
from plesty.lib.device.params import ConfigSystem
system = ConfigSystem()
system.register_config(
key="GAIN",
dtype=int,
min_value=1,
max_value=10,
default=5,
command="CONF:GAIN",
)
system.check_write_config("GAIN", 7)
system.set_config_value("GAIN", "7") # auto-cast to int
print(system.get_config_value("GAIN"))
print(system.param_summary())
Best Practices
Always set
dtypefor robust validation and auto-casting.Prefer explicit
min_valueandmax_valuefor numeric parameters.Use
optionsfor categorical values.Keep command strings in parameter metadata so solver logic stays generic.
Use schema-driven registration for larger devices.
Include
descriptionandunitfields to improve generated summaries and docs.