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 @@
+