建立简单的数据库备份和恢复机制
This commit is contained in:
parent
d9da8e95ce
commit
a655e8092c
@ -64,6 +64,9 @@
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
@ -131,6 +134,7 @@
|
||||
<!-- Core - Configuration Management -->
|
||||
<Compile Include="src\Core\Config\SystemConfig.cs" />
|
||||
<Compile Include="src\Core\Config\ConfigManager.cs" />
|
||||
<Compile Include="src\Core\Database\BackupManager.cs" />
|
||||
<!-- Commands - Command Pattern Framework (for testing) -->
|
||||
<Compile Include="src\Commands\IPathPlanningCommand.cs" />
|
||||
<Compile Include="src\Commands\CommandBase.cs" />
|
||||
|
||||
@ -2,6 +2,12 @@
|
||||
|
||||
## 功能点
|
||||
|
||||
### [2026/2/6]
|
||||
|
||||
1. [x] (功能)在碰撞报告中支持多张截图
|
||||
2. [x] (功能)增加提取元素的包围盒信息,并一键拷贝到坐标编辑窗口
|
||||
3. [x] (功能)在状态栏增加路径可视化快捷按钮
|
||||
|
||||
### [2026/2/3]
|
||||
|
||||
1. [x] (优化)预计算高亮正确,结果高亮错误,高亮了很多不相干的同名物体
|
||||
|
||||
@ -6,4 +6,5 @@
|
||||
<package id="MSTest.TestAdapter" version="3.0.4" targetFramework="net48" />
|
||||
<package id="System.Data.SQLite.Core" version="1.0.118.0" targetFramework="net48" />
|
||||
<package id="geometry4Sharp" version="1.0.0" targetFramework="net48" />
|
||||
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
|
||||
</packages>
|
||||
276
src/Core/Database/BackupManager.cs
Normal file
276
src/Core/Database/BackupManager.cs
Normal file
@ -0,0 +1,276 @@
|
||||
using System;
|
||||
using System.Data.SQLite;
|
||||
using System.IO;
|
||||
|
||||
namespace NavisworksTransport.Core.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// 简单数据库备份管理器
|
||||
/// 直接备份整个 SQLite 数据库文件,100% 保留所有数据
|
||||
/// </summary>
|
||||
public class BackupManager
|
||||
{
|
||||
private readonly PathDatabase _database;
|
||||
private readonly string _dbPath;
|
||||
|
||||
public BackupManager(PathDatabase database)
|
||||
{
|
||||
_database = database ?? throw new ArgumentNullException(nameof(database));
|
||||
_dbPath = GetDatabaseFilePath(database);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据库文件路径
|
||||
/// </summary>
|
||||
private string GetDatabaseFilePath(PathDatabase database)
|
||||
{
|
||||
// 通过反射获取私有字段 _dbPath
|
||||
var field = typeof(PathDatabase).GetField("_dbPath", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
return field?.GetValue(database) as string ?? "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据库版本号
|
||||
/// </summary>
|
||||
public int GetDatabaseVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var cmd = new SQLiteCommand("PRAGMA user_version", _database.Connection))
|
||||
{
|
||||
return Convert.ToInt32(cmd.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格式化版本号显示
|
||||
/// </summary>
|
||||
public static string FormatVersion(int version)
|
||||
{
|
||||
if (version == 0) return "未知版本";
|
||||
int major = version / 10000;
|
||||
int minor = (version % 10000) / 100;
|
||||
int patch = version % 100;
|
||||
return $"v{major}.{minor}.{patch}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查两个版本是否兼容(主版本必须相同)
|
||||
/// </summary>
|
||||
public static bool IsVersionCompatible(int sourceVersion, int targetVersion)
|
||||
{
|
||||
if (sourceVersion == 0 || targetVersion == 0) return false;
|
||||
int sourceMajor = sourceVersion / 10000;
|
||||
int targetMajor = targetVersion / 10000;
|
||||
return sourceMajor == targetMajor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 备份数据库(直接复制文件)
|
||||
/// </summary>
|
||||
public string BackupDatabase(string exportDirectory = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(_dbPath) || !File.Exists(_dbPath))
|
||||
{
|
||||
throw new InvalidOperationException("数据库文件不存在");
|
||||
}
|
||||
|
||||
// 确保目录存在
|
||||
if (string.IsNullOrEmpty(exportDirectory))
|
||||
{
|
||||
exportDirectory = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"NavisworksTransport", "Backups");
|
||||
}
|
||||
|
||||
if (!Directory.Exists(exportDirectory))
|
||||
Directory.CreateDirectory(exportDirectory);
|
||||
|
||||
// 获取当前版本号
|
||||
int version = GetDatabaseVersion();
|
||||
string versionStr = FormatVersion(version).Replace(".", "_");
|
||||
|
||||
// 生成备份文件名(包含版本号)
|
||||
string fileName = $"NavisworksTransport_{versionStr}_{DateTime.Now:yyyyMMdd_HHmmss}.db";
|
||||
string backupPath = Path.Combine(exportDirectory, fileName);
|
||||
|
||||
// 关闭数据库连接以释放文件锁
|
||||
_database.Connection.Close();
|
||||
|
||||
try
|
||||
{
|
||||
// 复制数据库文件
|
||||
File.Copy(_dbPath, backupPath, true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 重新打开连接
|
||||
_database.Connection.Open();
|
||||
}
|
||||
|
||||
LogManager.Info($"数据库备份完成: {backupPath}");
|
||||
return backupPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"数据库备份失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定数据库文件的版本号
|
||||
/// </summary>
|
||||
private int GetBackupFileVersion(string backupFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var connection = new SQLiteConnection($"Data Source={backupFilePath};Version=3;"))
|
||||
{
|
||||
connection.Open();
|
||||
using (var cmd = new SQLiteCommand("PRAGMA user_version", connection))
|
||||
{
|
||||
return Convert.ToInt32(cmd.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 恢复数据库(直接替换文件)
|
||||
/// </summary>
|
||||
public void RestoreDatabase(string backupFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(backupFilePath))
|
||||
{
|
||||
throw new FileNotFoundException("备份文件不存在", backupFilePath);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_dbPath))
|
||||
{
|
||||
throw new InvalidOperationException("数据库路径未知");
|
||||
}
|
||||
|
||||
// 获取版本信息
|
||||
int currentVersion = GetDatabaseVersion();
|
||||
int backupVersion = GetBackupFileVersion(backupFilePath);
|
||||
|
||||
LogManager.Info($"数据库版本检查: 当前={FormatVersion(currentVersion)}, 备份={FormatVersion(backupVersion)}");
|
||||
|
||||
// 版本兼容性检查
|
||||
if (!IsVersionCompatible(backupVersion, currentVersion))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"版本不兼容!\n\n" +
|
||||
$"备份版本: {FormatVersion(backupVersion)}\n" +
|
||||
$"当前版本: {FormatVersion(currentVersion)}\n\n" +
|
||||
$"主版本号必须相同才能恢复。\n" +
|
||||
$"请使用相同版本的插件进行恢复,或联系管理员进行数据库升级。");
|
||||
}
|
||||
|
||||
// 关闭数据库连接
|
||||
_database.Connection.Close();
|
||||
|
||||
try
|
||||
{
|
||||
// 备份当前数据库(以防万一)
|
||||
if (File.Exists(_dbPath))
|
||||
{
|
||||
string safetyBackup = _dbPath + $".backup_{DateTime.Now:yyyyMMdd_HHmmss}";
|
||||
File.Copy(_dbPath, safetyBackup, true);
|
||||
LogManager.Info($"已创建安全备份: {safetyBackup}");
|
||||
}
|
||||
|
||||
// 替换数据库文件
|
||||
File.Copy(backupFilePath, _dbPath, true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 重新打开连接
|
||||
try { _database.Connection.Open(); } catch { }
|
||||
}
|
||||
|
||||
LogManager.Info($"数据库恢复完成: {backupFilePath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"数据库恢复失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据库信息
|
||||
/// </summary>
|
||||
public DatabaseInfo GetDatabaseInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
var info = new DatabaseInfo
|
||||
{
|
||||
FilePath = _dbPath,
|
||||
FileSize = File.Exists(_dbPath) ? new FileInfo(_dbPath).Length : 0,
|
||||
Version = GetDatabaseVersion()
|
||||
};
|
||||
|
||||
// 获取表行数
|
||||
info.PathRoutesCount = GetTableCount("PathRoutes");
|
||||
info.PathPointsCount = GetTableCount("PathPoints");
|
||||
info.PathEdgesCount = GetTableCount("PathEdges");
|
||||
info.ClashResultsCount = GetTableCount("ClashDetectiveResults");
|
||||
info.ScreenshotsCount = GetTableCount("CollisionReportScreenshots");
|
||||
|
||||
return info;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取数据库信息失败: {ex.Message}");
|
||||
return new DatabaseInfo();
|
||||
}
|
||||
}
|
||||
|
||||
private int GetTableCount(string tableName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sql = $"SELECT COUNT(*) FROM {tableName}";
|
||||
using (var cmd = new SQLiteCommand(sql, _database.Connection))
|
||||
{
|
||||
return Convert.ToInt32(cmd.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DatabaseInfo
|
||||
{
|
||||
public string FilePath { get; set; }
|
||||
public long FileSize { get; set; }
|
||||
public int Version { get; set; }
|
||||
public int PathRoutesCount { get; set; }
|
||||
public int PathPointsCount { get; set; }
|
||||
public int PathEdgesCount { get; set; }
|
||||
public int ClashResultsCount { get; set; }
|
||||
public int ScreenshotsCount { get; set; }
|
||||
|
||||
public string FileSizeFormatted => $"{FileSize / 1024} KB";
|
||||
public string VersionFormatted => BackupManager.FormatVersion(Version);
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using NavisworksTransport.Core.Config;
|
||||
using NavisworksTransport.Core.Models;
|
||||
using NavisworksTransport.Core.Database;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
@ -18,12 +19,18 @@ namespace NavisworksTransport
|
||||
{
|
||||
internal SQLiteConnection _connection;
|
||||
private readonly string _dbPath;
|
||||
private BackupManager _backupManager;
|
||||
|
||||
/// <summary>
|
||||
/// 数据库连接(供内部使用)
|
||||
/// </summary>
|
||||
internal SQLiteConnection Connection => _connection;
|
||||
|
||||
/// <summary>
|
||||
/// 简单数据库备份管理器(直接备份文件,最可靠)
|
||||
/// </summary>
|
||||
public BackupManager Backup => _backupManager ?? (_backupManager = new BackupManager(this));
|
||||
|
||||
/// <summary>
|
||||
/// 初始化路径数据库
|
||||
/// </summary>
|
||||
@ -217,6 +224,11 @@ namespace NavisworksTransport
|
||||
)
|
||||
");
|
||||
|
||||
// 9. 设置数据库版本(SQLite内置user_version)
|
||||
// 版本号格式:主版本*10000 + 次版本*100 + 修订号
|
||||
// 例如:2.1.3 = 20103
|
||||
ExecuteNonQuery("PRAGMA user_version = 20100"); // v2.1.0
|
||||
|
||||
// 创建索引
|
||||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_reports_route ON CollisionReports(RouteId)");
|
||||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_pathpoints_route ON PathPoints(RouteId)");
|
||||
@ -868,6 +880,107 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有碰撞检测结果
|
||||
/// </summary>
|
||||
public List<ClashDetectiveResultRecord> GetAllClashDetectiveResults()
|
||||
{
|
||||
var results = new List<ClashDetectiveResultRecord>();
|
||||
|
||||
try
|
||||
{
|
||||
var sql = @"
|
||||
SELECT cdr.Id, cdr.TestName, cdr.RouteId, pr.Name AS PathName, cdr.TestTime,
|
||||
cdr.CollisionCount, cdr.AnimationCollisionCount,
|
||||
cdr.FrameRate, cdr.Duration, cdr.DetectionGap, cdr.AnimatedObjectName,
|
||||
cdr.CreatedAt, cdr.ScreenshotPath
|
||||
FROM ClashDetectiveResults cdr
|
||||
INNER JOIN PathRoutes pr ON cdr.RouteId = pr.Id
|
||||
ORDER BY cdr.TestTime DESC
|
||||
";
|
||||
|
||||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
results.Add(new ClashDetectiveResultRecord
|
||||
{
|
||||
Id = Convert.ToInt32(reader["Id"]),
|
||||
TestName = reader["TestName"].ToString(),
|
||||
RouteId = reader["RouteId"].ToString(),
|
||||
PathName = reader["PathName"].ToString(),
|
||||
TestTime = Convert.ToDateTime(reader["TestTime"]),
|
||||
CollisionCount = Convert.ToInt32(reader["CollisionCount"]),
|
||||
AnimationCollisionCount = Convert.ToInt32(reader["AnimationCollisionCount"]),
|
||||
FrameRate = Convert.ToInt32(reader["FrameRate"]),
|
||||
Duration = Convert.ToDouble(reader["Duration"]),
|
||||
DetectionGap = Convert.ToDouble(reader["DetectionGap"]),
|
||||
AnimatedObjectName = reader["AnimatedObjectName"].ToString(),
|
||||
CreatedAt = Convert.ToDateTime(reader["CreatedAt"]),
|
||||
ScreenshotPath = reader["ScreenshotPath"]?.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取所有碰撞检测结果失败: {ex.Message}", ex);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有数据
|
||||
/// </summary>
|
||||
public void ClearAllData()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 按依赖关系顺序删除(先删除子表,再删除主表)
|
||||
var tables = new[]
|
||||
{
|
||||
"CollisionReportScreenshots",
|
||||
"ClashDetectiveCollisionObjects",
|
||||
"ClashDetectiveResults",
|
||||
"PathEdges",
|
||||
"PathPoints",
|
||||
"AnalysisResults",
|
||||
"CollisionReports",
|
||||
"PathRoutes",
|
||||
"BatchQueueItems"
|
||||
};
|
||||
|
||||
foreach (var table in tables)
|
||||
{
|
||||
try
|
||||
{
|
||||
ExecuteNonQuery($"DELETE FROM {table}");
|
||||
LogManager.Debug($"已清空表: {table}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"清空表 {table} 失败(可能表不存在): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 重置自增ID
|
||||
try
|
||||
{
|
||||
ExecuteNonQuery("DELETE FROM sqlite_sequence");
|
||||
}
|
||||
catch { }
|
||||
|
||||
LogManager.Info("已清空所有数据表");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"清空数据失败: {ex.Message}", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据测试名称获取ClashDetective结果记录
|
||||
/// </summary>
|
||||
|
||||
@ -30,6 +30,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
private string _memoryUsage = "0 MB";
|
||||
private string _runningTime = "00:00:00";
|
||||
|
||||
// 数据管理字段
|
||||
private string _databaseVersion = "--";
|
||||
private string _pathCount = "--";
|
||||
private string _collisionRecordCount = "--";
|
||||
private bool _autoBackupEnabled = true;
|
||||
private int _selectedBackupKeepCount = 10;
|
||||
private string _lastBackupInfo = "暂无备份记录";
|
||||
|
||||
// 🔧 修复:添加释放状态标志
|
||||
private bool _disposed = false;
|
||||
|
||||
@ -103,6 +111,44 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
set => SetProperty(ref _runningTime, value);
|
||||
}
|
||||
|
||||
// 数据管理属性
|
||||
public string DatabaseVersion
|
||||
{
|
||||
get => _databaseVersion;
|
||||
set => SetProperty(ref _databaseVersion, value);
|
||||
}
|
||||
|
||||
public string PathCount
|
||||
{
|
||||
get => _pathCount;
|
||||
set => SetProperty(ref _pathCount, value);
|
||||
}
|
||||
|
||||
public string CollisionRecordCount
|
||||
{
|
||||
get => _collisionRecordCount;
|
||||
set => SetProperty(ref _collisionRecordCount, value);
|
||||
}
|
||||
|
||||
public bool AutoBackupEnabled
|
||||
{
|
||||
get => _autoBackupEnabled;
|
||||
set => SetProperty(ref _autoBackupEnabled, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<int> BackupKeepCountOptions { get; } = new ObservableCollection<int> { 5, 10, 20, 50 };
|
||||
|
||||
public int SelectedBackupKeepCount
|
||||
{
|
||||
get => _selectedBackupKeepCount;
|
||||
set => SetProperty(ref _selectedBackupKeepCount, value);
|
||||
}
|
||||
|
||||
public string LastBackupInfo
|
||||
{
|
||||
get => _lastBackupInfo;
|
||||
set => SetProperty(ref _lastBackupInfo, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -114,6 +160,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
public ICommand GeneratePerformanceReportCommand { get; private set; }
|
||||
public ICommand DiagnosticCommand { get; private set; }
|
||||
|
||||
// 数据管理命令
|
||||
public ICommand BackupDataCommand { get; private set; }
|
||||
public ICommand RestoreDataCommand { get; private set; }
|
||||
public ICommand RepairDatabaseCommand { get; private set; }
|
||||
public ICommand ClearAllDataCommand { get; private set; }
|
||||
|
||||
// 功能测试命令
|
||||
public ICommand TestVoxelGridSDFCommand { get; private set; }
|
||||
public ICommand TestVoxelPathFindingCommand { get; private set; }
|
||||
@ -245,6 +297,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
GeneratePerformanceReportCommand = new RelayCommand(() => ExecuteGeneratePerformanceReport());
|
||||
DiagnosticCommand = new RelayCommand(() => ExecuteDiagnostic());
|
||||
|
||||
// 数据管理命令
|
||||
BackupDataCommand = new RelayCommand(() => ExecuteBackupData());
|
||||
RestoreDataCommand = new RelayCommand(() => ExecuteRestoreData());
|
||||
RepairDatabaseCommand = new RelayCommand(() => ExecuteRepairDatabase());
|
||||
ClearAllDataCommand = new RelayCommand(() => ExecuteClearAllData());
|
||||
|
||||
// 功能测试命令
|
||||
TestVoxelGridSDFCommand = new RelayCommand(() => ExecuteTestVoxelGridSDF());
|
||||
TestVoxelPathFindingCommand = new RelayCommand(() => ExecuteTestVoxelPathFinding());
|
||||
@ -531,6 +589,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 初始化系统信息
|
||||
PluginVersion = "v1.0";
|
||||
NavisworksVersion = "2026";
|
||||
|
||||
// 初始化数据库状态(如果数据库已连接)
|
||||
RefreshDatabaseStatus();
|
||||
|
||||
UpdateMainStatus("系统管理初始化完成");
|
||||
});
|
||||
|
||||
@ -852,6 +914,323 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
#region 数据管理方法
|
||||
|
||||
/// <summary>
|
||||
/// 刷新数据库状态信息
|
||||
/// </summary>
|
||||
private void RefreshDatabaseStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
|
||||
if (pathDatabase != null)
|
||||
{
|
||||
// 获取数据库信息
|
||||
var dbInfo = pathDatabase.Backup.GetDatabaseInfo();
|
||||
DatabaseVersion = dbInfo.VersionFormatted;
|
||||
PathCount = dbInfo.PathRoutesCount.ToString();
|
||||
CollisionRecordCount = dbInfo.ClashResultsCount.ToString();
|
||||
LogManager.Debug($"数据库状态: 版本{dbInfo.VersionFormatted}, 路径{dbInfo.PathRoutesCount}, 点{dbInfo.PathPointsCount}, 边{dbInfo.PathEdgesCount}, 碰撞{dbInfo.ClashResultsCount}");
|
||||
}
|
||||
else
|
||||
{
|
||||
DatabaseVersion = "未连接";
|
||||
PathCount = "--";
|
||||
CollisionRecordCount = "--";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"刷新数据库状态失败: {ex.Message}");
|
||||
DatabaseVersion = "错误";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行数据备份
|
||||
/// </summary>
|
||||
private void ExecuteBackupData()
|
||||
{
|
||||
SafeExecute(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
|
||||
if (pathDatabase == null)
|
||||
{
|
||||
System.Windows.MessageBox.Show(
|
||||
"未找到数据库连接,请确保已加载模型",
|
||||
"备份数据",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateMainStatus("正在备份数据库...");
|
||||
LogManager.Info("开始数据库文件备份");
|
||||
|
||||
string backupPath = pathDatabase.Backup.BackupDatabase();
|
||||
|
||||
if (!string.IsNullOrEmpty(backupPath))
|
||||
{
|
||||
var info = pathDatabase.Backup.GetDatabaseInfo();
|
||||
LastBackupInfo = $"上次备份: {DateTime.Now:yyyy-MM-dd HH:mm:ss}, 版本{info.VersionFormatted}, {info.FileSizeFormatted}";
|
||||
System.Windows.MessageBox.Show(
|
||||
$"数据库备份完成!\n\n" +
|
||||
$"版本: {info.VersionFormatted}\n" +
|
||||
$"文件: {backupPath}\n\n" +
|
||||
$"包含数据:\n" +
|
||||
$"- 路径: {info.PathRoutesCount} 条\n" +
|
||||
$"- 路径点: {info.PathPointsCount} 个\n" +
|
||||
$"- 碰撞记录: {info.ClashResultsCount} 条\n" +
|
||||
$"- 截图: {info.ScreenshotsCount} 张",
|
||||
"备份成功",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Information);
|
||||
UpdateMainStatus("数据库备份完成");
|
||||
LogManager.Info($"数据库备份完成: {backupPath}, 版本{info.VersionFormatted}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"数据备份失败: {ex.Message}");
|
||||
System.Windows.MessageBox.Show(
|
||||
$"数据备份失败: {ex.Message}",
|
||||
"备份失败",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Error);
|
||||
UpdateMainStatus("数据备份失败");
|
||||
}
|
||||
}, "数据备份");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行数据恢复
|
||||
/// </summary>
|
||||
private void ExecuteRestoreData()
|
||||
{
|
||||
SafeExecute(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
|
||||
if (pathDatabase == null)
|
||||
{
|
||||
System.Windows.MessageBox.Show(
|
||||
"未找到数据库连接,请确保已加载模型",
|
||||
"恢复数据",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 选择备份文件
|
||||
var openFileDialog = new Microsoft.Win32.OpenFileDialog
|
||||
{
|
||||
Title = "选择数据库备份文件",
|
||||
Filter = "数据库备份文件 (*.db)|*.db",
|
||||
InitialDirectory = System.IO.Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"NavisworksTransport", "Backups")
|
||||
};
|
||||
|
||||
if (openFileDialog.ShowDialog() != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 确认恢复
|
||||
var result = System.Windows.MessageBox.Show(
|
||||
"恢复数据将覆盖现有数据,建议先备份当前数据。\n\n是否继续?",
|
||||
"确认恢复",
|
||||
System.Windows.MessageBoxButton.YesNo,
|
||||
System.Windows.MessageBoxImage.Warning);
|
||||
|
||||
if (result != System.Windows.MessageBoxResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateMainStatus("正在恢复数据库...");
|
||||
LogManager.Info($"开始数据库恢复: {openFileDialog.FileName}");
|
||||
|
||||
pathDatabase.Backup.RestoreDatabase(openFileDialog.FileName);
|
||||
|
||||
// 恢复成功
|
||||
bool restoreSuccess = true;
|
||||
|
||||
if (restoreSuccess)
|
||||
{
|
||||
// 刷新数据库状态
|
||||
RefreshDatabaseStatus();
|
||||
|
||||
var info = pathDatabase.Backup.GetDatabaseInfo();
|
||||
System.Windows.MessageBox.Show(
|
||||
$"数据库恢复完成!\n\n" +
|
||||
$"当前数据:\n" +
|
||||
$"- 路径: {info.PathRoutesCount} 条\n" +
|
||||
$"- 路径点: {info.PathPointsCount} 个\n" +
|
||||
$"- 碰撞记录: {info.ClashResultsCount} 条\n" +
|
||||
$"- 截图: {info.ScreenshotsCount} 张\n\n" +
|
||||
$"请重新启动 Navisworks 以加载恢复的数据。",
|
||||
"恢复成功",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Information);
|
||||
UpdateMainStatus("数据恢复完成");
|
||||
LogManager.Info("数据恢复完成");
|
||||
|
||||
// 刷新显示
|
||||
RefreshDatabaseStatus();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"数据恢复失败: {ex.Message}");
|
||||
System.Windows.MessageBox.Show(
|
||||
$"数据恢复失败: {ex.Message}",
|
||||
"恢复失败",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Error);
|
||||
UpdateMainStatus("数据恢复失败");
|
||||
}
|
||||
}, "数据恢复");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行数据库修复
|
||||
/// </summary>
|
||||
private void ExecuteRepairDatabase()
|
||||
{
|
||||
SafeExecute(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
|
||||
if (pathDatabase == null)
|
||||
{
|
||||
System.Windows.MessageBox.Show(
|
||||
"未找到数据库连接",
|
||||
"修复数据库",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateMainStatus("正在检查数据库...");
|
||||
LogManager.Info("开始数据库修复检查");
|
||||
|
||||
// 获取数据库信息
|
||||
var info = pathDatabase.Backup.GetDatabaseInfo();
|
||||
|
||||
System.Windows.MessageBox.Show(
|
||||
$"数据库检查结果:\n\n" +
|
||||
$"版本: {info.VersionFormatted}\n" +
|
||||
$"文件大小: {info.FileSizeFormatted}\n" +
|
||||
$"路径数量: {info.PathRoutesCount} 条\n" +
|
||||
$"路径点: {info.PathPointsCount} 个\n" +
|
||||
$"路径边: {info.PathEdgesCount} 条\n" +
|
||||
$"碰撞记录: {info.ClashResultsCount} 条\n" +
|
||||
$"截图: {info.ScreenshotsCount} 张",
|
||||
"检查结果",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Information);
|
||||
|
||||
// 刷新状态
|
||||
RefreshDatabaseStatus();
|
||||
UpdateMainStatus("数据库检查完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"数据库修复失败: {ex.Message}");
|
||||
System.Windows.MessageBox.Show(
|
||||
$"数据库修复失败: {ex.Message}",
|
||||
"修复失败",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Error);
|
||||
UpdateMainStatus("数据库修复失败");
|
||||
}
|
||||
}, "数据库修复");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行清空所有数据
|
||||
/// </summary>
|
||||
private void ExecuteClearAllData()
|
||||
{
|
||||
SafeExecute(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
|
||||
if (pathDatabase == null)
|
||||
{
|
||||
System.Windows.MessageBox.Show(
|
||||
"未找到数据库连接",
|
||||
"清空数据",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 双重确认
|
||||
var result1 = System.Windows.MessageBox.Show(
|
||||
"⚠️ 警告:此操作将永久删除所有路径和碰撞记录!\n\n" +
|
||||
"建议先备份数据。\n\n" +
|
||||
"是否继续?",
|
||||
"确认清空数据",
|
||||
System.Windows.MessageBoxButton.YesNo,
|
||||
System.Windows.MessageBoxImage.Warning);
|
||||
|
||||
if (result1 != System.Windows.MessageBoxResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result2 = System.Windows.MessageBox.Show(
|
||||
"请再次确认:是否永久删除所有数据?\n\n" +
|
||||
"此操作不可恢复!",
|
||||
"最终确认",
|
||||
System.Windows.MessageBoxButton.YesNo,
|
||||
System.Windows.MessageBoxImage.Warning);
|
||||
|
||||
if (result2 != System.Windows.MessageBoxResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateMainStatus("正在清空数据...");
|
||||
LogManager.Info("开始清空所有数据");
|
||||
|
||||
// 清空所有表
|
||||
pathDatabase.ClearAllData();
|
||||
|
||||
System.Windows.MessageBox.Show(
|
||||
"所有数据已清空!",
|
||||
"清空完成",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Information);
|
||||
UpdateMainStatus("数据已清空");
|
||||
LogManager.Info("所有数据已清空");
|
||||
|
||||
// 刷新状态
|
||||
RefreshDatabaseStatus();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"清空数据失败: {ex.Message}");
|
||||
System.Windows.MessageBox.Show(
|
||||
$"清空数据失败: {ex.Message}",
|
||||
"操作失败",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Error);
|
||||
UpdateMainStatus("清空数据失败");
|
||||
}
|
||||
}, "清空数据");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 执行体素网格 SDF 测试(使用 MeshSignedDistanceGrid)
|
||||
/// </summary>
|
||||
|
||||
@ -42,6 +42,80 @@ NavisworksTransport 系统管理页签视图 - 采用与其他页签一致的Nav
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" Padding="10">
|
||||
<StackPanel>
|
||||
<!-- 区域0: 数据管理 -->
|
||||
<Border BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="0" Margin="0,0,0,15" Padding="12"
|
||||
Background="#FFF8FBFF">
|
||||
<StackPanel>
|
||||
<Label Content="📦 数据管理" Style="{StaticResource SectionHeaderStyle}"/>
|
||||
|
||||
<!-- 数据库状态信息 -->
|
||||
<Grid Margin="0,5,0,10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="数据库版本:" Style="{StaticResource ParameterLabelStyle}" Width="100"/>
|
||||
<Label Grid.Row="0" Grid.Column="1" Content="{Binding DatabaseVersion}" Style="{StaticResource InfoTextStyle}"/>
|
||||
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="路径数量:" Style="{StaticResource ParameterLabelStyle}" Width="100"/>
|
||||
<Label Grid.Row="1" Grid.Column="1" Content="{Binding PathCount}" Style="{StaticResource InfoTextStyle}"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="碰撞记录:" Style="{StaticResource ParameterLabelStyle}" Width="100"/>
|
||||
<Label Grid.Row="2" Grid.Column="1" Content="{Binding CollisionRecordCount}" Style="{StaticResource InfoTextStyle}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 数据操作按钮 -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,5,0,5">
|
||||
<Button Content="💾 备份数据"
|
||||
Command="{Binding BackupDataCommand}"
|
||||
Style="{StaticResource ActionButtonStyle}"
|
||||
ToolTip="导出所有路径、碰撞记录和截图到备份文件"/>
|
||||
|
||||
<Button Content="📂 恢复数据"
|
||||
Command="{Binding RestoreDataCommand}"
|
||||
Style="{StaticResource SecondaryButtonStyle}"
|
||||
ToolTip="从备份文件恢复数据"/>
|
||||
|
||||
<Button Content="🔧 修复数据库"
|
||||
Command="{Binding RepairDatabaseCommand}"
|
||||
Style="{StaticResource SecondaryButtonStyle}"
|
||||
ToolTip="检查并修复数据库完整性"/>
|
||||
|
||||
<Button Content="🗑️ 清空数据"
|
||||
Command="{Binding ClearAllDataCommand}"
|
||||
Style="{StaticResource SecondaryButtonStyle}"
|
||||
ToolTip="清空所有路径和碰撞记录(谨慎使用)"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 自动备份设置 -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<CheckBox Content="启用自动备份"
|
||||
IsChecked="{Binding AutoBackupEnabled}"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,10,0"/>
|
||||
<TextBlock Text="保留最近" VerticalAlignment="Center" FontSize="10"/>
|
||||
<ComboBox ItemsSource="{Binding BackupKeepCountOptions}"
|
||||
SelectedItem="{Binding SelectedBackupKeepCount}"
|
||||
Width="50"
|
||||
Margin="5,0"/>
|
||||
<TextBlock Text="个备份" VerticalAlignment="Center" FontSize="10"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 上次备份信息 -->
|
||||
<TextBlock Text="{Binding LastBackupInfo}"
|
||||
FontSize="10"
|
||||
Foreground="{StaticResource NavisworksDarkBrush}"
|
||||
Margin="0,5,0,0"
|
||||
TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 区域1: 日志管理 -->
|
||||
<Border BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="0" Margin="0,0,0,15" Padding="12">
|
||||
<StackPanel>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user