Skip to content

Commit

Permalink
Add dropdowns for EPICS mbb records
Browse files Browse the repository at this point in the history
  • Loading branch information
jsouter committed Jun 6, 2024
1 parent fb929a1 commit 4ec97c2
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 18 deletions.
15 changes: 12 additions & 3 deletions src/fastcs/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,19 @@ def __init__(
access_mode: AttrMode,
group: str | None = None,
handler: Any = None,
enum_mapping: dict[str, int] | None = None,
) -> None:
assert (
datatype.dtype in ATTRIBUTE_TYPES
), f"Attr type must be one of {ATTRIBUTE_TYPES}, received type {datatype.dtype}"
self._datatype: DataType[T] = datatype
self._access_mode: AttrMode = access_mode
self._group = group
self._enum_mapping = enum_mapping

@property
def enum_mapping(self) -> dict[str, str] | None:
return self._enum_mapping

@property
def datatype(self) -> DataType[T]:
Expand Down Expand Up @@ -85,8 +91,9 @@ def __init__(
access_mode=AttrMode.READ,
group: str | None = None,
handler: Updater | None = None,
enum_mapping: dict[str, int] | None = None,
) -> None:
super().__init__(datatype, access_mode, group, handler) # type: ignore
super().__init__(datatype, access_mode, group, handler, enum_mapping) # type: ignore
self._value: T = datatype.dtype()
self._update_callback: AttrCallback[T] | None = None
self._updater = handler
Expand Down Expand Up @@ -117,8 +124,9 @@ def __init__(
access_mode=AttrMode.WRITE,
group: str | None = None,
handler: Sender | None = None,
enum_mapping: dict[str, int] | None = None,
) -> None:
super().__init__(datatype, access_mode, group, handler) # type: ignore
super().__init__(datatype, access_mode, group, handler, enum_mapping) # type: ignore
self._process_callback: AttrCallback[T] | None = None
self._write_display_callback: AttrCallback[T] | None = None
self._sender = handler
Expand Down Expand Up @@ -156,8 +164,9 @@ def __init__(
access_mode=AttrMode.READ_WRITE,
group: str | None = None,
handler: Handler | None = None,
enum_mapping: dict[str, int] | None = None,
) -> None:
super().__init__(datatype, access_mode, group, handler) # type: ignore
super().__init__(datatype, access_mode, group, handler, enum_mapping) # type: ignore

async def process(self, value: T) -> None:
await self.set(value)
Expand Down
23 changes: 15 additions & 8 deletions src/fastcs/backends/epics/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from pvi.device import (
LED,
ButtonPanel,
ComboBox,
Component,
Device,
Grid,
Expand All @@ -26,7 +27,7 @@

from fastcs.attributes import Attribute, AttrR, AttrRW, AttrW
from fastcs.cs_methods import Command
from fastcs.datatypes import Bool, DataType, Float, Int, String
from fastcs.datatypes import Bool, Float, Int, String
from fastcs.exceptions import FastCSException
from fastcs.mapping import Mapping, SingleMapping
from fastcs.util import snake_to_pascal
Expand Down Expand Up @@ -56,7 +57,8 @@ def _get_pv(self, attr_path: str, name: str):
return f"{self._pv_prefix}{attr_path.upper()}{name.title().replace('_', '')}"

@staticmethod
def _get_read_widget(datatype: DataType) -> ReadWidget:
def _get_read_widget(attribute: Attribute) -> ReadWidget:
datatype = attribute.datatype
match datatype:
case Bool():
return LED()
Expand All @@ -68,11 +70,16 @@ def _get_read_widget(datatype: DataType) -> ReadWidget:
raise FastCSException(f"Unsupported type {type(datatype)}: {datatype}")

@staticmethod
def _get_write_widget(datatype: DataType) -> WriteWidget:
def _get_write_widget(attribute: Attribute) -> WriteWidget:
datatype = attribute.datatype
match datatype:
case Bool():
return ToggleButton()
case Int() | Float():
case Int():
if attribute.enum_mapping is None:
return TextWrite()
return ComboBox(choices=list(attribute.enum_mapping.keys()))
case Float():
return TextWrite()
case String():
return TextWrite(format=TextFormat.string)
Expand All @@ -85,8 +92,8 @@ def _get_attribute_component(self, attr_path: str, name: str, attribute: Attribu

match attribute:
case AttrRW():
read_widget = self._get_read_widget(attribute.datatype)
write_widget = self._get_write_widget(attribute.datatype)
read_widget = self._get_read_widget(attribute)
write_widget = self._get_write_widget(attribute)
return SignalRW(
name=name,
write_pv=pv,
Expand All @@ -95,10 +102,10 @@ def _get_attribute_component(self, attr_path: str, name: str, attribute: Attribu
read_widget=read_widget,
)
case AttrR():
read_widget = self._get_read_widget(attribute.datatype)
read_widget = self._get_read_widget(attribute)
return SignalR(name=name, read_pv=pv, read_widget=read_widget)
case AttrW():
write_widget = self._get_write_widget(attribute.datatype)
write_widget = self._get_write_widget(attribute)
return SignalW(name=name, write_pv=pv, write_widget=write_widget)

def _get_command_component(self, attr_path: str, name: str):
Expand Down
49 changes: 42 additions & 7 deletions src/fastcs/backends/epics/ioc.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,48 @@

from fastcs.attributes import AttrR, AttrRW, AttrW
from fastcs.backend import Backend
from fastcs.datatypes import Bool, DataType, Float, Int, String
from fastcs.datatypes import Bool, Float, Int, String
from fastcs.exceptions import FastCSException
from fastcs.mapping import Mapping

ENUM_ST_MAPPING = {
0: "ZRST",
1: "ONST",
2: "TWST",
3: "THST",
4: "FRST",
5: "FVST",
6: "SXST",
7: "SVST",
8: "EIST",
9: "NIST",
10: "TEST",
11: "ELST",
12: "TVST",
13: "TTST",
14: "FTST",
15: "FFST",
}


@dataclass
class EpicsIOCOptions:
terminal: bool = True


def _get_input_record(pv_name: str, datatype: DataType) -> RecordWrapper:
def _get_input_record(pv_name: str, attribute: AttrR) -> RecordWrapper:
datatype = attribute.datatype
match datatype:
case Bool(znam, onam):
return builder.boolIn(pv_name, ZNAM=znam, ONAM=onam)
case Int():
return builder.longIn(pv_name)
if attribute.enum_mapping is None:
return builder.longIn(pv_name)
kwargs = {
ENUM_ST_MAPPING[enum_val]: enum_name
for enum_name, enum_val in attribute.enum_mapping.items()
}
return builder.mbbIn(pv_name, **kwargs)
case Float(prec):
return builder.aIn(pv_name, PREC=prec)
case String():
Expand All @@ -33,15 +59,16 @@ def _get_input_record(pv_name: str, datatype: DataType) -> RecordWrapper:


def _create_and_link_read_pv(pv_name: str, attribute: AttrR) -> None:
record = _get_input_record(pv_name, attribute._datatype)
record = _get_input_record(pv_name, attribute)

async def async_wrapper(v):
record.set(v)

attribute.set_update_callback(async_wrapper)


def _get_output_record(pv_name: str, datatype: DataType, on_update: Callable) -> Any:
def _get_output_record(pv_name: str, attribute: AttrW, on_update: Callable) -> Any:
datatype = attribute.datatype
match datatype:
case Bool(znam, onam):
return builder.boolOut(
Expand All @@ -52,7 +79,15 @@ def _get_output_record(pv_name: str, datatype: DataType, on_update: Callable) ->
on_update=on_update,
)
case Int():
return builder.longOut(pv_name, always_update=True, on_update=on_update)
if attribute.enum_mapping is None:
return builder.longOut(pv_name, always_update=True, on_update=on_update)
kwargs = {
ENUM_ST_MAPPING[enum_val]: enum_name
for enum_name, enum_val in attribute.enum_mapping.items()
}
return builder.mbbOut(
pv_name, always_update=True, on_update=on_update, **kwargs
)
case Float(prec):
return builder.aOut(
pv_name, always_update=True, on_update=on_update, PREC=prec
Expand All @@ -67,7 +102,7 @@ def _get_output_record(pv_name: str, datatype: DataType, on_update: Callable) ->

def _create_and_link_write_pv(pv_name: str, attribute: AttrW) -> None:
record = _get_output_record(
pv_name, attribute.datatype, on_update=attribute.process_without_display_update
pv_name, attribute, on_update=attribute.process_without_display_update
)

async def async_wrapper(v):
Expand Down

0 comments on commit 4ec97c2

Please sign in to comment.