diff --git a/src/Core/Services/TimeTagTimeLinerIntegration.cs b/src/Core/Services/TimeTagTimeLinerIntegration.cs index dbea091..dd23fa0 100644 --- a/src/Core/Services/TimeTagTimeLinerIntegration.cs +++ b/src/Core/Services/TimeTagTimeLinerIntegration.cs @@ -2,7 +2,9 @@ using System; using System.Collections.Generic; using System.Linq; using Autodesk.Navisworks.Api; +using Autodesk.Navisworks.Api.Timeliner; using NavisworksTransport.Core.Models; +using NavisworksTransport.Core.Animation; using NavisworksTransport.Utils; namespace NavisworksTransport.Core.Services @@ -13,20 +15,28 @@ namespace NavisworksTransport.Core.Services public class TimeTagTimeLinerIntegration { private readonly TimeTagService _timeTagService; + private readonly TimeLinerIntegrationManager _timeLinerManager; public TimeTagTimeLinerIntegration(TimeTagService timeTagService) { _timeTagService = timeTagService ?? throw new ArgumentNullException(nameof(timeTagService)); + _timeLinerManager = new TimeLinerIntegrationManager(); } + /// + /// TimeLiner 是否可用 + /// + public bool IsTimeLinerAvailable => _timeLinerManager?.IsTimeLinerAvailable ?? false; + /// /// 将时标配置导入为TimeLiner仿真任务 /// /// 时标配置 /// 路径 + /// 运输物体(可选) /// 任务名称 /// 是否成功 - public bool ImportToTimeLiner(TimeTagProfile profile, PathRoute route, string taskName = null) + public bool ImportToTimeLiner(TimeTagProfile profile, PathRoute route, ModelItem movingObject = null, string taskName = null) { try { @@ -36,17 +46,44 @@ namespace NavisworksTransport.Core.Services return false; } + if (!IsTimeLinerAvailable) + { + LogManager.Warning("TimeLiner 不可用,无法导入任务"); + return false; + } + string name = taskName ?? $"{route.Name} - {profile.Name}"; LogManager.Info($"开始导入TimeLiner: {name}"); - // 创建仿真任务数据 + // 创建仿真数据 var simulationData = CreateSimulationData(profile, route); - LogManager.Info($"TimeLiner数据已准备: {name}, 总时间 {profile.TotalTime:F1}s"); - - // 注意:实际的TimeLiner导入需要通过COM API实现 - // 这里返回成功表示数据已准备好 - return true; + // 计算持续时间 + var duration = TimeSpan.FromSeconds(profile.TotalTime); + + // 创建 TimeLiner 任务 + string taskId = null; + if (movingObject != null) + { + // 如果有选中物体,创建完整任务 + taskId = _timeLinerManager.CreateTransportTask(name, simulationData.Waypoints.Select(w => w.Position).ToList(), duration, movingObject); + } + else + { + // 没有选中物体,创建简单任务(仅信息展示) + taskId = CreateSimpleTimelinerTask(name, profile, route, duration); + } + + if (taskId != null) + { + LogManager.Info($"✓ TimeLiner 任务创建成功: {name}, 任务ID: {taskId}"); + return true; + } + else + { + LogManager.Error("TimeLiner 任务创建失败"); + return false; + } } catch (Exception ex) { @@ -55,6 +92,35 @@ namespace NavisworksTransport.Core.Services } } + /// + /// 创建简单的 TimeLiner 任务(无物体关联,仅信息展示) + /// + private string CreateSimpleTimelinerTask(string taskName, TimeTagProfile profile, PathRoute route, TimeSpan duration) + { + try + { + var doc = Application.ActiveDocument; + var timeliner = doc?.GetTimeliner(); + if (timeliner == null) return null; + + var task = new TimelinerTask(); + task.DisplayName = $"运输任务:{taskName}"; + // Comments 属性是只读的,使用 DisplayName 包含更多信息 + task.DisplayName = $"运输任务:{taskName} ({profile.TotalTime:F1}s, {route.TotalLength:F1}m)"; + + // 添加到 TimeLiner + timeliner.TaskAddCopy(task); + + LogManager.Info($"✓ 简单 TimeLiner 任务创建成功: {taskName}"); + return Guid.NewGuid().ToString(); + } + catch (Exception ex) + { + LogManager.Error($"创建简单 TimeLiner 任务失败: {ex.Message}"); + return null; + } + } + /// /// 创建仿真数据 - 用于动画系统 /// @@ -76,7 +142,6 @@ namespace NavisworksTransport.Core.Services var events = profile.Events.OrderBy(e => e.Distance).ToList(); // 生成仿真路径点 - double currentTime = 0; for (int i = 0; i < events.Count; i++) { var evt = events[i]; @@ -95,8 +160,6 @@ namespace NavisworksTransport.Core.Services ActionTime = evt.ActionTime }); } - - currentTime = evt.CumulativeTime + evt.WaitTime + evt.ActionTime; } // 计算每段的实际速度 @@ -122,7 +185,6 @@ namespace NavisworksTransport.Core.Services if (accumulated + segLength >= distance) { - // 在此段内插值 double t = (distance - accumulated) / segLength; return InterpolatePoint(p1, p2, t); } @@ -130,13 +192,9 @@ namespace NavisworksTransport.Core.Services accumulated += segLength; } - // 返回最后一个点 return route.Points[route.Points.Count - 1]; } - /// - /// 计算两点距离 - /// private double CalculateDistance(PathPoint p1, PathPoint p2) { double dx = p2.Position.X - p1.Position.X; @@ -145,9 +203,6 @@ namespace NavisworksTransport.Core.Services return Math.Sqrt(dx * dx + dy * dy + dz * dz); } - /// - /// 线性插值两个路径点 - /// private PathPoint InterpolatePoint(PathPoint p1, PathPoint p2, double t) { return new PathPoint @@ -161,9 +216,6 @@ namespace NavisworksTransport.Core.Services }; } - /// - /// 计算每段的速度 - /// private void CalculateSegmentSpeeds(SimulationData data, TimeTagProfile profile) { if (data.Waypoints.Count < 2) return; @@ -218,9 +270,6 @@ namespace NavisworksTransport.Core.Services return ExportSimulationDataToJson(data); } - /// - /// 将仿真数据转换为JSON - /// private string ExportSimulationDataToJson(SimulationData data) { var sb = new System.Text.StringBuilder(); @@ -256,9 +305,6 @@ namespace NavisworksTransport.Core.Services return sb.ToString(); } - /// - /// 转义JSON字符串 - /// private string EscapeJson(string str) { if (string.IsNullOrEmpty(str)) return ""; diff --git a/src/UI/WPF/ViewModels/TimeTagViewModel.cs b/src/UI/WPF/ViewModels/TimeTagViewModel.cs index 6197b1c..34734de 100644 --- a/src/UI/WPF/ViewModels/TimeTagViewModel.cs +++ b/src/UI/WPF/ViewModels/TimeTagViewModel.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Windows.Input; using Microsoft.Win32; +using Autodesk.Navisworks.Api; using NavisworksTransport.Core.Models; using NavisworksTransport.Core.Services; @@ -17,6 +18,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { private readonly TimeTagService _timeTagService; private readonly TimeTagExporter _exporter; + private readonly TimeTagTimeLinerIntegration _timeLinerIntegration; private PathRoute _currentRoute; private TimeTagProfile _currentProfile; private TimeTagEvent _selectedEvent; @@ -206,6 +208,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels public ICommand DeleteProfileCommand { get; } public ICommand RefreshCommand { get; } public ICommand ExportCommand { get; } + public ICommand ImportToTimeLinerCommand { get; } public ICommand InsertEventCommand { get; } public ICommand DeleteEventCommand { get; } @@ -215,6 +218,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { _timeTagService = timeTagService ?? throw new ArgumentNullException(nameof(timeTagService)); _exporter = new TimeTagExporter(); + _timeLinerIntegration = new TimeTagTimeLinerIntegration(timeTagService); _currentRoute = route; Profiles = new ObservableCollection(); @@ -224,6 +228,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels DeleteProfileCommand = new RelayCommand(() => DeleteProfile(), () => CurrentProfile != null); RefreshCommand = new RelayCommand(() => RefreshProfiles()); ExportCommand = new RelayCommand(() => Export(), () => CurrentProfile != null); + ImportToTimeLinerCommand = new RelayCommand(() => ImportToTimeLiner(), () => CurrentProfile != null); InsertEventCommand = new RelayCommand(() => InsertEvent(), () => CurrentProfile != null && _currentRoute != null); DeleteEventCommand = new RelayCommand(() => DeleteEvent(), () => SelectedEvent != null); @@ -518,6 +523,73 @@ namespace NavisworksTransport.UI.WPF.ViewModels } } + /// + /// 导入到 TimeLiner + /// + private void ImportToTimeLiner() + { + if (CurrentProfile == null || _currentRoute == null) return; + + // 检查 TimeLiner 是否可用 + if (!_timeLinerIntegration.IsTimeLinerAvailable) + { + System.Windows.MessageBox.Show( + "TimeLiner 不可用。请确保 Navisworks TimeLiner 插件已加载。", + "TimeLiner 不可用", + System.Windows.MessageBoxButton.OK, + System.Windows.MessageBoxImage.Warning); + return; + } + + // 获取当前选中的模型元素(可选) + var doc = Autodesk.Navisworks.Api.Application.ActiveDocument; + var selectedItems = doc?.CurrentSelection?.SelectedItems; + ModelItem movingObject = null; + if (selectedItems != null && selectedItems.Count > 0) + { + movingObject = selectedItems.First(); + } + + var result = System.Windows.MessageBox.Show( + movingObject != null + ? $"将时标配置 \"{CurrentProfile.Name}\" 导入到 TimeLiner 吗?\n\n已选中物体: {movingObject.DisplayName}\n总时间: {CurrentProfile.TotalTime:F1}秒\n事件数: {CurrentProfile.Events?.Count ?? 0}" + : $"将时标配置 \"{CurrentProfile.Name}\" 导入到 TimeLiner 吗?\n\n未选中运输物体,将创建信息任务(无动画)。\n总时间: {CurrentProfile.TotalTime:F1}秒\n事件数: {CurrentProfile.Events?.Count ?? 0}", + "导入到 TimeLiner", + System.Windows.MessageBoxButton.YesNo, + System.Windows.MessageBoxImage.Question); + + if (result != System.Windows.MessageBoxResult.Yes) return; + + try + { + bool success = _timeLinerIntegration.ImportToTimeLiner(CurrentProfile, _currentRoute, movingObject); + + if (success) + { + System.Windows.MessageBox.Show( + "时标配置已成功导入到 TimeLiner!\n\n请在 Navisworks 的 TimeLiner 窗口中查看任务。", + "导入成功", + System.Windows.MessageBoxButton.OK, + System.Windows.MessageBoxImage.Information); + } + else + { + System.Windows.MessageBox.Show( + "导入失败,请查看日志了解详情。", + "导入失败", + System.Windows.MessageBoxButton.OK, + System.Windows.MessageBoxImage.Error); + } + } + catch (Exception ex) + { + LogManager.Error($"导入 TimeLiner 失败: {ex.Message}"); + System.Windows.MessageBox.Show($"导入失败: {ex.Message}", "错误", + System.Windows.MessageBoxButton.OK, + System.Windows.MessageBoxImage.Error); + } + } + #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; diff --git a/src/UI/WPF/Views/TimeTagDialog.xaml b/src/UI/WPF/Views/TimeTagDialog.xaml index 04ddd7c..30314a7 100644 --- a/src/UI/WPF/Views/TimeTagDialog.xaml +++ b/src/UI/WPF/Views/TimeTagDialog.xaml @@ -56,6 +56,7 @@