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
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# yaml-language-server: $schema=schema.json
controllers:
- name: BL01T-EA-TEMP-01
- id: BL01T-EA-TEMP-01
type: fastcs.TemperatureController
ip_settings:
ip: "localhost"
Expand Down
8 changes: 8 additions & 0 deletions src/techui_builder/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ def _extract_entities(self, service_name: str, service_yaml: Path):
entity_key = match.group()

for entity in ioc_conf[entity_key]:
component_name = None
if entity["type"].startswith("fastcs"):
component_name = entity["type"]
entity["type"] = "fastcs*"

if entity["type"] in self.techui_support.support_modules:
support_mapping: SupportEntity = (
self.techui_support.support_modules[entity["type"]]
Expand All @@ -178,10 +183,12 @@ def _extract_entities(self, service_name: str, service_yaml: Path):
desc=entity.get("desc", None),
prefix=prefix,
macros=macros,
name=component_name,
)

pv_root = prefix.split(":", maxsplit=1)[0]
self.entities[pv_root].append(new_entity)

break

def _generate_screen(self, screen_name: str):
Expand Down Expand Up @@ -216,6 +223,7 @@ def create_screens(self):
# with the same prefix as the component
for entity in self.entities[component.prefix]:
entity.child_labels = component.child_labels
entity.file = component.file

screen_entities.extend(self.entities[component.prefix])

Expand Down
55 changes: 40 additions & 15 deletions src/techui_builder/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
from collections import defaultdict
from collections.abc import Mapping
from dataclasses import dataclass, field
from io import BytesIO
from pathlib import Path

import requests
from jinja2 import Template
from lxml import objectify
from phoebusgen import screen as pscreen
Expand Down Expand Up @@ -42,13 +44,17 @@ class Generator:
group_padding: int = field(default=50, init=False, repr=False)
label_flag: bool = field(default=False, init=False, repr=False)

def _get_screen_dimensions(self, file: str) -> tuple[int, int]:
def _get_screen_dimensions(self, file: Path | bytes) -> tuple[int, int]:
"""
Parses the bob files for information on the height
and width of the screen
"""
# Read the bob file
tree = objectify.parse(file)
if isinstance(file, bytes):
tree = objectify.parse(BytesIO(file))
else:
tree = objectify.parse(str(file))

root = tree.getroot()
try:
height_element = root.height
Expand Down Expand Up @@ -162,7 +168,11 @@ def _update_macros(self, component: Entity) -> tuple[str, dict[str, str]]:
suffix_key = next(k for k, v in component.macros.items() if v == suffix)
except (IndexError, ValueError):
prefix = component.prefix
component_name = component.type
component_name = (
component.name
if component.type == "fastcs*" and component.name is not None
else component.type
)
suffix_key = suffix = ""

# Try to get name from child labels if they exist,
Expand All @@ -189,23 +199,35 @@ def _allocate_widget(

# Get relative path to screen
file = Template(screen_mapping["file"]).render(component.macros)

# IF the file starts with IOC, and needs macro expansion
if file.startswith("$(IOC)"):
screen_path = support_screen_path = file.replace(
"$(IOC)", f"{self.beamline_url}/{component.service_name}"
) # Only works with related displays as
# embedded displays need to access the file to get dimensions

assert screen_mapping["type"] == "related", (
"Only related displays can have remote screens"
)
# For embedded screens, that need to be placed on screen and dimensions,
# it is required to fetch the screen from remote
if screen_mapping["type"] == "embedded" and str(
support_screen_path
).startswith("https"):
try:
screen_path = requests.get(str(support_screen_path)).content
except requests.RequestException:
logger_.warning(
f"Could not retrieve file from link {support_screen_path}"
)
else:
screen_path = self.support_path / f"bob/{file}"
logger_.debug(f"Screen path: {screen_path}")
support_bob = (self.support_path / "bob").resolve()
configured_path = Path(file)

# Path of screen relative to synoptic/
support_screen_path = screen_path.relative_to(
self.synoptic_dir, walk_up=True
)
if configured_path.is_absolute():
screen_path = configured_path.resolve()
elif configured_path.parts[:2] == ("techui-support", "bob"):
screen_path = (self.synoptic_dir / configured_path).resolve()
else:
screen_path = (support_bob / configured_path).resolve()

support_screen_path = screen_path.relative_to(self.synoptic_dir.resolve())

# For Gui Components with multiple components embedded, we add a suffix field
# to the components, and adjust the name and suffix accordingly
Expand All @@ -228,7 +250,7 @@ def _allocate_widget(
pass

if screen_mapping["type"] == "embedded":
height, width = self._get_screen_dimensions(str(screen_path))
height, width = self._get_screen_dimensions(screen_path)
new_widget = pwidget.EmbeddedDisplay(
component_name,
str(support_screen_path),
Expand Down Expand Up @@ -283,6 +305,9 @@ def _create_widgets(
{name}. Skipping..."
)
return None
# if component is fastcs, and has the field of file, add it to the support
if component.file:
screen_mapping.append({"file": component.file, "type": "embedded"})

for screen_dict in screen_mapping:
new_widget.append(self._allocate_widget(screen_dict, component))
Expand Down
8 changes: 8 additions & 0 deletions src/techui_builder/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,14 @@ class Entity(BaseModel):
dict[str, Any],
Field(description="Macros for the matching screen (can be empty)"),
]
file: Annotated[
str | None,
Field(description="File path for custom screen"),
] = None
name: Annotated[
str | None,
Field(description="placeholder name for fastcs screens"),
] = None


class SupportEntity(BaseModel):
Expand Down
4 changes: 1 addition & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,7 @@ def techui_support():
},
],
),
"fastcs.TemperatureController": SupportEntity(
prefix="{{ name }}", macros=["name"], screens=[{}]
),
"fastcs*": SupportEntity(prefix="{{ id }}", macros=["id"], screens=[{}]),
}

return ts
Expand Down
8 changes: 5 additions & 3 deletions tests/test_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,20 @@ def test_gb_extract_entities_ioc_yaml(


@pytest.mark.parametrize(
"index, type, desc, pv, macros",
"index, type, name, desc, pv, macros",
[
(
0,
"fastcs*",
"fastcs.TemperatureController",
None,
"BL01T-EA-TEMP-01",
{"name": "BL01T-EA-TEMP-01"},
{"id": "BL01T-EA-TEMP-01"},
),
],
)
def test_gb_extract_entities_fastcs_yaml(
builder, techui_support, index, type, desc, pv, macros
builder, techui_support, index, type, name, desc, pv, macros
):
# We don't want to use builder_with_setup as that calls _extract_services()
# and in turn that calls _extract_entities()
Expand All @@ -151,6 +152,7 @@ def test_gb_extract_entities_fastcs_yaml(
)
entity = builder.entities[prefix][index]
assert entity.type == type
assert entity.name == name
assert entity.desc == desc
assert entity.prefix == pv
assert entity.macros == macros
Expand Down