Skip to content

Commit fb15d56

Browse files
committed
Implement per-adapter caching of parameter trees
1 parent bb67852 commit fb15d56

File tree

12 files changed

+269
-83
lines changed

12 files changed

+269
-83
lines changed

src/fastcs_odin/controllers/odin_controller.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from fastcs.attributes import AttributeIO
22
from fastcs.connections.ip_connection import IPConnectionSettings
33
from fastcs.controllers import BaseController, Controller
4+
from fastcs.util import snake_to_pascal
45

56
from fastcs_odin.controllers.odin_data.frame_processor import (
67
FrameProcessorAdapterController,
@@ -17,19 +18,12 @@
1718
StatusSummaryAttributeIO,
1819
initialise_summary_attributes,
1920
)
20-
from fastcs_odin.util import (
21-
REQUEST_METADATA_HEADER,
22-
AdapterType,
23-
OdinParameter,
24-
create_odin_parameters,
25-
)
21+
from fastcs_odin.util import AdapterType, OdinParameter, create_odin_parameters
2622

2723

2824
class OdinController(Controller):
2925
"""A root ``Controller`` for an odin control server."""
3026

31-
API_PREFIX = "api/0.1"
32-
3327
def __init__(self, settings: IPConnectionSettings) -> None:
3428
self.connection = HTTPConnection(settings.ip, settings.port)
3529
self._ios: list[AttributeIO] = [
@@ -43,7 +37,7 @@ def __init__(self, settings: IPConnectionSettings) -> None:
4337
async def initialise(self) -> None:
4438
self.connection.open()
4539

46-
adapters_response = await self.connection.get(f"{self.API_PREFIX}/adapters")
40+
adapters_response = await self.connection.get_adapters()
4741
match adapters_response:
4842
case {"adapters": [*adapter_list]}:
4943
adapters = tuple(a for a in adapter_list if isinstance(a, str))
@@ -57,9 +51,7 @@ async def initialise(self) -> None:
5751
for adapter in adapters:
5852
# Get full parameter tree and split into parameters at the root and under
5953
# an index where there are N identical trees for each underlying process
60-
response = await self.connection.get(
61-
f"{self.API_PREFIX}/{adapter}", headers=REQUEST_METADATA_HEADER
62-
)
54+
response = await self.connection.get(adapter, with_metadata=True)
6355
# Extract the module name of the adapter
6456
match response:
6557
case {"module": {"value": str() as module}}:
@@ -70,7 +62,9 @@ async def initialise(self) -> None:
7062
adapter_controller = self._create_adapter_controller(
7163
self.connection, create_odin_parameters(response), adapter, module
7264
)
73-
self.add_sub_controller(adapter.upper(), adapter_controller)
65+
self.add_sub_controller(
66+
snake_to_pascal(adapter).upper(), adapter_controller
67+
)
7468
await adapter_controller.initialise()
7569

7670
initialise_summary_attributes(self)
@@ -87,17 +81,29 @@ def _create_adapter_controller(
8781
match module:
8882
case AdapterType.FRAME_PROCESSOR:
8983
return FrameProcessorAdapterController(
90-
connection, parameters, f"{self.API_PREFIX}/{adapter}", self._ios
84+
connection, parameters, adapter, self._ios
9185
)
9286
case AdapterType.FRAME_RECEIVER:
9387
return FrameReceiverAdapterController(
94-
connection, parameters, f"{self.API_PREFIX}/{adapter}", self._ios
88+
connection, parameters, adapter, self._ios
9589
)
9690
case AdapterType.META_WRITER:
9791
return MetaWriterAdapterController(
98-
connection, parameters, f"{self.API_PREFIX}/{adapter}", self._ios
92+
connection, parameters, adapter, self._ios
9993
)
10094
case _:
101-
return OdinSubController(
102-
connection, parameters, f"{self.API_PREFIX}/{adapter}", self._ios
103-
)
95+
return OdinSubController(connection, parameters, adapter, self._ios)
96+
97+
def enable_request_timers(self) -> None:
98+
"""Enable request timers on all parameter tree caches."""
99+
for io in self._ios:
100+
match io:
101+
case ParameterTreeAttributeIO() as ptio:
102+
ptio.enable_request_timers()
103+
104+
def disable_request_timers(self) -> None:
105+
"""Disable request timers on all parameter tree caches."""
106+
for io in self._ios:
107+
match io:
108+
case ParameterTreeAttributeIO() as ptio:
109+
ptio.disable_request_timers()

src/fastcs_odin/controllers/odin_data/frame_processor.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class FrameProcessorController(OdinSubController):
3333

3434
async def initialise(self):
3535
plugins_response = await self.connection.get(
36-
f"{self._api_prefix}/status/plugins/names"
36+
f"{self._adapter}/status/plugins/names"
3737
)
3838
match plugins_response:
3939
case {"names": [*plugin_list]}:
@@ -57,7 +57,7 @@ async def initialise(self):
5757
for parameter in self.parameters:
5858
self.add_attribute(
5959
parameter.name,
60-
create_attribute(parameter=parameter, api_prefix=self._api_prefix),
60+
create_attribute(parameter=parameter, adapter=self._adapter),
6161
)
6262

6363
async def _create_plugin_sub_controllers(self, plugins: Sequence[str]):
@@ -74,7 +74,7 @@ def __parameter_in_plugin(
7474
plugin_controller = FrameProcessorPluginController(
7575
self.connection,
7676
plugin_parameters,
77-
f"{self._api_prefix}",
77+
f"{self._adapter}",
7878
self._ios,
7979
)
8080
self.add_sub_controller(plugin.upper(), plugin_controller)
@@ -171,13 +171,13 @@ async def initialise(self):
171171
parameter.set_path(["current_acquisition_id"])
172172
self.add_attribute(
173173
parameter.name,
174-
create_attribute(parameter=parameter, api_prefix=self._api_prefix),
174+
create_attribute(parameter=parameter, adapter=self._adapter),
175175
)
176176

177177
async def _create_commands(self):
178178
plugin_name = self.path[-1].lower()
179179
command_response = await self.connection.get(
180-
f"{self._api_prefix}/command/{plugin_name}/allowed"
180+
f"{self._adapter}/command/{plugin_name}/allowed"
181181
)
182182

183183
try:
@@ -200,7 +200,7 @@ def __dataset_parameter(param: OdinParameter):
200200
dataset_controller = FrameProcessorDatasetController(
201201
self.connection,
202202
dataset_parameters,
203-
f"{self._api_prefix}",
203+
f"{self._adapter}",
204204
self._ios,
205205
)
206206
self.add_sub_controller("DS", dataset_controller)
@@ -210,7 +210,7 @@ def _construct_command(self, command_name, plugin_name):
210210
async def submit_command() -> None:
211211
logger.info("Executing command", plugin=plugin_name, command=command_name)
212212
await self.connection.put(
213-
f"{self._api_prefix}/command/{plugin_name}/execute", command_name
213+
f"{self._adapter}/command/{plugin_name}/execute", command_name
214214
)
215215

216216
setattr(self, command_name, Command(submit_command))
@@ -224,5 +224,5 @@ async def initialise(self):
224224
parameter.set_path(parameter.uri[3:])
225225
self.add_attribute(
226226
parameter.name,
227-
create_attribute(parameter=parameter, api_prefix=self._api_prefix),
227+
create_attribute(parameter=parameter, adapter=self._adapter),
228228
)

src/fastcs_odin/controllers/odin_data/frame_receiver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ async def initialise(self):
2323

2424
self.add_attribute(
2525
parameter.name,
26-
create_attribute(parameter=parameter, api_prefix=self._api_prefix),
26+
create_attribute(parameter=parameter, adapter=self._adapter),
2727
)
2828

2929

src/fastcs_odin/controllers/odin_data/meta_writer.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,21 @@ async def initialise(self):
1717
parameter.set_path(parameter.path[2:])
1818
self.add_attribute(
1919
parameter.name,
20-
create_attribute(parameter=parameter, api_prefix=self._api_prefix),
20+
create_attribute(parameter=parameter, adapter=self._adapter),
2121
)
2222

2323
acquisition_id = AttrRW(
24-
String(), io_ref=ParameterTreeAttributeIORef("api/0.1/mw/config/acquisition_id")
24+
String(), io_ref=ParameterTreeAttributeIORef("mw", "config/acquisition_id")
2525
)
2626
directory = AttrRW(
27-
String(), io_ref=ParameterTreeAttributeIORef("api/0.1/mw/config/directory")
27+
String(), io_ref=ParameterTreeAttributeIORef("mw", "config/directory")
2828
)
2929
file_prefix = AttrRW(
30-
String(), io_ref=ParameterTreeAttributeIORef("api/0.1/mw/config/file_prefix")
31-
)
32-
writing = AttrR(
33-
Bool(), io_ref=ParameterTreeAttributeIORef("api/0.1/mw/status/writing")
34-
)
35-
written = AttrR(
36-
Int(), io_ref=ParameterTreeAttributeIORef("api/0.1/mw/status/written")
30+
String(), io_ref=ParameterTreeAttributeIORef("mw", "config/file_prefix")
3731
)
32+
writing = AttrR(Bool(), io_ref=ParameterTreeAttributeIORef("mw", "status/writing"))
33+
written = AttrR(Int(), io_ref=ParameterTreeAttributeIORef("mw", "status/written"))
3834

3935
@command()
4036
async def stop(self):
41-
await self.connection.put("api/0.1/mw/config/stop", True)
37+
await self.connection.put("mw/config/stop", True)

src/fastcs_odin/controllers/odin_data/odin_data_adapter.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,21 @@ def __init__(
2727
self,
2828
connection: HTTPConnection,
2929
parameters: list[OdinParameter],
30-
api_prefix: str,
30+
adapter: str,
3131
ios: Sequence[AttributeIO[DType_T, AttributeIORefT]],
3232
):
3333
"""
3434
Args:
3535
connection: HTTP connection to communicate with odin server
3636
parameters: The parameters in the adapter
37-
api_prefix: The base URL of this adapter in the odin server API
37+
adapter: The base URL of this adapter in the odin server API
3838
3939
"""
4040
super().__init__(ios=ios, children={})
4141

4242
self.connection = connection
4343
self.parameters = parameters
44-
self._api_prefix = api_prefix
44+
self._adapter = adapter
4545
self._ios = ios
4646

4747
async def initialise(self):
@@ -58,7 +58,7 @@ async def initialise(self):
5858
adapter_controller = self._subcontroller_cls(
5959
self.connection,
6060
fp_parameters,
61-
f"{self._api_prefix}/{idx}",
61+
f"{self._adapter}/{idx}",
6262
self._ios,
6363
)
6464
self[int(idx)] = adapter_controller
@@ -67,7 +67,7 @@ async def initialise(self):
6767
for parameter in self.parameters:
6868
self.add_attribute(
6969
parameter.name,
70-
create_attribute(parameter=parameter, api_prefix=self._api_prefix),
70+
create_attribute(parameter=parameter, adapter=self._adapter),
7171
)
7272
self._create_config_fan_attributes()
7373
initialise_summary_attributes(self)

src/fastcs_odin/controllers/odin_subcontroller.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,26 @@ def __init__(
1515
self,
1616
connection: HTTPConnection,
1717
parameters: list[OdinParameter],
18-
api_prefix: str,
18+
adapter: str,
1919
ios: Sequence[AttributeIO[DType_T, AttributeIORefT]],
2020
):
2121
"""
2222
Args:
2323
connection: HTTP connection to communicate with odin server
2424
parameters: The parameters in the adapter
25-
api_prefix: The base URL of this adapter in the odin server API
25+
adapter: The base URL of this adapter in the odin server API
2626
2727
"""
2828
super().__init__(ios=ios)
2929

3030
self.connection = connection
3131
self.parameters = parameters
32-
self._api_prefix = api_prefix
32+
self._adapter = adapter
3333
self._ios = ios
3434

3535
async def initialise(self):
3636
for parameter in self.parameters:
3737
self.add_attribute(
3838
parameter.name,
39-
create_attribute(parameter=parameter, api_prefix=self._api_prefix),
39+
create_attribute(parameter=parameter, adapter=self._adapter),
4040
)

src/fastcs_odin/http_connection.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@
88

99

1010
class HTTPConnection:
11-
def __init__(self, ip: str, port: int):
11+
DEFAULT_API_PREFIX = "api/0.1"
12+
REQUEST_METADATA_HEADER = {"Accept": "application/json;metadata=true"}
13+
14+
def __init__(self, ip: str, port: int, api_prefix: str = DEFAULT_API_PREFIX):
1215
self._session: ClientSession | None = None
1316
self._ip = ip
1417
self._port = port
18+
self._api_prefix = api_prefix
1519

1620
def full_url(self, uri: str) -> str:
1721
"""Expand IP address, port and URI into full URL.
@@ -20,7 +24,7 @@ def full_url(self, uri: str) -> str:
2024
uri: Identifier for a resource for the current connection
2125
2226
"""
23-
return f"http://{self._ip}:{self._port}/{uri}"
27+
return f"http://{self._ip}:{self._port}/{self._api_prefix}/{uri}"
2428

2529
def open(self):
2630
"""Create the underlying aiohttp ClientSession.
@@ -44,7 +48,15 @@ def get_session(self) -> ClientSession:
4448

4549
raise ConnectionRefusedError("Session is not open")
4650

47-
async def get(self, uri: str, headers: dict | None = None) -> dict[str, JsonType]:
51+
async def get_adapters(self) -> dict[str, JsonType]:
52+
"""Get a list of adapters from the server.
53+
54+
Returns: Response payload as JSON
55+
56+
"""
57+
return await self.get("adapters")
58+
59+
async def get(self, uri: str, with_metadata: bool = False) -> dict[str, JsonType]:
4860
"""Perform HTTP GET request and return response content as JSON.
4961
5062
Args:
@@ -54,6 +66,9 @@ async def get(self, uri: str, headers: dict | None = None) -> dict[str, JsonType
5466
5567
"""
5668
session = self.get_session()
69+
70+
headers = self.REQUEST_METADATA_HEADER if with_metadata else None
71+
5772
async with session.get(self.full_url(uri), headers=headers) as response:
5873
match await response.json():
5974
case dict() as d:

0 commit comments

Comments
 (0)