|
- #!/usr/bin/env python3
- """Interactive console for technicians testing the PLSR firmware."""
-
- from __future__ import annotations
-
- import time
-
- import plsr_test
-
-
- def ask_int(prompt: str, default: int, minimum: int, maximum: int) -> int:
- while True:
- raw = input(f"{prompt} [{default}]: ").strip()
- if not raw:
- return default
- try:
- value = int(raw)
- except ValueError:
- print("请输入整数。")
- continue
- if minimum <= value <= maximum:
- return value
- print(f"允许范围:{minimum}..{maximum}")
-
-
- def ask_yes(prompt: str) -> bool:
- return input(f"{prompt} [y/N]: ").strip().lower() in ("y", "yes")
-
-
- def choose_port() -> str:
- if plsr_test.list_ports is None:
- raise plsr_test.PlsrError("缺少 pyserial,请重新运行启动批处理。")
- ports = sorted(plsr_test.list_ports.comports(), key=lambda item: item.device)
- if ports:
- print("\n检测到的串口:")
- for index, item in enumerate(ports, 1):
- print(f" {index}. {item.device:<8} {item.description}")
- devices = [item.device for item in ports]
- default = "COM9" if "COM9" in devices else (devices[0] if devices else "COM9")
- selected = input(f"请输入测试串口 [{default}]: ").strip().upper()
- return selected or default
-
-
- def confirm_motion() -> bool:
- print("\n安全确认:断开机械/伺服负载,仅连接测试灯或示波器。")
- return ask_yes("已满足上述条件,允许输出脉冲吗?")
-
-
- def show_menu(port: str, protocol: str) -> None:
- print(
- f"""
- ================ PLSR Python 测试工具 ================
- 当前串口:{port} 参数:115200 / 8E1 / 站号1 / 映射:{protocol}
- 1. 读取一次状态
- 2. 持续监视状态
- 3. 配置并保存脉冲点/方向点
- 4. 正向低速灯测
- 5. 反向低速灯测
- 6. 正反向自动测试
- 7. 受控停止
- 8. 立即停止
- 9. 复位
- 10. 清零累计脉冲
- 11. 显示原始通信帧开关
- 12. 更换串口
- 0. 退出
- ======================================================
- """
- )
-
-
- def run_motion(client: plsr_test.ModbusRtuClient, direction: int) -> None:
- if not confirm_motion():
- print("已取消。")
- return
- if client.protocol == plsr_test.MAP_SPEC:
- pulses = ask_int("脉冲数(可负,0 不允许)", 10, -2147483648, 2147483647)
- while pulses == 0:
- print("脉冲数不能为 0。")
- pulses = ask_int("脉冲数(可负,0 不允许)", 10, -2147483648, 2147483647)
- frequency = ask_int("频率 Hz(1..100000,肉眼观察建议 1..5)", 2, 1, 100000)
- else:
- pulses = ask_int("脉冲数", 10, 1, 0xFFFFFFFF)
- frequency = ask_int("频率 Hz(肉眼观察建议 1..5)", 2, 1, 1000)
- client.start_motion(pulses, frequency, direction)
- result = plsr_test.wait_for_terminal(client, max(10.0, pulses / frequency + 5.0))
- if result.state == 5:
- print("PASS:运动正常完成。")
- else:
- print(f"FAIL:运动结束状态为 {result.state_name}。")
-
-
- def auto_test(client: plsr_test.ModbusRtuClient) -> None:
- if not confirm_motion():
- print("已取消。")
- return
- if client.protocol == plsr_test.MAP_SPEC:
- pulse_output = ask_int("脉冲输出 Y0..Y3", 0, 0, 3)
- direction_output = ask_int("方向输出 Y12..Y15(填 0..3)", 0, 0, 3)
- else:
- pulse_output = ask_int("脉冲输出 Q 点", 4, 0, 7)
- direction_output = ask_int("方向输出 Q 点", 1, 0, 7)
- if pulse_output == direction_output:
- print("脉冲点和方向点不能相同。")
- return
- pulses = ask_int("每个方向的脉冲数", 6, 1, 10000)
- frequency = ask_int("频率 Hz", 2, 1, 100000 if client.protocol == plsr_test.MAP_SPEC else 1000)
- save = False if client.protocol == plsr_test.MAP_SPEC else ask_yes("将 Q 点配置保存到 Flash 吗?")
- plsr_test.print_snapshot(client.configure_outputs(pulse_output, direction_output, save))
- client.command(plsr_test.COMMAND_CLEAR_COUNT)
-
- print("\n开始正向测试,请观察脉冲灯。")
- client.start_motion(pulses, frequency, 0)
- forward = plsr_test.wait_for_terminal(client, max(10.0, pulses / frequency + 5.0))
- if forward.state != 5 or forward.total_pulses != pulses:
- raise plsr_test.PlsrError(
- f"正向失败:state={forward.state_name}, total={forward.total_pulses}, expected={pulses}"
- )
-
- print("\n开始反向测试,脉冲灯应继续闪烁,方向点电平应改变。")
- client.start_motion(pulses, frequency, 1)
- reverse = plsr_test.wait_for_terminal(client, max(10.0, pulses / frequency + 5.0))
- expected = pulses * 2
- if reverse.state != 5 or reverse.total_pulses != expected:
- raise plsr_test.PlsrError(
- f"反向失败:state={reverse.state_name}, total={reverse.total_pulses}, expected={expected}"
- )
- print(f"PASS:正反向自动测试完成,累计脉冲={reverse.total_pulses}。")
-
-
- def run_menu(port: str, protocol: str) -> str | None:
- verbose = False
- with plsr_test.ModbusRtuClient(port, timeout=1.0, retries=2, verbose=verbose, protocol=protocol) as client:
- print("\n串口连接成功。")
- try:
- plsr_test.print_snapshot(client.read_snapshot())
- except plsr_test.PlsrError as exc:
- print(f"首次状态读取失败:{exc}")
-
- while True:
- show_menu(port, protocol)
- choice = input("请选择操作: ").strip()
- try:
- if choice == "0":
- return None
- if choice == "1":
- plsr_test.print_snapshot(client.read_snapshot())
- elif choice == "2":
- print("持续监视中,按 Ctrl+C 返回菜单。")
- try:
- while True:
- plsr_test.print_snapshot(client.read_snapshot())
- time.sleep(0.5)
- except KeyboardInterrupt:
- print("\n已停止监视。")
- elif choice == "3":
- if protocol == plsr_test.MAP_SPEC:
- pulse = ask_int("脉冲输出 Y0..Y3", 0, 0, 3)
- direction = ask_int("方向输出 Y12..Y15(填 0..3)", 0, 0, 3)
- else:
- pulse = ask_int("脉冲输出 Q 点", 4, 0, 7)
- direction = ask_int("方向输出 Q 点", 1, 0, 7)
- result = client.configure_outputs(pulse, direction, save=False)
- plsr_test.print_snapshot(result)
- print("配置已写入设备。需求版是否掉电保存由固件保存策略决定。")
- elif choice == "4":
- run_motion(client, 0)
- elif choice == "5":
- run_motion(client, 1)
- elif choice == "6":
- auto_test(client)
- elif choice == "7":
- client.command(plsr_test.COMMAND_STOP)
- plsr_test.print_snapshot(client.read_snapshot())
- elif choice == "8":
- client.command(plsr_test.COMMAND_IMMEDIATE_STOP)
- plsr_test.print_snapshot(client.read_snapshot())
- elif choice == "9":
- client.command(plsr_test.COMMAND_RESET)
- plsr_test.print_snapshot(client.read_snapshot())
- elif choice == "10":
- client.command(plsr_test.COMMAND_CLEAR_COUNT)
- plsr_test.print_snapshot(client.read_snapshot())
- elif choice == "11":
- verbose = not verbose
- client.verbose = verbose
- print(f"原始帧显示:{'开启' if verbose else '关闭'}")
- elif choice == "12":
- return choose_port()
- else:
- print("无效选项。")
- except (OSError, ValueError, plsr_test.PlsrError) as exc:
- print(f"操作失败:{exc}")
-
-
- def main() -> int:
- print("PLSR Python 测试工具")
- protocol = input("协议映射 spec(参数表-2026)/ legacy(旧固件) [spec]: ").strip().lower() or plsr_test.MAP_SPEC
- if protocol not in (plsr_test.MAP_SPEC, plsr_test.MAP_LEGACY):
- print("无效映射,使用 spec。")
- protocol = plsr_test.MAP_SPEC
- port = choose_port()
- while port:
- try:
- port = run_menu(port, protocol)
- except (OSError, plsr_test.PlsrError) as exc:
- print(f"\n无法使用 {port}:{exc}")
- if not ask_yes("重新选择串口吗?"):
- return 1
- port = choose_port()
- print("测试工具已退出。")
- return 0
-
-
- if __name__ == "__main__":
- raise SystemExit(main())
|