|
1 | | -from __future__ import annotations |
2 | | - |
3 | 1 | import json |
4 | | -from dataclasses import dataclass |
5 | | -from typing import Any, Literal |
| 2 | +from dataclasses import KW_ONLY, dataclass |
| 3 | +from typing import Any, Literal, TypeVar |
6 | 4 |
|
7 | 5 | from pydantic import BaseModel, ConfigDict, ValidationError |
8 | 6 |
|
9 | | -from fastcs.attributes import AttrHandlerRW, Attribute, AttrR, AttrRW, AttrW |
| 7 | +from fastcs.attribute_io import AttributeIO |
| 8 | +from fastcs.attribute_io_ref import AttributeIORef |
| 9 | +from fastcs.attributes import Attribute, AttrR, AttrRW, AttrW |
10 | 10 | from fastcs.connections import IPConnection, IPConnectionSettings |
11 | 11 | from fastcs.controller import Controller |
12 | 12 | from fastcs.datatypes import Bool, DataType, Float, Int, String |
13 | 13 | from fastcs.launch import FastCS |
14 | | -from fastcs.transport.epics.ca.options import EpicsCAOptions |
| 14 | +from fastcs.transport.epics.ca.transport import EpicsCATransport |
15 | 15 | from fastcs.transport.epics.options import EpicsIOCOptions |
16 | 16 |
|
17 | 17 |
|
@@ -46,74 +46,95 @@ def create_attributes(parameters: dict[str, Any]) -> dict[str, Attribute]: |
46 | 46 | print(f"Failed to validate parameter '{parameter}'\n{e}") |
47 | 47 | continue |
48 | 48 |
|
49 | | - handler = TemperatureControllerHandler(parameter.command) |
| 49 | + io_ref = TemperatureControllerAttributeIORef(parameter.command) |
50 | 50 | match parameter.access_mode: |
51 | 51 | case "r": |
52 | | - attributes[name] = AttrR(parameter.fastcs_datatype, handler=handler) |
| 52 | + attributes[name] = AttrR(parameter.fastcs_datatype, io_ref=io_ref) |
53 | 53 | case "rw": |
54 | | - attributes[name] = AttrRW(parameter.fastcs_datatype, handler=handler) |
| 54 | + attributes[name] = AttrRW(parameter.fastcs_datatype, io_ref=io_ref) |
55 | 55 |
|
56 | 56 | return attributes |
57 | 57 |
|
58 | 58 |
|
| 59 | +NumberT = TypeVar("NumberT", int, float) |
| 60 | + |
| 61 | + |
59 | 62 | @dataclass |
60 | | -class TemperatureControllerHandler(AttrHandlerRW): |
61 | | - command_name: str |
| 63 | +class TemperatureControllerAttributeIORef(AttributeIORef): |
| 64 | + name: str |
| 65 | + _: KW_ONLY |
62 | 66 | update_period: float | None = 0.2 |
63 | | - _controller: TemperatureController | None = None |
64 | 67 |
|
65 | | - async def update(self, attr: AttrR): |
66 | | - response = await self.controller.connection.send_query( |
67 | | - f"{self.command_name}?\r\n" |
68 | | - ) |
| 68 | + |
| 69 | +class TemperatureControllerAttributeIO( |
| 70 | + AttributeIO[NumberT, TemperatureControllerAttributeIORef] |
| 71 | +): |
| 72 | + def __init__(self, connection: IPConnection): |
| 73 | + super().__init__() |
| 74 | + |
| 75 | + self._connection = connection |
| 76 | + |
| 77 | + async def update(self, attr: AttrR[NumberT, TemperatureControllerAttributeIORef]): |
| 78 | + query = f"{attr.io_ref.name}?" |
| 79 | + response = await self._connection.send_query(f"{query}\r\n") |
69 | 80 | value = response.strip("\r\n") |
70 | 81 |
|
71 | 82 | await attr.set(attr.dtype(value)) |
72 | 83 |
|
73 | | - async def put(self, attr: AttrW, value: Any): |
74 | | - await self.controller.connection.send_command( |
75 | | - f"{self.command_name}={value}\r\n" |
76 | | - ) |
| 84 | + async def send( |
| 85 | + self, attr: AttrW[NumberT, TemperatureControllerAttributeIORef], value: NumberT |
| 86 | + ) -> None: |
| 87 | + command = f"{attr.io_ref.name}={attr.dtype(value)}" |
| 88 | + await self._connection.send_command(f"{command}\r\n") |
77 | 89 |
|
78 | 90 |
|
79 | 91 | class TemperatureRampController(Controller): |
80 | | - def __init__(self, index: int, connection: IPConnection): |
81 | | - super().__init__(f"Ramp {index}") |
| 92 | + def __init__( |
| 93 | + self, |
| 94 | + index: int, |
| 95 | + parameters: dict[str, TemperatureControllerParameter], |
| 96 | + io: TemperatureControllerAttributeIO, |
| 97 | + ): |
| 98 | + self._parameters = parameters |
| 99 | + super().__init__(f"Ramp{index}", ios=[io]) |
82 | 100 |
|
83 | | - self.connection = connection |
84 | | - |
85 | | - async def initialise(self, parameters: dict[str, Any]): |
86 | | - self.attributes.update(create_attributes(parameters)) |
| 101 | + async def initialise(self): |
| 102 | + self.attributes.update(create_attributes(self._parameters)) |
87 | 103 |
|
88 | 104 |
|
89 | 105 | class TemperatureController(Controller): |
90 | 106 | def __init__(self, settings: IPConnectionSettings): |
91 | | - super().__init__() |
92 | | - |
93 | 107 | self._ip_settings = settings |
94 | | - self.connection = IPConnection() |
| 108 | + self._connection = IPConnection() |
| 109 | + |
| 110 | + self._io = TemperatureControllerAttributeIO(self._connection) |
| 111 | + super().__init__(ios=[self._io]) |
95 | 112 |
|
96 | 113 | async def connect(self): |
97 | | - await self.connection.connect(self._ip_settings) |
| 114 | + await self._connection.connect(self._ip_settings) |
98 | 115 |
|
99 | 116 | async def initialise(self): |
100 | 117 | await self.connect() |
101 | 118 |
|
102 | | - api = json.loads((await self.connection.send_query("API?\r\n")).strip("\r\n")) |
| 119 | + api = json.loads((await self._connection.send_query("API?\r\n")).strip("\r\n")) |
103 | 120 |
|
104 | 121 | ramps_api = api.pop("Ramps") |
105 | 122 | self.attributes.update(create_attributes(api)) |
106 | 123 |
|
107 | 124 | for idx, ramp_parameters in enumerate(ramps_api): |
108 | | - ramp_controller = TemperatureRampController(idx + 1, self.connection) |
| 125 | + ramp_controller = TemperatureRampController( |
| 126 | + idx + 1, ramp_parameters, self._io |
| 127 | + ) |
| 128 | + await ramp_controller.initialise() |
109 | 129 | self.register_sub_controller(f"Ramp{idx + 1:02d}", ramp_controller) |
110 | | - await ramp_controller.initialise(ramp_parameters) |
111 | 130 |
|
112 | | - await self.connection.close() |
| 131 | + await self._connection.close() |
113 | 132 |
|
114 | 133 |
|
115 | | -epics_options = EpicsCAOptions(ca_ioc=EpicsIOCOptions(pv_prefix="DEMO")) |
| 134 | +epics_ca = EpicsCATransport(ca_ioc=EpicsIOCOptions(pv_prefix="DEMO")) |
116 | 135 | connection_settings = IPConnectionSettings("localhost", 25565) |
117 | | -fastcs = FastCS(TemperatureController(connection_settings), [epics_options]) |
| 136 | +fastcs = FastCS(TemperatureController(connection_settings), [epics_ca]) |
| 137 | + |
118 | 138 |
|
119 | | -# fastcs.run() # Commented as this will block |
| 139 | +if __name__ == "__main__": |
| 140 | + fastcs.run() |
0 commit comments