grpc_client/clients/http_server.py

133 lines
3.9 KiB
Python
Raw Normal View History

2026-02-02 09:37:47 +08:00
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
)