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