Skip to content

Commit c70ae18

Browse files
authored
Merge pull request #234 from DiamondLightSource/update-tutorials
Update tutorials
2 parents 71e1a2e + dcd7b5e commit c70ae18

19 files changed

+462
-457
lines changed

docs/snippets/dynamic.py

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
from __future__ import annotations
2-
31
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
64

75
from pydantic import BaseModel, ConfigDict, ValidationError
86

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
1010
from fastcs.connections import IPConnection, IPConnectionSettings
1111
from fastcs.controller import Controller
1212
from fastcs.datatypes import Bool, DataType, Float, Int, String
1313
from fastcs.launch import FastCS
14-
from fastcs.transport.epics.ca.options import EpicsCAOptions
14+
from fastcs.transport.epics.ca.transport import EpicsCATransport
1515
from fastcs.transport.epics.options import EpicsIOCOptions
1616

1717

@@ -46,74 +46,95 @@ def create_attributes(parameters: dict[str, Any]) -> dict[str, Attribute]:
4646
print(f"Failed to validate parameter '{parameter}'\n{e}")
4747
continue
4848

49-
handler = TemperatureControllerHandler(parameter.command)
49+
io_ref = TemperatureControllerAttributeIORef(parameter.command)
5050
match parameter.access_mode:
5151
case "r":
52-
attributes[name] = AttrR(parameter.fastcs_datatype, handler=handler)
52+
attributes[name] = AttrR(parameter.fastcs_datatype, io_ref=io_ref)
5353
case "rw":
54-
attributes[name] = AttrRW(parameter.fastcs_datatype, handler=handler)
54+
attributes[name] = AttrRW(parameter.fastcs_datatype, io_ref=io_ref)
5555

5656
return attributes
5757

5858

59+
NumberT = TypeVar("NumberT", int, float)
60+
61+
5962
@dataclass
60-
class TemperatureControllerHandler(AttrHandlerRW):
61-
command_name: str
63+
class TemperatureControllerAttributeIORef(AttributeIORef):
64+
name: str
65+
_: KW_ONLY
6266
update_period: float | None = 0.2
63-
_controller: TemperatureController | None = None
6467

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")
6980
value = response.strip("\r\n")
7081

7182
await attr.set(attr.dtype(value))
7283

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")
7789

7890

7991
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])
82100

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))
87103

88104

89105
class TemperatureController(Controller):
90106
def __init__(self, settings: IPConnectionSettings):
91-
super().__init__()
92-
93107
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])
95112

96113
async def connect(self):
97-
await self.connection.connect(self._ip_settings)
114+
await self._connection.connect(self._ip_settings)
98115

99116
async def initialise(self):
100117
await self.connect()
101118

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"))
103120

104121
ramps_api = api.pop("Ramps")
105122
self.attributes.update(create_attributes(api))
106123

107124
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()
109129
self.register_sub_controller(f"Ramp{idx + 1:02d}", ramp_controller)
110-
await ramp_controller.initialise(ramp_parameters)
111130

112-
await self.connection.close()
131+
await self._connection.close()
113132

114133

115-
epics_options = EpicsCAOptions(ca_ioc=EpicsIOCOptions(pv_prefix="DEMO"))
134+
epics_ca = EpicsCATransport(ca_ioc=EpicsIOCOptions(pv_prefix="DEMO"))
116135
connection_settings = IPConnectionSettings("localhost", 25565)
117-
fastcs = FastCS(TemperatureController(connection_settings), [epics_options])
136+
fastcs = FastCS(TemperatureController(connection_settings), [epics_ca])
137+
118138

119-
# fastcs.run() # Commented as this will block
139+
if __name__ == "__main__":
140+
fastcs.run()

docs/snippets/static02.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ class TemperatureController(Controller):
77

88

99
fastcs = FastCS(TemperatureController(), [])
10-
# fastcs.run() # Commented as this will block
10+
11+
if __name__ == "__main__":
12+
fastcs.run()

docs/snippets/static03.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ class TemperatureController(Controller):
99

1010

1111
fastcs = FastCS(TemperatureController(), [])
12-
# fastcs.run() # Commented as this will block
12+
13+
if __name__ == "__main__":
14+
fastcs.run()

docs/snippets/static04.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22
from fastcs.controller import Controller
33
from fastcs.datatypes import String
44
from fastcs.launch import FastCS
5-
from fastcs.transport.epics.ca.options import EpicsCAOptions
5+
from fastcs.transport.epics.ca.transport import EpicsCATransport
66
from fastcs.transport.epics.options import EpicsIOCOptions
77

88

99
class TemperatureController(Controller):
1010
device_id = AttrR(String())
1111

1212

13-
epics_options = EpicsCAOptions(ca_ioc=EpicsIOCOptions(pv_prefix="DEMO"))
14-
fastcs = FastCS(TemperatureController(), [epics_options])
13+
epics_ca = EpicsCATransport(ca_ioc=EpicsIOCOptions(pv_prefix="DEMO"))
14+
fastcs = FastCS(TemperatureController(), [epics_ca])
1515

16-
# fastcs.run() # Commented as this will block
16+
if __name__ == "__main__":
17+
fastcs.run()

docs/snippets/static05.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
from fastcs.controller import Controller
55
from fastcs.datatypes import String
66
from fastcs.launch import FastCS
7-
from fastcs.transport.epics.ca.options import EpicsCAOptions, EpicsGUIOptions
7+
from fastcs.transport.epics.ca.options import EpicsGUIOptions
8+
from fastcs.transport.epics.ca.transport import EpicsCATransport
89
from fastcs.transport.epics.options import EpicsIOCOptions
910

1011

@@ -15,12 +16,10 @@ class TemperatureController(Controller):
1516
gui_options = EpicsGUIOptions(
1617
output_path=Path(".") / "demo.bob", title="Demo Temperature Controller"
1718
)
18-
epics_options = EpicsCAOptions(
19-
gui=gui_options,
20-
ca_ioc=EpicsIOCOptions(pv_prefix="DEMO"),
21-
)
22-
fastcs = FastCS(TemperatureController(), [epics_options])
19+
epics_ca = EpicsCATransport(gui=gui_options, ca_ioc=EpicsIOCOptions(pv_prefix="DEMO"))
20+
fastcs = FastCS(TemperatureController(), [epics_ca])
2321

2422
fastcs.create_gui()
2523

26-
# fastcs.run() # Commented as this will block
24+
if __name__ == "__main__":
25+
fastcs.run()

docs/snippets/static06.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from fastcs.controller import Controller
66
from fastcs.datatypes import String
77
from fastcs.launch import FastCS
8-
from fastcs.transport.epics.ca.options import EpicsCAOptions
8+
from fastcs.transport.epics.ca.transport import EpicsCATransport
99
from fastcs.transport.epics.options import EpicsGUIOptions, EpicsIOCOptions
1010

1111

@@ -16,22 +16,20 @@ def __init__(self, settings: IPConnectionSettings):
1616
super().__init__()
1717

1818
self._ip_settings = settings
19-
self.connection = IPConnection()
19+
self._connection = IPConnection()
2020

2121
async def connect(self):
22-
await self.connection.connect(self._ip_settings)
22+
await self._connection.connect(self._ip_settings)
2323

2424

2525
gui_options = EpicsGUIOptions(
2626
output_path=Path(".") / "demo.bob", title="Demo Temperature Controller"
2727
)
28-
epics_options = EpicsCAOptions(
29-
gui=gui_options,
30-
ca_ioc=EpicsIOCOptions(pv_prefix="DEMO"),
31-
)
28+
epics_ca = EpicsCATransport(gui=gui_options, ca_ioc=EpicsIOCOptions(pv_prefix="DEMO"))
3229
connection_settings = IPConnectionSettings("localhost", 25565)
33-
fastcs = FastCS(TemperatureController(connection_settings), [epics_options])
30+
fastcs = FastCS(TemperatureController(connection_settings), [epics_ca])
3431

3532
fastcs.create_gui()
3633

37-
# fastcs.run() # Commented as this will block
34+
if __name__ == "__main__":
35+
fastcs.run()

docs/snippets/static07.py

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,59 @@
1-
from __future__ import annotations
2-
31
from dataclasses import dataclass
42
from pathlib import Path
3+
from typing import TypeVar
54

6-
from fastcs.attributes import AttrHandlerR, AttrR
5+
from fastcs.attribute_io import AttributeIO
6+
from fastcs.attribute_io_ref import AttributeIORef
7+
from fastcs.attributes import AttrR
78
from fastcs.connections import IPConnection, IPConnectionSettings
8-
from fastcs.controller import BaseController, Controller
9+
from fastcs.controller import Controller
910
from fastcs.datatypes import String
1011
from fastcs.launch import FastCS
11-
from fastcs.transport.epics.ca.options import EpicsCAOptions
12+
from fastcs.transport.epics.ca.transport import EpicsCATransport
1213
from fastcs.transport.epics.options import EpicsGUIOptions, EpicsIOCOptions
1314

15+
NumberT = TypeVar("NumberT", int, float)
16+
1417

1518
@dataclass
16-
class IDUpdater(AttrHandlerR):
19+
class IDAttributeIORef(AttributeIORef):
1720
update_period: float | None = 0.2
18-
_controller: TemperatureController | None = None
1921

20-
async def initialise(self, controller: BaseController):
21-
assert isinstance(controller, TemperatureController)
22-
self._controller = controller
2322

24-
@property
25-
def controller(self) -> TemperatureController:
26-
if self._controller is None:
27-
raise RuntimeError("Handler not initialised")
23+
class IDAttributeIO(AttributeIO[NumberT, IDAttributeIORef]):
24+
def __init__(self, connection: IPConnection):
25+
super().__init__()
2826

29-
return self._controller
27+
self._connection = connection
3028

31-
async def update(self, attr: AttrR):
32-
response = await self.controller.connection.send_query("ID?\r\n")
29+
async def update(self, attr: AttrR[NumberT, IDAttributeIORef]):
30+
response = await self._connection.send_query("ID?\r\n")
3331
value = response.strip("\r\n")
3432

35-
await attr.set(value)
33+
await attr.set(attr.dtype(value))
3634

3735

3836
class TemperatureController(Controller):
39-
device_id = AttrR(String(), handler=IDUpdater())
37+
device_id = AttrR(String(), io_ref=IDAttributeIORef())
4038

4139
def __init__(self, settings: IPConnectionSettings):
42-
super().__init__()
43-
4440
self._ip_settings = settings
45-
self.connection = IPConnection()
41+
self._connection = IPConnection()
42+
43+
super().__init__(ios=[IDAttributeIO(self._connection)])
4644

4745
async def connect(self):
48-
await self.connection.connect(self._ip_settings)
46+
await self._connection.connect(self._ip_settings)
4947

5048

5149
gui_options = EpicsGUIOptions(
5250
output_path=Path(".") / "demo.bob", title="Demo Temperature Controller"
5351
)
54-
epics_options = EpicsCAOptions(
55-
gui=gui_options,
56-
ca_ioc=EpicsIOCOptions(pv_prefix="DEMO"),
57-
)
52+
epics_ca = EpicsCATransport(gui=gui_options, ca_ioc=EpicsIOCOptions(pv_prefix="DEMO"))
5853
connection_settings = IPConnectionSettings("localhost", 25565)
59-
fastcs = FastCS(TemperatureController(connection_settings), [epics_options])
54+
fastcs = FastCS(TemperatureController(connection_settings), [epics_ca])
6055

6156
fastcs.create_gui()
6257

63-
# fastcs.run() # Commented as this will block
58+
if __name__ == "__main__":
59+
fastcs.run()

0 commit comments

Comments
 (0)