core¶
Конфигурация, типы и исключения. Пакет без ввода-вывода — за одним исключением: инстанцирование настроек создаёт директории и открывает лог-файлы, см. Архитектуру.
Настройки¶
Сборка всех разделов настроек в единственный синглтон settings.
Настройки инстанцируются на импорте модуля: читается .env и проверяются
обязательные STATION__*. Диска импорт не трогает — рабочие директории и
файловые обработчики логов поднимает configure_runtime(), который зовут
из main().
- class core.configs.Settings(_case_sensitive=None, _nested_model_default_partial_update=None, _env_prefix=None, _env_prefix_target=None, _env_file=PosixPath('.'), _env_file_encoding=None, _env_ignore_empty=None, _env_nested_delimiter=None, _env_nested_max_split=None, _env_parse_none_str=None, _env_parse_enums=None, _cli_prog_name=None, _cli_parse_args=None, _cli_settings_source=None, _cli_parse_none_str=None, _cli_hide_none_type=None, _cli_avoid_json=None, _cli_enforce_required=None, _cli_use_class_docs_for_groups=None, _cli_show_env_vars=None, _cli_exit_on_error=None, _cli_prefix=None, _cli_flag_prefix_char=None, _cli_implicit_flags=None, _cli_ignore_unknown_args=None, _cli_kebab_case=None, _cli_shortcuts=None, _secrets_dir=None, _build_sources=None, *, antenna=AntennaSettings(rotator=RotatorSettings(ENABLED=False, MODEL='ROT_MODEL_NETROTCTL', BAUD=9600, PORT='rotctld:4533', THRESHOLD=4.0, MODE=<RotatorMode.NORMAL: 'Normal'>, MIN_ELEVATION=0.0), rig=RigSettings(IP='rigctld', PORT=4532)), url=URLSettings(BASE='https://sonik.space/api/', JOBS_PATH='jobs/', OBSERVATIONS_PATH='observations/', BUNDLES_PATH='v2/bundles/', STATIONS_PATH='v2/stations/'), api=APISettings(TIMEOUT_IN_SECONDS_REQUEST_DATA=45, TIMEOUT_IN_SECONDS_SEND_DATA=120), flowgraph=FlowgraphSettings(RX_SAMP_RATE=None, RX_BANDWIDTH=None, DOPPLER_CORR_PER_SEC=None, LO_OFFSET=None, LO_TRANSVERTER=None, PPM_ERROR=0, GAIN_MODE='Overall', RF_GAIN=None, ANTENNA='RX', DEV_ARGS=None, STREAM_ARGS=None, TUNE_ARGS=None, OTHER_SETTINGS=None, DC_REMOVAL=None, BB_FREQ=None, ENABLE_IQ_DUMP=False, IQ_DUMP_FILENAME=None, UDP_DUMP_HOST='127.0.0.1', UDP_DUMP_PORT=57356, FLOWGRAPH_DISPATCHER='flowgraph_dispatcher', DEFAULT_MODE='FM'), health=HealthSettings(PORT=8080), log=LoggingSettings(FORMAT='%(asctime)s - %(name)s - %(levelname)s - %(message)s', LEVEL='INFO', SCRIPT_LEVEL='INFO', FLOWGRAPH_LEVEL='INFO', DIRECTORY='/var/lib/soniks-client/logs', FILE_NAME='client.log', MAX_BYTES=5242880, BACKUP_COUNT=5, ENCODING='utf-8'), mqtt=MqttSettings(ENABLED=True, URL='wss://sonik.space/mqtt'), observation=ObservationSettings(REMOVE_WATERFALL_RAW_FILES=True, REMOVE_OBSERVATION_DATA=True, SOAPY_RX_DEVICE='driver=airspy,biastee=true', TIME_FORMAT_IN_FILENAME='%Y-%m-%dT%H-%M-%S', FILENAME_TEMPLATE='{observation_dir}/{prefix}_{observation_id}_{timestamp}.{extension}', RIG_UPDATE_INTERVAL=0.1, ROTATOR_UPDATE_INTERVAL=3, PRE_OBSERVATION_SCRIPT='satnogs-pre', POST_OBSERVATION_SCRIPT='satnogs-post', RUN_PRE_OBSERVATION_SCRIPT=True, RUN_POST_OBSERVATION_SCRIPT=True, SCRIPT_TIMEOUT_IN_SECONDS=900, BATCH_DELAY=1, prefix=ObservationPrefix(PAYLOAD='payload', WATERFALL='waterfall', RAW_WATERFALL='raw_waterfall', DATA='data', METADATA='metadata'), extension=ObservationExtension(PAYLOAD='ogg', WATERFALL='png', DATA_APT='png', METADATA='json', RAW_WATERFALL='dat'), sending_key=ObservationSendingKey(PAYLOAD='payload', WATERFALL='waterfall', DEMODDATA='demoddata', CLIENT_METADATA='client_metadata'), upload_types=ObservationUploadTypes(PAYLOAD=True, WATERFALL=True)), paths=PathSettings(BASE='/tmp/.soniks', OUTPUT_DIR='output', COMPLETE_DIR='complete', INCOMPLETE_DIR='incomplete', STATE_FILE='/var/lib/soniks-client/desired-state.json'), station, jobs=JobSettings(PREFIX='job_'), waterfall=WaterfallSettings(AUTORANGE=False, THRESHOLD=-200, MIN_VALID_SAMPLES=10000, DEFAULT_MIN_VALUE=-110, DEFAULT_MAX_VALUE=-50, MIN_VALUE=-110, MAX_VALUE=-50, WIDTH=8.32, HEIGHT=16.03, HELP_LINES=False, GRIDSPEC={'nrows': 1, 'ncols': 2, 'width_ratios': [0.93, 0.03], 'left': 0.09, 'right': 0.91, 'top': 0.995, 'bottom': 0.03}, TIMESTAMP_FORMAT='%Y-%m-%dT%H:%M:%S.%fZ', TIME_FORMAT='%H:%M', INTERVAL_IN_MINUTES=1), scheduler=SchedulerSettings(MAX_WORKERS=30, COALESCE=True, MAX_INSTANCES=1, MISFIRE_GRACE_TIME_IN_SECOND=30, JOB_REQUEST_INTERVAL_IN_MINUTES=1, RESENDING_INTERVAL_IN_MINUTES=20, SAT_DATA_SYNC_INTERVAL_IN_HOURS=6, LOG_LEVEL='WARNING'), sdr=SdrSettings(SCAN_INTERVAL_IN_MINUTES=60, IDLE_WINDOW_IN_MINUTES=3, GAIN_STEP_DB=2, NOISE_LIFT_DB=10, CALIBRATION_SAMPLES=65536))[исходный код]¶
Базовые классы:
BaseSettingsКорневая модель настроек. Разделы адресуются через
__в именах переменных окружения:STATION__TOKEN,ANTENNA__ROTATOR__MODE.- Параметры:
_case_sensitive (bool | None)
_nested_model_default_partial_update (bool | None)
_env_prefix (str | None)
_env_prefix_target (EnvPrefixTarget | None)
_env_file (DotenvType | None)
_env_file_encoding (str | None)
_env_ignore_empty (bool | None)
_env_nested_delimiter (str | None)
_env_nested_max_split (int | None)
_env_parse_none_str (str | None)
_env_parse_enums (bool | None)
_cli_prog_name (str | None)
_cli_parse_args (bool | list[str] | tuple[str, ...] | None)
_cli_settings_source (CliSettingsSource[Any] | None)
_cli_parse_none_str (str | None)
_cli_hide_none_type (bool | None)
_cli_avoid_json (bool | None)
_cli_enforce_required (bool | None)
_cli_use_class_docs_for_groups (bool | None)
_cli_show_env_vars (bool | None)
_cli_exit_on_error (bool | None)
_cli_prefix (str | None)
_cli_flag_prefix_char (str | None)
_cli_implicit_flags (bool | Literal['dual', 'toggle'] | None)
_cli_ignore_unknown_args (bool | None)
_cli_kebab_case (bool | Literal['all', 'no_enums'] | None)
_cli_shortcuts (Mapping[str, str | list[str]] | None)
_secrets_dir (PathType | None)
_build_sources (tuple[tuple[PydanticBaseSettingsSource, ...], dict[str, Any]] | None)
antenna (AntennaSettings)
url (URLSettings)
api (APISettings)
flowgraph (FlowgraphSettings)
health (HealthSettings)
log (LoggingSettings)
mqtt (MqttSettings)
observation (ObservationSettings)
paths (PathSettings)
station (StationSettings)
jobs (JobSettings)
waterfall (WaterfallSettings)
scheduler (SchedulerSettings)
sdr (SdrSettings)
- model_config = {'arbitrary_types_allowed': True, 'case_sensitive': False, 'cli_avoid_json': False, 'cli_enforce_required': False, 'cli_exit_on_error': True, 'cli_flag_prefix_char': '-', 'cli_hide_none_type': False, 'cli_ignore_unknown_args': False, 'cli_implicit_flags': False, 'cli_kebab_case': False, 'cli_parse_args': None, 'cli_parse_none_str': None, 'cli_prefix': '', 'cli_prog_name': None, 'cli_shortcuts': None, 'cli_show_env_vars': False, 'cli_use_class_docs_for_groups': False, 'enable_decoding': True, 'env_file': '.env', 'env_file_encoding': None, 'env_ignore_empty': False, 'env_nested_delimiter': '__', 'env_nested_max_split': None, 'env_parse_enums': None, 'env_parse_none_str': None, 'env_prefix': '', 'env_prefix_target': 'variable', 'extra': 'forbid', 'json_file': None, 'json_file_encoding': None, 'nested_model_default_partial_update': False, 'protected_namespaces': ('model_validate', 'model_dump', 'settings_customise_sources'), 'secrets_dir': None, 'toml_file': None, 'validate_default': True, 'yaml_config_section': None, 'yaml_file': None, 'yaml_file_encoding': None}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- antenna: AntennaSettings¶
- url: URLSettings¶
- api: APISettings¶
- flowgraph: FlowgraphSettings¶
- health: HealthSettings¶
- log: LoggingSettings¶
- mqtt: MqttSettings¶
- observation: ObservationSettings¶
- paths: PathSettings¶
- station: StationSettings¶
- jobs: JobSettings¶
- waterfall: WaterfallSettings¶
- scheduler: SchedulerSettings¶
- sdr: SdrSettings¶
- core.configs.configure_runtime()[исходный код]¶
Поднять то, что трогает диск: обработчики логов и рабочие директории.
Вызывается один раз при старте приложения. Вынесено из импорта, чтобы
importчего угодно изsrc/не создавал каталоги и файл лога.- Тип результата:
None
Разделы настроек¶
Параметры станции — единственный обязательный раздел настроек.
- class core.configs.station.StationSettings(*, TOKEN=None, ID=None, LATITUDE=None, LONGITUDE=None, ELEVATION=None)[исходный код]¶
Параметры станции.
Обязательны
TOKENиID— без них клиент не стартует. Координаты и высота необязательны: если их нет в.env, они берутся с портала вместе с конфигурацией станции (remote_config). Заданные в.envимеют приоритет и, как раньше, уходят на портал в запросе расписания.- Параметры:
TOKEN (str | None)
ID (int | None)
LATITUDE (float | None)
LONGITUDE (float | None)
ELEVATION (int | None)
- TOKEN: str | None¶
- ID: int | None¶
- LATITUDE: float | None¶
- LONGITUDE: float | None¶
- ELEVATION: int | None¶
- check_required_fields()[исходный код]¶
Проверить, что заполнены
STATION__TOKENиSTATION__ID.- Исключение:
ValueError – Со списком незаполненных параметров.
- Тип результата:
Self
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Настройки поворотного устройства и приёмного тракта.
- class core.configs.antenna.RotatorMode(value)[исходный код]¶
Режим слежения поворотного устройства.
- NORMAL = 'Normal'¶
- FLIP = 'Flip'¶
- RANGE_250 = '250'¶
- class core.configs.antenna.RotatorSettings(*, ENABLED=False, MODEL='ROT_MODEL_NETROTCTL', BAUD=9600, PORT='rotctld:4533', THRESHOLD=4.0, MODE=RotatorMode.NORMAL, MIN_ELEVATION=0.0)[исходный код]¶
Настройки поворотного устройства (
ANTENNA__ROTATOR__*).- Параметры:
ENABLED (bool)
MODEL (str)
BAUD (int)
PORT (str)
THRESHOLD (float)
MODE (RotatorMode)
MIN_ELEVATION (float)
- ENABLED: bool¶
- MODEL: str¶
- BAUD: int¶
- PORT: str¶
- THRESHOLD: float¶
- MODE: RotatorMode¶
- MIN_ELEVATION: float¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class core.configs.antenna.RigSettings(*, IP='rigctld', PORT=4532)[исходный код]¶
Адрес демона
rigctld(ANTENNA__RIG__*).- Параметры:
IP (str)
PORT (int)
- IP: str¶
- PORT: int¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class core.configs.antenna.AntennaSettings(*, rotator=RotatorSettings(ENABLED=False, MODEL='ROT_MODEL_NETROTCTL', BAUD=9600, PORT='rotctld:4533', THRESHOLD=4.0, MODE=<RotatorMode.NORMAL: 'Normal'>, MIN_ELEVATION=0.0), rig=RigSettings(IP='rigctld', PORT=4532))[исходный код]¶
Настройки антенного тракта: поворотное и приёмное устройства.
- Параметры:
rotator (RotatorSettings)
rig (RigSettings)
- rotator: RotatorSettings¶
- rig: RigSettings¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Настройки наблюдения: префиксы, расширения и состав выгружаемых файлов.
- class core.configs.observation.ObservationSendingKey(*, PAYLOAD='payload', WATERFALL='waterfall', DEMODDATA='demoddata', CLIENT_METADATA='client_metadata')[исходный код]¶
Имена полей формы при выгрузке на портал.
- Параметры:
PAYLOAD (str)
WATERFALL (str)
DEMODDATA (str)
CLIENT_METADATA (str)
- PAYLOAD: str¶
- WATERFALL: str¶
- DEMODDATA: str¶
- CLIENT_METADATA: str¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class core.configs.observation.ObservationPrefix(*, PAYLOAD='payload', WATERFALL='waterfall', RAW_WATERFALL='raw_waterfall', DATA='data', METADATA='metadata')[исходный код]¶
Префиксы имён файлов наблюдения.
- Параметры:
PAYLOAD (str)
WATERFALL (str)
RAW_WATERFALL (str)
DATA (str)
METADATA (str)
- PAYLOAD: str¶
- WATERFALL: str¶
- RAW_WATERFALL: str¶
- DATA: str¶
- METADATA: str¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class core.configs.observation.ObservationExtension(*, PAYLOAD='ogg', WATERFALL='png', DATA_APT='png', METADATA='json', RAW_WATERFALL='dat')[исходный код]¶
Расширения файлов наблюдения.
- Параметры:
PAYLOAD (str)
WATERFALL (str)
DATA_APT (str)
METADATA (str)
RAW_WATERFALL (str)
- PAYLOAD: str¶
- WATERFALL: str¶
- DATA_APT: str¶
- METADATA: str¶
- RAW_WATERFALL: str¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class core.configs.observation.ObservationUploadTypes(*, PAYLOAD=True, WATERFALL=True)[исходный код]¶
Какие типы файлов выгружать на портал.
- Параметры:
PAYLOAD (bool)
WATERFALL (bool)
- PAYLOAD: bool¶
- WATERFALL: bool¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class core.configs.observation.ObservationSettings(*, REMOVE_WATERFALL_RAW_FILES=True, REMOVE_OBSERVATION_DATA=True, SOAPY_RX_DEVICE='driver=airspy,biastee=true', TIME_FORMAT_IN_FILENAME='%Y-%m-%dT%H-%M-%S', FILENAME_TEMPLATE='{observation_dir}/{prefix}_{observation_id}_{timestamp}.{extension}', RIG_UPDATE_INTERVAL=0.1, ROTATOR_UPDATE_INTERVAL=3, PRE_OBSERVATION_SCRIPT='satnogs-pre', POST_OBSERVATION_SCRIPT='satnogs-post', RUN_PRE_OBSERVATION_SCRIPT=True, RUN_POST_OBSERVATION_SCRIPT=True, SCRIPT_TIMEOUT_IN_SECONDS=900, BATCH_DELAY=1, prefix=ObservationPrefix(PAYLOAD='payload', WATERFALL='waterfall', RAW_WATERFALL='raw_waterfall', DATA='data', METADATA='metadata'), extension=ObservationExtension(PAYLOAD='ogg', WATERFALL='png', DATA_APT='png', METADATA='json', RAW_WATERFALL='dat'), sending_key=ObservationSendingKey(PAYLOAD='payload', WATERFALL='waterfall', DEMODDATA='demoddata', CLIENT_METADATA='client_metadata'), upload_types=ObservationUploadTypes(PAYLOAD=True, WATERFALL=True))[исходный код]¶
Параметры проведения наблюдения и состав выгружаемых файлов.
- Параметры:
REMOVE_WATERFALL_RAW_FILES (bool)
REMOVE_OBSERVATION_DATA (bool)
SOAPY_RX_DEVICE (str)
TIME_FORMAT_IN_FILENAME (str)
FILENAME_TEMPLATE (str)
RIG_UPDATE_INTERVAL (float)
ROTATOR_UPDATE_INTERVAL (float)
PRE_OBSERVATION_SCRIPT (str)
POST_OBSERVATION_SCRIPT (str)
RUN_PRE_OBSERVATION_SCRIPT (bool)
RUN_POST_OBSERVATION_SCRIPT (bool)
SCRIPT_TIMEOUT_IN_SECONDS (float)
BATCH_DELAY (float)
prefix (ObservationPrefix)
extension (ObservationExtension)
sending_key (ObservationSendingKey)
upload_types (ObservationUploadTypes)
- REMOVE_WATERFALL_RAW_FILES: bool¶
- REMOVE_OBSERVATION_DATA: bool¶
- SOAPY_RX_DEVICE: str¶
- TIME_FORMAT_IN_FILENAME: str¶
- FILENAME_TEMPLATE: str¶
- RIG_UPDATE_INTERVAL: float¶
- ROTATOR_UPDATE_INTERVAL: float¶
- PRE_OBSERVATION_SCRIPT: str¶
- POST_OBSERVATION_SCRIPT: str¶
- RUN_PRE_OBSERVATION_SCRIPT: bool¶
- RUN_POST_OBSERVATION_SCRIPT: bool¶
- SCRIPT_TIMEOUT_IN_SECONDS: float¶
- BATCH_DELAY: float¶
- prefix: ObservationPrefix¶
- extension: ObservationExtension¶
- sending_key: ObservationSendingKey¶
- upload_types: ObservationUploadTypes¶
- get_prefixes_of_files_available_for_upload()[исходный код]¶
Вернуть префиксы файлов, разрешённых к выгрузке.
Кадры и метаданные выгружаются всегда, аудио и водопад — по
OBSERVATION__UPLOAD_TYPES__*. Метаданные первыми: порядок префиксов задаёт порядок отправки, а до появления очереди они уходили на портал раньше остальных файлов.- Тип результата:
list[str]
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Настройки потокового графа и таблицы соответствия режимов демодуляции.
Каждое поле FlowgraphSettings доезжает до командной строки
flowgraph_dispatcher как аргумент; поля со значением None
не передаются.
SCRIPTS и MODES — предметные данные, а не настройки: они описывают
внешний контракт с gr-satnogs и не переопределяются из окружения.
Полями модели они были по недосмотру, из-за чего FLOWGRAPH__MODES
с JSON-значением молча перезаписывал таблицу целиком.
- class core.configs.flowgraph.FlowgraphSettings(*, RX_SAMP_RATE=None, RX_BANDWIDTH=None, DOPPLER_CORR_PER_SEC=None, LO_OFFSET=None, LO_TRANSVERTER=None, PPM_ERROR=0, GAIN_MODE='Overall', RF_GAIN=None, ANTENNA='RX', DEV_ARGS=None, STREAM_ARGS=None, TUNE_ARGS=None, OTHER_SETTINGS=None, DC_REMOVAL=None, BB_FREQ=None, ENABLE_IQ_DUMP=False, IQ_DUMP_FILENAME=None, UDP_DUMP_HOST='127.0.0.1', UDP_DUMP_PORT=57356, FLOWGRAPH_DISPATCHER='flowgraph_dispatcher', DEFAULT_MODE='FM')[исходный код]¶
Параметры потокового графа.
Каждое поле становится аргументом
--kebab-case=значение; поля со значениемNoneв командную строку не попадают. Таблицы режимов живут рядом, в константах модуля:SCRIPTSиMODES.- Параметры:
RX_SAMP_RATE (str | None)
RX_BANDWIDTH (int | None)
DOPPLER_CORR_PER_SEC (int | None)
LO_OFFSET (int | None)
LO_TRANSVERTER (int | None)
PPM_ERROR (float)
GAIN_MODE (str)
RF_GAIN (float | None)
ANTENNA (str)
DEV_ARGS (str | None)
STREAM_ARGS (str | None)
TUNE_ARGS (str | None)
OTHER_SETTINGS (str | None)
DC_REMOVAL (bool | None)
BB_FREQ (int | None)
ENABLE_IQ_DUMP (bool)
IQ_DUMP_FILENAME (str | None)
UDP_DUMP_HOST (str)
UDP_DUMP_PORT (int)
FLOWGRAPH_DISPATCHER (str)
DEFAULT_MODE (str)
- RX_SAMP_RATE: str | None¶
- RX_BANDWIDTH: int | None¶
- DOPPLER_CORR_PER_SEC: int | None¶
- LO_OFFSET: int | None¶
- LO_TRANSVERTER: int | None¶
- PPM_ERROR: float¶
- GAIN_MODE: str¶
- RF_GAIN: float | None¶
- ANTENNA: str¶
- DEV_ARGS: str | None¶
- STREAM_ARGS: str | None¶
- TUNE_ARGS: str | None¶
- OTHER_SETTINGS: str | None¶
- DC_REMOVAL: bool | None¶
- BB_FREQ: int | None¶
- ENABLE_IQ_DUMP: bool¶
- IQ_DUMP_FILENAME: str | None¶
- UDP_DUMP_HOST: str¶
- UDP_DUMP_PORT: int¶
- FLOWGRAPH_DISPATCHER: str¶
- DEFAULT_MODE: str¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Настройки построения и анализа водопада.
- class core.configs.waterfall.WaterfallSettings(*, AUTORANGE=False, THRESHOLD=-200, MIN_VALID_SAMPLES=10000, DEFAULT_MIN_VALUE=-110, DEFAULT_MAX_VALUE=-50, MIN_VALUE=-110, MAX_VALUE=-50, WIDTH=8.32, HEIGHT=16.03, HELP_LINES=False, GRIDSPEC={'bottom': 0.03, 'left': 0.09, 'ncols': 2, 'nrows': 1, 'right': 0.91, 'top': 0.995, 'width_ratios': [0.93, 0.03]}, TIMESTAMP_FORMAT='%Y-%m-%dT%H:%M:%S.%fZ', TIME_FORMAT='%H:%M', INTERVAL_IN_MINUTES=1)[исходный код]¶
Параметры отрисовки водопада и границы динамического диапазона.
- Параметры:
AUTORANGE (bool)
THRESHOLD (int)
MIN_VALID_SAMPLES (int)
DEFAULT_MIN_VALUE (int)
DEFAULT_MAX_VALUE (int)
MIN_VALUE (int)
MAX_VALUE (int)
WIDTH (float)
HEIGHT (float)
HELP_LINES (bool)
GRIDSPEC (dict[str, float | list[float]])
TIMESTAMP_FORMAT (str)
TIME_FORMAT (str)
INTERVAL_IN_MINUTES (int)
- AUTORANGE: bool¶
- THRESHOLD: int¶
- MIN_VALID_SAMPLES: int¶
- DEFAULT_MIN_VALUE: int¶
- DEFAULT_MAX_VALUE: int¶
- MIN_VALUE: int¶
- MAX_VALUE: int¶
- WIDTH: float¶
- HEIGHT: float¶
- HELP_LINES: bool¶
- TIMESTAMP_FORMAT: str¶
- TIME_FORMAT: str¶
- INTERVAL_IN_MINUTES: int¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Адреса конечных точек API портала.
- class core.configs.url.URLSettings(*, BASE='https://sonik.space/api/', JOBS_PATH='jobs/', OBSERVATIONS_PATH='observations/', BUNDLES_PATH='v2/bundles/', STATIONS_PATH='v2/stations/')[исходный код]¶
Адреса конечных точек API портала.
- Параметры:
BASE (str)
JOBS_PATH (str)
OBSERVATIONS_PATH (str)
BUNDLES_PATH (str)
STATIONS_PATH (str)
- BASE: str¶
- JOBS_PATH: str¶
- OBSERVATIONS_PATH: str¶
- BUNDLES_PATH: str¶
- STATIONS_PATH: str¶
- property jobs_url: str¶
- property stations_url: str¶
- property bundles_url: str¶
- property observations_url: str¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Таймауты HTTP-обмена с порталом.
- class core.configs.api.APISettings(*, TIMEOUT_IN_SECONDS_REQUEST_DATA=45, TIMEOUT_IN_SECONDS_SEND_DATA=120)[исходный код]¶
Таймауты HTTP-запросов к порталу.
- Параметры:
TIMEOUT_IN_SECONDS_REQUEST_DATA (int)
TIMEOUT_IN_SECONDS_SEND_DATA (int)
- TIMEOUT_IN_SECONDS_REQUEST_DATA: int¶
- TIMEOUT_IN_SECONDS_SEND_DATA: int¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Настройка логгеров приложения и вывода внешних скриптов.
- class core.configs.log.LoggingSettings(*, FORMAT='%(asctime)s - %(name)s - %(levelname)s - %(message)s', LEVEL='INFO', SCRIPT_LEVEL='INFO', FLOWGRAPH_LEVEL='INFO', DIRECTORY='/var/lib/soniks-client/logs', FILE_NAME='client.log', MAX_BYTES=5242880, BACKUP_COUNT=5, ENCODING='utf-8')[исходный код]¶
Настройка логгеров: консоль плюс файл с ротацией.
- Параметры:
FORMAT (str)
LEVEL (str)
SCRIPT_LEVEL (str)
FLOWGRAPH_LEVEL (str)
DIRECTORY (str)
FILE_NAME (str)
MAX_BYTES (int)
BACKUP_COUNT (int)
ENCODING (str)
- FORMAT: str¶
- LEVEL: str¶
- SCRIPT_LEVEL: str¶
- FLOWGRAPH_LEVEL: str¶
- DIRECTORY: str¶
- FILE_NAME: str¶
- MAX_BYTES: int¶
- BACKUP_COUNT: int¶
- ENCODING: str¶
- property script_log_level: int¶
- property flowgraph_log_level: int¶
- configure_logger(name='app')[исходный код]¶
Собрать основной логгер приложения.
- Параметры:
name (str)
- Тип результата:
Logger
- configure_raw_logger(name='row_logs')[исходный код]¶
Собрать логгер сырого вывода внешних скриптов (без префиксов).
- Параметры:
name (str)
- Тип результата:
Logger
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Живой канал с порталом (MQTT__*).
- class core.configs.mqtt.MqttSettings(*, ENABLED=True, URL='wss://sonik.space/mqtt')[исходный код]¶
Подключение к брокеру портала.
Один хост на всё: брокер стоит за тем же
sonik.spaceи слушает WebSocket на 443 — закрытым сетям ничего дополнительно открывать не нужно. Отключённый или недоступный MQTT станцию не ломает: конфигурация всё равно забирается REST’ом на каждой сверке расписания, просто не мгновенно.- Параметры:
ENABLED (bool)
URL (str)
- ENABLED: bool¶
- URL: str¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Настройки планировщика заданий APScheduler.
- class core.configs.scheduler.SchedulerSettings(*, MAX_WORKERS=30, COALESCE=True, MAX_INSTANCES=1, MISFIRE_GRACE_TIME_IN_SECOND=30, JOB_REQUEST_INTERVAL_IN_MINUTES=1, RESENDING_INTERVAL_IN_MINUTES=20, SAT_DATA_SYNC_INTERVAL_IN_HOURS=6, LOG_LEVEL='WARNING')[исходный код]¶
Параметры планировщика APScheduler и периодичность заданий.
- Параметры:
MAX_WORKERS (int)
COALESCE (bool)
MAX_INSTANCES (int)
MISFIRE_GRACE_TIME_IN_SECOND (int)
JOB_REQUEST_INTERVAL_IN_MINUTES (int)
RESENDING_INTERVAL_IN_MINUTES (int)
SAT_DATA_SYNC_INTERVAL_IN_HOURS (int)
LOG_LEVEL (str)
- MAX_WORKERS: int¶
- COALESCE: bool¶
- MAX_INSTANCES: int¶
- MISFIRE_GRACE_TIME_IN_SECOND: int¶
- JOB_REQUEST_INTERVAL_IN_MINUTES: int¶
- RESENDING_INTERVAL_IN_MINUTES: int¶
- SAT_DATA_SYNC_INTERVAL_IN_HOURS: int¶
- LOG_LEVEL: str¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Рабочие директории клиента.
Пути считаются от BASE, на диск ничего не пишется до явного вызова
create_directories() — его делает configure_runtime() при старте
приложения. Раньше директории создавались валидатором, то есть побочным
эффектом импорта.
- class core.configs.path.PathSettings(*, BASE='/tmp/.soniks', OUTPUT_DIR='output', COMPLETE_DIR='complete', INCOMPLETE_DIR='incomplete', STATE_FILE='/var/lib/soniks-client/desired-state.json')[исходный код]¶
Рабочие директории клиента под
PATHS__BASE.- Параметры:
BASE (str)
OUTPUT_DIR (str)
COMPLETE_DIR (str)
INCOMPLETE_DIR (str)
STATE_FILE (str)
- BASE: str¶
- OUTPUT_DIR: str¶
- COMPLETE_DIR: str¶
- INCOMPLETE_DIR: str¶
- STATE_FILE: str¶
- property base_path: Path¶
- property output_path: Path¶
- property complete_path: Path¶
- property incomplete_path: Path¶
- create_directories()[исходный код]¶
Создать рабочие директории на диске.
- Тип результата:
None
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Настройки именования заданий наблюдения в планировщике.
- class core.configs.job.JobSettings(*, PREFIX='job_')[исходный код]¶
Именование заданий наблюдения в планировщике.
- Параметры:
PREFIX (str)
- PREFIX: str¶
- get_job_name(job_id)[исходный код]¶
Построить имя задания планировщика по идентификатору наблюдения.
- Параметры:
job_id (str)
- Тип результата:
str
- is_observation_job(job_name)[исходный код]¶
Задание наблюдения ли это.
Именно префикс, а не вхождение подстроки:
PREFIX in job.nameотбирал по совпадению, а не по замыслу, и любое задание со словомjob_в имени попадало в сверку расписания.- Параметры:
job_name (str)
- Тип результата:
bool
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Эндпоинт состояния станции.
- class core.configs.health.HealthSettings(*, PORT=8080)[исходный код]¶
Параметры
/healthz.- Параметры:
PORT (int)
- PORT: int¶
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Исключения¶
Сигнализируют, что делать с файлами наблюдения, см. Загрузку данных.
Исключения клиента.
Служат не для всплытия наверх, а для выбора дальнейшей судьбы файлов
наблюдения: удалить, переместить в complete или отложить в
incomplete до повторной отправки.
- exception core.exceptions.ObservationNotFoundError[исходный код]¶
Базовые классы:
ExceptionПортал ответил 404: наблюдение удалено на его стороне.
Отправка прекращается. С первой попытки данные не удаляются — файлы откладываются в
incomplete; удаляет директорию только повторная отправка, получившая 404 второй раз.
- exception core.exceptions.FileNotUploadedError[исходный код]¶
Базовые классы:
ExceptionФайл выгрузить не удалось: сеть, таймаут или ошибка портала.
Файл переезжает в
incompleteи будет отправлен повторно.
- exception core.exceptions.FileRejectedError[исходный код]¶
Базовые классы:
FileNotUploadedErrorПортал ответил 400: файл отвергнут, те же байты получат тот же ответ.
Подкласс
FileNotUploadedError, поэтому первый отказ ведёт себя как любая ошибка выгрузки — файл ложится вincomplete. Закрывает его, как и второй 404, только повторная отправка, получившая 400 ещё раз.
- exception core.exceptions.NoSessionParametersError[исходный код]¶
Базовые классы:
ExceptionСессия связи запущена без параметров пролёта.
- exception core.exceptions.RotatorPositionError[исходный код]¶
Базовые классы:
ExceptionНе удалось получить или задать положение поворотного устройства.
- exception core.exceptions.EquipmentConnectionError[исходный код]¶
Базовые классы:
ExceptionHamlib не смог открыть соединение с приёмником или ротатором.
Hamlib возвращает коды ошибок, а не исключения: без явной проверки неудачный
open()невидим, а все последующие команды становятся тихим no-op. Сессия связи ловит это исключение и не начинает проход.
- exception core.exceptions.NoCompatibleRxDeviceError[исходный код]¶
Базовые классы:
ExceptionНи один приёмник не подходит под частоту прохода.
Наблюдение не начинается; см.
OBSERVATION__SOAPY_RX_DEVICE.
- exception core.exceptions.EmptyWaterfallError[исходный код]¶
Базовые классы:
ExceptionФайл водопада прочитан, но не содержит отсчётов.
- exception core.exceptions.WaterfallFileError[исходный код]¶
Базовые классы:
ExceptionФайл водопада не найден или не читается.
- exception core.exceptions.TimestampError[исходный код]¶
Базовые классы:
ExceptionМетка времени в файле водопада не разобрана.
Типы¶
Типы-псевдонимы, общие для всего клиента.