22658dbfc1
- 设备设置新增“单位与换算”页:显示各轴 UNITS、读回值、脉冲/mm 换算与核对状态; 默认锁定,点击“修改”后才可编辑,应用前弹窗确认,旋转轴偏离标定值时额外警告 - 轴调试页:脉冲当量改为只读并提供入口;新增报警与事件日志(报警、使能、 回零、读取失败、单位不符等)及单位核对摘要 - 新增 motion/unit_report.py、motion/events.py 及对应测试
96 lines
4.0 KiB
Python
96 lines
4.0 KiB
Python
"""The axis event log: only changes, a baseline on first sight, nothing across a gap."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from orisys_autocal.motion import AxisState, Snapshot
|
|
from orisys_autocal.motion import events as ev
|
|
from orisys_autocal.motion.models import AxisStatusBits
|
|
from orisys_autocal.motion.events import AxisEventTracker
|
|
|
|
|
|
def _axis(axis=0, *, alarm=False, enabled=True, read_ok=True, homing=False, home_status=0, units=10.0, error=""):
|
|
raw = (512 if alarm else 0) | (64 if homing else 0)
|
|
return AxisState(
|
|
axis=axis,
|
|
enabled=enabled,
|
|
units=units,
|
|
status=AxisStatusBits(raw),
|
|
home_status=home_status,
|
|
read_ok=read_ok,
|
|
error=error,
|
|
)
|
|
|
|
|
|
def _snap(*axes, connected=True) -> Snapshot:
|
|
return Snapshot(connected=connected, axes=tuple(axes))
|
|
|
|
|
|
def _kinds(events):
|
|
return [(event.axis, event.kind) for event in events]
|
|
|
|
|
|
def test_the_first_poll_is_a_baseline_and_a_steady_state_is_silent():
|
|
tracker = AxisEventTracker(clock=lambda: 1.0)
|
|
assert tracker.update(_snap(_axis(0), _axis(1))) == []
|
|
assert tracker.update(_snap(_axis(0), _axis(1))) == []
|
|
assert tracker.events == []
|
|
|
|
|
|
def test_an_alarm_that_is_already_up_when_first_seen_is_reported():
|
|
tracker = AxisEventTracker()
|
|
assert _kinds(tracker.update(_snap(_axis(2, alarm=True)))) == [(2, ev.ALARM_ON)]
|
|
|
|
|
|
def test_alarm_enable_and_homing_edges_are_reported_once():
|
|
tracker = AxisEventTracker()
|
|
tracker.update(_snap(_axis(0, enabled=False)))
|
|
assert _kinds(tracker.update(_snap(_axis(0, enabled=True)))) == [(0, ev.ENABLED)]
|
|
assert _kinds(tracker.update(_snap(_axis(0, alarm=True)))) == [(0, ev.ALARM_ON)]
|
|
assert tracker.update(_snap(_axis(0, alarm=True))) == []
|
|
assert _kinds(tracker.update(_snap(_axis(0)))) == [(0, ev.ALARM_OFF)]
|
|
assert _kinds(tracker.update(_snap(_axis(0, homing=True)))) == [(0, ev.HOME_STARTED)]
|
|
assert _kinds(tracker.update(_snap(_axis(0, home_status=1)))) == [(0, ev.HOME_DONE)]
|
|
tracker.update(_snap(_axis(0, homing=True)))
|
|
assert _kinds(tracker.update(_snap(_axis(0, home_status=0)))) == [(0, ev.HOME_INCOMPLETE)]
|
|
|
|
|
|
def test_a_read_failure_and_its_recovery_are_reported():
|
|
tracker = AxisEventTracker()
|
|
tracker.update(_snap(_axis(1)))
|
|
failed = tracker.update(_snap(_axis(1, read_ok=False, error="timeout")))
|
|
assert [(e.axis, e.kind, e.detail) for e in failed] == [(1, ev.READ_FAILED, "timeout")]
|
|
assert tracker.update(_snap(_axis(1, read_ok=False, error="timeout"))) == []
|
|
assert _kinds(tracker.update(_snap(_axis(1)))) == [(1, ev.READ_OK)]
|
|
|
|
|
|
def test_a_gap_in_the_connection_resets_the_baseline():
|
|
tracker = AxisEventTracker()
|
|
tracker.update(_snap(_axis(0)))
|
|
assert _kinds(tracker.update(_snap(connected=False))) == [(ev.NO_AXIS, ev.DISCONNECTED)]
|
|
# Back again with the axis alarmed: the connection edge, then the alarm as a
|
|
# first sight; nothing is compared with a state from before the gap.
|
|
back = tracker.update(_snap(_axis(0, alarm=True)))
|
|
assert _kinds(back) == [(ev.NO_AXIS, ev.CONNECTED), (0, ev.ALARM_ON)]
|
|
|
|
|
|
def test_units_disagreeing_with_the_configuration_is_an_event_both_ways():
|
|
tracker = AxisEventTracker()
|
|
expected = {0: 10.0}
|
|
assert tracker.update(_snap(_axis(0, units=10.0)), expected) == []
|
|
mismatch = tracker.update(_snap(_axis(0, units=5.0)), expected)
|
|
assert [(e.kind, e.detail) for e in mismatch] == [(ev.UNITS_MISMATCH, "10|5")]
|
|
assert tracker.update(_snap(_axis(0, units=5.0)), expected) == []
|
|
assert _kinds(tracker.update(_snap(_axis(0, units=10.0)), expected)) == [(0, ev.UNITS_OK)]
|
|
# With nothing to compare against no verdict is invented.
|
|
assert AxisEventTracker().update(_snap(_axis(0, units=5.0))) == []
|
|
|
|
|
|
def test_the_log_is_bounded_and_can_be_cleared():
|
|
tracker = AxisEventTracker(limit=3)
|
|
tracker.update(_snap(_axis(0)))
|
|
for index in range(10):
|
|
tracker.update(_snap(_axis(0, alarm=bool(index % 2 == 0))))
|
|
assert len(tracker.events) == 3
|
|
tracker.clear()
|
|
assert tracker.events == []
|