439 lines
16 KiB
C#
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}' 的类型错误,应为布尔类型");
|
|
}
|
|
}
|
|
}
|