#!/usr/bin/env python
"""
K.NAKADA, kengo.nakada@gmail.com, kengo.nakada@mat.shimane-u.ac.jp
"""
import sys
import traceback
from typing import Any, Union, List, Dict, Tuple, Callable, Optional, Sequence
import logging
from pathlib import Path
import time
# from PySide6.QtWidgets import QMessageBox
# from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import QTimer
# from PySide6.QtGui import QPixmap
from cobotta2.server_grpc import JoyPadClient
from x_logger import XLogger
logging.getLogger("httpx").setLevel(logging.WARNING)
[ドキュメント]
class CobottaError:
def __init__(
self,
parent: Any,
client: JoyPadClient,
logger: Optional[XLogger] = None,
# ui: Optional[Ui_MainWindow] = None,
) -> None:
"""
JoyPad GUI における COBOTTA のエラー監視・表示・リセットを担当するクラス。
本クラスの責務は以下に限定される。
- JoyPadClient を通じてサーバ側からエラー情報を取得する
- 現在のエラー件数・エラー内容を GUI に反映する
- エラーの巡回表示(Prev / Next)を管理する
- エラーリセット要求を JoyPadClient に送信する
ロボット制御そのものは行わず、あくまで
「エラー状態の可視化と UI 操作の仲介」に特化したクラスである。
"""
self._parent = parent
self.ui = parent.ui
self._client = client
# --- JoyPad Server 初期化用 ---
self._error_count = 0
self._error_count_index = 0 # 現在表示しているエラー個数番目
self._error_current_index = 0
# --- Error info の初期化(接続とは無関係に行う)---
if self._client is not None:
if self._client.is_connected():
try:
self._error_count = self._client.get_error_count()
except Exception:
self._error_count = 0
self._update_error_indicator(self._error_count > 0)
self._update_error_count_index()
self._update_error_description()
# --- エラー監視タイマー ---
self._error_timer = QTimer(parent)
self._error_timer.setInterval(300) # 300ms
self._error_timer.timeout.connect(self._update_error_count)
self._error_timer.start()
# エラーリセットボタン
self.ui.JoyPad_button_reset_error.clicked.connect(
self._on_reset_error,
)
# エラー取得ボタン
self.ui.JoyPad_button_get_error_description.clicked.connect(
self._on_get_error_description
)
# エラー巡回(Prev / Next)
self.ui.JoyPad_button_error_prev.clicked.connect(self._on_error_prev)
self.ui.JoyPad_button_error_next.clicked.connect(self._on_error_next)
# -----------------------------------------------
# JoyPad error monitor
# -----------------------------------------------
[ドキュメント]
def _update_error_count(self) -> None:
"""
現在のエラー件数をサーバから取得し、内部状態と UI を更新する。
Behavior:
- JoyPadClient.get_error_count() を呼び出す
- エラー件数に応じてインジケータ表示を切り替える
- エラー件数表示およびエラー内容表示を更新する
Notes:
- クライアント未接続時や例外発生時は何も行わず終了する
- QTimer から周期的に呼び出されることを前提としている
"""
if self._client is None:
return
if not self._client.is_connected():
return
try:
self._error_count = self._client.get_error_count()
except Exception:
return
if self._error_count == 0:
self._update_error_indicator(False)
else:
self._update_error_indicator(True)
self._update_error_count_index()
self._update_error_description()
[ドキュメント]
def _update_error_indicator(self, has_error: bool) -> None:
"""
エラー有無を示すインジケータ表示を更新する。
Args:
has_error (bool):
エラーが存在するかどうか。
Behavior:
- エラーが存在する場合は赤系のスタイルを適用する
- エラーが存在しない場合は緑系のスタイルを適用する
Notes:
- UI が存在しない環境では何も行わない
- 実際の表示判定は内部の _error_count を基準に行われる
"""
try:
label = self.ui.JoyPad_label_error_indicator
except AttributeError:
return
label.setText("")
if self._error_count > 0:
label.setStyleSheet(
"background-color: #cc4444; border: 1px solid #880000; border-radius: 6px;"
)
else:
label.setStyleSheet(
"background-color: #44cc44; border: 1px solid #008800; border-radius: 6px;"
)
[ドキュメント]
def _update_error_count_index(self) -> None:
"""
現在表示中のエラー番号と総エラー件数を UI に反映する。
Behavior:
- エラーが存在しない場合は「- / -」を表示する
- エラーが存在する場合は「現在位置 / 総件数」を表示する
Notes:
- エラー番号は内部カウンタ _error_count_index に基づく
"""
try:
label = self.ui.JoyPad_label_error_count
except AttributeError:
return
if self._error_count == 0:
label.setText("- / -")
return
label.setText(f"{self._error_count_index} / {self._error_count}")
[ドキュメント]
def _update_error_description(self) -> None:
"""
現在選択されているエラーの詳細内容を UI に表示する。
Behavior:
- エラーが存在しない場合は「エラーはありません。」と表示する
- エラーが 1 件の場合は現在エラー情報を取得して表示する
- 複数件存在する場合は指定インデックスのエラー情報を表示する
Notes:
- エラー情報は JoyPadClient から dict として取得されることを前提とする
- 表示内容は [エラーコード] 説明文 の形式とする
"""
if self._client is None:
return
if self._error_count == 0:
self.ui.JoyPad_label_error_description.setText("エラーはありません。")
return
count = self._client.get_error_count()
if count == 0:
self.ui.JoyPad_label_error_description.setText("エラーはありません")
return
if count == 1:
error_info = self._client.get_current_error_info()
code: int = error_info["error_code"]
description: str = error_info["description"]
self.ui.JoyPad_label_error_description.setText(f"[{code}] {description}")
return
error_info = self._client.get_error_info(self._error_count_index)
code = error_info["error_code"]
description = error_info["description"]
self.ui.JoyPad_label_error_description.setText(f"[{code}] {description}")
[ドキュメント]
def _on_get_error_description(self) -> None:
"""
UI の「エラー取得」ボタンが押されたときのスロット。
Behavior:
- 現在のエラー内容を再取得し、UI 表示を更新する
"""
self._update_error_description()
[ドキュメント]
def _on_error_prev(self) -> None:
"""
UI の「前のエラー」ボタンが押されたときのスロット。
Behavior:
- エラーが存在しない場合はその旨を UI に表示する
- エラーが存在する場合は表示インデックスを更新し、内容を再表示する
Notes:
- インデックスの循環処理は行わず、現在位置の再表示に留まる
"""
if self._client is None:
return
if self._error_count == 0:
self.ui.JoyPad_label_error_description.setText("エラーはありません。")
self._update_error_count_index()
return
self._update_error_count_index()
self._update_error_description()
[ドキュメント]
def _on_error_next(self) -> None:
"""
UI の「次のエラー」ボタンが押されたときのスロット。
Behavior:
- 表示中のエラーインデックスをインクリメントする
- 最後のエラーに達した場合は先頭に戻る
- 更新後のエラー内容を UI に表示する
"""
if self._client is None:
return
if self._error_count == 0:
self.ui.JoyPad_label_error_description.setText("エラーはありません。")
self._update_error_count_index()
return
self._update_error_count_index()
self._error_count_index = self._error_count_index + 1
if self._error_count_index >= self._error_count:
self._error_count_index = 0
self._update_error_description()
[ドキュメント]
def _on_reset_error(self) -> None:
"""
UI の「エラーリセット」ボタンが押されたときのスロット。
Behavior:
- JoyPadClient.reset_error() を呼び出す
- 成功時は内部エラー状態を初期化し、UI 表示を更新する
- 失敗時はエラーメッセージを UI に表示する
Notes:
- 例外発生時もユーザに失敗を通知する
"""
if self._client is None:
return
try:
ok = self._client.reset_error()
except Exception:
self.ui.JoyPad_label_error_description.setText(
"エラーリセットに失敗しました。"
)
return
if not ok:
self.ui.JoyPad_label_error_description.setText(
"エラーリセットできませんでした。"
)
return
self._error_count_index = 0
self._update_error_indicator(False)
self._update_error_count_index()
self.ui.JoyPad_label_error_description.setText("エラーはありません。")