"""Parse the slot v2 the Orchestrator writes to Redis (contract C1, SIENTIAPDE-1974). Each server carries its connection (`url`, `subscription_period_ms`), its OPC security (`security`: mode + policy) and its client certificate (`certificate`: certificate_id, application_uri and the MinIO objects of the cert and the private key; None = connect without security, D5). Each tag carries `names`: one entry per collection / tag name / model the reading is written to. """ SERVER_FIELDS = ('server_id', 'url', 'subscription_period_ms', 'security', 'certificate') # What a connection/subscription needs (the pool keys clients by server_id itself). CONNECTION_FIELDS = tuple(f for f in SERVER_FIELDS if f != 'server_id') NAME_FIELDS = ('collection', 'tag_name', 'model_id') def _parse_names(address: str, tag: dict) -> list[dict]: names = tag.get('names') if not names: raise ValueError(f'tag {address!r} has no names') for name in names: missing = [field for field in NAME_FIELDS if field not in name] if missing: raise ValueError(f'tag {address!r} has a name without {missing}') return names def _parse_tag(address: str, tag: dict) -> dict: return { 'frequency': tag['frequency'], 'names': _parse_names(address, tag), 'active_read': tag.get('active_read', {'enabled': False}), } def _parse_server(name: str, server: dict) -> dict: if not isinstance(server.get('security'), dict): raise ValueError(f'server {name!r} has no security') parsed = {field: server.get(field) for field in SERVER_FIELDS} parsed['name'] = server.get('name', name) parsed['tags'] = {addr: _parse_tag(addr, tag) for addr, tag in server['tags'].items()} return parsed def parse_slot(raw: dict) -> dict: """Normalize a raw slot v2 into `{server_name: {..., tags: {addr: tag}}}`. Raises: KeyError | TypeError | ValueError: When the slot is not a valid slot v2 (e.g. a slot v1 with `topics`). """ return {name: _parse_server(name, server) for name, server in raw.items()}