133 lines
3.9 KiB
Python
133 lines
3.9 KiB
Python
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import time
|
|||
|
|
from threading import Lock
|
|||
|
|
from fastapi import FastAPI
|
|||
|
|
from pydantic import BaseModel
|
|||
|
|
from typing import Optional
|
|||
|
|
|
|||
|
|
from clients.movej_client import MoveJClient, action_music,action_air,action_drive_comfort,action_drive_economy,action_hello
|
|||
|
|
|
|||
|
|
|
|||
|
|
app = FastAPI(title="Humanoid Robot Control API (Blocking + Mutex + Ignore)")
|
|||
|
|
|
|||
|
|
class ActionResponse(BaseModel):
|
|||
|
|
code: int
|
|||
|
|
msg: str
|
|||
|
|
action: Optional[str] = None
|
|||
|
|
ignored: bool = False
|
|||
|
|
|
|||
|
|
class StatusResponse(BaseModel):
|
|||
|
|
running: bool
|
|||
|
|
current_action: Optional[str]
|
|||
|
|
last_action: Optional[str]
|
|||
|
|
last_finished_at: Optional[float]
|
|||
|
|
|
|||
|
|
|
|||
|
|
# 🔒 全局唯一互斥锁:保证同一时间只能执行一个 action
|
|||
|
|
action_lock = Lock()
|
|||
|
|
|
|||
|
|
# 状态变量(简单可用;多线程更严谨可再加锁,这里够用)
|
|||
|
|
running = False
|
|||
|
|
current_action = None
|
|||
|
|
last_action = None
|
|||
|
|
last_finished_at = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
def run_action_blocking_ignore_if_busy(action_func, action_name: str) -> ActionResponse:
|
|||
|
|
"""
|
|||
|
|
通用执行模板:
|
|||
|
|
- 忙:直接忽略(立即返回,不执行)
|
|||
|
|
- 空闲:阻塞执行,执行完再返回
|
|||
|
|
"""
|
|||
|
|
global running, current_action, last_action, last_finished_at
|
|||
|
|
|
|||
|
|
# 忙就忽略(静默丢弃动作)
|
|||
|
|
if not action_lock.acquire(blocking=False):
|
|||
|
|
return ActionResponse(
|
|||
|
|
code=0,
|
|||
|
|
msg=f"{action_name} ignored: robot busy",
|
|||
|
|
action=action_name,
|
|||
|
|
ignored=True,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
client = MoveJClient()
|
|||
|
|
# client = 0
|
|||
|
|
running = True
|
|||
|
|
current_action = action_name
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
action_func(client) # ✅ 阻塞到动作执行结束
|
|||
|
|
last_action = action_name
|
|||
|
|
last_finished_at = time.time()
|
|||
|
|
return ActionResponse(
|
|||
|
|
code=0,
|
|||
|
|
msg=f"{action_name} finished",
|
|||
|
|
action=action_name,
|
|||
|
|
ignored=False,
|
|||
|
|
)
|
|||
|
|
except Exception as e:
|
|||
|
|
return ActionResponse(
|
|||
|
|
code=1,
|
|||
|
|
msg=f"{action_name} failed: {e}",
|
|||
|
|
action=action_name,
|
|||
|
|
ignored=False,
|
|||
|
|
)
|
|||
|
|
finally:
|
|||
|
|
try:
|
|||
|
|
# print("closing action")
|
|||
|
|
client.close()
|
|||
|
|
finally:
|
|||
|
|
running = False
|
|||
|
|
current_action = None
|
|||
|
|
action_lock.release()
|
|||
|
|
|
|||
|
|
|
|||
|
|
# @app.post("/action/one", response_model=ActionResponse)
|
|||
|
|
# def call_action_one():
|
|||
|
|
# return run_action_blocking_ignore_if_busy(action_one, "action_one")
|
|||
|
|
|
|||
|
|
@app.post("/action/music", response_model=ActionResponse)
|
|||
|
|
def call_action_music():
|
|||
|
|
return run_action_blocking_ignore_if_busy(action_music, "action_music")
|
|||
|
|
|
|||
|
|
@app.post("/action/air", response_model=ActionResponse)
|
|||
|
|
def call_action_air():
|
|||
|
|
return run_action_blocking_ignore_if_busy(action_air, "action_air")
|
|||
|
|
|
|||
|
|
@app.post("/action/hello", response_model=ActionResponse)
|
|||
|
|
def call_action_hello():
|
|||
|
|
return run_action_blocking_ignore_if_busy(action_hello, "action_hello")
|
|||
|
|
# @app.post("/action/drive_comfort", response_model=ActionResponse)
|
|||
|
|
# def call_action_drive_comfort():
|
|||
|
|
# return run_action_blocking_ignore_if_busy(action_drive_comfort, "action_drive_comfort")
|
|||
|
|
#
|
|||
|
|
# @app.post("/action/drive_economy", response_model=ActionResponse)
|
|||
|
|
# def call_action_drive_economy():
|
|||
|
|
# return run_action_blocking_ignore_if_busy(action_drive_economy, "action_drive_economy")
|
|||
|
|
#
|
|||
|
|
# @app.post("/action/two", response_model=ActionResponse)
|
|||
|
|
# def call_action_two():
|
|||
|
|
# return run_action_blocking_ignore_if_busy(action_two, "action_two")
|
|||
|
|
# @app.post("/action/two", response_model=ActionResponse)
|
|||
|
|
# def call_action_two():
|
|||
|
|
# return run_action_blocking_ignore_if_busy(action_two, "action_two")
|
|||
|
|
|
|||
|
|
@app.get("/status", response_model=StatusResponse)
|
|||
|
|
def get_status():
|
|||
|
|
return StatusResponse(
|
|||
|
|
running=running,
|
|||
|
|
current_action=current_action,
|
|||
|
|
last_action=last_action,
|
|||
|
|
last_finished_at=last_finished_at,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
import uvicorn
|
|||
|
|
uvicorn.run(
|
|||
|
|
"http_server:app",
|
|||
|
|
host="0.0.0.0",
|
|||
|
|
port=8000,
|
|||
|
|
reload=False
|
|||
|
|
)
|