feat: 实现api调用

This commit is contained in:
zpff 2025-08-11 21:16:23 +08:00
parent afe64406e5
commit b65b94643c
3 changed files with 174 additions and 21 deletions

View File

@ -12,6 +12,7 @@ from flask import jsonify, request
import gazebo_ctrl
from heartbeat import HeartbeatServer
from model_mgr.manager import ModelManager
from model_mgr.model_types import Model
from scene_mgr.manager import SceneManager
@ -86,8 +87,12 @@ class GazeboSimHttpServer(HttpRPCServer):
# 模块与数据
self.rlock = threading.RLock()
self.scene_mgr = SceneManager()
self.model_mgr = ModelManager()
self.heartbeat_server = HeartbeatServer()
self.model_mgr = None
def llost():
self.shut_scene()
self.heartbeat_server = HeartbeatServer(close_func=llost)
self.heartbeat_server.start()
# 启动roscore
self.roscore_proc = subprocess.Popen(
['roscore'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
@ -95,47 +100,160 @@ class GazeboSimHttpServer(HttpRPCServer):
rospy.init_node('gazebo_world_manager', anonymous=True)
# 添加接口
self._app.add_url_rule('/api/v1/scenes', view_func=self.query_scenes, methods=['GET'])
self._app.add_url_rule('/api/v1/scene/load', view_func=self.load_scene, methods=['GET'])
self._app.add_url_rule('/api/v1/scene/objects', view_func=self.get_scene_objects, methods=['GET'])
self._app.add_url_rule('/api/v1/scene/object', view_func=self.modify_scene_object, methods=['POST'])
self._app.add_url_rule('/api/v1/scene/object', view_func=self.add_scene_object, methods=['PUT'])
self._app.add_url_rule('/api/v1/scene/object', view_func=self.remove_scene_object, methods=['DELETE'])
self._app.add_url_rule(
'/api/v1/scenes', view_func=self.query_scenes, methods=['GET'])
self._app.add_url_rule('/api/v1/scene/load',
view_func=self.start_scene, methods=['GET'])
self._app.add_url_rule('/api/v1/scene/objects',
view_func=self.get_all_models, methods=['GET'])
self._app.add_url_rule(
'/api/v1/scene/object', view_func=self.modify_model, methods=['POST'])
self._app.add_url_rule('/api/v1/scene/object',
view_func=self.add_model, methods=['PUT'])
self._app.add_url_rule(
'/api/v1/scene/object', view_func=self.remove_model, methods=['DELETE'])
print('Sever init done.')
def query_scenes(self):
# 查询场景
with self.rlock:
pass
ret = {'code': 200, 'message': ''}
try:
scs = self.scene_mgr.get_scenes()
data = [sc.to_dict for sc in scs]
ret['data'] = data
except Exception as e:
ret['code'] = 400
ret['message'] = str(e)
print('Server query scenes done.')
return jsonify(ret)
def start_scene(self):
# 启动场景
with self.rlock:
pass
ret = {'code': 200, 'message': ''}
try:
# 检查参数
id = request.args.get('id', type=int, default=None)
if not id:
raise ValueError('Scene ID is required')
# 关闭现有场景
if self.scene_mgr.running_scene:
self.shut_scene()
time.sleep(25)
# 启动场景
self.scene_mgr.start_scene(id)
self.model_mgr = ModelManager()
time.sleep(30)
self.model_mgr.load_models(
self.scene_mgr.scene_id_dict[id].preload_models)
except Exception as e:
ret['code'] = 400
ret['message'] = str(e)
print('Server start scene done.')
return jsonify(ret)
def shut_scene(self):
# 关闭场景仅供start_scene、心跳线程使用
with self.rlock:
pass
try:
if not self.scene_mgr.running_scene:
raise RuntimeError('No scene is running')
def shutt():
self.scene_mgr.shut_scene_truly()
threading.Thread(target=shutt, daemon=True).start()
self.model_mgr = None
except Exception as e:
print(str(e))
print('Server shut scene done.')
def get_all_models(self):
# 获取所有模型
with self.rlock:
pass
ret = {'code': 200, 'message': ''}
try:
mods = self.model_mgr.get_all_models()
cnt = len(mods)
data = {}
data['total_count'] = cnt
data['list'] = [m.to_dict() for m in mods]
except Exception as e:
ret['code'] = 400
ret['message'] = str(e)
print('Server get all models done.')
return jsonify(ret)
def modify_model(self):
# 修改模型
with self.rlock:
pass
ret = {'code': 200, 'message': ''}
try:
# 解析参数
info = request.get_json()
scene_id = info.get('scene_id', type=int, default=None)
if not scene_id or scene_id != self.scene_mgr.running_scene:
raise ValueError(
f'Scene {scene_id} does not match the running scene {self.scene_mgr.running_scene}')
object_id = info.get('object_id', type=int, default=None)
if not object_id:
raise ValueError('Object ID is required')
# 制作Model
info['id'] = object_id
model = self.model_mgr.get_model(object_id)
model.overwrite_from_dict(info)
# 添加模型
self.model_mgr.modify_model(model)
except Exception as e:
ret['code'] = 400
ret['message'] = str(e)
print('Server modify model done.')
return jsonify(ret)
def add_model(self):
# 添加模型
with self.rlock:
pass
ret = {'code': 200, 'message': ''}
try:
# 解析参数
info = request.get_json()
scene_id = info.get('scene_id', type=int, default=None)
if not scene_id or scene_id != self.scene_mgr.running_scene:
raise ValueError(
f'Scene {scene_id} does not match the running scene {self.scene_mgr.running_scene}')
# 制作Model和modify的区别在于没有id
info['id'] = -1
model = Model.from_dict(info)
# 添加模型
self.model_mgr.add_model(model)
except Exception as e:
ret['code'] = 400
ret['message'] = str(e)
print('Server add model done.')
return jsonify(ret)
def remove_model(self):
# 删除模型
with self.rlock:
pass
ret = {'code': 200, 'message': ''}
try:
# 解析参数
info = request.get_json()
scene_id = info.get('scene_id', type=int, default=None)
if not scene_id or scene_id != self.scene_mgr.running_scene:
raise ValueError(
f'Scene {scene_id} does not match the running scene {self.scene_mgr.running_scene}')
object_id = info.get('object_id', type=int, default=None)
# 删除模型
self.model_mgr.remove_model(object_id)
except Exception as e:
ret['code'] = 400
ret['message'] = str(e)
print('Server remove model done.')
return jsonify(ret)
if __name__ == '__main__':
server = GazeboSimHttpServer()

View File

@ -13,6 +13,11 @@ class Position:
def to_dict(self):
return {"x": self.x, "y": self.y, "z": self.z}
def overwrite_from_dict(self, data):
self.x = data.get("x", self.x)
self.y = data.get("y", self.y)
self.z = data.get("z", self.z)
@classmethod
def from_dict(cls, data):
@ -32,6 +37,11 @@ class Orientation:
def to_dict(self):
return {"roll": self.roll, "pitch": self.pitch, "yaw": self.yaw}
def overwrite_from_dict(self, data):
self.roll = data.get("roll", self.roll)
self.pitch = data.get("pitch", self.pitch)
self.yaw = data.get("yaw", self.yaw)
@classmethod
def from_dict(cls, data):
return cls(
@ -52,6 +62,10 @@ class Pose:
position: Position = dataclasses.field(default_factory=Position)
orientation: Orientation = dataclasses.field(default_factory=Orientation)
def overwrite_from_dict(self, data):
self.position.overwrite_from_dict(data.get("position", {}))
self.orientation.overwrite_from_dict(data.get("orientation", {}))
def to_dict(self):
return {
"position": self.position.to_dict(),
@ -79,6 +93,11 @@ class Size:
"height": self.height,
}
def overwrite_from_dict(self, data):
self.length = data.get("length", self.length)
self.width = data.get("width", self.width)
self.height = data.get("height", self.height)
@classmethod
def from_dict(cls, data):
return cls(
@ -113,12 +132,17 @@ class Model:
mass: int = 1
size: Size = dataclasses.field(default_factory=Size)
def __post_init__(self):
@classmethod
def gen_id(cls):
global _nr_model_id
global _nr_model_id_lock
with _nr_model_id_lock:
_nr_model_id += 1
return _nr_model_id
def __post_init__(self):
if self.id == -1:
with _nr_model_id_lock:
self.id = _nr_model_id
_nr_model_id += 1
self.id = self.gen_id()
def to_dict(self):
return {
@ -133,12 +157,23 @@ class Model:
"size": self.size.to_dict(),
}
def overwrite_from_dict(self, data):
self.name = data.get("name", self.name)
self.obj_type = ModelType(data.get("obj_type", self.obj_type))
self.id = data.get("id", self.id)
self.pose.overwrite_from_dict(data.get("pose", {}))
self.description = data.get("description", self.description)
self.ability_code = data.get("ability_code", self.ability_code)
self.tag_id = data.get("tag_id", self.tag_id)
self.mass = data.get("mass", self.mass)
self.size.overwrite_from_dict(data.get("size", {}))
@classmethod
def from_dict(cls, data):
return cls(
name=data.get("name", ""),
obj_type=ModelType(data.get("obj_type", 0)),
id=data.get("id", -1),
id=data.get("id", cls.gen_id()), # 添加模型用
pose=Pose.from_dict(data.get("pose", {})),
description=data.get("description", ""),
ability_code=data.get("ability_code", []),

View File

@ -52,7 +52,7 @@ class SceneManager:
self.scene_name_dict[name] = scene
self.scene_id_dict[scene.id] = scene
def get_scene(self):
def get_scenes(self):
return list(self.scene_name_dict.values())
def get_scripts(self, name):