Skip to content

Commit a07fd5e

Browse files
committed
Moved _make record functions from ioc.py into util.py plus minor improvements.
1 parent 70c0c96 commit a07fd5e

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,27 +1,21 @@
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.ca.util import (
18-
DATATYPE_FIELD_TO_IN_RECORD_FIELD,
19-
DATATYPE_FIELD_TO_OUT_RECORD_FIELD,
20-
DEFAULT_STRING_WAVEFORM_LENGTH,
21-
MBB_MAX_CHOICES,
15+
_make_in_record,
16+
_make_out_record,
2217
cast_from_epics_type,
2318
cast_to_epics_type,
24-
create_state_keys,
2519
)
2620
from fastcs.transports.epics.util import controller_pv_prefix
2721
from fastcs.util import snake_to_pascal
@@ -193,164 +187,6 @@ async def async_record_set(value: DType_T):
193187
attribute.add_on_update_callback(async_record_set)
194188

195189

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