Skip to content

[GANIL-27] extend gui to support park engage for 3 axes - #3505

Open
nandishjpatel wants to merge 1 commit into
delmic:masterfrom
nandishjpatel:GANIL-27-extend-gui-to-support-park-engage-for-3-axes
Open

[GANIL-27] extend gui to support park engage for 3 axes#3505
nandishjpatel wants to merge 1 commit into
delmic:masterfrom
nandishjpatel:GANIL-27-extend-gui-to-support-park-engage-for-3-axes

Conversation

@nandishjpatel

Copy link
Copy Markdown
Contributor
image

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends the SPARC2 GUI/controller logic to support a park/engage mirror actuator with 3 axes (x/y/z), including updated XRC widgets and actuator binding/mapping so existing controls can be reused for the additional axis.

Changes:

  • Updated SPARC2 align panel XRC to provide separate XY vs per-axis step-size sliders (with runtime hide/show).
  • Added mirror axis set constants + helper (get_mirror_pos_parked) and updated SPARC2 chamber/align tabs to work with LS or XYZ mirror axes.
  • Extended ActuatorController to allow overriding slider↔stepsize and button↔(actuator, axis, factor) bindings; added a stage None guard in acquisition.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/odemis/gui/xmlh/resources/panel_tab_sparc2_align.xrc Adds new named step-size labels/sliders for XY and individual axes (X/Y hidden by default).
src/odemis/gui/model/tab_gui_data.py Renames mirror XY step-size key and adds a mirror Z step-size definition.
src/odemis/gui/main_xrc.py Wires up new XRC controls and embeds the updated XRC resource XML.
src/odemis/gui/cont/tabs/sparc2_chamber_tab.py Generalizes mirror park/engage logic to LS and XYZ actuators using parked-position helper/constants.
src/odemis/gui/cont/tabs/sparc2_align_tab.py Reuses existing stage Z widgets/buttons to drive mirror Z for XYZ mirrors; updates parked distance calc.
src/odemis/gui/cont/tabs/sparc_acquisition_tab.py Adds main_data.stage existence guard before dereferencing.
src/odemis/gui/cont/tabs/_constants.py Introduces mirror axis constants and parked-position helper.
src/odemis/gui/cont/actuators.py Adds optional override maps for slider/button bindings to support control reuse.

Comment thread src/odemis/gui/cont/tabs/sparc2_chamber_tab.py
Comment thread src/odemis/gui/cont/tabs/sparc2_chamber_tab.py
Comment thread src/odemis/gui/cont/tabs/_constants.py Outdated
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The changes generalize mirror actuator handling from fixed l/s assumptions to mirror-specific axis sets. ActuatorController accepts optional slider and button override maps. Parked positions use mirror metadata or per-axis defaults. SPARC2 alignment and chamber tabs use axis-aware movement, state, and display-priority logic. Mirror step-size controls are split into XY, X, and Y sliders. Scan-stage centering now checks for an available stage.

Sequence Diagram(s)

sequenceDiagram
  participant Sparc2AlignTab
  participant TabData
  participant ActuatorController
  participant Panel

  Sparc2AlignTab->>TabData: read mirror axes and stepsizes
  Sparc2AlignTab->>ActuatorController: provide slider and button overrides
  ActuatorController->>TabData: derive default bindings
  ActuatorController->>Panel: bind available slider and button widgets
  ActuatorController-->>Sparc2AlignTab: controller ready
Loading
sequenceDiagram
  participant ChamberTab
  participant Mirror
  participant ParkedPosition
  participant UI

  ChamberTab->>Mirror: read axes, position, and metadata
  ChamberTab->>ParkedPosition: compute parked position
  ParkedPosition-->>ChamberTab: return metadata or zero fallback
  ChamberTab->>Mirror: execute axis-ordered park or reference moves
  ChamberTab->>UI: update progress and switch state
Loading

Possibly related PRs

  • delmic/odemis#3396: Directly connects to Sparc2AlignTab and its mirror-alignment GUI controls.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: GUI support for park and engage operations across three axes.
Description check ✅ Passed The image shows the three-axis mirror controls added by the changeset, so the description is related to the pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/odemis/gui/model/tab_gui_data.py (1)

763-775: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep the LS mirror axis mapping

main.mirror is also used for LS mirrors ({"l", "s"}), but this branch only registers {"x", "y", "z"}. For LS hardware that leaves tab_data.axes empty, which hides the mirror controls and breaks auto-align. Preserve the l/s bindings for main.mirror here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/gui/model/tab_gui_data.py` around lines 763 - 775, The main.mirror
fallback in tab_gui_data should preserve LS axis bindings, not just X/Y/Z.
Update the mirror registration logic so the branch in the tab data setup that
handles main.mirror also keeps the {"l", "s"} mappings used by LS hardware,
while still supporting the existing {"x", "y", "z"} cases. Use the
mirror_xy/mirror handling around ss_def.update in tab_gui_data as the place to
adjust the axis map so tab_data.axes is populated correctly for LS mirrors.
🧹 Nitpick comments (5)
src/odemis/gui/cont/actuators.py (3)

98-101: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Missing type hints on tab_data, tab_panel, tab_prefix, and return type.

Only the new override parameters are typed; the existing parameters and the return type (None) remain unannotated.

🔧 Suggested fix
     def __init__(
-            self, tab_data, tab_panel, tab_prefix, slider_ss_map: Optional[dict] = None,
-            btn_actuator_map: Optional[dict] = None
-        ):
+            self, tab_data: "ActuatorGUIData", tab_panel: wx.Frame, tab_prefix: str,
+            slider_ss_map: Optional[dict] = None,
+            btn_actuator_map: Optional[dict] = None
+        ) -> None:

As per coding guidelines, "Always use type hints for function parameters and return types in Python code".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/gui/cont/actuators.py` around lines 98 - 101, The __init__ method
in the Actuators controller is missing type annotations for the existing
parameters and return type. Update the signature of __init__ in the Actuators
class so tab_data, tab_panel, and tab_prefix all have explicit type hints, and
add the None return annotation as well; keep the existing Optional dict
annotations for slider_ss_map and btn_actuator_map unchanged.

Source: Coding guidelines


102-113: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Docstring embeds type information in parentheses.

Entries like "tab_data (ActuatorGUIData): the data model of the tab" and "tab_panel: (wx.Frame): the main frame of the GUI" include type info inline, which the coding guidelines ask to avoid since types are now expressed via annotations.

As per coding guidelines, "Include docstrings for all functions and classes, following the reStructuredText style guide, without type information and without using inline formatting markers or backticks".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/gui/cont/actuators.py` around lines 102 - 113, The docstring on
the actuator binding helper still embeds type information inline, which should
be removed to match the docstring guidelines. Update the docstring for the
binding function in actuators.py so parameter descriptions only describe the
values and purpose, without parenthesized types or other inline type markers;
keep the reStructuredText style and rely on the existing function annotations
for types. Focus on the docstring attached to the actuator/button binding
routine that mentions tab_data, tab_panel, tab_prefix, slider_ss_map, and
btn_actuator_map.

Source: Coding guidelines


125-138: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing-widget lookups now fail silently.

AttributeError on getattr(tab_panel, slider_name) / getattr(tab_panel, btn_name) are swallowed with no logging, whereas the previous implementation logged these misses for debugging. If an override map contains a typo'd widget name, the mistake will go unnoticed instead of surfacing in logs.

🔧 Suggested fix
             try:
                 slider = getattr(tab_panel, slider_name)
             except AttributeError:
+                logging.debug("Skipping slider %s, not found on panel", slider_name)
                 continue
             try:
                 btn = getattr(tab_panel, btn_name)
             except AttributeError:
+                logging.debug("Skipping button %s, not found on panel", btn_name)
                 continue

Also applies to: 152-167

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/gui/cont/actuators.py` around lines 125 - 138, The widget lookup
in the actuator setup is swallowing missing attributes too quietly: when getatt
r(tab_panel, slider_name) or getatt r(tab_panel, btn_name) raises
AttributeError, the code in the slider/button mapping loop should log which
widget name was not found before continuing. Update the lookup handling in the
relevant actuator initialization logic (the slider and button connector paths in
actuators.py) so typos in override maps are surfaced through logging instead of
failing silently.
src/odemis/gui/cont/tabs/_constants.py (2)

51-59: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Function returns shared mutable module-level constants directly.

MIRROR_POS_PARKED_LS / MIRROR_POS_PARKED_XYZ (and the metadata dict) are returned by reference. If any current or future caller mutates the returned dict, it would corrupt the shared constant for all subsequent callers.

🛡️ Suggested fix
     pos_parked = mirror.getMetadata().get(model.MD_FAV_POS_DEACTIVE, None)
     if pos_parked is not None:
-        return pos_parked
+        return dict(pos_parked)
     axes = set(mirror.axes.keys())
     if axes == MIRROR_AXES_XYZ:
-        return MIRROR_POS_PARKED_XYZ
+        return dict(MIRROR_POS_PARKED_XYZ)
     elif axes == MIRROR_AXES_LS:
-        return MIRROR_POS_PARKED_LS
+        return dict(MIRROR_POS_PARKED_LS)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/gui/cont/tabs/_constants.py` around lines 51 - 59, The helper that
resolves parked mirror positions is returning shared mutable module-level data
directly, so callers can accidentally mutate constants. Update the logic in the
function that checks MD_FAV_POS_DEACTIVE and branches on MIRROR_AXES_XYZ /
MIRROR_AXES_LS to return a fresh copy of the metadata dict each time instead of
the module-level MIRROR_POS_PARKED_LS or MIRROR_POS_PARKED_XYZ object, and
preserve the same behavior for the metadata fallback path.

44-50: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Docstring uses RST directive markup (:param:, :return:, :raises:).

Based on learnings, this repo's convention is plain-text docstrings without RST markup/directives like ":param:", ":return:", or ":type:"; write documentation as unformatted text instead.

🔧 Suggested fix
 def get_mirror_pos_parked(mirror: model.HwComponent) -> Dict[str, float]:
     """
-    Return the parked position dict for the given mirror actuator.
-    :param mirror: the mirror component (must have .axes)
-    :return: parked position
-    :raises: ValueError if the mirror has unknown axes
+    Return the parked position dict for the given mirror actuator (must have .axes).
+    Raises ValueError if the mirror has unknown axes.
     """
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/gui/cont/tabs/_constants.py` around lines 44 - 50, The docstring
on get_mirror_pos_parked uses RST-style directives, but this codebase expects
plain-text docstrings. Update the documentation in get_mirror_pos_parked to
remove :param:, :return:, and :raises: markup and rewrite the description as
simple unformatted text that still explains the mirror argument, the returned
parked position dict, and the ValueError condition.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/odemis/gui/model/tab_gui_data.py`:
- Around line 763-775: The main.mirror fallback in tab_gui_data should preserve
LS axis bindings, not just X/Y/Z. Update the mirror registration logic so the
branch in the tab data setup that handles main.mirror also keeps the {"l", "s"}
mappings used by LS hardware, while still supporting the existing {"x", "y",
"z"} cases. Use the mirror_xy/mirror handling around ss_def.update in
tab_gui_data as the place to adjust the axis map so tab_data.axes is populated
correctly for LS mirrors.

---

Nitpick comments:
In `@src/odemis/gui/cont/actuators.py`:
- Around line 98-101: The __init__ method in the Actuators controller is missing
type annotations for the existing parameters and return type. Update the
signature of __init__ in the Actuators class so tab_data, tab_panel, and
tab_prefix all have explicit type hints, and add the None return annotation as
well; keep the existing Optional dict annotations for slider_ss_map and
btn_actuator_map unchanged.
- Around line 102-113: The docstring on the actuator binding helper still embeds
type information inline, which should be removed to match the docstring
guidelines. Update the docstring for the binding function in actuators.py so
parameter descriptions only describe the values and purpose, without
parenthesized types or other inline type markers; keep the reStructuredText
style and rely on the existing function annotations for types. Focus on the
docstring attached to the actuator/button binding routine that mentions
tab_data, tab_panel, tab_prefix, slider_ss_map, and btn_actuator_map.
- Around line 125-138: The widget lookup in the actuator setup is swallowing
missing attributes too quietly: when getatt r(tab_panel, slider_name) or getatt
r(tab_panel, btn_name) raises AttributeError, the code in the slider/button
mapping loop should log which widget name was not found before continuing.
Update the lookup handling in the relevant actuator initialization logic (the
slider and button connector paths in actuators.py) so typos in override maps are
surfaced through logging instead of failing silently.

In `@src/odemis/gui/cont/tabs/_constants.py`:
- Around line 51-59: The helper that resolves parked mirror positions is
returning shared mutable module-level data directly, so callers can accidentally
mutate constants. Update the logic in the function that checks
MD_FAV_POS_DEACTIVE and branches on MIRROR_AXES_XYZ / MIRROR_AXES_LS to return a
fresh copy of the metadata dict each time instead of the module-level
MIRROR_POS_PARKED_LS or MIRROR_POS_PARKED_XYZ object, and preserve the same
behavior for the metadata fallback path.
- Around line 44-50: The docstring on get_mirror_pos_parked uses RST-style
directives, but this codebase expects plain-text docstrings. Update the
documentation in get_mirror_pos_parked to remove :param:, :return:, and :raises:
markup and rewrite the description as simple unformatted text that still
explains the mirror argument, the returned parked position dict, and the
ValueError condition.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 34a35a3e-53d4-4d2d-9735-f63c8b24f75f

📥 Commits

Reviewing files that changed from the base of the PR and between e6cb92c and f5576c3.

📒 Files selected for processing (8)
  • src/odemis/gui/cont/actuators.py
  • src/odemis/gui/cont/tabs/_constants.py
  • src/odemis/gui/cont/tabs/sparc2_align_tab.py
  • src/odemis/gui/cont/tabs/sparc2_chamber_tab.py
  • src/odemis/gui/cont/tabs/sparc_acquisition_tab.py
  • src/odemis/gui/main_xrc.py
  • src/odemis/gui/model/tab_gui_data.py
  • src/odemis/gui/xmlh/resources/panel_tab_sparc2_align.xrc

@pieleric pieleric left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we are soon going to have another system, with a X,Y, and RZ axes, let's try to keep the code generic whenever possible.
I'd made suggestions so that the only part required to support RZ will be the actuators related code.

Comment thread src/odemis/gui/cont/tabs/sparc2_chamber_tab.py Outdated
Comment thread src/odemis/gui/cont/tabs/sparc2_chamber_tab.py Outdated
Comment thread src/odemis/gui/cont/tabs/_constants.py Outdated
Comment thread src/odemis/gui/cont/actuators.py
Comment thread src/odemis/gui/cont/tabs/sparc2_chamber_tab.py Outdated
Comment thread src/odemis/gui/cont/tabs/sparc2_chamber_tab.py Outdated
Comment thread src/odemis/gui/cont/tabs/_constants.py Outdated
Comment thread src/odemis/gui/cont/tabs/_constants.py Outdated
Comment thread src/odemis/gui/cont/tabs/sparc2_align_tab.py Outdated
Comment thread src/odemis/gui/cont/actuators.py Outdated
Comment thread src/odemis/gui/cont/actuators.py
@nandishjpatel
nandishjpatel force-pushed the GANIL-27-extend-gui-to-support-park-engage-for-3-axes branch from f5576c3 to 8501476 Compare July 9, 2026 12:04
@nandishjpatel
nandishjpatel requested a review from tepals July 9, 2026 12:05

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/odemis/gui/cont/tabs/_constants.py (1)

38-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Docstring uses RST field markup contrary to recent reviewer guidance.

The docstring uses :param:/:return: directives. A recent learning from this same repo states docstrings should be plain text, without RST directives like :param:, :return:, :type:.

📝 Proposed plain-text docstring
     """
     Return the position dict corresponding to the parked position of the given mirror actuator.
     If MD_FAV_POS_DEACTIVE metadata is defined for the mirror, it is used. Otherwise, default to 0
     for each axis.
-    :param mirror: the mirror component (must have .axes)
-    :return: parked position as a dict of axis name -> position (m)
+    mirror is the mirror component (must have .axes).
+    Returns the parked position as a dict of axis name to position, in meters.
     """

Based on learnings, "keep docstrings as plain text only. Do not use reStructuredText (RST) markup/directives such as ':param:', ':return:', or ':type:' in docstrings; write the documentation as unformatted text instead." Note the coding guideline also asks to follow "the reStructuredText style guide, without type information and without using inline formatting markers or backticks" for **/*.py, so please reconcile these two expectations going forward.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/gui/cont/tabs/_constants.py` around lines 38 - 45, The docstring
for get_mirror_pos_parked still uses RST-style field directives, which conflicts
with the repo guidance for plain-text docstrings. Update the function’s
documentation to remove :param: and :return: markup and rewrite the descriptions
as simple prose while keeping the same meaning. Keep the docstring associated
with get_mirror_pos_parked clear and readable, and avoid adding any other RST
directives or inline markup.

Sources: Coding guidelines, Learnings

src/odemis/gui/cont/tabs/sparc2_chamber_tab.py (1)

234-237: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Chain the raised exception to the original KeyError (Ruff B904).

Static analysis flags this: raising inside an except clause without from ... obscures the original cause in tracebacks.

🔧 Proposed fix
-            try:
-                axes_order = mirror_md[model.MD_AXES_ORDER_REF]
-            except KeyError:
-                raise ValueError("Mirror actuator has no metadata AXES_ORDER_REF")
+            try:
+                axes_order = mirror_md[model.MD_AXES_ORDER_REF]
+            except KeyError as ex:
+                raise ValueError("Mirror actuator has no metadata AXES_ORDER_REF") from ex
As per static analysis hints: "Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/gui/cont/tabs/sparc2_chamber_tab.py` around lines 234 - 237, The
try/except in the mirror metadata lookup should chain the new ValueError to the
original KeyError to satisfy Ruff B904. Update the exception handling around the
`axes_order = mirror_md[model.MD_AXES_ORDER_REF]` access in
`sparc2_chamber_tab.py` so the raised ValueError preserves the caught `KeyError`
as its cause, keeping the existing message while making the traceback explicit.

Source: Linters/SAST tools

src/odemis/gui/cont/tabs/sparc2_align_tab.py (1)

858-858: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider a named constant for the mirror x/y/z axis set.

{"x", "y", "z"} is an inline literal here, whereas the LS case has a dedicated MIRROR_AXES_LS constant in _constants.py. A matching MIRROR_AXES_XYZ constant would avoid duplicating this literal if other layers need the same check (e.g., tab_gui_data.py per the stack summary) and keeps the axis-set vocabulary centralized.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/gui/cont/tabs/sparc2_align_tab.py` at line 858, The mirror axis
check in the tab logic uses an inline {"x", "y", "z"} literal, so replace it
with a shared named constant like MIRROR_AXES_XYZ in _constants.py and use that
constant in the relevant mirror-axis checks (including the condition in the
align tab and any other matching callers such as tab_gui_data.py). Keep the
axis-set definition centralized alongside MIRROR_AXES_LS so the vocabulary is
consistent and reused instead of duplicated.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/odemis/gui/cont/tabs/_constants.py`:
- Around line 46-49: The get_mirror_pos_parked() helper currently returns
MD_FAV_POS_DEACTIVE unchanged, so partial parked-position metadata can still
break callers like the chamber and align tabs when they access every axis.
Update the logic in get_mirror_pos_parked() to validate the returned metadata
against mirror.axes and either fill any missing axes with 0 or ignore incomplete
metadata and fall back to the default zeroed position.

In `@src/odemis/gui/cont/tabs/sparc2_align_tab.py`:
- Around line 854-882: The x/y/z mirror branch in the auto-align setup is
rebinding stage-Z UI controls to the mirror via slider_ss_map and
btn_actuator_map, so the visible slider_stage, btn_p_stage_z, and btn_m_stage_z
end up driving mirror_z instead of the stage. Update the logic around the
mirror-axis handling in this tab setup so the auto-align path and the x/y/z
mirror remapping are mutually exclusive, or explicitly skip remapping those
controls when auto-align is active. Use the existing panel and
ActuatorController wiring to keep the control labels and bound actuators
consistent.

In `@src/odemis/gui/cont/tabs/sparc2_chamber_tab.py`:
- Around line 362-373: The switch-mirror idle label logic in
sparc2_chamber_tab.py is choosing text from mirror_axes and mstate only, which
makes it diverge from the in-flight wording used by _on_switch_btn. Update the
label selection in the switch-mirror refresh path so it also considers
reference_is_deactive, matching the same decision used for “PARKING MIRROR” vs
“REFERENCING MIRROR” and keeping the idle button text consistent with the action
that will actually run. Use the existing switch-mirror label update block and
_on_switch_btn as the reference points for the fix.

---

Nitpick comments:
In `@src/odemis/gui/cont/tabs/_constants.py`:
- Around line 38-45: The docstring for get_mirror_pos_parked still uses
RST-style field directives, which conflicts with the repo guidance for
plain-text docstrings. Update the function’s documentation to remove :param: and
:return: markup and rewrite the descriptions as simple prose while keeping the
same meaning. Keep the docstring associated with get_mirror_pos_parked clear and
readable, and avoid adding any other RST directives or inline markup.

In `@src/odemis/gui/cont/tabs/sparc2_align_tab.py`:
- Line 858: The mirror axis check in the tab logic uses an inline {"x", "y",
"z"} literal, so replace it with a shared named constant like MIRROR_AXES_XYZ in
_constants.py and use that constant in the relevant mirror-axis checks
(including the condition in the align tab and any other matching callers such as
tab_gui_data.py). Keep the axis-set definition centralized alongside
MIRROR_AXES_LS so the vocabulary is consistent and reused instead of duplicated.

In `@src/odemis/gui/cont/tabs/sparc2_chamber_tab.py`:
- Around line 234-237: The try/except in the mirror metadata lookup should chain
the new ValueError to the original KeyError to satisfy Ruff B904. Update the
exception handling around the `axes_order = mirror_md[model.MD_AXES_ORDER_REF]`
access in `sparc2_chamber_tab.py` so the raised ValueError preserves the caught
`KeyError` as its cause, keeping the existing message while making the traceback
explicit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4c0e2154-9175-4b84-8d54-56be2a4f661e

📥 Commits

Reviewing files that changed from the base of the PR and between f5576c3 and 8501476.

📒 Files selected for processing (8)
  • src/odemis/gui/cont/actuators.py
  • src/odemis/gui/cont/tabs/_constants.py
  • src/odemis/gui/cont/tabs/sparc2_align_tab.py
  • src/odemis/gui/cont/tabs/sparc2_chamber_tab.py
  • src/odemis/gui/cont/tabs/sparc_acquisition_tab.py
  • src/odemis/gui/main_xrc.py
  • src/odemis/gui/model/tab_gui_data.py
  • src/odemis/gui/xmlh/resources/panel_tab_sparc2_align.xrc
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/odemis/gui/cont/tabs/sparc_acquisition_tab.py
  • src/odemis/gui/model/tab_gui_data.py
  • src/odemis/gui/main_xrc.py
  • src/odemis/gui/xmlh/resources/panel_tab_sparc2_align.xrc
  • src/odemis/gui/cont/actuators.py

Comment thread src/odemis/gui/cont/tabs/_constants.py
Comment thread src/odemis/gui/cont/tabs/sparc2_align_tab.py
Comment thread src/odemis/gui/cont/tabs/sparc2_chamber_tab.py
Comment thread src/odemis/gui/cont/actuators.py
@pieleric

Copy link
Copy Markdown
Member

This GUI change don't seem to align with the hardware change (PR #3508):
The driver expects to have .reference() called just once, with all 3 axes. For now the GUI (in case mstate == MIRROR_NOT_REFD) will call it once per axis.
You might need to introduce (yet) another config flag to indicate something like "the driver handles by itself the referencing, just do it once for all axes". For now, this flag might actually be sufficient to distinguish the use case, and then you might want to remove the reference_is_deactive flag, to keep the code simple.

@nandishjpatel
nandishjpatel force-pushed the GANIL-27-extend-gui-to-support-park-engage-for-3-axes branch from 8501476 to 4c8bd99 Compare July 16, 2026 11:08

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/odemis/gui/cont/tabs/sparc2_chamber_tab.py (1)

369-375: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Align idle switch-mirror labels with reference_at_once action.

The idle switch-mirror UI logic incorrectly relies on the mirror axis set instead of checking the reference_at_once configuration, causing a mismatch with the in-flight actions executed by _on_switch_btn.

  • src/odemis/gui/cont/tabs/sparc2_chamber_tab.py#L369-L375: Update the button label selection to check reference_at_once instead of the axis set, choosing "REFERENCE MIRROR" when true and "PARK MIRROR" otherwise.
  • src/odemis/gui/cont/tabs/sparc2_chamber_tab.py#L346-L355: Update the warning text to similarly depend on reference_at_once.
🐛 Proposed fixes to align with reference_at_once

For the warning text (src/odemis/gui/cont/tabs/sparc2_chamber_tab.py#L346-L355):

         if mstate == MIRROR_NOT_REFD:
-            if mirror_axes == MIRROR_AXES_LS:
+            reference_at_once = mirror.getMetadata().get(model.MD_CALIB, {}).get("reference_at_once", False)
+            if not reference_at_once:
                 txt_warning = ("Parking the mirror is required at least once in order "
                                "to reference the actuators.")
             else:
                 txt_warning = "Referencing the mirror is required at least once."

For the button label (src/odemis/gui/cont/tabs/sparc2_chamber_tab.py#L369-L375):

         if mstate == MIRROR_PARKED:
             btn_text = "ENGAGE MIRROR"
         else:
-            if mirror_axes == MIRROR_AXES_LS:
-                btn_text = "PARK MIRROR"
-            else:
-                if mstate == MIRROR_NOT_REFD:
-                    btn_text = "REFERENCE MIRROR"
-                else:
-                    btn_text = "PARK MIRROR"
+            reference_at_once = mirror.getMetadata().get(model.MD_CALIB, {}).get("reference_at_once", False)
+            if mstate == MIRROR_NOT_REFD and reference_at_once:
+                btn_text = "REFERENCE MIRROR"
+            else:
+                btn_text = "PARK MIRROR"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/gui/cont/tabs/sparc2_chamber_tab.py` around lines 369 - 375,
Update the idle switch-mirror logic in
src/odemis/gui/cont/tabs/sparc2_chamber_tab.py at lines 369-375 and 346-355 to
use the reference_at_once configuration, matching _on_switch_btn: show
“REFERENCE MIRROR” and the corresponding warning when true, otherwise show “PARK
MIRROR” and its warning. Remove the mirror_axes-based decision while preserving
the existing mstate handling where applicable.
src/odemis/gui/cont/tabs/sparc2_align_tab.py (1)

854-882: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Stage-Z controls are rebound to the mirror in x/y/z mode, conflicting with auto-align.

The x/y/z mirror branch rebinds slider_stage, btn_p_stage_z, and btn_m_stage_z to mirror_z. If auto-align is also active, it exposes these exact widgets to control the stage, but they will end up driving the mirror instead. Keep these branches mutually exclusive or explicitly skip remapping those controls when auto-align is active.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/gui/cont/tabs/sparc2_align_tab.py` around lines 854 - 882, The
SPARCv2 x/y/z mirror setup in the initialization branch remaps stage-Z widgets
that auto-align requires for stage control. Make the mirror remapping
conditional on auto-align being inactive, or otherwise exclude slider_stage,
btn_p_stage_z, and btn_m_stage_z from btn_actuator_map/slider_ss_map when
auto-align is active, while preserving mirror x/y mappings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/odemis/gui/cont/tabs/sparc2_align_tab.py`:
- Around line 2409-2411: Update the listed function signatures with the
requested parameter and return annotations: in
src/odemis/gui/cont/tabs/sparc2_align_tab.py lines 2409-2411, annotate
_onMirrorPos as pos: dict[str, float] -> None; in lines 2619, annotate
get_display_priority as main_data: model.MainGUIData -> int | None; in
src/odemis/gui/cont/tabs/sparc2_chamber_tab.py lines 141-157, annotate
_get_mirror_state as mirror: model.HwComponent -> int; lines 184-187, annotate
_update_progress_bar as pos: dict[str, float] -> None; lines 228-242, annotate
_on_switch_btn as evt: wx.Event -> None; and line 420, annotate
get_display_priority as main_data: model.MainGUIData -> int | None.

In `@src/odemis/gui/cont/tabs/sparc2_chamber_tab.py`:
- Around line 234-237: Update the KeyError handler in the axes_order lookup to
raise the existing ValueError with explicit exception suppression using “from
None”, while preserving the current error message and behavior.

---

Duplicate comments:
In `@src/odemis/gui/cont/tabs/sparc2_align_tab.py`:
- Around line 854-882: The SPARCv2 x/y/z mirror setup in the initialization
branch remaps stage-Z widgets that auto-align requires for stage control. Make
the mirror remapping conditional on auto-align being inactive, or otherwise
exclude slider_stage, btn_p_stage_z, and btn_m_stage_z from
btn_actuator_map/slider_ss_map when auto-align is active, while preserving
mirror x/y mappings.

In `@src/odemis/gui/cont/tabs/sparc2_chamber_tab.py`:
- Around line 369-375: Update the idle switch-mirror logic in
src/odemis/gui/cont/tabs/sparc2_chamber_tab.py at lines 369-375 and 346-355 to
use the reference_at_once configuration, matching _on_switch_btn: show
“REFERENCE MIRROR” and the corresponding warning when true, otherwise show “PARK
MIRROR” and its warning. Remove the mirror_axes-based decision while preserving
the existing mstate handling where applicable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 793b9761-7ed4-496e-a0cd-c69a47a8dd64

📥 Commits

Reviewing files that changed from the base of the PR and between 8501476 and 4c8bd99.

📒 Files selected for processing (8)
  • src/odemis/gui/cont/actuators.py
  • src/odemis/gui/cont/tabs/_constants.py
  • src/odemis/gui/cont/tabs/sparc2_align_tab.py
  • src/odemis/gui/cont/tabs/sparc2_chamber_tab.py
  • src/odemis/gui/cont/tabs/sparc_acquisition_tab.py
  • src/odemis/gui/main_xrc.py
  • src/odemis/gui/model/tab_gui_data.py
  • src/odemis/gui/xmlh/resources/panel_tab_sparc2_align.xrc
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/odemis/gui/cont/tabs/sparc_acquisition_tab.py
  • src/odemis/gui/model/tab_gui_data.py
  • src/odemis/gui/xmlh/resources/panel_tab_sparc2_align.xrc
  • src/odemis/gui/main_xrc.py
  • src/odemis/gui/cont/tabs/_constants.py
  • src/odemis/gui/cont/actuators.py

Comment thread src/odemis/gui/cont/tabs/sparc2_align_tab.py
Comment thread src/odemis/gui/cont/tabs/sparc2_chamber_tab.py
@nandishjpatel
nandishjpatel force-pushed the GANIL-27-extend-gui-to-support-park-engage-for-3-axes branch from 4c8bd99 to e11a3f4 Compare July 16, 2026 12:30

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/odemis/gui/cont/tabs/sparc2_chamber_tab.py (1)

347-374: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use reference_at_once instead of mirror_axes for UI state text to avoid mismatch with the in-flight action.

The idle switch-mirror button label and status warning currently ignore reference_at_once, relying instead on mirror_axes == MIRROR_AXES_LS. This causes a mismatch with the in-flight action triggered in _on_switch_btn, where the logic and in-flight labels ("PARKING MIRROR" vs "REFERENCING MIRROR") are chosen based on reference_at_once. Using the flag uniformly ensures the visual state exactly matches what will execute.

🐛 Proposed fix
-        mirror_axes = set(mirror.axes.keys())
+        reference_at_once = mirror.getMetadata().get(model.MD_CALIB, {}).get("reference_at_once", False)
 
         if mstate == MIRROR_NOT_REFD:
-            if mirror_axes == MIRROR_AXES_LS:
+            if not reference_at_once:
                 txt_warning = ("Parking the mirror is required at least once in order "
                                "to reference the actuators.")
             else:
                 txt_warning = "Referencing the mirror is required at least once."
         elif mstate == MIRROR_BAD:
             txt_warning = "The mirror is neither fully parked nor entirely engaged."
         else:
             txt_warning = None
 
         self.panel.pnl_ref_msg.Show(txt_warning is not None)
         if txt_warning:
             self.panel.txt_warning.SetLabel(txt_warning)
             self.panel.txt_warning.Wrap(self.panel.pnl_ref_msg.Size[0] - 16)
 
         if mstate == MIRROR_PARKED:
             btn_text = "ENGAGE MIRROR"
         else:
-            if mirror_axes == MIRROR_AXES_LS:
-                btn_text = "PARK MIRROR"
-            else:
-                if mstate == MIRROR_NOT_REFD:
-                    btn_text = "REFERENCE MIRROR"
-                else:
-                    btn_text = "PARK MIRROR"
+            if mstate == MIRROR_NOT_REFD and reference_at_once:
+                btn_text = "REFERENCE MIRROR"
+            else:
+                btn_text = "PARK MIRROR"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/gui/cont/tabs/sparc2_chamber_tab.py` around lines 347 - 374,
Update the idle warning and button-label logic in the mirror state handler to
use reference_at_once instead of mirror_axes == MIRROR_AXES_LS when choosing
reference-versus-parking text. Keep the existing mstate handling and labels
unchanged otherwise, so the UI matches the action selection in _on_switch_btn.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/odemis/gui/cont/tabs/sparc_acquisition_tab.py`:
- Line 277: Update the centering guard in the SPARC acquisition flow to use `if
not main_data.stage or main_data.stage.name not in sstage.affects.value`,
ensuring centering runs when no main stage is configured or when the stage is
independent. Preserve the existing centering behavior for affected stages.

---

Duplicate comments:
In `@src/odemis/gui/cont/tabs/sparc2_chamber_tab.py`:
- Around line 347-374: Update the idle warning and button-label logic in the
mirror state handler to use reference_at_once instead of mirror_axes ==
MIRROR_AXES_LS when choosing reference-versus-parking text. Keep the existing
mstate handling and labels unchanged otherwise, so the UI matches the action
selection in _on_switch_btn.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 664dc4c9-1f7e-43a3-b935-1640fba156c7

📥 Commits

Reviewing files that changed from the base of the PR and between 4c8bd99 and e11a3f4.

📒 Files selected for processing (8)
  • src/odemis/gui/cont/actuators.py
  • src/odemis/gui/cont/tabs/_constants.py
  • src/odemis/gui/cont/tabs/sparc2_align_tab.py
  • src/odemis/gui/cont/tabs/sparc2_chamber_tab.py
  • src/odemis/gui/cont/tabs/sparc_acquisition_tab.py
  • src/odemis/gui/main_xrc.py
  • src/odemis/gui/model/tab_gui_data.py
  • src/odemis/gui/xmlh/resources/panel_tab_sparc2_align.xrc
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/odemis/gui/cont/tabs/_constants.py
  • src/odemis/gui/xmlh/resources/panel_tab_sparc2_align.xrc
  • src/odemis/gui/model/tab_gui_data.py
  • src/odemis/gui/main_xrc.py
  • src/odemis/gui/cont/actuators.py
  • src/odemis/gui/cont/tabs/sparc2_align_tab.py

Comment thread src/odemis/gui/cont/tabs/sparc_acquisition_tab.py Outdated
@nandishjpatel
nandishjpatel force-pushed the GANIL-27-extend-gui-to-support-park-engage-for-3-axes branch from e11a3f4 to 41a502d Compare July 16, 2026 12:35
@nandishjpatel
nandishjpatel requested review from K4rishma and pieleric and removed request for tepals July 21, 2026 07:48
Comment thread src/odemis/gui/cont/tabs/sparc2_chamber_tab.py Outdated
Comment thread src/odemis/gui/cont/tabs/sparc2_chamber_tab.py Outdated
Comment thread src/odemis/gui/cont/tabs/sparc2_chamber_tab.py Outdated
Copilot AI review requested due to automatic review settings August 2, 2026 16:17
@nandishjpatel
nandishjpatel force-pushed the GANIL-27-extend-gui-to-support-park-engage-for-3-axes branch from 41a502d to 304b47a Compare August 2, 2026 16:17

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/odemis/gui/cont/tabs/sparc2_chamber_tab.py`:
- Around line 237-239: Update the axes-order validation near the warning in the
chamber tab so an `axes_order` that is not exactly a permutation of
`mirror_axes` is rejected before any move-scheduling loops run. Replace the
warning-only behavior with the method’s established invalid-input handling,
ensuring invalid metadata cannot omit, duplicate, or introduce mirror axes while
valid orders continue unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5f84d242-9ad4-4dfe-8e44-3eb06b9e3a2f

📥 Commits

Reviewing files that changed from the base of the PR and between e11a3f4 and 304b47a.

📒 Files selected for processing (8)
  • src/odemis/gui/cont/actuators.py
  • src/odemis/gui/cont/tabs/_constants.py
  • src/odemis/gui/cont/tabs/sparc2_align_tab.py
  • src/odemis/gui/cont/tabs/sparc2_chamber_tab.py
  • src/odemis/gui/cont/tabs/sparc_acquisition_tab.py
  • src/odemis/gui/main_xrc.py
  • src/odemis/gui/model/tab_gui_data.py
  • src/odemis/gui/xmlh/resources/panel_tab_sparc2_align.xrc
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/odemis/gui/cont/tabs/sparc_acquisition_tab.py
  • src/odemis/gui/main_xrc.py
  • src/odemis/gui/model/tab_gui_data.py
  • src/odemis/gui/xmlh/resources/panel_tab_sparc2_align.xrc
  • src/odemis/gui/cont/actuators.py
  • src/odemis/gui/cont/tabs/_constants.py
  • src/odemis/gui/cont/tabs/sparc2_align_tab.py

Comment on lines +237 to +239
if set(axes_order) != mirror_axes:
logging.warning("Axes order of mirror is %s, while should have %s axes",
axes_order, mirror_axes)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject an invalid axis order before scheduling moves.

At Line 237, an invalid axes_order only writes a warning. The later loops can then omit a mirror axis, move one axis twice, or send a command for an unknown axis. Reject metadata that is not a permutation of the mirror axes.

Proposed fix
-        if set(axes_order) != mirror_axes:
-            logging.warning("Axes order of mirror is %s, while should have %s axes",
-                            axes_order, mirror_axes)
+        if len(axes_order) != len(mirror_axes) or set(axes_order) != mirror_axes:
+            raise ValueError(
+                "Mirror actuator metadata AXES_ORDER_REF must contain each mirror axis exactly once"
+            )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if set(axes_order) != mirror_axes:
logging.warning("Axes order of mirror is %s, while should have %s axes",
axes_order, mirror_axes)
if len(axes_order) != len(mirror_axes) or set(axes_order) != mirror_axes:
raise ValueError(
"Mirror actuator metadata AXES_ORDER_REF must contain each mirror axis exactly once"
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/odemis/gui/cont/tabs/sparc2_chamber_tab.py` around lines 237 - 239,
Update the axes-order validation near the warning in the chamber tab so an
`axes_order` that is not exactly a permutation of `mirror_axes` is rejected
before any move-scheduling loops run. Replace the warning-only behavior with the
method’s established invalid-input handling, ensuring invalid metadata cannot
omit, duplicate, or introduce mirror axes while valid orders continue unchanged.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/odemis/gui/cont/tabs/sparc2_chamber_tab.py:237

  • Raising ValueError here will likely bubble up from the GUI event handler and can crash the tab if a non-{l,s} mirror actuator lacks MD_AXES_ORDER_REF metadata. It would be safer to fall back to a deterministic axis order (e.g. mirror.axes key order) and log a warning instead of raising.
        else:
            try:
                axes_order = mirror_md[model.MD_AXES_ORDER_REF]
            except KeyError:
                raise ValueError("Mirror actuator has no metadata AXES_ORDER_REF")
        if set(axes_order) != mirror_axes:

@nandishjpatel
nandishjpatel requested a review from pieleric August 4, 2026 09:09
dist_parked = math.hypot(pos["l"] - MIRROR_POS_PARKED["l"],
pos["s"] - MIRROR_POS_PARKED["s"])
pos_parked = get_mirror_pos_parked(mirror)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe an empty line is not needed

@nandishjpatel
nandishjpatel requested a review from tepals August 15, 2026 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants