将时标导出到TimeLiner任务

This commit is contained in:
tian 2026-02-16 19:56:07 +08:00
parent 57751e85fa
commit 08b4ecc5d5
3 changed files with 146 additions and 27 deletions

View File

@ -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();
}
/// <summary>
/// TimeLiner 是否可用
/// </summary>
public bool IsTimeLinerAvailable => _timeLinerManager?.IsTimeLinerAvailable ?? false;
/// <summary>
/// 将时标配置导入为TimeLiner仿真任务
/// </summary>
/// <param name="profile">时标配置</param>
/// <param name="route">路径</param>
/// <param name="movingObject">运输物体(可选)</param>
/// <param name="taskName">任务名称</param>
/// <returns>是否成功</returns>
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
}
}
/// <summary>
/// 创建简单的 TimeLiner 任务(无物体关联,仅信息展示)
/// </summary>
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;
}
}
/// <summary>
/// 创建仿真数据 - 用于动画系统
/// </summary>
@ -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];
}
/// <summary>
/// 计算两点距离
/// </summary>
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);
}
/// <summary>
/// 线性插值两个路径点
/// </summary>
private PathPoint InterpolatePoint(PathPoint p1, PathPoint p2, double t)
{
return new PathPoint
@ -161,9 +216,6 @@ namespace NavisworksTransport.Core.Services
};
}
/// <summary>
/// 计算每段的速度
/// </summary>
private void CalculateSegmentSpeeds(SimulationData data, TimeTagProfile profile)
{
if (data.Waypoints.Count < 2) return;
@ -218,9 +270,6 @@ namespace NavisworksTransport.Core.Services
return ExportSimulationDataToJson(data);
}
/// <summary>
/// 将仿真数据转换为JSON
/// </summary>
private string ExportSimulationDataToJson(SimulationData data)
{
var sb = new System.Text.StringBuilder();
@ -256,9 +305,6 @@ namespace NavisworksTransport.Core.Services
return sb.ToString();
}
/// <summary>
/// 转义JSON字符串
/// </summary>
private string EscapeJson(string str)
{
if (string.IsNullOrEmpty(str)) return "";

View File

@ -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<TimeTagProfile>();
@ -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
}
}
/// <summary>
/// 导入到 TimeLiner
/// </summary>
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;

View File

@ -56,6 +56,7 @@
<Button Content="删除配置" Margin="5,0,0,0" Padding="15,3" Command="{Binding DeleteProfileCommand}"/>
<Separator Margin="20,0" Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}"/>
<Button Content="导出" Padding="15,3" Command="{Binding ExportCommand}"/>
<Button Content="导入到TimeLiner" Padding="15,3" Margin="10,0,0,0" Command="{Binding ImportToTimeLinerCommand}"/>
</StackPanel>
<!-- 主内容区 -->