Skip to content
Merged
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: 5 additions & 3 deletions src/fastcs/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

from fastcs.attribute_io_ref import AttributeIORefT
from fastcs.datatypes import ATTRIBUTE_TYPES, DataType, T
from fastcs.logging import logger as _logger
from fastcs.logging import bind_logger
from fastcs.tracer import Tracer

ONCE = float("inf")
"""Special value to indicate that an attribute should be updated once on start up."""

logger = _logger.bind(logger_name=__name__)
logger = bind_logger(logger_name=__name__)


class Attribute(Generic[T, AttributeIORefT], Tracer):
Expand Down Expand Up @@ -171,6 +171,7 @@ async def update(self, value: T) -> None:
logger.opt(exception=e).error(
"On update callback failed", attribute=self, value=value
)
raise

def add_on_update_callback(self, callback: AttrOnUpdateCallback[T]) -> None:
"""Add a callback to be called when the value of the attribute is updated
Expand Down Expand Up @@ -248,7 +249,8 @@ async def put(self, setpoint: T, sync_setpoint: bool = False) -> None:
be rejected.

To directly change the value of the attribute, for example from an update loop
that has read a new value from some underlying source, call the ``set`` method.
that has read a new value from some underlying source, call the ``update``
method.

"""
setpoint = self._datatype.validate(setpoint)
Expand Down
20 changes: 12 additions & 8 deletions src/fastcs/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,15 @@ def _validate_io(self, ios: Sequence[AttributeIO[T, AttributeIORefT]]):
def add_attribute(self, name, attribute: Attribute):
if name in self.attributes and attribute is not self.attributes[name]:
raise ValueError(
f"Cannot add attribute {name}. "
f"Controller {self} has has existing attribute {name}"
f"Cannot add attribute {attribute}. "
f"Controller {self} has has existing attribute {name}: "
f"{self.attributes[name]}"
)
elif name in self.__sub_controller_tree.keys():
raise ValueError(
f"Cannot add attribute {name}. "
f"Controller {self} has existing sub controller {name}"
f"Cannot add attribute {attribute}. "
f"Controller {self} has existing sub controller {name}: "
f"{self.__sub_controller_tree[name]}"
)

attribute.set_name(name)
Expand All @@ -145,13 +147,15 @@ def add_attribute(self, name, attribute: Attribute):
def add_sub_controller(self, name: str, sub_controller: Controller):
if name in self.__sub_controller_tree.keys():
raise ValueError(
f"Cannot add sub controller {name}. "
f"Controller {self} has existing sub controller {name}"
f"Cannot add sub controller {sub_controller}. "
f"Controller {self} has existing sub controller {name}: "
f"{self.__sub_controller_tree[name]}"
)
elif name in self.attributes:
raise ValueError(
f"Cannot add sub controller {name}. "
f"Controller {self} has existing attribute {name}"
f"Cannot add sub controller {sub_controller}. "
f"Controller {self} has existing attribute {name}: "
f"{self.attributes[name]}"
)

sub_controller.set_path(self.path + [name])
Expand Down
16 changes: 14 additions & 2 deletions src/fastcs/logging/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from ._graylog import GraylogStaticFields as GraylogStaticFields
from ._graylog import parse_graylog_env_fields as parse_graylog_env_fields
from ._graylog import parse_graylog_static_fields as parse_graylog_static_fields
from ._logging import LogLevel, _configure_logger
from ._logging import Logger, LogLevel, _configure_logger

logger = _logger.bind(logger_name="fastcs")
"""FastCS logger
Expand Down Expand Up @@ -50,13 +50,25 @@
fastcs. Instead there is a ``Tracer`` class for verbose logging with fine-grained
controls that can be enabled by the user at runtime.

Use ``configure_logging`` to re-confi the logger at runtime. For more advanced
Use ``configure_logging`` to re-configure the logger at runtime. For more advanced
controls, configure the ``logger`` singleton directly.

See the ``loguru`` docs for more information: https://loguru.readthedocs.io
"""


def bind_logger(logger_name: str) -> Logger:
"""Create a wrapper of the singleton fastcs logger with the given name bound

The name will be displayed in all log messages from the returned wrapper.

See the docstring for ``fastcs.logging.logger`` for more information.

"""

return logger.bind(logger_name=logger_name)


def configure_logging(
level: LogLevel | None = None,
graylog_endpoint: GraylogEndpoint | None = None,
Expand Down
4 changes: 2 additions & 2 deletions src/fastcs/transport/epics/ca/ioc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from fastcs.controller_api import ControllerAPI
from fastcs.cs_methods import Command
from fastcs.datatypes import DataType, T
from fastcs.logging import logger as _logger
from fastcs.logging import bind_logger
from fastcs.tracer import Tracer
from fastcs.transport.epics.ca.util import (
builder_callable_from_attribute,
Expand All @@ -27,7 +27,7 @@


tracer = Tracer(name=__name__)
logger = _logger.bind(logger_name=__name__)
logger = bind_logger(logger_name=__name__)


class EpicsCAIOC:
Expand Down
5 changes: 4 additions & 1 deletion src/fastcs/transport/epics/ca/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@


EPICS_ALLOWED_DATATYPES = (Bool, DataType, Enum, Float, Int, String, Waveform)
EPICS_WAVEFORM_LENGTH = 256

DATATYPE_FIELD_TO_RECORD_FIELD = {
"prec": "PREC",
Expand Down Expand Up @@ -124,8 +125,10 @@ def cast_to_epics_type(datatype: DataType[T], value: T) -> object:
return datatype.index_of(datatype.validate(value))
else: # enum backed by string record
return datatype.validate(value).name
case String():
return value[: EPICS_WAVEFORM_LENGTH - 1]
case datatype if issubclass(type(datatype), EPICS_ALLOWED_DATATYPES):
return datatype.validate(value)
return value
case _:
raise ValueError(f"Unsupported datatype {datatype}")

Expand Down