327 lines
15 KiB
Python
Raw Normal View History

2025-09-12 16:15:13 +08:00
import json
import os
class SceneBasicConverter:
def save_bin_locations_list(self):
"""
如果smap文件中有binLocationsList则单独保存为converted/binLocationsList.json
"""
if hasattr(self, 'map_data') and 'binLocationsList' in self.map_data:
self.ensure_converted_dir()
filename = f"{self.converted_dir}/binLocationsList.json"
with open(filename, 'w', encoding='utf-8') as f:
json.dump({'binLocationsList': self.map_data['binLocationsList']}, f, indent=2, ensure_ascii=False)
print(f"已单独保存 binLocationsList 到: {filename}")
def __init__(self, scene_id, setting, ratio):
self.scene_data = {}
self.converted_dir = "converted"
self.scene_data['id'] = scene_id
self.scene_data['setting'] = setting
self.scene_data['ratio'] = ratio
def ensure_converted_dir(self):
if not os.path.exists(self.converted_dir):
os.makedirs(self.converted_dir)
print(f"创建转换结果目录: {self.converted_dir}")
def save_single_key_json(self, key_name, key_data):
self.ensure_converted_dir()
filename = f"{self.converted_dir}/{key_name}.json"
single_key_data = {key_name: key_data}
with open(filename, 'w', encoding='utf-8') as f:
json.dump(single_key_data, f, indent=2, ensure_ascii=False)
print(f"已保存 {key_name} 的独立JSON文件到: {filename}")
def load_source_files(self, smap_file, scene_file):
self.smap_file = smap_file
self.scene_file = scene_file
if os.path.exists(smap_file):
with open(smap_file, 'r', encoding='utf-8') as f:
self.map_data = json.load(f)
else:
print(f"警告: 地图文件 {smap_file} 不存在")
self.map_data = {}
if os.path.exists(scene_file):
with open(scene_file, 'r', encoding='utf-8') as f:
self.scene_source = json.load(f)
else:
print(f"警告: 场景文件 {scene_file} 不存在")
self.scene_source = {}
def extract_basic_info(self):
if 'header' in self.map_data:
header = self.map_data['header']
self.scene_data['name'] = header.get('mapName', '')
min_pos = header.get('minPos', {'x': 0, 'y': 0})
max_pos = header.get('maxPos', {'x': 0, 'y': 0})
width = max_pos['x'] - min_pos['x']
height = max_pos['y'] - min_pos['y']
self.scene_data['width'] = abs(width)
self.scene_data['height'] = abs(height)
else:
raise ValueError("无法计算地图尺寸map_data中缺少header信息")
basic_info_data = {
'id': self.scene_data['id'],
'name': self.scene_data['name'],
'width': self.scene_data['width'],
'height': self.scene_data['height'],
'setting': self.scene_data['setting'],
'ratio': self.scene_data['ratio']
}
self.save_single_key_json('basic_info', basic_info_data)
def extract_coordinate_system(self):
if self.scene_source:
self.scene_data['scale'] = self.scene_source.get('scale', 0.6599999999999997)
self.scene_data['origin'] = self.scene_source.get('origin', {
'x': -3679.5080777864587,
'y': -3925.231448210223
})
else:
self.scene_data['scale'] = 0.6599999999999997
self.scene_data['origin'] = {
'x': -3679.5080777864587,
'y': -3925.231448210223
}
coordinate_data = {
'scale': self.scene_data['scale'],
'origin': self.scene_data['origin']
}
self.save_single_key_json('coordinate_system', coordinate_data)
def extract_robots(self):
# 无论源数据如何robots 字段都输出空列表
self.scene_data['robots'] = []
self.save_single_key_json('robots', self.scene_data['robots'])
def extract_points(self):
if 'advancedPointList' in self.map_data:
self.scene_data['points'] = self.extract_advanced_points()
elif self.scene_source and 'points' in self.scene_source:
self.scene_data['points'] = self.scene_source['points']
else:
self.scene_data['points'] = []
self.save_single_key_json('points', self.scene_data['points'])
def extract_advanced_points(self):
points = []
if 'advancedPointList' in self.map_data:
advanced_points = self.map_data['advancedPointList']
for i, point in enumerate(advanced_points):
point_id = str(10000001 + i)
name = point.get('instanceName', f'Point{i+1}')
pos = point.get('pos', {})
x = pos.get('x', 0)
y = pos.get('y', 0)
className = point.get('className', 'ActionPoint')
point_type = self.get_point_type_by_class(className)
properties = []
if 'property' in point:
for prop in point['property']:
property_obj = {
"key": prop.get('key', ''),
"type": prop.get('type', ''),
"value": prop.get('value', ''),
"boolValue": prop.get('boolValue', False)
}
prop_type = prop.get('type', '')
if prop_type == 'bool':
property_obj["boolValue"] = prop.get('boolValue', False)
elif prop_type == 'string':
property_obj["stringValue"] = prop.get('stringValue', '')
elif prop_type == 'int32':
property_obj["int32Value"] = prop.get('int32Value', 0)
elif prop_type == 'float':
property_obj["floatValue"] = prop.get('floatValue', 0.0)
properties.append(property_obj)
point_obj = {
"id": point_id,
"name": name,
"x": x,
"y": y,
"type": point_type,
"config": {},
"properties": properties,
"associatedStorageLocations": [],
"robots": [],
"enabled": 1
}
points.append(point_obj)
return points
def get_point_type_by_class(self, className):
type_mapping = {
'LocationMark': 1,
'ParkPoint': 14,
'SwitchMap': 5,
'HomeRegion': 2,
'ActionPoint': 15,
'TransferLocation': 14,
'WorkingLocation': 16,
'ChargePoint': 13
}
if className not in type_mapping:
if className.startswith('LM'):
return 1
elif className.startswith('PP'):
return 14
elif className.startswith('SW'):
return 5
elif className.startswith('HR'):
return 2
elif className.startswith('AP'):
return 15
elif className.startswith('TL'):
return 14
elif className.startswith('WL'):
return 16
elif className.startswith('CP'):
return 13
else:
return 1
return type_mapping.get(className, 15)
def extract_routes(self):
self.scene_data['routes'] = self.extract_advanced_curves()
self.save_single_key_json('routes', self.scene_data['routes'])
def extract_advanced_curves(self):
routes = []
name_to_id_map = {}
points_file = f"{self.converted_dir}/points.json"
if os.path.exists(points_file):
try:
with open(points_file, 'r', encoding='utf-8') as f:
points_data = json.load(f)
if 'points' in points_data:
for point in points_data['points']:
name_to_id_map[point['name']] = point['id']
except Exception as e:
print(f"警告: 无法读取 {points_file} 文件: {e}")
else:
print(f"警告: 站点文件 {points_file} 不存在,无法建立映射关系")
curve_data = self.map_data
if 'advancedCurveList' in curve_data:
advanced_curves = curve_data['advancedCurveList']
print(f"找到 {len(advanced_curves)} 条路径数据")
# 路径类型映射表优先用className判断
path_type_map = {
"StraightPath": "line",
"BezierPath": "BezierPath",
"ArcPath": "bezier2",
"DegenerateBezier": "bezier3",
"NURBS6": "NURBS6"
}
for i, curve in enumerate(advanced_curves):
route_id = str(100000 + i)
desc = curve.get('instanceName', f'Route{i+1}')
start_pos = curve.get('startPos', {})
end_pos = curve.get('endPos', {})
from_instance_name = start_pos.get('instanceName', '')
to_instance_name = end_pos.get('instanceName', '')
from_point = name_to_id_map.get(from_instance_name, from_instance_name)
to_point = name_to_id_map.get(to_instance_name, to_instance_name)
if from_instance_name not in name_to_id_map:
print(f"警告: 找不到起点 '{from_instance_name}' 的映射")
if to_instance_name not in name_to_id_map:
print(f"警告: 找不到终点 '{to_instance_name}' 的映射")
control_pos1 = curve.get('controlPos1', {})
control_pos2 = curve.get('controlPos2', {})
control_pos3 = curve.get('controlPos3', {})
control_pos4 = curve.get('controlPos4', {})
properties = []
if 'property' in curve:
for prop in curve['property']:
properties.append({
"key": prop.get('key', ''),
"type": prop.get('type', ''),
"value": prop.get('value', ''),
"int32Value": prop.get('int32Value', 0),
"boolValue": prop.get('boolValue', False)
})
# 优先用className判断路径类型
class_name = curve.get('className', '')
curve_type = curve.get('type', '')
mapped_type = path_type_map.get(class_name, path_type_map.get(curve_type, "bezier3"))
route_obj = {
"id": route_id,
"desc": desc,
"from": from_point,
"to": to_point,
"type": mapped_type,
"pass": 0,
"config": {},
"properties": properties
}
# 按类型添加controlPos字段直线不添加c1/c2
if mapped_type == "BezierPath" or mapped_type == "bezier3":
route_obj["c1"] = {"x": control_pos1.get('x', 0), "y": control_pos1.get('y', 0)}
route_obj["c2"] = {"x": control_pos2.get('x', 0), "y": control_pos2.get('y', 0)}
elif mapped_type == "bezier2":
route_obj["c1"] = {"x": control_pos1.get('x', 0), "y": control_pos1.get('y', 0)}
elif mapped_type == "NURBS6":
route_obj["c1"] = {"x": control_pos1.get('x', 0), "y": control_pos1.get('y', 0), "z": control_pos1.get('z', 0)}
route_obj["c2"] = {"x": control_pos2.get('x', 0), "y": control_pos2.get('y', 0), "z": control_pos2.get('z', 0)}
route_obj["c3"] = {"x": control_pos3.get('x', 0), "y": control_pos3.get('y', 0), "z": control_pos3.get('z', 0)}
route_obj["c4"] = {"x": control_pos4.get('x', 0), "y": control_pos4.get('y', 0), "z": control_pos4.get('z', 0)}
# 直线line不添加c1/c2等字段
routes.append(route_obj)
print(f"成功生成 {len(routes)} 条路径")
else:
print("警告: 未找到advancedCurveList数据")
return routes
def extract_bin_locations(self):
"""
优先从converted/binLocationsList.json读取binLocationsList否则从map_data中读取并保存
"""
import os
filename = f"{self.converted_dir}/binLocationsList.json"
bin_locations = None
if os.path.exists(filename):
with open(filename, 'r', encoding='utf-8') as f:
data = json.load(f)
bin_locations = data.get('binLocationsList', [])
else:
bin_locations = self.map_data.get('binLocationsList', [])
self.scene_data['binLocationsList'] = bin_locations
self.save_single_key_json('binLocationsList', self.scene_data['binLocationsList'])
def convert_basic(self, smap_file, scene_file, output_file="converted_scene.scene"):
print(f"开始基本信息转换...")
print(f"源地图文件: {smap_file}")
print(f"源场景文件: {scene_file}")
print(f"输出文件: {output_file}")
print("-" * 50)
self.load_source_files(smap_file, scene_file)
self.save_bin_locations_list()
self.extract_basic_info()
self.extract_coordinate_system()
self.extract_robots()
self.extract_points()
self.extract_routes()
self.extract_bin_locations()
# 获取所有一级key
all_keys = [
'id', 'setting', 'ratio', 'name', 'width', 'height', 'scale', 'origin',
'robotGroups', 'robots', 'points', 'routes', 'areas', 'blocks', 'binLocationsList'
]
# 补全缺失的一级key值为空空列表、空字典或空字符串
for key in all_keys:
if key not in self.scene_data:
if key in ['robotGroups', 'robots', 'points', 'routes', 'areas', 'binLocationsList']:
self.scene_data[key] = []
elif key in ['origin', 'scale']:
self.scene_data[key] = {} if key == 'origin' else 0
elif key in ['blocks']:
self.scene_data[key] = ""
else:
self.scene_data[key] = ""
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(self.scene_data, f, indent=2, ensure_ascii=False)
print(f"基本信息转换完成!结果已保存到 {output_file}")
return self.scene_data