Skip to content

Commit f0b6d88

Browse files
committed
Moved _make record functions from ioc.py into util.py plus minor improvements.
1 parent 9f8e941 commit f0b6d88

File tree

3 files changed

+182
-191
lines changed

3 files changed

+182
-191
lines changed

src/fastcs/transports/epics/ca/ioc.py

Lines changed: 3 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,22 @@
11
import asyncio
2-
from collections.abc import Callable
3-
from dataclasses import asdict
42
from typing import Any, Literal
53

64
from softioc import builder, softioc
75
from softioc.asyncio_dispatcher import AsyncioDispatcher
86
from softioc.pythonSoftIoc import RecordWrapper
97

108
from fastcs.attributes import AttrR, AttrRW, AttrW
11-
from fastcs.datatypes import Bool, DataType, DType_T, Enum, Float, Int, String, Waveform
12-
from fastcs.exceptions import FastCSError
9+
from fastcs.datatypes import DType_T, Waveform
1310
from fastcs.logging import bind_logger
1411
from fastcs.methods import Command
1512
from fastcs.tracer import Tracer
1613
from fastcs.transports.controller_api import ControllerAPI
1714
from fastcs.transports.epics import EpicsIOCOptions
1815
from fastcs.transports.epics.ca.util import (
19-
DATATYPE_FIELD_TO_IN_RECORD_FIELD,
20-
DATATYPE_FIELD_TO_OUT_RECORD_FIELD,
21-
DEFAULT_STRING_WAVEFORM_LENGTH,
22-
MBB_MAX_CHOICES,
16+
_make_in_record,
17+
_make_out_record,
2318
cast_from_epics_type,
2419
cast_to_epics_type,
25-
create_state_keys,
2620
)
2721
from fastcs.transports.epics.util import controller_pv_prefix
2822
from fastcs.util import snake_to_pascal
@@ -196,164 +190,6 @@ async def async_record_set(value: DType_T):
196190
attribute.add_on_update_callback(async_record_set)
197191

198192

199-
def _make_in_record(pv: str, attribute: AttrR) -> RecordWrapper:
200-
attribute_record_metadata = {
201-
"DESC": attribute.description,
202-
"initial_value": cast_to_epics_type(attribute.datatype, attribute.get()),
203-
}
204-
205-
match attribute.datatype:
206-
case Bool():
207-
record = builder.boolIn(
208-
pv, ZNAM="False", ONAM="True", **attribute_record_metadata
209-
)
210-
case Int():
211-
record = builder.longIn(
212-
pv,
213-
LOPR=attribute.datatype.min_alarm,
214-
HOPR=attribute.datatype.max_alarm,
215-
EGU=attribute.datatype.units,
216-
**attribute_record_metadata,
217-
)
218-
case Float():
219-
record = builder.aIn(
220-
pv,
221-
LOPR=attribute.datatype.min_alarm,
222-
HOPR=attribute.datatype.max_alarm,
223-
EGU=attribute.datatype.units,
224-
PREC=attribute.datatype.prec,
225-
**attribute_record_metadata,
226-
)
227-
case String():
228-
record = builder.longStringIn(
229-
pv,
230-
length=attribute.datatype.length or DEFAULT_STRING_WAVEFORM_LENGTH,
231-
**attribute_record_metadata,
232-
)
233-
case Enum():
234-
if len(attribute.datatype.members) > MBB_MAX_CHOICES:
235-
record = builder.longStringIn(
236-
pv,
237-
**attribute_record_metadata,
238-
)
239-
else:
240-
attribute_record_metadata.update(create_state_keys(attribute.datatype))
241-
record = builder.mbbIn(
242-
pv,
243-
**attribute_record_metadata,
244-
)
245-
case Waveform():
246-
record = builder.WaveformIn(
247-
pv, length=attribute.datatype.shape[0], **attribute_record_metadata
248-
)
249-
case _:
250-
raise FastCSError(
251-
f"EPICS unsupported datatype on {attribute}: {attribute.datatype}"
252-
)
253-
254-
def datatype_updater(datatype: DataType):
255-
for name, value in asdict(datatype).items():
256-
if name in DATATYPE_FIELD_TO_IN_RECORD_FIELD:
257-
record.set_field(DATATYPE_FIELD_TO_IN_RECORD_FIELD[name], value)
258-
259-
attribute.add_update_datatype_callback(datatype_updater)
260-
return record
261-
262-
263-
def _make_out_record(
264-
pv: str,
265-
attribute: AttrW | AttrRW,
266-
on_update: Callable,
267-
) -> RecordWrapper:
268-
attribute_record_metadata = {
269-
"DESC": attribute.description,
270-
"initial_value": cast_to_epics_type(
271-
attribute.datatype,
272-
attribute.get()
273-
if isinstance(attribute, AttrR)
274-
else attribute.datatype.initial_value,
275-
),
276-
}
277-
278-
update = {"on_update": on_update, "always_update": True, "blocking": True}
279-
280-
match attribute.datatype:
281-
case Bool():
282-
record = builder.boolOut(
283-
pv, ZNAM="False", ONAM="True", **update, **attribute_record_metadata
284-
)
285-
case Int():
286-
record = builder.longOut(
287-
pv,
288-
LOPR=attribute.datatype.min_alarm,
289-
HOPR=attribute.datatype.max_alarm,
290-
EGU=attribute.datatype.units,
291-
DRVL=attribute.datatype.min,
292-
DRVH=attribute.datatype.max,
293-
**update,
294-
**attribute_record_metadata,
295-
)
296-
case Float():
297-
record = builder.aOut(
298-
pv,
299-
LOPR=attribute.datatype.min_alarm,
300-
HOPR=attribute.datatype.max_alarm,
301-
EGU=attribute.datatype.units,
302-
PREC=attribute.datatype.prec,
303-
DRVL=attribute.datatype.min,
304-
DRVH=attribute.datatype.max,
305-
**update,
306-
**attribute_record_metadata,
307-
)
308-
case String():
309-
record = builder.longStringOut(
310-
pv,
311-
length=attribute.datatype.length or DEFAULT_STRING_WAVEFORM_LENGTH,
312-
**update,
313-
**attribute_record_metadata,
314-
)
315-
case Enum():
316-
if len(attribute.datatype.members) > MBB_MAX_CHOICES:
317-
datatype: Enum = attribute.datatype
318-
319-
def _verify_in_datatype(_, value):
320-
return value in datatype.names
321-
322-
record = builder.longStringOut(
323-
pv,
324-
validate=_verify_in_datatype,
325-
**update,
326-
**attribute_record_metadata,
327-
)
328-
329-
else:
330-
attribute_record_metadata.update(create_state_keys(attribute.datatype))
331-
record = builder.mbbOut(
332-
pv,
333-
**update,
334-
**attribute_record_metadata,
335-
)
336-
case Waveform():
337-
record = builder.WaveformOut(
338-
pv,
339-
length=attribute.datatype.shape[0],
340-
**update,
341-
**attribute_record_metadata,
342-
)
343-
case _:
344-
raise FastCSError(
345-
f"EPICS unsupported datatype on {attribute}: {attribute.datatype}"
346-
)
347-
348-
def datatype_updater(datatype: DataType):
349-
for name, value in asdict(datatype).items():
350-
if name in DATATYPE_FIELD_TO_OUT_RECORD_FIELD:
351-
record.set_field(DATATYPE_FIELD_TO_OUT_RECORD_FIELD[name], value)
352-
353-
attribute.add_update_datatype_callback(datatype_updater)
354-
return record
355-
356-
357193
def _create_and_link_write_pv(
358194
pv_prefix: str, pv_name: str, attr_name: str, attribute: AttrW[DType_T]
359195
) -> None:

src/fastcs/transports/epics/ca/util.py

Lines changed: 153 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import enum
2+
from collections.abc import Callable
3+
from dataclasses import asdict
24
from typing import Any
35

4-
from fastcs.datatypes import Bool, DType_T, Enum, Float, Int, String, Waveform
5-
from fastcs.datatypes.datatype import DataType
6+
from softioc import builder
7+
from softioc.pythonSoftIoc import RecordWrapper
8+
9+
from fastcs.attributes import AttrR, AttrRW, AttrW
10+
from fastcs.datatypes import Bool, DataType, DType_T, Enum, Float, Int, String, Waveform
11+
from fastcs.exceptions import FastCSError
612

713
_MBB_FIELD_PREFIXES = (
814
"ZR",
@@ -48,6 +54,151 @@
4854
}
4955

5056

57+
def _make_in_record(pv: str, attribute: AttrR) -> RecordWrapper:
58+
common_fields = {
59+
"DESC": attribute.description,
60+
"initial_value": cast_to_epics_type(attribute.datatype, attribute.get()),
61+
}
62+
63+
match attribute.datatype:
64+
case Bool():
65+
record = builder.boolIn(pv, ZNAM="False", ONAM="True", **common_fields)
66+
case Int():
67+
record = builder.longIn(
68+
pv,
69+
LOPR=attribute.datatype.min_alarm,
70+
HOPR=attribute.datatype.max_alarm,
71+
EGU=attribute.datatype.units,
72+
**common_fields,
73+
)
74+
case Float():
75+
record = builder.aIn(
76+
pv,
77+
LOPR=attribute.datatype.min_alarm,
78+
HOPR=attribute.datatype.max_alarm,
79+
EGU=attribute.datatype.units,
80+
PREC=attribute.datatype.prec,
81+
**common_fields,
82+
)
83+
case String():
84+
record = builder.longStringIn(
85+
pv,
86+
length=attribute.datatype.length or DEFAULT_STRING_WAVEFORM_LENGTH,
87+
**common_fields,
88+
)
89+
case Enum():
90+
if len(attribute.datatype.members) > MBB_MAX_CHOICES:
91+
record = builder.longStringIn(
92+
pv,
93+
**common_fields,
94+
)
95+
else:
96+
common_fields.update(create_state_keys(attribute.datatype))
97+
record = builder.mbbIn(
98+
pv,
99+
**common_fields,
100+
)
101+
case Waveform():
102+
record = builder.WaveformIn(
103+
pv, length=attribute.datatype.shape[0], **common_fields
104+
)
105+
case _:
106+
raise FastCSError(
107+
f"EPICS unsupported datatype on {attribute}: {attribute.datatype}"
108+
)
109+
110+
def datatype_updater(datatype: DataType):
111+
for name, value in asdict(datatype).items():
112+
if name in DATATYPE_FIELD_TO_IN_RECORD_FIELD:
113+
record.set_field(DATATYPE_FIELD_TO_IN_RECORD_FIELD[name], value)
114+
115+
attribute.add_update_datatype_callback(datatype_updater)
116+
return record
117+
118+
119+
def _make_out_record(pv: str, attribute: AttrW, on_update: Callable) -> RecordWrapper:
120+
common_fields = {
121+
"DESC": attribute.description,
122+
"initial_value": cast_to_epics_type(
123+
attribute.datatype,
124+
attribute.get()
125+
if isinstance(attribute, AttrRW)
126+
else attribute.datatype.initial_value,
127+
),
128+
"on_update": on_update,
129+
"always_update": True,
130+
"blocking": True,
131+
}
132+
133+
match attribute.datatype:
134+
case Bool():
135+
record = builder.boolOut(pv, ZNAM="False", ONAM="True", **common_fields)
136+
case Int():
137+
record = builder.longOut(
138+
pv,
139+
LOPR=attribute.datatype.min_alarm,
140+
HOPR=attribute.datatype.max_alarm,
141+
EGU=attribute.datatype.units,
142+
DRVL=attribute.datatype.min,
143+
DRVH=attribute.datatype.max,
144+
**common_fields,
145+
)
146+
case Float():
147+
record = builder.aOut(
148+
pv,
149+
LOPR=attribute.datatype.min_alarm,
150+
HOPR=attribute.datatype.max_alarm,
151+
EGU=attribute.datatype.units,
152+
PREC=attribute.datatype.prec,
153+
DRVL=attribute.datatype.min,
154+
DRVH=attribute.datatype.max,
155+
**common_fields,
156+
)
157+
case String():
158+
record = builder.longStringOut(
159+
pv,
160+
length=attribute.datatype.length or DEFAULT_STRING_WAVEFORM_LENGTH,
161+
**common_fields,
162+
)
163+
case Enum():
164+
if len(attribute.datatype.members) > MBB_MAX_CHOICES:
165+
datatype: Enum = attribute.datatype
166+
167+
def _verify_in_datatype(_, value):
168+
return value in datatype.names
169+
170+
record = builder.longStringOut(
171+
pv,
172+
validate=_verify_in_datatype,
173+
**common_fields,
174+
)
175+
176+
else:
177+
common_fields.update(create_state_keys(attribute.datatype))
178+
record = builder.mbbOut(
179+
pv,
180+
**common_fields,
181+
)
182+
case Waveform():
183+
record = builder.WaveformOut(
184+
pv,
185+
length=attribute.datatype.shape[0],
186+
**common_fields,
187+
)
188+
case _:
189+
raise FastCSError(
190+
f"EPICS unsupported datatype on {attribute}: {attribute.datatype}"
191+
)
192+
193+
def datatype_updater(datatype: DataType):
194+
for name, value in asdict(datatype).items():
195+
if name in DATATYPE_FIELD_TO_OUT_RECORD_FIELD:
196+
record.set_field(DATATYPE_FIELD_TO_OUT_RECORD_FIELD[name], value)
197+
198+
attribute.add_update_datatype_callback(datatype_updater)
199+
return record
200+
201+
51202
def create_state_keys(datatype: Enum):
52203
"""Creates a dictionary of state field keys to names"""
53204
return dict(

0 commit comments

Comments
 (0)