NavisworksTransport/src/Core/Config/ConfigManager.cs

439 lines
16 KiB
C#

using System;
using System.IO;
using System.Reflection;
using Tomlyn;
using Tomlyn.Model;
using Tomlyn.Syntax;
namespace NavisworksTransport.Core.Config
{
/// <summary>
/// 系统配置管理器
/// 负责 TOML 配置文件的读取、保存和管理
/// </summary>
public class ConfigManager
{
private static ConfigManager _instance;
private static readonly object _lock = new object();
/// <summary>
/// 单例实例
/// </summary>
public static ConfigManager Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new ConfigManager();
}
}
}
return _instance;
}
}
/// <summary>
/// 配置文件路径
/// </summary>
public static string ConfigFilePath
{
get
{
return Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"Autodesk",
"Navisworks Manage 2026",
"plugins",
"NavisworksTransportPlugin",
"config.toml"
);
}
}
/// <summary>
/// 配置目录
/// </summary>
public static string ConfigDirectory
{
get { return Path.GetDirectoryName(ConfigFilePath); }
}
/// <summary>
/// 配置模板文件路径
/// </summary>
private static string ConfigTemplatePath
{
get
{
// 从插件当前目录获取模板文件
var pluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
return Path.Combine(pluginDirectory, "default_config.toml");
}
}
private SystemConfig _currentConfig;
private string _cachedTemplateContent;
private ConfigManager()
{
_currentConfig = LoadConfigFile();
}
/// <summary>
/// 获取当前配置
/// </summary>
public SystemConfig Current
{
get { return _currentConfig; }
}
/// <summary>
/// 读取配置模板文件内容
/// </summary>
private string ReadConfigTemplate()
{
if (!File.Exists(ConfigTemplatePath))
{
throw new FileNotFoundException($"配置模板文件不存在: {ConfigTemplatePath}");
}
LogManager.Info($"使用配置模板文件: {ConfigTemplatePath}");
return File.ReadAllText(ConfigTemplatePath);
}
/// <summary>
/// 加载配置文件,如果不存在则从模板创建
/// </summary>
private SystemConfig LoadConfigFile()
{
try
{
if (!File.Exists(ConfigFilePath))
{
LogManager.Info($"配置文件不存在,从模板创建: {ConfigFilePath}");
var templateContent = ReadConfigTemplate();
WriteConfigFileDirect(templateContent);
return ParseTomlToConfig(templateContent);
}
LogManager.Info($"加载配置文件: {ConfigFilePath}");
var tomlContent = File.ReadAllText(ConfigFilePath);
var config = ParseTomlToConfig(tomlContent);
LogManager.Info("配置文件加载成功");
return config;
}
catch (Exception ex)
{
LogManager.Error($"加载配置文件失败: {ex.Message}");
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
// 加载失败时使用模板中的默认配置
LogManager.Info("使用模板中的默认配置");
var templateContent = ReadConfigTemplate();
return ParseTomlToConfig(templateContent);
}
}
/// <summary>
/// 将 TOML 字符串解析为配置对象
/// </summary>
private SystemConfig ParseTomlToConfig(string tomlContent)
{
var model = Toml.ToModel(tomlContent);
var config = CreateEmptyConfigObject();
// 加载路径编辑配置
if (!model.ContainsKey("path_editing"))
{
throw new InvalidOperationException("配置文件缺少 [path_editing] 部分");
}
var pathEdit = model["path_editing"] as TomlTable;
if (pathEdit == null)
{
throw new InvalidOperationException("[path_editing] 部分格式错误");
}
config.PathEditing.CellSizeMeters = GetRequiredDoubleValue(pathEdit, "cell_size_meters");
config.PathEditing.MaxHeightDiffMeters = GetRequiredDoubleValue(pathEdit, "max_height_diff_meters");
config.PathEditing.VehicleLengthMeters = GetRequiredDoubleValue(pathEdit, "vehicle_length_meters");
config.PathEditing.VehicleWidthMeters = GetRequiredDoubleValue(pathEdit, "vehicle_width_meters");
config.PathEditing.VehicleHeightMeters = GetRequiredDoubleValue(pathEdit, "vehicle_height_meters");
config.PathEditing.SafetyMarginMeters = GetRequiredDoubleValue(pathEdit, "safety_margin_meters");
config.PathEditing.DefaultPathTurnRadius = GetRequiredDoubleValue(pathEdit, "default_path_turn_radius");
config.PathEditing.ArcSamplingStep = GetRequiredDoubleValue(pathEdit, "arc_sampling_step");
// 加载可视化配置
if (!model.ContainsKey("visualization"))
{
throw new InvalidOperationException("配置文件缺少 [visualization] 部分");
}
var visual = model["visualization"] as TomlTable;
if (visual == null)
{
throw new InvalidOperationException("[visualization] 部分格式错误");
}
config.Visualization.MarginRatio = GetRequiredDoubleValue(visual, "margin_ratio");
// 加载动画配置
if (!model.ContainsKey("animation"))
{
throw new InvalidOperationException("配置文件缺少 [animation] 部分");
}
var anim = model["animation"] as TomlTable;
if (anim == null)
{
throw new InvalidOperationException("[animation] 部分格式错误");
}
config.Animation.FrameRate = GetRequiredIntValue(anim, "frame_rate");
config.Animation.DurationSeconds = GetRequiredDoubleValue(anim, "duration_seconds");
config.Animation.DetectionGapMeters = GetRequiredDoubleValue(anim, "detection_gap_meters");
// 加载物流属性配置
if (!model.ContainsKey("logistics"))
{
throw new InvalidOperationException("配置文件缺少 [logistics] 部分");
}
var logistics = model["logistics"] as TomlTable;
if (logistics == null)
{
throw new InvalidOperationException("[logistics] 部分格式错误");
}
config.Logistics.Traversable = GetRequiredBoolValue(logistics, "traversable");
config.Logistics.Priority = GetRequiredIntValue(logistics, "priority");
config.Logistics.HeightLimitMeters = GetRequiredDoubleValue(logistics, "height_limit_meters");
config.Logistics.SpeedLimitMetersPerSecond = GetRequiredDoubleValue(logistics, "speed_limit_meters_per_second");
config.Logistics.WidthLimitMeters = GetRequiredDoubleValue(logistics, "width_limit_meters");
return config;
}
/// <summary>
/// 创建空的配置对象(初始化所有嵌套对象)
/// </summary>
private SystemConfig CreateEmptyConfigObject()
{
return new SystemConfig
{
GridGeneration = new GridGenerationConfig(),
PathPlanning = new PathPlanningConfig(),
PathEditing = new PathEditingConfig(),
Visualization = new VisualizationConfig(),
Animation = new AnimationConfig(),
Logistics = new LogisticsConfig()
};
}
/// <summary>
/// 保存配置到文件
/// </summary>
public void SaveConfig(SystemConfig config)
{
try
{
// 确保目录存在
Directory.CreateDirectory(ConfigDirectory);
var tomlContent = GenerateTomlFromConfig(config);
File.WriteAllText(ConfigFilePath, tomlContent);
_currentConfig = config;
LogManager.Info($"配置文件已保存: {ConfigFilePath}");
}
catch (Exception ex)
{
LogManager.Error($"保存配置文件失败: {ex.Message}");
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
throw;
}
}
/// <summary>
/// 直接写入配置文件内容(不经过模板转换)
/// </summary>
private void WriteConfigFileDirect(string tomlContent)
{
try
{
// 确保目录存在
Directory.CreateDirectory(ConfigDirectory);
File.WriteAllText(ConfigFilePath, tomlContent);
LogManager.Info($"配置文件已创建: {ConfigFilePath}");
}
catch (Exception ex)
{
LogManager.Error($"写入配置文件失败: {ex.Message}");
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
throw;
}
}
/// <summary>
/// 从配置对象生成 TOML 字符串(使用模板格式)
/// </summary>
private string GenerateTomlFromConfig(SystemConfig config)
{
// 加载模板内容(缓存)
if (_cachedTemplateContent == null)
{
_cachedTemplateContent = ReadConfigTemplate();
}
// 解析模板
var syntax = Toml.Parse(_cachedTemplateContent);
var model = syntax.ToModel();
// 更新配置值到模型
ApplyConfigValuesToModel(model, config);
// 转换回 TOML 字符串
return Toml.FromModel(model);
}
/// <summary>
/// 将配置对象的值应用到 TOML 模型
/// </summary>
private void ApplyConfigValuesToModel(TomlTable model, SystemConfig config)
{
// 更新路径编辑配置
if (model.ContainsKey("path_editing"))
{
var pathEdit = model["path_editing"] as TomlTable;
if (pathEdit != null)
{
pathEdit["cell_size_meters"] = config.PathEditing.CellSizeMeters;
pathEdit["max_height_diff_meters"] = config.PathEditing.MaxHeightDiffMeters;
pathEdit["vehicle_length_meters"] = config.PathEditing.VehicleLengthMeters;
pathEdit["vehicle_width_meters"] = config.PathEditing.VehicleWidthMeters;
pathEdit["vehicle_height_meters"] = config.PathEditing.VehicleHeightMeters;
pathEdit["safety_margin_meters"] = config.PathEditing.SafetyMarginMeters;
pathEdit["default_path_turn_radius"] = config.PathEditing.DefaultPathTurnRadius;
pathEdit["arc_sampling_step"] = config.PathEditing.ArcSamplingStep;
}
}
// 更新可视化配置
if (model.ContainsKey("visualization"))
{
var visual = model["visualization"] as TomlTable;
if (visual != null)
{
visual["margin_ratio"] = config.Visualization.MarginRatio;
}
}
// 更新动画配置
if (model.ContainsKey("animation"))
{
var anim = model["animation"] as TomlTable;
if (anim != null)
{
anim["frame_rate"] = config.Animation.FrameRate;
anim["duration_seconds"] = config.Animation.DurationSeconds;
anim["detection_gap_meters"] = config.Animation.DetectionGapMeters;
}
}
// 更新物流属性配置
if (model.ContainsKey("logistics"))
{
var logistics = model["logistics"] as TomlTable;
if (logistics != null)
{
logistics["traversable"] = config.Logistics.Traversable;
logistics["priority"] = config.Logistics.Priority;
logistics["height_limit_meters"] = config.Logistics.HeightLimitMeters;
logistics["speed_limit_meters_per_second"] = config.Logistics.SpeedLimitMetersPerSecond;
logistics["width_limit_meters"] = config.Logistics.WidthLimitMeters;
}
}
}
/// <summary>
/// 重新加载配置文件
/// </summary>
public void Reload()
{
_currentConfig = LoadConfigFile();
}
/// <summary>
/// 恢复为模板中的默认配置
/// </summary>
public void RestoreToDefaults()
{
LogManager.Info("开始恢复默认配置...");
var templateContent = ReadConfigTemplate();
LogManager.Info($"模板内容长度: {templateContent.Length}");
LogManager.Info($"模板内容前200字符: {templateContent.Substring(0, Math.Min(200, templateContent.Length))}");
WriteConfigFileDirect(templateContent);
var newConfig = ParseTomlToConfig(templateContent);
LogManager.Info($"解析后的配置 - CellSizeMeters: {newConfig.PathEditing.CellSizeMeters}");
LogManager.Info($"解析后的配置 - VehicleLengthMeters: {newConfig.PathEditing.VehicleLengthMeters}");
_currentConfig = newConfig;
LogManager.Info("默认配置恢复完成");
}
// 辅助方法:从 TomlTable 获取必需值
private double GetRequiredDoubleValue(TomlTable table, string key)
{
if (!table.ContainsKey(key))
{
throw new InvalidOperationException($"配置项 '{key}' 不存在");
}
var value = table[key];
if (value is long longValue)
return (double)longValue;
if (value is double doubleValue)
return doubleValue;
throw new InvalidOperationException($"配置项 '{key}' 的类型错误,应为数值类型");
}
private int GetRequiredIntValue(TomlTable table, string key)
{
if (!table.ContainsKey(key))
{
throw new InvalidOperationException($"配置项 '{key}' 不存在");
}
var value = table[key];
if (value is long longValue)
return (int)longValue;
throw new InvalidOperationException($"配置项 '{key}' 的类型错误,应为整数类型");
}
private bool GetRequiredBoolValue(TomlTable table, string key)
{
if (!table.ContainsKey(key))
{
throw new InvalidOperationException($"配置项 '{key}' 不存在");
}
var value = table[key];
if (value is bool boolValue)
return boolValue;
throw new InvalidOperationException($"配置项 '{key}' 的类型错误,应为布尔类型");
}
}
}