#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ BallController配置管理器 从config_manager.py中提取的BallController相关配置管理功能 支持从Excel读取BallController配置并与现有JSON配置合并 """ import json import os from pathlib import Path from datetime import datetime try: import pandas as pd PANDAS_AVAILABLE = True except ImportError: PANDAS_AVAILABLE = False print("警告: pandas未安装,无法处理Excel文件") class BallControllerConfigManager: """BallController配置管理器""" def __init__(self, excel_file_path=None, json_file_path=None): """初始化BallController配置管理器 Args: excel_file_path: Excel配置文件路径 json_file_path: JSON配置文件路径 """ self.script_dir = Path(__file__).parent # 设置默认路径 if excel_file_path is None: self.excel_file = self.script_dir / "BallController标准配置表.xlsx" else: self.excel_file = Path(excel_file_path) if json_file_path is None: self.json_file = self.script_dir.parent / "ballController.json" else: self.json_file = Path(json_file_path) print(f"Excel文件路径: {self.excel_file}") print(f"JSON文件路径: {self.json_file}") # BallController配置映射 self.ball_controller_mapping = { 'format_type': 'vertical', 'param_types': { 'baseSpeed': float, 'maxReflectionRandomness': float, 'antiTrapTimeWindow': int, 'antiTrapHitThreshold': int, 'deflectionAttemptThreshold': int, 'antiTrapDeflectionMultiplier': int, 'FIRE_COOLDOWN': float, 'ballRadius': int, 'gravityScale': int, 'linearDamping': int, 'angularDamping': int, 'colliderGroup': int, 'colliderTag': int, 'friction': int, 'restitution': int, 'safeDistance': int, 'edgeOffset': int, 'sensor': bool, 'maxAttempts': int } } def load_existing_json_config(self): """加载现有的JSON配置文件""" try: if self.json_file.exists(): with open(self.json_file, 'r', encoding='utf-8') as f: config = json.load(f) print(f"成功加载现有JSON配置,包含 {len(config)} 个参数") return config else: print(f"JSON文件不存在,将创建新配置: {self.json_file}") return {} except Exception as e: print(f"加载JSON配置失败: {e}") return {} def read_excel_config(self): """读取Excel配置文件""" if not PANDAS_AVAILABLE: raise Exception("pandas未安装,无法读取Excel文件") if not self.excel_file.exists(): raise Exception(f"Excel文件不存在: {self.excel_file}") try: # 读取Excel文件 df = pd.read_excel(self.excel_file) print(f"成功读取Excel文件,包含 {len(df)} 行数据") return df except Exception as e: raise Exception(f"读取Excel文件失败: {e}") def parse_ball_controller_data(self, df): """解析BallController配置数据""" try: config = {} # 检查数据格式 - 纵向表格格式(参数名在第一列,数值在第二列) for index, row in df.iterrows(): if len(row) >= 2: param_name = str(row.iloc[0]).strip() param_value = row.iloc[1] # 跳过表头行 if param_name in ['参数名', 'Parameter', 'Name']: continue # 根据映射转换数据类型 if param_name in self.ball_controller_mapping['param_types']: param_type = self.ball_controller_mapping['param_types'][param_name] try: if param_type == bool: # 处理布尔值 if isinstance(param_value, str): config[param_name] = param_value.lower() in ['true', '1', 'yes', 'on'] else: config[param_name] = bool(param_value) elif param_type == int: config[param_name] = int(float(param_value)) elif param_type == float: config[param_name] = float(param_value) else: config[param_name] = str(param_value).strip() except (ValueError, TypeError): print(f"警告: 参数 {param_name} 的值 {param_value} 无法转换为 {param_type.__name__},使用字符串类型") config[param_name] = str(param_value).strip() else: # 未知参数,尝试自动推断类型 try: if str(param_value).lower() in ['true', 'false']: config[param_name] = str(param_value).lower() == 'true' elif '.' in str(param_value): config[param_name] = float(param_value) else: config[param_name] = int(float(param_value)) except (ValueError, TypeError): config[param_name] = str(param_value).strip() print(f"成功解析 {len(config)} 个BallController参数") return config except Exception as e: print(f"解析BallController配置数据失败: {e}") return {} def merge_ball_controller_configs(self, existing_config, excel_config): """合并现有配置和Excel配置""" try: # 创建合并后的配置 merged_config = existing_config.copy() # 更新或添加Excel中的配置 for key, value in excel_config.items(): if key in merged_config: print(f"更新参数: {key} = {value} (原值: {merged_config[key]})") else: print(f"添加参数: {key} = {value}") merged_config[key] = value print(f"配置合并完成,共 {len(merged_config)} 个参数") return merged_config except Exception as e: print(f"合并配置失败: {e}") return existing_config def backup_json_config(self): """备份现有JSON配置""" try: if self.json_file.exists(): timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") backup_file = self.json_file.parent / f"{self.json_file.stem}_backup_{timestamp}.json" with open(self.json_file, 'r', encoding='utf-8') as src: with open(backup_file, 'w', encoding='utf-8') as dst: dst.write(src.read()) print(f"配置已备份到: {backup_file}") return backup_file else: print("JSON文件不存在,无需备份") return None except Exception as e: print(f"备份配置失败: {e}") return None def save_json_config(self, config): """保存配置到JSON文件""" try: # 确保目录存在 self.json_file.parent.mkdir(parents=True, exist_ok=True) with open(self.json_file, 'w', encoding='utf-8') as f: json.dump(config, f, ensure_ascii=False, indent=2) print(f"配置已保存到: {self.json_file}") return True except Exception as e: print(f"保存JSON文件失败: {e}") return False def import_ball_controller_config(self): """导入BallController配置的主方法""" try: print("开始导入BallController配置...") # 1. 加载现有JSON配置 existing_config = self.load_existing_json_config() # 2. 读取Excel配置 excel_df = self.read_excel_config() # 3. 解析Excel数据 excel_config = self.parse_ball_controller_data(excel_df) # 4. 合并配置 merged_config = self.merge_ball_controller_configs(existing_config, excel_config) # 5. 备份现有配置 self.backup_json_config() # 6. 保存新配置 if self.save_json_config(merged_config): print("BallController配置导入成功!") return True else: print("BallController配置保存失败!") return False except Exception as e: print(f"导入BallController配置失败: {e}") return False def main(): """主函数""" print("BallController配置管理器") print("=" * 50) # 创建BallController配置管理器 manager = BallControllerConfigManager() # 导入配置 success = manager.import_ball_controller_config() if success: print("\n✓ BallController配置导入完成") else: print("\n✗ BallController配置导入失败") if __name__ == "__main__": main()