diff --git a/src/IPA.Bcfier.App/Controllers/LastOpenedFilesController.cs b/src/IPA.Bcfier.App/Controllers/LastOpenedFilesController.cs index 2683a489..0748c584 100644 --- a/src/IPA.Bcfier.App/Controllers/LastOpenedFilesController.cs +++ b/src/IPA.Bcfier.App/Controllers/LastOpenedFilesController.cs @@ -1,5 +1,6 @@ using IPA.Bcfier.App.Data; using IPA.Bcfier.App.Models.Controllers.LastOpenedFiles; +using IPA.Bcfier.App.Services; using IPA.Bcfier.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -14,12 +15,15 @@ public class LastOpenedFilesController : ControllerBase { private readonly BcfierDbContext _context; private readonly SettingsService _settingsService; + private readonly LastOpenedFilesService _lastOpenedFilesService; public LastOpenedFilesController(BcfierDbContext context, - SettingsService settingsService) + SettingsService settingsService, + LastOpenedFilesService lastOpenedFilesService) { _context = context; _settingsService = settingsService; + _lastOpenedFilesService = lastOpenedFilesService; } [HttpGet("")] @@ -51,26 +55,7 @@ public async Task GetLastOpenedFilesAsync([FromQuery]Guid? projec [ProducesResponseType((int)HttpStatusCode.NoContent)] public async Task SetFileAsLastOpened([FromQuery]Guid? projectId, [FromQuery, Required]string filePath) { - var userName = (await _settingsService.LoadSettingsAsync()).Username; - var existingEntry = await _context.LastOpenedUserFiles - .FirstOrDefaultAsync(louf => louf.ProjectId == projectId - && louf.UserName == userName - && louf.FilePath == filePath); - if (existingEntry != null) - { - existingEntry.OpenedAtAtUtc = DateTimeOffset.UtcNow; - } - else - { - _context.LastOpenedUserFiles.Add(new Data.Models.LastOpenedUserFile - { - ProjectId = projectId, - UserName = userName, - FilePath = filePath - }); - } - - await _context.SaveChangesAsync(); + await _lastOpenedFilesService.SetFileAsLastOpenedAsync(projectId, filePath); return NoContent(); } } diff --git a/src/IPA.Bcfier.App/Controllers/ViewpointsController.cs b/src/IPA.Bcfier.App/Controllers/ViewpointsController.cs index 5c66a578..1fb89205 100644 --- a/src/IPA.Bcfier.App/Controllers/ViewpointsController.cs +++ b/src/IPA.Bcfier.App/Controllers/ViewpointsController.cs @@ -114,6 +114,51 @@ await ipcHandler.SendMessageAsync(JsonConvert.SerializeObject(new IpcMessage return BadRequest(); } + [HttpPost("navisworks-clashes")] + [ProducesResponseType(typeof(ApiError), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + public async Task CreateNavisworksClashDetectionResultIssuesAsync() + { + if (!_navisworksParameters.IsConnectedToNavisworks) + { + return BadRequest(new ApiError("The app is currently not connected to Navisworks")); + } + + using var ipcHandler = GetIpcHandler(); + await ipcHandler.InitializeAsync(); + + var correlationId = Guid.NewGuid(); + await ipcHandler.SendMessageAsync(JsonConvert.SerializeObject(new IpcMessage + { + CorrelationId = correlationId, + Command = IpcMessageCommand.CreateNavisworksClashDetectionIssues, + Data = null + })); + + var hasReceived = false; + var start = DateTime.Now; + // We're waiting up to 20 minutes for the results here - could take a while for large clash results + while (DateTime.UtcNow - start < TimeSpan.FromSeconds(1200) && !hasReceived) + { + if (IpcHandler.ReceivedMessages.TryDequeue(out var message)) + { + var ipcMessage = JsonConvert.DeserializeObject(message)!; + if (ipcMessage.CorrelationId == correlationId) + { + hasReceived = true; + var bcfViewpoint = JsonConvert.DeserializeObject>(ipcMessage.Data!)!; + return Ok(bcfViewpoint); + } + else + { + IpcHandler.ReceivedMessages.Enqueue(message); + } + } + } + + return BadRequest(); + } + private IpcHandler GetIpcHandler() { if (_revitParameters.IsConnectedToRevit) diff --git a/src/IPA.Bcfier.Navisworks/IpcNavisworksCommandListener.cs b/src/IPA.Bcfier.Navisworks/IpcNavisworksCommandListener.cs index 40b1d7ed..dc865086 100644 --- a/src/IPA.Bcfier.Navisworks/IpcNavisworksCommandListener.cs +++ b/src/IPA.Bcfier.Navisworks/IpcNavisworksCommandListener.cs @@ -51,6 +51,18 @@ await _ipcHandler.SendMessageAsync(JsonConvert.SerializeObject(new IpcMessage }); break; + case IpcMessageCommand.CreateNavisworksClashDetectionIssues: + _navisworksTaskHandler.CreateNavisworksClashIssuesCallbacks.Enqueue(async (data) => + { + await _ipcHandler.SendMessageAsync(JsonConvert.SerializeObject(new IpcMessage + { + CorrelationId = ipcMessage.CorrelationId, + Command = IpcMessageCommand.NavisworksClashDetectionIssuesCreated, + Data = data + })); + }); + break; + case IpcMessageCommand.ShowViewpoint: _navisworksTaskHandler.ShowViewpointQueueItems.Enqueue(new ShowViewpointQueueItem { diff --git a/src/IPA.Bcfier.Navisworks/NavisworksTaskQueueHandler.cs b/src/IPA.Bcfier.Navisworks/NavisworksTaskQueueHandler.cs index e06a8953..ba8916cc 100644 --- a/src/IPA.Bcfier.Navisworks/NavisworksTaskQueueHandler.cs +++ b/src/IPA.Bcfier.Navisworks/NavisworksTaskQueueHandler.cs @@ -10,6 +10,7 @@ namespace IPA.Bcfier.Navisworks public class NavisworksTaskQueueHandler { public Queue> CreateNavisworksViewpointCallbacks { get; } = new Queue>(); + public Queue> CreateNavisworksClashIssuesCallbacks { get; } = new Queue>(); public Queue ShowViewpointQueueItems { get; } = new Queue(); private bool shouldUnregister = false; @@ -27,6 +28,13 @@ public void OnIdling(object sender, EventArgs args) HandleCreateNavisworksViewpointCallback(callback, uiDocument); } + if (CreateNavisworksClashIssuesCallbacks.Count > 0) + { + var uiDocument = Application.ActiveDocument; + var callback = CreateNavisworksClashIssuesCallbacks.Dequeue(); + HandleCreateNavisworksClashIssuesCallback(callback, uiDocument); + } + if (ShowViewpointQueueItems.Count > 0) { var uiDocument = Application.ActiveDocument; @@ -66,6 +74,32 @@ private void HandleCreateNavisworksViewpointCallback(Func callback }); } + private void HandleCreateNavisworksClashIssuesCallback(Func callback, Document uiDocument) + { + var viewpointService = new NavisworksViewpointCreationService(uiDocument); + var clashIssues = viewpointService.CreateClashIssues(); + var contractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy() + }; + var serializerSettings = new JsonSerializerSettings + { + ContractResolver = contractResolver, + Formatting = Formatting.Indented + }; + Task.Run(async () => + { + if (clashIssues == null) + { + await callback("[]"); + } + else + { + await callback(JsonConvert.SerializeObject(clashIssues, serializerSettings)); + } + }); + } + private void HandleShowNavisworksViewpointCallback(Func? callback, BcfViewpoint? viewpoint, Document uiDocument) { if (callback == null || viewpoint == null) diff --git a/src/IPA.Bcfier.Navisworks/Services/NavisworksViewpointCreationService.cs b/src/IPA.Bcfier.Navisworks/Services/NavisworksViewpointCreationService.cs index cfcfc72e..49c696b8 100644 --- a/src/IPA.Bcfier.Navisworks/Services/NavisworksViewpointCreationService.cs +++ b/src/IPA.Bcfier.Navisworks/Services/NavisworksViewpointCreationService.cs @@ -1,6 +1,8 @@ using Autodesk.Navisworks.Api; +using Autodesk.Navisworks.Api.Clash; using IPA.Bcfier.Models.Bcf; using IPA.Bcfier.Navisworks.Utilities; +using System.Drawing.Imaging; namespace IPA.Bcfier.Navisworks.Services { @@ -23,108 +25,296 @@ public NavisworksViewpointCreationService(Document doc) { var viewpoint = _doc.CurrentViewpoint.Value; NavisUtils.GetGunits(_doc); - var v = new BcfViewpoint(); + var v = GetViewpointFromNavisworksViewpoint(viewpoint); + return v; + } + catch (System.Exception ex1) + { + // TODO + //TaskDialog.Show("Error generating viewpoint", "exception: " + ex1); + } + return null; + } - Vector3D vi = GetViewDirection(viewpoint); - Vector3D up = GetViewUp(viewpoint); - Point3D c = new Point3D(viewpoint.Position.X, viewpoint.Position.Y, viewpoint.Position.Z); - double zoomValue = 1; + private BcfViewpoint GetViewpointFromNavisworksViewpoint(Viewpoint viewpoint) + { + var v = new BcfViewpoint(); + Vector3D vi = GetViewDirection(viewpoint); + Vector3D up = GetViewUp(viewpoint); + Point3D c = new Point3D(viewpoint.Position.X, viewpoint.Position.Y, viewpoint.Position.Z); + double zoomValue = 1; - //prepare view - viewpoint = viewpoint.CreateCopy(); - if (!viewpoint.HasFocalDistance) - viewpoint.FocalDistance = 1; + //prepare view + viewpoint = viewpoint.CreateCopy(); + if (!viewpoint.HasFocalDistance) + { + viewpoint.FocalDistance = 1; + } - // it is a orthogonal view - if (viewpoint.Projection == ViewpointProjection.Orthographic) - { - //TODO: needs checking!!! - double dist = viewpoint.VerticalExtentAtFocalDistance / 2; - zoomValue = 3.125 * dist / (up.Length * 1.25); + // it is a orthogonal view + if (viewpoint.Projection == ViewpointProjection.Orthographic) + { + //TODO: needs checking!!! + double dist = viewpoint.VerticalExtentAtFocalDistance / 2; + zoomValue = 3.125 * dist / (up.Length * 1.25); - v.OrthogonalCamera = new BcfViewpointOrthogonalCamera - { - ViewPoint = + v.OrthogonalCamera = new BcfViewpointOrthogonalCamera + { + ViewPoint = { X = c.X.FromInternal(), Y = c.Y.FromInternal(), Z = c.Z.FromInternal() }, - UpVector = + UpVector = { X = up.X.FromInternal(), Y = up.Y.FromInternal(), Z = up.Z.FromInternal() }, - Direction = + Direction = { X = vi.X.FromInternal(), Y = vi.Y.FromInternal(), Z = vi.Z.FromInternal() }, - ViewToWorldScale = zoomValue.FromInternal() - }; - } - else - { - zoomValue = viewpoint.FocalDistance; + ViewToWorldScale = zoomValue.FromInternal() + }; + } + else + { + zoomValue = viewpoint.FocalDistance; - v.PerspectiveCamera = new BcfViewpointPerspectiveCamera - { - ViewPoint = + v.PerspectiveCamera = new BcfViewpointPerspectiveCamera + { + ViewPoint = { X = c.X.FromInternal(), Y = c.Y.FromInternal(), Z = c.Z.FromInternal() }, - UpVector = + UpVector = { X = up.X.FromInternal(), Y = up.Y.FromInternal(), Z = up.Z.FromInternal() }, - Direction = + Direction = { X = vi.X.FromInternal(), Y = vi.Y.FromInternal(), Z = vi.Z.FromInternal() }, - FieldOfView = zoomValue - }; - } + FieldOfView = zoomValue + }; + } - var selectedIfcGuids = _doc.CurrentSelection.SelectedItems.Select(selectedItem => selectedItem.InstanceGuid.ToIfcGuid()).ToList(); - if (selectedIfcGuids.Any()) + var selectedIfcGuids = _doc.CurrentSelection.SelectedItems.Select(selectedItem => selectedItem.InstanceGuid.ToIfcGuid()).ToList(); + if (selectedIfcGuids.Any()) + { + v.ViewpointComponents = new BcfViewpointComponents { - v.ViewpointComponents = new BcfViewpointComponents + SelectedComponents = selectedIfcGuids.Select(ifcGuid => new BcfViewpointComponent { - SelectedComponents = selectedIfcGuids.Select(ifcGuid => new BcfViewpointComponent - { - IfcGuid = ifcGuid, - OriginatingSystem = "IPA.BCFier.Navisworks", - }).ToList() - }; - } + IfcGuid = ifcGuid, + OriginatingSystem = "IPA.BCFier.Navisworks", + }).ToList() + }; + } #if NAVISWORKS_2023 || NAVISWORKS_2022 || NAVISWORKS_2021 var navisworksSnapshot = _doc.GenerateImage(ImageGenerationStyle.Scene, 1920, 1080); #else - var navisworksSnapshot = _doc.GenerateImage(ImageGenerationStyle.Scene, 1920, 1080, true); + var navisworksSnapshot = _doc.GenerateImage(ImageGenerationStyle.Scene, 1920, 1080, true); #endif - using var imageStream = new MemoryStream(); - navisworksSnapshot.Save(imageStream, System.Drawing.Imaging.ImageFormat.Png); - v.SnapshotBase64 = Convert.ToBase64String(imageStream.ToArray()); + using var imageStream = new MemoryStream(); + navisworksSnapshot.Save(imageStream, System.Drawing.Imaging.ImageFormat.Png); + v.SnapshotBase64 = Convert.ToBase64String(imageStream.ToArray()); - return v; - } - catch (System.Exception ex1) + return v; + } + + // Mostly taken from here: + // https://forums.autodesk.com/t5/navisworks-api/here-s-how-to-export-clash-result-images-using-navisworks-api/td-p/9089222 + public List CreateClashIssues() + { + var bcfTopics = new List(); + NavisUtils.GetGunits(_doc); + + var doc = _doc; + var tests = doc.GetClash().TestsData.Tests; + + // Assuming you've already run all Clash Tests and have the results + foreach (ClashTest test in tests) { - // TODO - //TaskDialog.Show("Error generating viewpoint", "exception: " + ex1); + if (test.Children.Count <= 0) continue; + + // test.IsGroup // TODO? + if (test.IsGroup) + { + var wow = test.Children.OfType().ToList(); + // TODO + } + + foreach (var testItem in test.Children) + { + if (testItem is ClashResult result) + { + var viewpoint = doc.CurrentViewpoint.Value; + // Create a collection of the 2 clashing items from the ClashResult + var items = new ModelItemCollection(); + items.Add(result.Item1); + items.Add(result.Item2); + // Select the 2 clashing items + doc.CurrentSelection.Clear(); + doc.CurrentSelection.CopyFrom(items); + // Focus on the clashing items + doc.ActiveView.FocusOnCurrentSelection(); + // Make all items visible + doc.Models.ResetAllHidden(); + + // Hide everything except for the 2 clashing items + var to_hide = new ModelItemCollection(); + var to_show = new ModelItemCollection(); + foreach (var item in doc.CurrentSelection.SelectedItems) + { + // Collect all items upstream to the root + if (item.AncestorsAndSelf != null) + to_show.AddRange(item.AncestorsAndSelf); + // Collect all subtrees of the item + if (item.Descendants != null) + to_show.AddRange(item.Descendants); + } + + foreach (var item in to_show) + { + // If an item has no parent (root item) save the subtrees + if (!NativeHandle.ReferenceEquals(item.Parent, null)) + to_hide.AddRange(item.Parent.Children); + } + // Remove the to be shown items from list of the to be hidden items + foreach (var item in to_show) + to_hide.Remove(item); + // Hide all explicitly to be hidden items + doc.Models.SetHidden(to_hide, true); + // Hide all other items except those already hidden and those to be shown + doc.Models.SetHidden(doc.Models + .SelectMany( + (Func)(c => c.RootItem.Children) + ) + .Except(to_hide) + .Except(to_show) + , true); + // Remove selction color from the clashing items + doc.CurrentSelection.Clear(); + + // Adjust the camera and lighting + var copy = viewpoint.CreateCopy(); + copy.Lighting = ViewpointLighting.None; + doc.Models.ResetAllPermanentMaterials(); + doc.CurrentViewpoint.CopyFrom(copy); + + // Paint the clashing items in Red and Green respectively + doc.Models.OverridePermanentColor(new ModelItem[1] { items.ElementAtOrDefault(0) }, Color.Red); + doc.Models.OverridePermanentColor(new ModelItem[1] { items.ElementAtOrDefault(1) }, Color.Green); + // Adjust the camera angle + doc.ActiveView.LookFromFrontRightTop(); + // Prevent redraw for every test and item + doc.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All); + + + var bcfViewpoint = GetViewpointFromNavisworksViewpoint(viewpoint); + bcfTopics.Add(new BcfTopic + { + Viewpoints = new List + { + bcfViewpoint + }, + Title = $"{test.DisplayName} - {result.DisplayName}" + }); + } + else if (testItem is ClashResultGroup resultGroup) + { + var viewpoint = doc.CurrentViewpoint.Value; + // Create a collection of the 2 clashing items from the ClashResult + var items = new ModelItemCollection(); + resultGroup.CompositeItemSelection1.CopyTo(items); + resultGroup.CompositeItemSelection2.CopyTo(items); + // Select the clashing items + doc.CurrentSelection.Clear(); + doc.CurrentSelection.CopyFrom(items); + // Focus on the clashing items + doc.ActiveView.FocusOnCurrentSelection(); + // Make all items visible + doc.Models.ResetAllHidden(); + + // Hide everything except for the 2 clashing items + var to_hide = new ModelItemCollection(); + var to_show = new ModelItemCollection(); + foreach (var item in doc.CurrentSelection.SelectedItems) + { + // Collect all items upstream to the root + if (item.AncestorsAndSelf != null) + to_show.AddRange(item.AncestorsAndSelf); + // Collect all subtrees of the item + if (item.Descendants != null) + to_show.AddRange(item.Descendants); + } + + foreach (var item in to_show) + { + // If an item has no parent (root item) save the subtrees + if (!NativeHandle.ReferenceEquals(item.Parent, null)) + to_hide.AddRange(item.Parent.Children); + } + // Remove the to be shown items from list of the to be hidden items + foreach (var item in to_show) + to_hide.Remove(item); + // Hide all explicitly to be hidden items + doc.Models.SetHidden(to_hide, true); + // Hide all other items except those already hidden and those to be shown + doc.Models.SetHidden(doc.Models + .SelectMany( + (Func)(c => c.RootItem.Children) + ) + .Except(to_hide) + .Except(to_show) + , true); + // Remove selction color from the clashing items + doc.CurrentSelection.Clear(); + + // Adjust the camera and lighting + var copy = viewpoint.CreateCopy(); + copy.Lighting = ViewpointLighting.None; + doc.Models.ResetAllPermanentMaterials(); + doc.CurrentViewpoint.CopyFrom(copy); + + // Paint the clashing items in Red and Green respectively + doc.Models.OverridePermanentColor(new ModelItem[1] { items.ElementAtOrDefault(0) }, Color.Red); + doc.Models.OverridePermanentColor(new ModelItem[1] { items.ElementAtOrDefault(1) }, Color.Green); + // Adjust the camera angle + doc.ActiveView.LookFromFrontRightTop(); + // Prevent redraw for every test and item + doc.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All); + + + var bcfViewpoint = GetViewpointFromNavisworksViewpoint(viewpoint); + bcfTopics.Add(new BcfTopic + { + CreationDate = DateTime.Now, + Viewpoints = new List + { + bcfViewpoint + }, + Title = $"{test.DisplayName} - {resultGroup.DisplayName} (Group)" + }); + } + } } - return null; + + return bcfTopics; } private static Vector3D GetViewDirection(Viewpoint v) @@ -168,5 +358,92 @@ private static Rotation3D MultiplyRotation3D(Rotation3D r2, Rotation3D r1) rot.Normalize(); return rot; } + + private void SaveAllClashPictures(string image_dir) + { + var doc = _doc; + var tests = doc.GetClash().TestsData.Tests; + + // Assuming you've already run all Clash Tests and have the results + foreach (ClashTest test in tests) + { + if (test.Children.Count <= 0) continue; + + // test.IsGroup // TODO? + + foreach (ClashResult result in test.Children) + { + var viewpoint = doc.CurrentViewpoint.Value; + // Create a collection of the 2 clashing items from the ClashResult + var items = new ModelItemCollection(); + items.Add(result.Item1); + items.Add(result.Item2); + // Select the 2 clashing items + doc.CurrentSelection.Clear(); + doc.CurrentSelection.CopyFrom(items); + // Focus on the clashing items + doc.ActiveView.FocusOnCurrentSelection(); + // Make all items visible + doc.Models.ResetAllHidden(); + + // Hide everything except for the 2 clashing items + var to_hide = new ModelItemCollection(); + var to_show = new ModelItemCollection(); + foreach (var item in doc.CurrentSelection.SelectedItems) + { + // Collect all items upstream to the root + if (item.AncestorsAndSelf != null) + to_show.AddRange(item.AncestorsAndSelf); + // Collect all subtrees of the item + if (item.Descendants != null) + to_show.AddRange(item.Descendants); + } + + foreach (var item in to_show) + { + // If an item has no parent (root item) save the subtrees + if (!NativeHandle.ReferenceEquals(item.Parent, null)) + to_hide.AddRange(item.Parent.Children); + } + // Remove the to be shown items from list of the to be hidden items + foreach (var item in to_show) + to_hide.Remove(item); + // Hide all explicitly to be hidden items + doc.Models.SetHidden(to_hide, true); + // Hide all other items except those already hidden and those to be shown + doc.Models.SetHidden(doc.Models + .SelectMany( + (Func)(c => c.RootItem.Children) + ) + .Except(to_hide) + .Except(to_show) + , true); + // Remove selction color from the clashing items + doc.CurrentSelection.Clear(); + + // Adjust the camera and lighting + var copy = viewpoint.CreateCopy(); + copy.Lighting = ViewpointLighting.None; + doc.Models.ResetAllPermanentMaterials(); + doc.CurrentViewpoint.CopyFrom(copy); + + // Paint the clashing items in Red and Green respectively + doc.Models.OverridePermanentColor(new ModelItem[1] { items.ElementAtOrDefault(0) }, Color.Red); + doc.Models.OverridePermanentColor(new ModelItem[1] { items.ElementAtOrDefault(1) }, Color.Green); + // Adjust the camera angle + doc.ActiveView.LookFromFrontRightTop(); + // Prevent redraw for every test and item + doc.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All); + + // Save the Clash image + var clashImage = doc.GenerateImage(ImageGenerationStyle.Scene, 500, 500, true); + //var clashImage = doc.ActiveView.GenerateThumbnail(500, 500); + + // Save the view image as PNG file + var clashResultImageName = System.IO.Path.Combine(image_dir, test.DisplayName, result.DisplayName + ".png"); + clashImage.Save(clashResultImageName, ImageFormat.Png); + } + } + } } } diff --git a/src/IPA.Bcfier/Ipc/IpcMessageCommand.cs b/src/IPA.Bcfier/Ipc/IpcMessageCommand.cs index 31d10f2f..2bc5a396 100644 --- a/src/IPA.Bcfier/Ipc/IpcMessageCommand.cs +++ b/src/IPA.Bcfier/Ipc/IpcMessageCommand.cs @@ -11,5 +11,9 @@ public enum IpcMessageCommand ShowViewpoint = 3, ViewpointShown = 4, + + CreateNavisworksClashDetectionIssues = 5, + + NavisworksClashDetectionIssuesCreated = 6 } } diff --git a/src/IPA.Bcfier/Models/Config/FrontendConfig.cs b/src/IPA.Bcfier/Models/Config/FrontendConfig.cs index 81aa9913..24f7de41 100644 --- a/src/IPA.Bcfier/Models/Config/FrontendConfig.cs +++ b/src/IPA.Bcfier/Models/Config/FrontendConfig.cs @@ -14,5 +14,8 @@ public class FrontendConfig public bool IsConnectedToNavisworks { get; set; } = false; public string? RevitProjectPath { get; set; } + + [Required] + public string Environment { get; set; } } } diff --git a/src/ipa-bcfier-ui/src/app/components/bcf-file/bcf-file.component.html b/src/ipa-bcfier-ui/src/app/components/bcf-file/bcf-file.component.html index 0cffdf7e..6de4d2cf 100644 --- a/src/ipa-bcfier-ui/src/app/components/bcf-file/bcf-file.component.html +++ b/src/ipa-bcfier-ui/src/app/components/bcf-file/bcf-file.component.html @@ -26,6 +26,17 @@ Delete Issue + @if(isInNavisworks) { +
+ +
+ }
diff --git a/src/ipa-bcfier-ui/src/app/components/bcf-file/bcf-file.component.scss b/src/ipa-bcfier-ui/src/app/components/bcf-file/bcf-file.component.scss index f2b41802..fbab2882 100644 --- a/src/ipa-bcfier-ui/src/app/components/bcf-file/bcf-file.component.scss +++ b/src/ipa-bcfier-ui/src/app/components/bcf-file/bcf-file.component.scss @@ -19,9 +19,10 @@ flex-direction: row; justify-content: space-evenly; margin-bottom: 4px; + gap: 8px; button { - width: 45%; + flex: 1; } } diff --git a/src/ipa-bcfier-ui/src/app/components/bcf-file/bcf-file.component.ts b/src/ipa-bcfier-ui/src/app/components/bcf-file/bcf-file.component.ts index 4c8dc1d3..5ce6befc 100644 --- a/src/ipa-bcfier-ui/src/app/components/bcf-file/bcf-file.component.ts +++ b/src/ipa-bcfier-ui/src/app/components/bcf-file/bcf-file.component.ts @@ -1,4 +1,8 @@ -import { BcfFile, BcfTopic } from '../../generated-client/generated-client'; +import { + BcfFile, + BcfTopic, + ViewpointsClient, +} from '../../generated-client/generated-client'; import { ChangeDetectorRef, Component, Input, inject } from '@angular/core'; import { FormGroup, FormsModule } from '@angular/forms'; import { @@ -6,26 +10,29 @@ import { IssueFiltersComponent, } from '../issue-filters/issue-filters.component'; +import { AppConfigService } from '../../services/AppConfigService'; import { BcfFileAutomaticallySaveService } from '../../services/bcf-file-automaticaly-save.service'; import { CommonModule } from '@angular/common'; import { IssueFilterService } from '../../services/issue-filter.service'; import { IssueStatusesService } from '../../services/issue-statuses.service'; import { IssueTypesService } from '../../services/issue-types.service'; +import { LoadingService } from '../../services/loading.service'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatSidenavModule } from '@angular/material/sidenav'; +import { NotificationsService } from '../../services/notifications.service'; import { SafeUrlPipe } from '../../pipes/safe-url.pipe'; +import { SettingsMessengerService } from '../../services/settings-messenger.service'; +import { TeamsMessengerService } from '../../services/teams-messenger.service'; import { TopicDetailComponent } from '../topic-detail/topic-detail.component'; import { TopicFilterPipe } from '../../pipes/topic-filter.pipe'; +import { TopicMessengerService } from '../../services/topic-messenger.service'; import { TopicPreviewImageDirective } from '../../directives/topic-preview-image.directive'; import { UsersService } from '../../services/users.service'; import { getNewRandomGuid } from '../../functions/uuid'; -import { TeamsMessengerService } from '../../services/teams-messenger.service'; -import { TopicMessengerService } from '../../services/topic-messenger.service'; -import { SettingsMessengerService } from '../../services/settings-messenger.service'; import { take } from 'rxjs'; @Component({ @@ -62,6 +69,11 @@ export class BcfFileComponent { cdr = inject(ChangeDetectorRef); selectedTopic: BcfTopic | null = null; filtredTopics: BcfTopic[] = []; + isInNavisworks = + inject(AppConfigService).getFrontendConfig().isConnectedToNavisworks; + viewpointsClient = inject(ViewpointsClient); + loadingService = inject(LoadingService); + notificationsService = inject(NotificationsService); ngOnInit() { this.selectedTopic = this.bcfFile.topics[0] || null; @@ -162,4 +174,33 @@ export class BcfFileComponent { ] : this.bcfFile.topics; } + + addNavisworksClashIssues(): void { + this.notificationsService.info( + 'If there are many clashes, generation of the data could take a few minutes.' + ); + this.loadingService.showLoadingScreen(); + this.viewpointsClient + .createNavisworksClashDetectionResultIssues() + .subscribe({ + next: (createdTopics) => { + this.loadingService.hideLoadingScreen(); + this.settingsMessengerService.settings + .pipe(take(1)) + .subscribe((s) => { + createdTopics.forEach((topic) => { + topic.creationAuthor = s.username; + }); + + this.bcfFile.topics.push(...createdTopics); + this.filtredTopics = [...this.bcfFile.topics]; + this.bcfFileAutomaticallySaveService.saveCurrentActiveBcfFileAutomatically(); + }); + }, + error: (error) => { + this.loadingService.hideLoadingScreen(); + console.error(error); + }, + }); + } } diff --git a/src/ipa-bcfier-ui/src/app/components/top-menu/top-menu.component.ts b/src/ipa-bcfier-ui/src/app/components/top-menu/top-menu.component.ts index 973d3329..49634b3d 100644 --- a/src/ipa-bcfier-ui/src/app/components/top-menu/top-menu.component.ts +++ b/src/ipa-bcfier-ui/src/app/components/top-menu/top-menu.component.ts @@ -96,7 +96,7 @@ export class TopMenuComponent implements OnDestroy { this.bcfFilesMessengerService.saveCurrentActiveBcfFileAs(); } - checkOpenedFileAndSendInfo(): void { + private checkOpenedFileAndSendInfo(): void { this.bcfFilesMessengerService.bcfFileSelected .pipe(distinctUntilChanged(), takeUntil(this.destroyed$)) .subscribe((bcfFile) => { diff --git a/src/ipa-bcfier-ui/src/app/generated-client/generated-client.ts b/src/ipa-bcfier-ui/src/app/generated-client/generated-client.ts index 45727b70..68ccb4ec 100644 --- a/src/ipa-bcfier-ui/src/app/generated-client/generated-client.ts +++ b/src/ipa-bcfier-ui/src/app/generated-client/generated-client.ts @@ -1307,6 +1307,7 @@ export class UsersClient implements IUsersClient { export interface IViewpointsClient { showViewpoint(viewpoint: BcfViewpoint): Observable; createViewpoint(): Observable; + createNavisworksClashDetectionResultIssues(): Observable; } @Injectable({ @@ -1428,6 +1429,59 @@ export class ViewpointsClient implements IViewpointsClient { } return _observableOf(null as any); } + + createNavisworksClashDetectionResultIssues(): Observable { + let url_ = this.baseUrl + "/api/viewpoints/navisworks-clashes"; + url_ = url_.replace(/[?&]$/, ""); + + let options_ : any = { + observe: "response", + responseType: "blob", + headers: new HttpHeaders({ + "Accept": "application/json" + }) + }; + + return this.http.request("post", url_, options_).pipe(_observableMergeMap((response_ : any) => { + return this.processCreateNavisworksClashDetectionResultIssues(response_); + })).pipe(_observableCatch((response_: any) => { + if (response_ instanceof HttpResponseBase) { + try { + return this.processCreateNavisworksClashDetectionResultIssues(response_ as any); + } catch (e) { + return _observableThrow(e) as any as Observable; + } + } else + return _observableThrow(response_) as any as Observable; + })); + } + + protected processCreateNavisworksClashDetectionResultIssues(response: HttpResponseBase): Observable { + const status = response.status; + const responseBlob = + response instanceof HttpResponse ? response.body : + (response as any).error instanceof Blob ? (response as any).error : undefined; + + let _headers: any = {}; if (response.headers) { for (let key of response.headers.keys()) { _headers[key] = response.headers.get(key); }} + if (status === 400) { + return blobToText(responseBlob).pipe(_observableMergeMap((_responseText: string) => { + let result400: any = null; + result400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApiError; + return throwException("A server side error occurred.", status, _responseText, _headers, result400); + })); + } else if (status === 200) { + return blobToText(responseBlob).pipe(_observableMergeMap((_responseText: string) => { + let result200: any = null; + result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BcfTopic[]; + return _observableOf(result200); + })); + } else if (status !== 200 && status !== 204) { + return blobToText(responseBlob).pipe(_observableMergeMap((_responseText: string) => { + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + })); + } + return _observableOf(null as any); + } } /** Data transfer class to convey api errors */