ball_controller_config_manager.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. BallController配置管理器
  5. 从config_manager.py中提取的BallController相关配置管理功能
  6. 支持从Excel读取BallController配置并与现有JSON配置合并
  7. """
  8. import json
  9. import os
  10. from pathlib import Path
  11. from datetime import datetime
  12. try:
  13. import pandas as pd
  14. PANDAS_AVAILABLE = True
  15. except ImportError:
  16. PANDAS_AVAILABLE = False
  17. print("警告: pandas未安装,无法处理Excel文件")
  18. class BallControllerConfigManager:
  19. """BallController配置管理器"""
  20. def __init__(self, excel_file_path=None, json_file_path=None):
  21. """初始化BallController配置管理器
  22. Args:
  23. excel_file_path: Excel配置文件路径
  24. json_file_path: JSON配置文件路径
  25. """
  26. self.script_dir = Path(__file__).parent
  27. # 设置默认路径
  28. if excel_file_path is None:
  29. self.excel_file = self.script_dir / "BallController标准配置表.xlsx"
  30. else:
  31. self.excel_file = Path(excel_file_path)
  32. if json_file_path is None:
  33. self.json_file = self.script_dir.parent / "ballController.json"
  34. else:
  35. self.json_file = Path(json_file_path)
  36. print(f"Excel文件路径: {self.excel_file}")
  37. print(f"JSON文件路径: {self.json_file}")
  38. # BallController配置映射
  39. self.ball_controller_mapping = {
  40. 'format_type': 'vertical',
  41. 'param_types': {
  42. 'baseSpeed': float,
  43. 'maxReflectionRandomness': float,
  44. 'antiTrapTimeWindow': int,
  45. 'antiTrapHitThreshold': int,
  46. 'deflectionAttemptThreshold': int,
  47. 'antiTrapDeflectionMultiplier': int,
  48. 'FIRE_COOLDOWN': float,
  49. 'ballRadius': int,
  50. 'gravityScale': int,
  51. 'linearDamping': int,
  52. 'angularDamping': int,
  53. 'colliderGroup': int,
  54. 'colliderTag': int,
  55. 'friction': int,
  56. 'restitution': int,
  57. 'safeDistance': int,
  58. 'edgeOffset': int,
  59. 'sensor': bool,
  60. 'maxAttempts': int
  61. }
  62. }
  63. def load_existing_json_config(self):
  64. """加载现有的JSON配置文件"""
  65. try:
  66. if self.json_file.exists():
  67. with open(self.json_file, 'r', encoding='utf-8') as f:
  68. config = json.load(f)
  69. print(f"成功加载现有JSON配置,包含 {len(config)} 个参数")
  70. return config
  71. else:
  72. print(f"JSON文件不存在,将创建新配置: {self.json_file}")
  73. return {}
  74. except Exception as e:
  75. print(f"加载JSON配置失败: {e}")
  76. return {}
  77. def read_excel_config(self):
  78. """读取Excel配置文件"""
  79. if not PANDAS_AVAILABLE:
  80. raise Exception("pandas未安装,无法读取Excel文件")
  81. if not self.excel_file.exists():
  82. raise Exception(f"Excel文件不存在: {self.excel_file}")
  83. try:
  84. # 读取Excel文件
  85. df = pd.read_excel(self.excel_file)
  86. print(f"成功读取Excel文件,包含 {len(df)} 行数据")
  87. return df
  88. except Exception as e:
  89. raise Exception(f"读取Excel文件失败: {e}")
  90. def parse_ball_controller_data(self, df):
  91. """解析BallController配置数据"""
  92. try:
  93. config = {}
  94. # 检查数据格式 - 纵向表格格式(参数名在第一列,数值在第二列)
  95. for index, row in df.iterrows():
  96. if len(row) >= 2:
  97. param_name = str(row.iloc[0]).strip()
  98. param_value = row.iloc[1]
  99. # 跳过表头行
  100. if param_name in ['参数名', 'Parameter', 'Name']:
  101. continue
  102. # 根据映射转换数据类型
  103. if param_name in self.ball_controller_mapping['param_types']:
  104. param_type = self.ball_controller_mapping['param_types'][param_name]
  105. try:
  106. if param_type == bool:
  107. # 处理布尔值
  108. if isinstance(param_value, str):
  109. config[param_name] = param_value.lower() in ['true', '1', 'yes', 'on']
  110. else:
  111. config[param_name] = bool(param_value)
  112. elif param_type == int:
  113. config[param_name] = int(float(param_value))
  114. elif param_type == float:
  115. config[param_name] = float(param_value)
  116. else:
  117. config[param_name] = str(param_value).strip()
  118. except (ValueError, TypeError):
  119. print(f"警告: 参数 {param_name} 的值 {param_value} 无法转换为 {param_type.__name__},使用字符串类型")
  120. config[param_name] = str(param_value).strip()
  121. else:
  122. # 未知参数,尝试自动推断类型
  123. try:
  124. if str(param_value).lower() in ['true', 'false']:
  125. config[param_name] = str(param_value).lower() == 'true'
  126. elif '.' in str(param_value):
  127. config[param_name] = float(param_value)
  128. else:
  129. config[param_name] = int(float(param_value))
  130. except (ValueError, TypeError):
  131. config[param_name] = str(param_value).strip()
  132. print(f"成功解析 {len(config)} 个BallController参数")
  133. return config
  134. except Exception as e:
  135. print(f"解析BallController配置数据失败: {e}")
  136. return {}
  137. def merge_ball_controller_configs(self, existing_config, excel_config):
  138. """合并现有配置和Excel配置"""
  139. try:
  140. # 创建合并后的配置
  141. merged_config = existing_config.copy()
  142. # 更新或添加Excel中的配置
  143. for key, value in excel_config.items():
  144. if key in merged_config:
  145. print(f"更新参数: {key} = {value} (原值: {merged_config[key]})")
  146. else:
  147. print(f"添加参数: {key} = {value}")
  148. merged_config[key] = value
  149. print(f"配置合并完成,共 {len(merged_config)} 个参数")
  150. return merged_config
  151. except Exception as e:
  152. print(f"合并配置失败: {e}")
  153. return existing_config
  154. def backup_json_config(self):
  155. """备份现有JSON配置"""
  156. try:
  157. if self.json_file.exists():
  158. timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
  159. backup_file = self.json_file.parent / f"{self.json_file.stem}_backup_{timestamp}.json"
  160. with open(self.json_file, 'r', encoding='utf-8') as src:
  161. with open(backup_file, 'w', encoding='utf-8') as dst:
  162. dst.write(src.read())
  163. print(f"配置已备份到: {backup_file}")
  164. return backup_file
  165. else:
  166. print("JSON文件不存在,无需备份")
  167. return None
  168. except Exception as e:
  169. print(f"备份配置失败: {e}")
  170. return None
  171. def save_json_config(self, config):
  172. """保存配置到JSON文件"""
  173. try:
  174. # 确保目录存在
  175. self.json_file.parent.mkdir(parents=True, exist_ok=True)
  176. with open(self.json_file, 'w', encoding='utf-8') as f:
  177. json.dump(config, f, ensure_ascii=False, indent=2)
  178. print(f"配置已保存到: {self.json_file}")
  179. return True
  180. except Exception as e:
  181. print(f"保存JSON文件失败: {e}")
  182. return False
  183. def import_ball_controller_config(self):
  184. """导入BallController配置的主方法"""
  185. try:
  186. print("开始导入BallController配置...")
  187. # 1. 加载现有JSON配置
  188. existing_config = self.load_existing_json_config()
  189. # 2. 读取Excel配置
  190. excel_df = self.read_excel_config()
  191. # 3. 解析Excel数据
  192. excel_config = self.parse_ball_controller_data(excel_df)
  193. # 4. 合并配置
  194. merged_config = self.merge_ball_controller_configs(existing_config, excel_config)
  195. # 5. 备份现有配置
  196. self.backup_json_config()
  197. # 6. 保存新配置
  198. if self.save_json_config(merged_config):
  199. print("BallController配置导入成功!")
  200. return True
  201. else:
  202. print("BallController配置保存失败!")
  203. return False
  204. except Exception as e:
  205. print(f"导入BallController配置失败: {e}")
  206. return False
  207. def main():
  208. """主函数"""
  209. print("BallController配置管理器")
  210. print("=" * 50)
  211. # 创建BallController配置管理器
  212. manager = BallControllerConfigManager()
  213. # 导入配置
  214. success = manager.import_ball_controller_config()
  215. if success:
  216. print("\n✓ BallController配置导入完成")
  217. else:
  218. print("\n✗ BallController配置导入失败")
  219. if __name__ == "__main__":
  220. main()