Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/fastcs/attributes/attr_r.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ async def update(self, value: Any) -> None:
ValueError: If the value fails to be validated to DType_T

"""
self.log_event(
"Attribute set", value=value, value_type=type(value), attribute=self
)
self.log_event("Attribute set", value=repr(value), attribute=self)

_previous_value = self._value
self._value = self._datatype.validate(value)

self.log_event("Value validated", value=repr(self._value), attribute=self)

self._on_update_events -= {
e for e in self._on_update_events if e.set(self._value)
}
Expand All @@ -94,7 +94,7 @@ async def update(self, value: Any) -> None:
await asyncio.gather(*[cb(self._value) for cb in callbacks_to_call])
except Exception as e:
logger.opt(exception=e).error(
"On update callbacks failed", attribute=self, value=value
"On update callbacks failed", attribute=self, value=repr(value)
)
raise

Expand Down
2 changes: 1 addition & 1 deletion src/fastcs/attributes/attr_rw.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@ async def update(self, value: DType_T):
await super().update(value)

if not self._setpoint_initialised:
await self._call_sync_setpoint_callbacks(value)
await self._call_sync_setpoint_callbacks(self._value)
self._setpoint_initialised = True
8 changes: 5 additions & 3 deletions src/fastcs/transports/epics/ca/ioc.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,9 @@ def _create_and_link_read_pv(
pv = f"{pv_prefix}:{pv_name}"

async def async_record_set(value: DType_T):
tracer.log_event("PV set from attribute", topic=attribute, pv=pv, value=value)
tracer.log_event(
"PV set from attribute", topic=attribute, pv=pv, value=repr(value)
)

record.set(cast_to_epics_type(attribute.datatype, value))

Expand Down Expand Up @@ -217,13 +219,13 @@ def _create_and_link_write_pv(
pv = f"{pv_prefix}:{pv_name}"

async def on_update(value):
logger.info("PV put: {pv} = {value}", pv=pv, value=value)
logger.info("PV put: {pv} = {value}", pv=pv, value=repr(value))

await attribute.put(cast_from_epics_type(attribute.datatype, value))

async def set_setpoint_without_process(value: DType_T):
tracer.log_event(
"PV setpoint set from attribute", topic=attribute, pv=pv, value=value
"PV setpoint set from attribute", topic=attribute, pv=pv, value=repr(value)
)

record.set(cast_to_epics_type(attribute.datatype, value), process=False)
Expand Down
2 changes: 1 addition & 1 deletion src/fastcs/transports/epics/ca/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def cast_to_epics_type(datatype: DataType[DType_T], value: DType_T) -> Any:
else: # enum backed by string record
return datatype.validate(value).name
case String() as string:
return value[: string.length]
return value[: string.length or DEFAULT_STRING_WAVEFORM_LENGTH]
case datatype if issubclass(type(datatype), EPICS_ALLOWED_DATATYPES):
return value
case _:
Expand Down
27 changes: 27 additions & 0 deletions tests/test_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,33 @@ def test_attr_r():
assert attr.get() == ""


@pytest.mark.asyncio
async def test_attr_update(mocker: MockerFixture):
attr = AttrRW(Int())

await attr.update(42)
assert attr.get() == 42

await attr.update("100") # type: ignore
assert attr.get() == 100

with pytest.raises(ValueError, match="Failed to cast"):
await attr.update("not_an_int") # type: ignore

attr = AttrRW(Int())
sync_setpoint_mock = mocker.AsyncMock()
attr.add_sync_setpoint_callback(sync_setpoint_mock)

await attr.update("200") # type: ignore
assert attr.get() == 200
sync_setpoint_mock.assert_called_once_with(200)

sync_setpoint_mock.reset_mock()
await attr.update(20)
assert attr.get() == 20
sync_setpoint_mock.assert_not_called()


@pytest.mark.asyncio
async def test_wait_for_predicate(mocker: MockerFixture):
attr = AttrR(Int(), initial_value=0)
Expand Down