added ruff test

This commit is contained in:
ykcheungab
2026-09-27 19:20:43 +08:00
parent bcab2790bf
commit fa2703968e
178 changed files with 3277 additions and 1034 deletions
+83 -5
View File
@@ -13,7 +13,12 @@ from typing import Any
from orisys_app_shared import prefs as _prefs
from orisys_app_shared.paths import preference_path
from .motion.service import DEFAULT_BAS_TASK, PLATFORM_AXES, clamp_bas_task
from .motion.service import (
DEFAULT_AXIS_ROLES,
DEFAULT_BAS_TASK,
PLATFORM_AXES,
clamp_bas_task,
)
from .motion.units import DEFAULT_ACCEL, DEFAULT_SPEED, DEFAULT_UNITS
#: Connection modes, in the order the combo offers them.
@@ -81,6 +86,10 @@ def _default_safety_limits() -> dict[str, float]:
``0`` means "not set" for the ones where a real value is required, and the
app reports that at the P0 gate rather than treating it as unlimited.
Lengths are millimetres and angles degrees, because the roles they bound are:
one number cannot bound both, and a degree target checked against a millimetre
limit is not a check.
"""
return {
"max_travel_mm": 100.0,
@@ -90,6 +99,11 @@ def _default_safety_limits() -> dict[str, float]:
#: How far the axis may descend looking for contact before the run is
#: aborted (workflow P7, no-contact abort).
"contact_search_mm": 3.0,
#: Stage envelope, from the confirmed bench values: the yaw stage turns a
#: half-turn either way, the pitch stage a full turn. These are the limits a
#: pose move is checked against before any axis is commanded.
"max_yaw_deg": 180.0,
"max_pitch_deg": 360.0,
}
@@ -104,9 +118,10 @@ def _default_sensor_setup() -> dict[str, Any]:
"sensor_type": "box",
"config_ref": "",
"geometry_id": "",
#: Which axis presses, and the probe tip offset from the platform's
#: reference point. Empty offset means "not set", which blocks P5.
"press_axis": 2,
#: The probe tip's offset from the platform's reference point. Empty offset
#: means "not set", which blocks P5. Which axis presses is **not** here: it
#: is ``axis_roles["z"]``, because one fact written twice is one fact that
#: eventually disagrees.
"probe_tip_offset_mm": [0.0, 0.0, 0.0],
"probe_tip_set": False,
#: 4x4 row-major sensor -> platform transform, plus how well it fitted.
@@ -173,6 +188,19 @@ def default_preferences() -> dict[str, Any]:
# --- workflow blocks (see apps/autocal/docs/workflow.md) --------------
#: P0: limits enforced from P2 onward.
"safety_limits": _default_safety_limits(),
#: Which drive is which platform direction, from the bench wiring: drives
#: 0-4 are x, y, pitch, yaw, z. There is no way to read this off the bus —
#: five CiA-402 axes report numbers, not names — so it is configuration, and
#: the pose-move path refuses to run until it is complete and free of
#: duplicates. It is also the *only* place "which axis presses" is written:
#: the sensor setup used to carry a second copy, which is how the two would
#: have drifted apart.
"axis_roles": dict(DEFAULT_AXIS_ROLES),
#: Ask before a pose move. On by default — the first moves on a machine are
#: the ones worth reading — and turnable off once the numbers are trusted,
#: because a prompt that cannot be dismissed unread is one that gets clicked
#: through without being read.
"move_confirm": True,
#: P3/P4: sensor under test, probe tip, sensor<->platform transform.
"sensor_setup": _default_sensor_setup(),
#: P5: grid generation and run parameters.
@@ -206,9 +234,26 @@ def default_preferences() -> dict[str, Any]:
}
#: Preference blocks whose *keys* are added to over time. Stored preferences are
#: merged one level deep, so a block saved by an older build replaces the whole
#: default block and any key added since reads as absent. For a P0 limit that is not
#: a cosmetic problem: the move is refused with "the limit is not set" while the file
#: in front of the operator says nothing about it. Filling the block key by key keeps
#: what was chosen and supplies only what was never there.
_MERGED_BLOCKS = ("connection", "safety_limits", "sensor_setup", "sequence_profile")
def load_preferences() -> dict[str, Any]:
"""Stored preferences with AutoCal's defaults filling the gaps."""
prefs = _prefs.load_preferences(default_preferences())
defaults = default_preferences()
prefs = _prefs.load_preferences(defaults)
for name in _MERGED_BLOCKS:
stored = prefs.get(name)
if not isinstance(stored, dict):
continue
block = dict(defaults.get(name) or {})
block.update(stored)
prefs[name] = block
# A stored document from an older build may miss a newly added axis block.
axis_params = prefs.setdefault("axis_params", {})
for axis, values in _default_axis_params().items():
@@ -252,6 +297,39 @@ def load_preferences() -> dict[str, Any]:
prefs["finger_tool_xy_mm"] = tool
# A hand-edited task number must not reach the controller as typed.
prefs["bas_task"] = clamp_bas_task(prefs.get("bas_task"))
prefs = _migrate_axis_roles(prefs)
return prefs
def _migrate_axis_roles(prefs: dict[str, Any]) -> dict[str, Any]:
"""Complete the axis mapping, and take the old duplicate press axis out.
``sensor_setup.press_axis`` named the pressing drive on its own. That is
``axis_roles["z"]`` now — the same fact — and a stored value is moved there when
the mapping has not been set, so a bench that had it configured keeps working.
The old key is removed either way: leaving it would keep a second answer to
"which axis presses" in the file.
"""
roles = prefs.get("axis_roles")
roles = dict(roles) if isinstance(roles, dict) else {}
sensor = prefs.get("sensor_setup")
if isinstance(sensor, dict):
legacy = sensor.pop("press_axis", None)
if legacy is not None and "z" not in roles:
try:
roles["z"] = int(legacy)
except (TypeError, ValueError):
pass
merged = dict(DEFAULT_AXIS_ROLES)
# A stored mapping wins where it is usable; the default fills what it does not
# cover, so an old document that only named the press axis still comes up with a
# complete mapping rather than a refusal.
for role, value in roles.items():
try:
merged[role] = int(value)
except (TypeError, ValueError):
continue
prefs["axis_roles"] = merged
return prefs