694 lines
23 KiB
Python
694 lines
23 KiB
Python
"""
|
||
配置管理器模块
|
||
负责管理触觉反馈系统的配置和用户偏好设置
|
||
"""
|
||
|
||
import time
|
||
import json
|
||
import os
|
||
from typing import Dict, Any, List, Optional
|
||
from pathlib import Path
|
||
|
||
class HapticConfigManager:
|
||
"""
|
||
触觉配置管理器
|
||
负责管理触觉反馈系统的配置和用户偏好设置
|
||
"""
|
||
|
||
def __init__(self, plugin):
|
||
"""
|
||
初始化触觉配置管理器
|
||
|
||
Args:
|
||
plugin: 触觉反馈系统插件实例
|
||
"""
|
||
self.plugin = plugin
|
||
self.enabled = False
|
||
self.initialized = False
|
||
|
||
# 配置文件路径
|
||
self.config_path = "/home/hello/EG/plugins/user/haptic_feedback_system/config/haptic_config.json"
|
||
|
||
# 默认配置
|
||
self.default_config = {
|
||
'global': {
|
||
'enabled': True,
|
||
'intensity_scale': 1.0,
|
||
'frequency_scale': 1.0,
|
||
'duration_scale': 1.0,
|
||
'master_volume': 1.0,
|
||
'enable_device_detection': True,
|
||
'auto_enable_new_devices': True
|
||
},
|
||
'devices': {
|
||
'default_intensity': 0.8,
|
||
'default_frequency': 50.0,
|
||
'max_concurrent_effects': 16,
|
||
'crossfade_time': 0.1,
|
||
'device_timeout': 30.0
|
||
},
|
||
'effects': {
|
||
'enable_preprocessing': True,
|
||
'enable_postprocessing': True,
|
||
'max_effect_duration': 10.0,
|
||
'effect_smoothing': 0.1,
|
||
'enable_custom_effects': True
|
||
},
|
||
'physics': {
|
||
'enable_collision_feedback': True,
|
||
'enable_force_feedback': True,
|
||
'enable_friction_feedback': True,
|
||
'collision_intensity_scale': 1.0,
|
||
'force_intensity_scale': 1.0,
|
||
'friction_intensity_scale': 1.0
|
||
},
|
||
'audio': {
|
||
'enable_audio_haptics': True,
|
||
'enable_beat_detection': True,
|
||
'audio_analysis_rate': 30.0,
|
||
'frequency_bands': 8,
|
||
'audio_intensity_scale': 1.0
|
||
},
|
||
'events': {
|
||
'enable_event_processing': True,
|
||
'enable_event_suppression': True,
|
||
'enable_event_combination': True,
|
||
'event_queue_size': 100,
|
||
'event_suppression_time': 0.1
|
||
},
|
||
'profiles': {
|
||
'current_profile': 'default',
|
||
'profiles': {
|
||
'default': {
|
||
'name': 'Default',
|
||
'description': 'Default haptic profile',
|
||
'settings': {}
|
||
},
|
||
'cinematic': {
|
||
'name': 'Cinematic',
|
||
'description': 'Enhanced haptic feedback for cinematic experiences',
|
||
'settings': {
|
||
'intensity_scale': 1.2,
|
||
'frequency_scale': 1.1
|
||
}
|
||
},
|
||
'gaming': {
|
||
'name': 'Gaming',
|
||
'description': 'Optimized haptic feedback for gaming',
|
||
'settings': {
|
||
'intensity_scale': 1.0,
|
||
'response_time': 0.01
|
||
}
|
||
},
|
||
'subtle': {
|
||
'name': 'Subtle',
|
||
'description': 'Subtle haptic feedback for casual use',
|
||
'settings': {
|
||
'intensity_scale': 0.5,
|
||
'frequency_scale': 0.8
|
||
}
|
||
}
|
||
}
|
||
},
|
||
'user_preferences': {
|
||
'enable_haptics': True,
|
||
'preferred_devices': [],
|
||
'blocked_devices': [],
|
||
'custom_mappings': {},
|
||
'sensitivity': 1.0,
|
||
'comfort_mode': False
|
||
}
|
||
}
|
||
|
||
# 当前配置
|
||
self.current_config = self.default_config.copy()
|
||
|
||
# 配置变更监听器
|
||
self.config_listeners = {}
|
||
|
||
# 配置验证规则
|
||
self.validation_rules = {
|
||
'global.intensity_scale': (0.0, 2.0),
|
||
'global.frequency_scale': (0.1, 5.0),
|
||
'global.duration_scale': (0.1, 5.0),
|
||
'global.master_volume': (0.0, 1.0),
|
||
'devices.default_intensity': (0.0, 1.0),
|
||
'devices.default_frequency': (1.0, 100.0),
|
||
'devices.max_concurrent_effects': (1, 64),
|
||
'devices.crossfade_time': (0.01, 1.0),
|
||
'devices.device_timeout': (1.0, 300.0),
|
||
'audio.audio_analysis_rate': (1.0, 120.0),
|
||
'audio.frequency_bands': (1, 32)
|
||
}
|
||
|
||
# 配置历史
|
||
self.config_history = []
|
||
self.max_history_size = 50
|
||
|
||
# 统计信息
|
||
self.stats = {
|
||
'config_loads': 0,
|
||
'config_saves': 0,
|
||
'config_changes': 0,
|
||
'validation_errors': 0
|
||
}
|
||
|
||
print("✓ 触觉配置管理器已创建")
|
||
|
||
def initialize(self) -> bool:
|
||
"""
|
||
初始化触觉配置管理器
|
||
|
||
Returns:
|
||
是否初始化成功
|
||
"""
|
||
try:
|
||
# 创建配置目录
|
||
config_dir = Path(self.config_path).parent
|
||
config_dir.mkdir(parents=True, exist_ok=True)
|
||
|
||
# 加载配置
|
||
self.load_config()
|
||
|
||
self.initialized = True
|
||
print("✓ 触觉配置管理器初始化完成")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 触觉配置管理器初始化失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def enable(self) -> bool:
|
||
"""
|
||
启用触觉配置管理器
|
||
|
||
Returns:
|
||
是否启用成功
|
||
"""
|
||
try:
|
||
if not self.initialized:
|
||
print("✗ 触觉配置管理器未初始化")
|
||
return False
|
||
|
||
self.enabled = True
|
||
print("✓ 触觉配置管理器已启用")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 触觉配置管理器启用失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def disable(self):
|
||
"""禁用触觉配置管理器"""
|
||
try:
|
||
self.enabled = False
|
||
print("✓ 触觉配置管理器已禁用")
|
||
|
||
except Exception as e:
|
||
print(f"✗ 触觉配置管理器禁用失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def finalize(self):
|
||
"""清理触觉配置管理器资源"""
|
||
try:
|
||
self.disable()
|
||
self.config_listeners.clear()
|
||
self.config_history.clear()
|
||
self.initialized = False
|
||
print("✓ 触觉配置管理器资源已清理")
|
||
|
||
except Exception as e:
|
||
print(f"✗ 触觉配置管理器资源清理失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def update(self, dt: float):
|
||
"""
|
||
更新触觉配置管理器状态
|
||
|
||
Args:
|
||
dt: 时间增量
|
||
"""
|
||
try:
|
||
if not self.enabled:
|
||
return
|
||
|
||
# 定期保存配置(如果需要)
|
||
pass
|
||
|
||
except Exception as e:
|
||
print(f"✗ 触觉配置管理器更新失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def load_config(self) -> bool:
|
||
"""
|
||
加载配置
|
||
|
||
Returns:
|
||
是否加载成功
|
||
"""
|
||
try:
|
||
if os.path.exists(self.config_path):
|
||
with open(self.config_path, 'r', encoding='utf-8') as f:
|
||
loaded_config = json.load(f)
|
||
|
||
# 合并配置(保留默认配置结构)
|
||
self._merge_config(self.current_config, loaded_config)
|
||
print(f"✓ 配置已从 {self.config_path} 加载")
|
||
else:
|
||
print("⚠ 配置文件不存在,使用默认配置")
|
||
|
||
self.stats['config_loads'] += 1
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 配置加载失败: {e}")
|
||
return False
|
||
|
||
def save_config(self) -> bool:
|
||
"""
|
||
保存配置
|
||
|
||
Returns:
|
||
是否保存成功
|
||
"""
|
||
try:
|
||
with open(self.config_path, 'w', encoding='utf-8') as f:
|
||
json.dump(self.current_config, f, indent=2, ensure_ascii=False)
|
||
|
||
self.stats['config_saves'] += 1
|
||
print(f"✓ 配置已保存到 {self.config_path}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 配置保存失败: {e}")
|
||
return False
|
||
|
||
def _merge_config(self, base_config: Dict[str, Any], new_config: Dict[str, Any]):
|
||
"""
|
||
合并配置
|
||
|
||
Args:
|
||
base_config: 基础配置
|
||
new_config: 新配置
|
||
"""
|
||
try:
|
||
for key, value in new_config.items():
|
||
if key in base_config and isinstance(base_config[key], dict) and isinstance(value, dict):
|
||
self._merge_config(base_config[key], value)
|
||
else:
|
||
base_config[key] = value
|
||
except Exception as e:
|
||
print(f"✗ 配置合并失败: {e}")
|
||
|
||
def get_config_value(self, key_path: str, default_value: Any = None) -> Any:
|
||
"""
|
||
获取配置值
|
||
|
||
Args:
|
||
key_path: 配置键路径(如 'global.intensity_scale')
|
||
default_value: 默认值
|
||
|
||
Returns:
|
||
配置值
|
||
"""
|
||
try:
|
||
keys = key_path.split('.')
|
||
config = self.current_config
|
||
|
||
for key in keys:
|
||
if isinstance(config, dict) and key in config:
|
||
config = config[key]
|
||
else:
|
||
return default_value
|
||
|
||
return config
|
||
except Exception as e:
|
||
print(f"✗ 配置值获取失败: {e}")
|
||
return default_value
|
||
|
||
def set_config_value(self, key_path: str, value: Any) -> bool:
|
||
"""
|
||
设置配置值
|
||
|
||
Args:
|
||
key_path: 配置键路径
|
||
value: 配置值
|
||
|
||
Returns:
|
||
是否设置成功
|
||
"""
|
||
try:
|
||
# 验证配置值
|
||
if not self._validate_config_value(key_path, value):
|
||
print(f"✗ 配置值验证失败: {key_path} = {value}")
|
||
self.stats['validation_errors'] += 1
|
||
return False
|
||
|
||
# 保存到历史记录
|
||
old_value = self.get_config_value(key_path)
|
||
self._add_to_history(key_path, old_value, value)
|
||
|
||
# 设置新值
|
||
keys = key_path.split('.')
|
||
config = self.current_config
|
||
|
||
for key in keys[:-1]:
|
||
if key not in config or not isinstance(config[key], dict):
|
||
config[key] = {}
|
||
config = config[key]
|
||
|
||
config[keys[-1]] = value
|
||
|
||
# 触发配置变更回调
|
||
self._trigger_config_change(key_path, old_value, value)
|
||
|
||
self.stats['config_changes'] += 1
|
||
print(f"✓ 配置已更新: {key_path} = {value}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 配置值设置失败: {e}")
|
||
return False
|
||
|
||
def _validate_config_value(self, key_path: str, value: Any) -> bool:
|
||
"""
|
||
验证配置值
|
||
|
||
Args:
|
||
key_path: 配置键路径
|
||
value: 配置值
|
||
|
||
Returns:
|
||
是否验证通过
|
||
"""
|
||
try:
|
||
if key_path in self.validation_rules:
|
||
rule = self.validation_rules[key_path]
|
||
if isinstance(rule, tuple) and len(rule) == 2:
|
||
min_val, max_val = rule
|
||
if isinstance(value, (int, float)):
|
||
return min_val <= value <= max_val
|
||
# 可以添加更多验证规则
|
||
return True # 默认通过验证
|
||
except Exception as e:
|
||
print(f"✗ 配置值验证异常: {e}")
|
||
return False
|
||
|
||
def _add_to_history(self, key_path: str, old_value: Any, new_value: Any):
|
||
"""
|
||
添加到配置历史
|
||
|
||
Args:
|
||
key_path: 配置键路径
|
||
old_value: 旧值
|
||
new_value: 新值
|
||
"""
|
||
try:
|
||
history_entry = {
|
||
'key_path': key_path,
|
||
'old_value': old_value,
|
||
'new_value': new_value,
|
||
'timestamp': time.time()
|
||
}
|
||
|
||
self.config_history.append(history_entry)
|
||
|
||
# 保持历史记录大小
|
||
if len(self.config_history) > self.max_history_size:
|
||
self.config_history.pop(0)
|
||
|
||
except Exception as e:
|
||
print(f"✗ 配置历史记录失败: {e}")
|
||
|
||
def reset_to_default(self) -> bool:
|
||
"""
|
||
重置为默认配置
|
||
|
||
Returns:
|
||
是否重置成功
|
||
"""
|
||
try:
|
||
self.current_config = self.default_config.copy()
|
||
print("✓ 配置已重置为默认值")
|
||
return True
|
||
except Exception as e:
|
||
print(f"✗ 配置重置失败: {e}")
|
||
return False
|
||
|
||
def get_current_config(self) -> Dict[str, Any]:
|
||
"""
|
||
获取当前配置
|
||
|
||
Returns:
|
||
当前配置字典
|
||
"""
|
||
return self.current_config.copy()
|
||
|
||
def get_default_config(self) -> Dict[str, Any]:
|
||
"""
|
||
获取默认配置
|
||
|
||
Returns:
|
||
默认配置字典
|
||
"""
|
||
return self.default_config.copy()
|
||
|
||
def export_config(self, file_path: str) -> bool:
|
||
"""
|
||
导出配置到文件
|
||
|
||
Args:
|
||
file_path: 导出文件路径
|
||
|
||
Returns:
|
||
是否导出成功
|
||
"""
|
||
try:
|
||
with open(file_path, 'w', encoding='utf-8') as f:
|
||
json.dump(self.current_config, f, indent=2, ensure_ascii=False)
|
||
|
||
print(f"✓ 配置已导出到 {file_path}")
|
||
return True
|
||
except Exception as e:
|
||
print(f"✗ 配置导出失败: {e}")
|
||
return False
|
||
|
||
def import_config(self, file_path: str) -> bool:
|
||
"""
|
||
从文件导入配置
|
||
|
||
Args:
|
||
file_path: 导入文件路径
|
||
|
||
Returns:
|
||
是否导入成功
|
||
"""
|
||
try:
|
||
with open(file_path, 'r', encoding='utf-8') as f:
|
||
imported_config = json.load(f)
|
||
|
||
self._merge_config(self.current_config, imported_config)
|
||
print(f"✓ 配置已从 {file_path} 导入")
|
||
return True
|
||
except Exception as e:
|
||
print(f"✗ 配置导入失败: {e}")
|
||
return False
|
||
|
||
def apply_profile(self, profile_name: str) -> bool:
|
||
"""
|
||
应用配置预设
|
||
|
||
Args:
|
||
profile_name: 预设名称
|
||
|
||
Returns:
|
||
是否应用成功
|
||
"""
|
||
try:
|
||
profiles = self.current_config.get('profiles', {}).get('profiles', {})
|
||
if profile_name not in profiles:
|
||
print(f"✗ 预设不存在: {profile_name}")
|
||
return False
|
||
|
||
profile = profiles[profile_name]
|
||
settings = profile.get('settings', {})
|
||
|
||
# 应用预设设置
|
||
for key, value in settings.items():
|
||
self.set_config_value(key, value)
|
||
|
||
# 更新当前预设
|
||
self.set_config_value('profiles.current_profile', profile_name)
|
||
|
||
print(f"✓ 预设已应用: {profile_name}")
|
||
return True
|
||
except Exception as e:
|
||
print(f"✗ 预设应用失败: {e}")
|
||
return False
|
||
|
||
def create_profile(self, profile_name: str, settings: Dict[str, Any],
|
||
name: str = None, description: str = None) -> bool:
|
||
"""
|
||
创建配置预设
|
||
|
||
Args:
|
||
profile_name: 预设名称
|
||
settings: 预设设置
|
||
name: 显示名称
|
||
description: 描述
|
||
|
||
Returns:
|
||
是否创建成功
|
||
"""
|
||
try:
|
||
profiles = self.current_config.setdefault('profiles', {}).setdefault('profiles', {})
|
||
|
||
profiles[profile_name] = {
|
||
'name': name or profile_name,
|
||
'description': description or f'Custom profile: {profile_name}',
|
||
'settings': settings
|
||
}
|
||
|
||
print(f"✓ 预设已创建: {profile_name}")
|
||
return True
|
||
except Exception as e:
|
||
print(f"✗ 预设创建失败: {e}")
|
||
return False
|
||
|
||
def delete_profile(self, profile_name: str) -> bool:
|
||
"""
|
||
删除配置预设
|
||
|
||
Args:
|
||
profile_name: 预设名称
|
||
|
||
Returns:
|
||
是否删除成功
|
||
"""
|
||
try:
|
||
profiles = self.current_config.get('profiles', {}).get('profiles', {})
|
||
if profile_name in profiles:
|
||
del profiles[profile_name]
|
||
print(f"✓ 预设已删除: {profile_name}")
|
||
return True
|
||
else:
|
||
print(f"✗ 预设不存在: {profile_name}")
|
||
return False
|
||
except Exception as e:
|
||
print(f"✗ 预设删除失败: {e}")
|
||
return False
|
||
|
||
def get_available_profiles(self) -> List[Dict[str, Any]]:
|
||
"""
|
||
获取可用预设列表
|
||
|
||
Returns:
|
||
预设列表
|
||
"""
|
||
try:
|
||
profiles = self.current_config.get('profiles', {}).get('profiles', {})
|
||
result = []
|
||
|
||
for name, profile in profiles.items():
|
||
result.append({
|
||
'name': name,
|
||
'display_name': profile.get('name', name),
|
||
'description': profile.get('description', ''),
|
||
'settings_count': len(profile.get('settings', {}))
|
||
})
|
||
|
||
return result
|
||
except Exception as e:
|
||
print(f"✗ 预设列表获取失败: {e}")
|
||
return []
|
||
|
||
def _trigger_config_change(self, key_path: str, old_value: Any, new_value: Any):
|
||
"""
|
||
触发配置变更回调
|
||
|
||
Args:
|
||
key_path: 配置键路径
|
||
old_value: 旧值
|
||
new_value: 新值
|
||
"""
|
||
try:
|
||
# 通知监听器
|
||
if key_path in self.config_listeners:
|
||
for callback in self.config_listeners[key_path]:
|
||
try:
|
||
callback(key_path, old_value, new_value)
|
||
except Exception as e:
|
||
print(f"✗ 配置变更回调执行失败: {e}")
|
||
|
||
# 通知全局监听器
|
||
if '*' in self.config_listeners:
|
||
for callback in self.config_listeners['*']:
|
||
try:
|
||
callback(key_path, old_value, new_value)
|
||
except Exception as e:
|
||
print(f"✗ 全局配置变更回调执行失败: {e}")
|
||
except Exception as e:
|
||
print(f"✗ 配置变更回调触发失败: {e}")
|
||
|
||
def register_config_listener(self, key_path: str, callback: callable):
|
||
"""
|
||
注册配置变更监听器
|
||
|
||
Args:
|
||
key_path: 配置键路径('*' 表示监听所有变更)
|
||
callback: 回调函数
|
||
"""
|
||
try:
|
||
if key_path not in self.config_listeners:
|
||
self.config_listeners[key_path] = []
|
||
self.config_listeners[key_path].append(callback)
|
||
print(f"✓ 配置监听器已注册: {key_path}")
|
||
except Exception as e:
|
||
print(f"✗ 配置监听器注册失败: {e}")
|
||
|
||
def unregister_config_listener(self, key_path: str, callback: callable):
|
||
"""
|
||
注销配置变更监听器
|
||
|
||
Args:
|
||
key_path: 配置键路径
|
||
callback: 回调函数
|
||
"""
|
||
try:
|
||
if key_path in self.config_listeners:
|
||
if callback in self.config_listeners[key_path]:
|
||
self.config_listeners[key_path].remove(callback)
|
||
print(f"✓ 配置监听器已注销: {key_path}")
|
||
except Exception as e:
|
||
print(f"✗ 配置监听器注销失败: {e}")
|
||
|
||
def get_stats(self) -> Dict[str, int]:
|
||
"""
|
||
获取统计信息
|
||
|
||
Returns:
|
||
统计信息字典
|
||
"""
|
||
return self.stats.copy()
|
||
|
||
def reset_stats(self):
|
||
"""重置统计信息"""
|
||
try:
|
||
self.stats = {
|
||
'config_loads': 0,
|
||
'config_saves': 0,
|
||
'config_changes': 0,
|
||
'validation_errors': 0
|
||
}
|
||
print("✓ 配置管理器统计信息已重置")
|
||
except Exception as e:
|
||
print(f"✗ 配置管理器统计信息重置失败: {e}")
|
||
|