Skip to content

Commit 37bec2b

Browse files
committed
Implement new DataType fields and Attribute.description in EpicsBackend
1 parent b4f978c commit 37bec2b

File tree

2 files changed

+94
-20
lines changed

2 files changed

+94
-20
lines changed

src/fastcs/backends/epics/ioc.py

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -172,22 +172,43 @@ async def async_record_set(value: T):
172172

173173

174174
def _get_input_record(pv: str, attribute: AttrR) -> RecordWrapper:
175+
attribute_fields = {}
176+
if attribute.description is not None:
177+
attribute_fields.update({"DESC": attribute.description})
178+
175179
if attr_is_enum(attribute):
176180
assert attribute.allowed_values is not None and all(
177181
isinstance(v, str) for v in attribute.allowed_values
178182
)
179183
state_keys = dict(zip(MBB_STATE_FIELDS, attribute.allowed_values, strict=False))
180-
return builder.mbbIn(pv, **state_keys)
184+
return builder.mbbIn(pv, **state_keys, **attribute_fields)
181185

182186
match attribute.datatype:
183187
case Bool(znam, onam):
184-
return builder.boolIn(pv, ZNAM=znam, ONAM=onam)
185-
case Int():
186-
return builder.longIn(pv)
187-
case Float(prec):
188-
return builder.aIn(pv, PREC=prec)
188+
return builder.boolIn(pv, ZNAM=znam, ONAM=onam, **attribute_fields)
189+
case Int(units, min, max, min_alarm, max_alarm):
190+
return builder.longIn(
191+
pv,
192+
EGU=units,
193+
DRVL=min,
194+
DRVH=max,
195+
LOPR=min_alarm,
196+
HOPR=max_alarm,
197+
**attribute_fields,
198+
)
199+
case Float(prec, units, min, max, min_alarm, max_alarm):
200+
return builder.aIn(
201+
pv,
202+
PREC=prec,
203+
EGU=units,
204+
DRVL=min,
205+
DRVH=max,
206+
LOPR=min_alarm,
207+
HOPR=max_alarm,
208+
**attribute_fields,
209+
)
189210
case String():
190-
return builder.longStringIn(pv)
211+
return builder.longStringIn(pv, **attribute_fields)
191212
case _:
192213
raise FastCSException(
193214
f"Unsupported type {type(attribute.datatype)}: {attribute.datatype}"
@@ -224,12 +245,21 @@ async def async_write_display(value: T):
224245

225246

226247
def _get_output_record(pv: str, attribute: AttrW, on_update: Callable) -> Any:
248+
attribute_fields = {}
249+
if attribute.description is not None:
250+
attribute_fields.update({"DESC": attribute.description})
227251
if attr_is_enum(attribute):
228252
assert attribute.allowed_values is not None and all(
229253
isinstance(v, str) for v in attribute.allowed_values
230254
)
231255
state_keys = dict(zip(MBB_STATE_FIELDS, attribute.allowed_values, strict=False))
232-
return builder.mbbOut(pv, always_update=True, on_update=on_update, **state_keys)
256+
return builder.mbbOut(
257+
pv,
258+
always_update=True,
259+
on_update=on_update,
260+
**state_keys,
261+
**attribute_fields,
262+
)
233263

234264
match attribute.datatype:
235265
case Bool(znam, onam):
@@ -240,12 +270,35 @@ def _get_output_record(pv: str, attribute: AttrW, on_update: Callable) -> Any:
240270
always_update=True,
241271
on_update=on_update,
242272
)
243-
case Int():
244-
return builder.longOut(pv, always_update=True, on_update=on_update)
245-
case Float(prec):
246-
return builder.aOut(pv, always_update=True, on_update=on_update, PREC=prec)
273+
case Int(units, min, max, min_alarm, max_alarm):
274+
return builder.longOut(
275+
pv,
276+
always_update=True,
277+
on_update=on_update,
278+
EGU=units,
279+
DRVL=min,
280+
DRVH=max,
281+
LOPR=min_alarm,
282+
HOPR=max_alarm,
283+
**attribute_fields,
284+
)
285+
case Float(prec, units, min, max, min_alarm, max_alarm):
286+
return builder.aOut(
287+
pv,
288+
always_update=True,
289+
on_update=on_update,
290+
PREC=prec,
291+
EGU=units,
292+
DRVL=min,
293+
DRVH=max,
294+
LOPR=min_alarm,
295+
HOPR=max_alarm,
296+
**attribute_fields,
297+
)
247298
case String():
248-
return builder.longStringOut(pv, always_update=True, on_update=on_update)
299+
return builder.longStringOut(
300+
pv, always_update=True, on_update=on_update, **attribute_fields
301+
)
249302
case _:
250303
raise FastCSException(
251304
f"Unsupported type {type(attribute.datatype)}: {attribute.datatype}"

tests/backends/epics/test_ioc.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,15 @@ def test_get_output_record_raises(mocker: MockerFixture):
211211
_get_output_record("PV", mocker.MagicMock(), on_update=mocker.MagicMock())
212212

213213

214+
DEFAULT_SCALAR_FIELD_ARGS = {
215+
"EGU": None,
216+
"DRVL": None,
217+
"DRVH": None,
218+
"LOPR": None,
219+
"HOPR": None,
220+
}
221+
222+
214223
def test_ioc(mocker: MockerFixture, mapping: Mapping):
215224
builder = mocker.patch("fastcs.backends.epics.ioc.builder")
216225
add_pvi_info = mocker.patch("fastcs.backends.epics.ioc._add_pvi_info")
@@ -222,15 +231,26 @@ def test_ioc(mocker: MockerFixture, mapping: Mapping):
222231

223232
# Check records are created
224233
builder.boolIn.assert_called_once_with(f"{DEVICE}:ReadBool", ZNAM="OFF", ONAM="ON")
225-
builder.longIn.assert_any_call(f"{DEVICE}:ReadInt")
226-
builder.aIn.assert_called_once_with(f"{DEVICE}:ReadWriteFloat_RBV", PREC=2)
234+
builder.longIn.assert_any_call(f"{DEVICE}:ReadInt", **DEFAULT_SCALAR_FIELD_ARGS)
235+
builder.aIn.assert_called_once_with(
236+
f"{DEVICE}:ReadWriteFloat_RBV", PREC=2, **DEFAULT_SCALAR_FIELD_ARGS
237+
)
227238
builder.aOut.assert_any_call(
228-
f"{DEVICE}:ReadWriteFloat", always_update=True, on_update=mocker.ANY, PREC=2
239+
f"{DEVICE}:ReadWriteFloat",
240+
always_update=True,
241+
on_update=mocker.ANY,
242+
PREC=2,
243+
**DEFAULT_SCALAR_FIELD_ARGS,
244+
)
245+
builder.longIn.assert_any_call(f"{DEVICE}:BigEnum", **DEFAULT_SCALAR_FIELD_ARGS)
246+
builder.longIn.assert_any_call(
247+
f"{DEVICE}:ReadWriteInt_RBV", **DEFAULT_SCALAR_FIELD_ARGS
229248
)
230-
builder.longIn.assert_any_call(f"{DEVICE}:BigEnum")
231-
builder.longIn.assert_any_call(f"{DEVICE}:ReadWriteInt_RBV")
232249
builder.longOut.assert_called_with(
233-
f"{DEVICE}:ReadWriteInt", always_update=True, on_update=mocker.ANY
250+
f"{DEVICE}:ReadWriteInt",
251+
always_update=True,
252+
on_update=mocker.ANY,
253+
**DEFAULT_SCALAR_FIELD_ARGS,
234254
)
235255
builder.mbbIn.assert_called_once_with(
236256
f"{DEVICE}:StringEnum_RBV", ZRST="red", ONST="green", TWST="blue"
@@ -390,9 +410,10 @@ def test_long_pv_names_discarded(mocker: MockerFixture):
390410
f"{DEVICE}:{short_pv_name}",
391411
always_update=True,
392412
on_update=mocker.ANY,
413+
**DEFAULT_SCALAR_FIELD_ARGS,
393414
)
394415
builder.longIn.assert_called_once_with(
395-
f"{DEVICE}:{short_pv_name}_RBV",
416+
f"{DEVICE}:{short_pv_name}_RBV", **DEFAULT_SCALAR_FIELD_ARGS
396417
)
397418

398419
long_pv_name = long_attr_name.title().replace("_", "")

0 commit comments

Comments
 (0)