feat(gateway): slot v2 loader (contract C1)
This commit is contained in:
0
ingestor/io/__init__.py
Normal file
0
ingestor/io/__init__.py
Normal file
25
ingestor/io/slots.py
Normal file
25
ingestor/io/slots.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""Parse the slot v2 structure written by the Orchestrator to Redis (contract C1)."""
|
||||
|
||||
SERVER_FIELDS = ('server_id', 'url', 'server_uri', 'use_security', 'subscription_period_ms')
|
||||
|
||||
|
||||
def _parse_tag(address: str, tag: dict) -> dict:
|
||||
names = tag.get('names')
|
||||
if not names:
|
||||
raise ValueError(f'tag {address!r} has no names')
|
||||
return {
|
||||
'frequency': tag['frequency'],
|
||||
'names': names,
|
||||
'active_read': tag.get('active_read', {'enabled': False}),
|
||||
}
|
||||
|
||||
|
||||
def _parse_server(server: dict) -> dict:
|
||||
parsed = {field: server[field] for field in SERVER_FIELDS if field in server}
|
||||
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 dict into `{server_name: {..., tags: {addr: tag}}}`."""
|
||||
return {name: _parse_server(server) for name, server in raw.items()}
|
||||
0
tests/unit/io/__init__.py
Normal file
0
tests/unit/io/__init__.py
Normal file
28
tests/unit/io/test_slots.py
Normal file
28
tests/unit/io/test_slots.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from ingestor.io.slots import parse_slot
|
||||
|
||||
RAW = {'server1': {'server_id': '1', 'url': 'opc.tcp://h:4840', 'use_security': True,
|
||||
'subscription_period_ms': 1000, 'tags': {'ns=2;i=2': {'frequency': '15000',
|
||||
'active_read': {'enabled': True, 'cycle_multiple': 3},
|
||||
'names': [{'collection': 'raw_x', 'tag_name': 'Counter', 'model_name': 'm1', 'model_id': '1'},
|
||||
{'collection': 'raw_y', 'tag_name': 'Contador', 'model_name': 'm2', 'model_id': '2'}]}}}}
|
||||
|
||||
|
||||
def test_parse_slot_multi_name():
|
||||
slot = parse_slot(RAW)
|
||||
tag = slot['server1']['tags']['ns=2;i=2']
|
||||
assert len(tag['names']) == 2
|
||||
assert tag['active_read']['cycle_multiple'] == 3
|
||||
|
||||
|
||||
def test_parse_slot_defaults_active_read_disabled():
|
||||
raw = {'s': {'server_id': '1', 'url': 'u', 'tags': {'ns=2;i=1': {
|
||||
'frequency': '1000', 'names': [{'collection': 'c', 'tag_name': 't', 'model_name': 'm', 'model_id': '1'}]}}}}
|
||||
tag = parse_slot(raw)['s']['tags']['ns=2;i=1']
|
||||
assert tag['active_read'] == {'enabled': False}
|
||||
|
||||
|
||||
def test_parse_slot_rejects_tag_without_names():
|
||||
raw = {'s': {'server_id': '1', 'url': 'u', 'tags': {'ns=2;i=1': {'frequency': '1000'}}}}
|
||||
import pytest
|
||||
with pytest.raises((KeyError, ValueError)):
|
||||
parse_slot(raw)
|
||||
Reference in New Issue
Block a user