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
20 changes: 20 additions & 0 deletions docs/arguments.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,26 @@ This argument allows users to override the output file if it already exists. Mos

This argument adds scroll bars to converted screens. It will ad a horizontal and vertical scroll bar. If this argument is not included, the screen will not contain a scroll bar.

## Site

`--site <name>`

This argument applies site-specific conversion rules during the conversion process. Some facilities have EDM widgets or patterns that have no equivalent in PyDM and should be skipped or handled differently. The `--site` flag lets you opt into these rules without affecting the default conversion behavior.

**Available sites:**

| Site | Rules Applied |
|--------|---------------|
| `slac` | Skips `activeExitButtonClass` (exit buttons present on every SLAC EDM screen that have no PyDM equivalent) |

**Usage:**
``` bash
pydmconverter /path/to/file.edl output.ui --site slac
```

**Adding a new site:**
To add conversion rules for a new site, create a new module in `pydmconverter/sites/` (e.g. `mysite.py`) that defines a `SKIP_WIDGETS` set, then register it in `pydmconverter/sites/__init__.py`.

## Help

`-h` or `--help`
Expand Down
7 changes: 6 additions & 1 deletion docs/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,10 @@ pydmconverter /afs/slac/g/lcls/edm/file.edl file.ui`
```
To convert EDM files in a folder called "edm" to PYDM file in the current folder :
``` bash
pydmconverter /afs/slac/g/lcls/edm . .edl`
pydmconverter /afs/slac/g/lcls/edm . .edl
```

To convert with SLAC-specific rules (e.g. skipping exit buttons):
``` bash
pydmconverter /afs/slac/g/lcls/edm/file.edl file.ui --site slac
```
23 changes: 15 additions & 8 deletions pydmconverter/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def run_gui() -> None:
subprocess.run(["bash", str(launch_script)], check=True)


def run(input_file, output_file, input_file_type=".edl", override=False, scrollable=False):
def run(input_file, output_file, input_file_type=".edl", override=False, scrollable=False, site=None):
input_path: Path = Path(input_file)
output_path: Path = Path(output_file)

Expand All @@ -39,15 +39,15 @@ def run(input_file, output_file, input_file_type=".edl", override=False, scrolla
if output_path.is_file() and not override:
raise FileExistsError(f"Output file '{output_path}' already exists. Use --override or -o to overwrite it.")
copy_img_files(input_path.parent, output_path.parent)
convert(str(input_path), str(output_path), scrollable)
convert(str(input_path), str(output_path), scrollable, site=site)
else:
if input_file_type[0] != ".": # prepending . so it will not pick up other file types with same suffix
input_file_type = "." + input_file_type
output_path.mkdir(parents=True, exist_ok=True)
files_found: int
files_failed: list[str]
files_found, files_failed = convert_files_in_folder(
input_path, output_path, input_file_type, override, scrollable
input_path, output_path, input_file_type, override, scrollable, site=site
)

if files_found == 0:
Expand Down Expand Up @@ -76,7 +76,8 @@ def run_cli(args: argparse.Namespace) -> None:
input_file_type: str = args.output_type
override: bool = args.override
scrollable: bool = args.scrollable
run(input_file, output_file, input_file_type, override, scrollable)
site: str = args.site
run(input_file, output_file, input_file_type, override, scrollable, site=site)


"""
Expand All @@ -98,7 +99,7 @@ def run_cli(args: argparse.Namespace) -> None:
files_found: int
files_failed: list[str]
files_found, files_failed = convert_files_in_folder(
input_path, output_path, input_file_type, override, scrollable
input_path, output_path, input_file_type, override, scrollable, site=site
)

if files_found == 0:
Expand All @@ -125,7 +126,7 @@ def copy_img_files(input_path: Path, output_path: Path) -> None:


def convert_files_in_folder(
input_path: Path, output_path: Path, input_file_type: str, override: bool, scrollable: bool
input_path: Path, output_path: Path, input_file_type: str, override: bool, scrollable: bool, site=None
) -> tuple[int, list[str]]:
"""Recursively runs convert on files in directory and subdirectories

Expand Down Expand Up @@ -161,7 +162,7 @@ def convert_files_in_folder(
logging.warning(f"Skipped: {output_file_path} already exists. Use --override or -o to overwrite it.")
else:
try:
convert(file, output_file_path, scrollable)
convert(file, output_file_path, scrollable, site=site)
except Exception as e:
files_failed.append(str(file))
logging.warning(f"Failed to convert {file}: {e}")
Expand All @@ -171,7 +172,7 @@ def convert_files_in_folder(
subdirectories = [item for item in input_path.iterdir() if item.is_dir()]
for subdir in subdirectories:
sub_found, sub_failed = convert_files_in_folder(
subdir, output_path / subdir.name, input_file_type, override, scrollable
subdir, output_path / subdir.name, input_file_type, override, scrollable, site=site
)
files_found += sub_found
files_failed += sub_failed
Expand Down Expand Up @@ -227,6 +228,12 @@ def main() -> None:
action="store_true",
help="create scrollable pydm windows that replicate edm windows (may cause spacing issues for embedded displays)",
)
parser.add_argument(
"--site",
type=str,
default=None,
help="Apply site-specific conversion rules (e.g., slac)",
)
args: argparse.Namespace = parser.parse_args()

if args.input_file:
Expand Down
4 changes: 2 additions & 2 deletions pydmconverter/edm/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
}


def convert(input_path, output_path, scrollable=False):
def convert(input_path, output_path, scrollable=False, site=None):
try:
edm_parser = EDMFileParser(input_path, output_path)
pprint(edm_parser.ui, indent=2)
Expand All @@ -61,7 +61,7 @@ def convert(input_path, output_path, scrollable=False):

# edm_parser.ui, _, _ = replace_calc_and_loc_in_edm_content(edm_parser.ui, input_path)

pydm_widgets, used_classes = convert_edm_to_pydm_widgets(edm_parser)
pydm_widgets, used_classes = convert_edm_to_pydm_widgets(edm_parser, site=site)
logger.info(f"Converted EDM objects to {len(pydm_widgets)} PyDM widgets.")

page_header = PageHeader()
Expand Down
9 changes: 8 additions & 1 deletion pydmconverter/edm/converter_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ def handle_button_polygon_overlaps(pydm_widgets):
return pydm_widgets


def convert_edm_to_pydm_widgets(parser: EDMFileParser):
def convert_edm_to_pydm_widgets(parser: EDMFileParser, site=None):
"""
Converts an EDMFileParser object into a collection of PyDM widget instances.

Expand All @@ -437,6 +437,10 @@ def convert_edm_to_pydm_widgets(parser: EDMFileParser):
List[Union[widgets.PyDMWidgetBase, widgets.PyDMGroup]]
A list of PyDM widget instances representing the EDM UI.
"""
from pydmconverter.sites import get_skip_widgets

skip_widgets = get_skip_widgets(site)

pydm_widgets = []
used_classes = set()
color_list_filepath = search_color_list()
Expand Down Expand Up @@ -529,6 +533,9 @@ def traverse_group(
)

elif isinstance(obj, EDMObject):
if obj.name.lower() in skip_widgets:
logger.info(f"Skipping {obj.name} (site rule: {site})")
continue
if obj.name.lower() == "activelineclass":
widget_type = get_polyline_widget_type(obj)
elif obj.name.lower() == "activearcclass":
Expand Down
12 changes: 12 additions & 0 deletions pydmconverter/sites/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from typing import Optional, Set


def get_skip_widgets(site: Optional[str]) -> Set[str]:
"""Return set of lowercase EDM widget class names to skip for the given site."""
if site is None:
return set()
if site == "slac":
from pydmconverter.sites.slac import SKIP_WIDGETS

return SKIP_WIDGETS
raise ValueError(f"Unknown site: {site}")
2 changes: 2 additions & 0 deletions pydmconverter/sites/slac.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# EDM widget class names (lowercase) to skip during conversion at SLAC
SKIP_WIDGETS = {"activeexitbuttonclass"}
16 changes: 16 additions & 0 deletions tests/test_sites.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pytest
from pydmconverter.sites import get_skip_widgets


def test_get_skip_widgets_none():
assert get_skip_widgets(None) == set()


def test_get_skip_widgets_slac():
result = get_skip_widgets("slac")
assert "activeexitbuttonclass" in result


def test_get_skip_widgets_unknown():
with pytest.raises(ValueError, match="Unknown site"):
get_skip_widgets("unknown")
Loading