#!/usr/bin/env python
"""
K.NAKADA, kengo.nakada@gmail.com, kengo.nakada@mat.shimane-u.ac.jp
"""
from typing import Any, Union, List, Dict, Tuple, Callable, Optional, Sequence
# grpc
from cobotta2.server_grpc import JoyPadClient
from x_logger import XLogger
[ドキュメント]
class CobottaBcapArm:
"""
b-CAP 制御された COBOTTA のアーム占有状態(Take / Give)のみを管理するクラス。
本クラスは以下の責務のみを持つ。
- JoyPadClient を通じてアームの取得 / 解放要求を送信する
- 「このクライアントがアームを保持しているか」という状態を内部に保持する
- アーム状態に応じて UI ボタンの有効 / 無効を切り替える
実際のロボット運動制御や座標制御は一切行わない。
本クラスは「状態管理 + UI 反映」に限定された薄い制御層である。
"""
def __init__(
self,
parent: Any,
client: JoyPadClient, # JoyPadClient
logger: XLogger = None,
):
"""
CobottaBcapArm を初期化する。
Args:
parent (Any):
親オブジェクト。
UI(Qt Designer で生成された ui 属性)を保持していることを前提とする。
client (JoyPadClient):
gRPC 経由でサーバ側の ctrl に接続するクライアント。
take_arm / give_arm RPC の呼び出しのみを行う。
logger (XLogger, optional):
ログ出力用ロガー。
指定されない場合は本クラス専用の logger を内部生成する。
Notes:
- 初期状態ではアームは未取得(_arm_taken = False)とする
- UI が存在しない実行環境(テスト等)でも動作するよう、
UI 操作部分は AttributeError を握りつぶす設計になっている
- Take / Give ボタンの signal-slot 接続もここで行う
"""
self._parent = parent
self._client = client
self._logger = logger or XLogger(logger_name="CobottaArmCtrl")
self._arm_taken = False
# ------------------
# UI
# ------------------
self.ui = self._parent.ui
self.ui.JoyPad_button_take_arm.clicked.connect(self._on_take_arm)
self.ui.JoyPad_button_give_arm.clicked.connect(self._on_give_arm)
# 初期ボタン状態
try:
self.ui.JoyPad_button_take_arm.setEnabled(True)
self.ui.JoyPad_button_give_arm.setEnabled(True)
except AttributeError:
pass
[ドキュメント]
def take_arm(self):
"""
アームの取得(Take Arm)を要求する。
Returns:
bool:
True : アーム取得に成功、または既に取得済み
False : 取得要求が失敗した場合
Behavior:
- すでにアームを保持している場合は RPC を呼ばず True を返す
- JoyPadClient.take_arm() を呼び出し、成功時のみ内部状態を更新する
- 内部状態 _arm_taken は UI 状態制御の判断材料としてのみ使用される
"""
if self._arm_taken:
return True
ok = self._client.take_arm()
if ok:
self._arm_taken = True
return True
else:
return False
[ドキュメント]
def give_arm(self) -> bool:
"""
アームの解放(Give Arm)を要求する。
Returns:
bool:
True : アーム解放に成功、または元々保持していない
False : 解放要求が失敗した場合
Behavior:
- アームを保持していない場合は RPC を呼ばず True を返す
- JoyPadClient.give_arm() を呼び出し、成功時のみ内部状態を更新する
"""
if not self._arm_taken:
return True
ok = self._client.give_arm()
if ok:
self._arm_taken = False
return True
else:
return False
@property
def arm_taken(self) -> bool:
"""
現在このクライアントがアームを保持しているかを返す。
Returns:
bool:
True : アームを保持している
False : アームを保持していない
Notes:
- 実機の状態を直接問い合わせるものではない
- 本クラス内で管理している状態フラグの参照のみを行う
"""
return self._arm_taken
# -----------------------------------------------
# SLOT
# JoyPad Take/Give Arm buttons
# -----------------------------------------------
[ドキュメント]
def _on_take_arm(self) -> None:
"""
UI の「Take Arm」ボタンが押されたときのスロット。
Behavior:
- take_arm() を呼び出してアーム取得を試みる
- 失敗した場合はログを出力して終了する
- 成功した場合は UI ボタンの有効 / 無効状態を更新する
Notes:
- UI の存在を前提としないため、AttributeError は無視される
- JoyPad 側 / CobottaCtrl 側の両方のボタン状態を同時に制御する
"""
self._logger.info("== on_take_arm()")
ok = self.take_arm()
if not ok:
self._logger.error("take_arm() failed")
return
try:
self.ui.JoyPad_button_take_arm.setEnabled(False)
self.ui.JoyPad_button_give_arm.setEnabled(True)
self.ui.CobottaCtrl_button_take_arm.setEnabled(False)
self.ui.CobottaCtrl_button_give_arm.setEnabled(False)
except AttributeError:
pass
[ドキュメント]
def _on_give_arm(self) -> None:
"""
UI の「Give Arm」ボタンが押されたときのスロット。
Behavior:
- give_arm() を呼び出してアーム解放を試みる
- 失敗した場合はログを出力して終了する
- 成功した場合は UI ボタンの有効 / 無効状態を更新する
Notes:
- Take Arm と対称な状態遷移を UI に反映する
- UI 非存在環境でも例外を出さない設計になっている
"""
self._logger.info("== on_give_arm()")
ok = self.give_arm()
if not ok:
self._logger.error("give_arm() failed")
return
try:
self.ui.JoyPad_button_take_arm.setEnabled(True)
self.ui.JoyPad_button_give_arm.setEnabled(False)
self.ui.CobottaCtrl_button_take_arm.setEnabled(True)
self.ui.CobottaCtrl_button_give_arm.setEnabled(False)
except AttributeError:
pass