feat: port p-rec-annot ortho batch update lifecycle
This commit is contained in:
@@ -52,6 +52,7 @@ public sealed class EntryPoint : IExtensionApplication
|
||||
[CommandMethod("PXP-COV")] public void CheckOrthoViewportCommand() => OrthoMaintenanceCommands.CheckViewport();
|
||||
[CommandMethod("PXP-UOB")] public void UpdateOrthoViewBlocksCommand() => PRecAnnot.Plant3D2026.UpdateOrthoViewBlocksCommand.Run();
|
||||
[CommandMethod("PXP-AC", CommandFlags.UsePickSet)] public void SwapAttributesCommand() => PRecAnnot.Plant3D2026.SwapAttributesCommand.Run();
|
||||
[CommandMethod("PXP-IOD", CommandFlags.Session)] public void UpdateOrthoDrawingsCommand() => PRecAnnot.Plant3D2026.UpdateOrthoDrawingsCommand.Run();
|
||||
|
||||
[CommandMethod("PHP_REC_AUTOMATION_START", CommandFlags.Session)]
|
||||
public void AutomationStartCommand() => FirstLimitedApplyAutomationBootstrap.Arm();
|
||||
|
||||
@@ -20,5 +20,6 @@
|
||||
<Reference Include="PnP3dOrthoDrawingsUI"><HintPath>$(Plant3D2026ManagedDir)\PnP3dOrthoDrawingsUI.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
<Reference Include="PnPDwg2dDef"><HintPath>$(Plant3D2026ManagedDir)\PnPDwg2dDef.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
<Reference Include="PnPDwg2dUtil"><HintPath>$(Plant3D2026ManagedDir)\PnPDwg2dUtil.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
<Reference Include="PnPDwg2dGen"><HintPath>$(Plant3D2026ManagedDir)\PnPDwg2dGen.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using Autodesk.AutoCAD.ApplicationServices;
|
||||
using Autodesk.AutoCAD.DatabaseServices;
|
||||
using Autodesk.AutoCAD.EditorInput;
|
||||
using Autodesk.AutoCAD.Geometry;
|
||||
using Autodesk.ProcessPower.Drawings2d;
|
||||
using Autodesk.ProcessPower.PlantInstance;
|
||||
using Autodesk.ProcessPower.P3dProjectParts;
|
||||
using Autodesk.ProcessPower.PnP3dOrthoDrawingsUI;
|
||||
using Autodesk.ProcessPower.ProjectManager;
|
||||
using PRecAnnot.Abstractions;
|
||||
using PRecAnnot.Core;
|
||||
using AcadApplication = Autodesk.AutoCAD.ApplicationServices.Application;
|
||||
|
||||
namespace PRecAnnot.Plant3D2026;
|
||||
|
||||
internal static class UpdateOrthoDrawingsCommand
|
||||
{
|
||||
public static void Run()
|
||||
{
|
||||
var owner = AcadApplication.DocumentManager.MdiActiveDocument;
|
||||
if (owner is null) return;
|
||||
try
|
||||
{
|
||||
var project = PlantApplication.CurrentProject ?? throw new InvalidOperationException("ORTHO_BATCH_PROJECT_REQUIRED");
|
||||
var ortho = PlantApplication.ProjectPart("Ortho") as OrthoProject ?? throw new InvalidOperationException("ORTHO_BATCH_PROJECT_PART_REQUIRED");
|
||||
var drawings = ReadDrawings(project, ortho);
|
||||
var selections = PromptSelections(owner.Editor, drawings);
|
||||
if (selections.Count == 0) return;
|
||||
var plan = new OrthoBatchPlanner().Build(drawings, selections);
|
||||
foreach (var diagnostic in plan.Diagnostics) owner.Editor.WriteMessage("\nPXP-IOD: " + diagnostic);
|
||||
owner.Editor.WriteMessage($"\nPXP-IOD preview: drawings={plan.Operations.Select(value => value.RelativePath).Distinct(StringComparer.OrdinalIgnoreCase).Count()}; views={plan.Operations.Count}; no drawings modified.");
|
||||
RuntimeDiagnostics.Write("UpdateOrthoDrawingsPreviewCompleted", commandName: "PXP-IOD", resultStatus: "PreviewCompleted", document: owner, details: new Dictionary<string, object?> { ["DrawingCount"] = plan.Operations.Select(value => value.RelativePath).Distinct(StringComparer.OrdinalIgnoreCase).Count(), ["ViewCount"] = plan.Operations.Count });
|
||||
if (!string.Equals(Environment.GetEnvironmentVariable("P_REC_ANNOT_RUNTIME_MODE"), "Apply", StringComparison.OrdinalIgnoreCase)) return;
|
||||
var fixtureRoot = ValidateFixture();
|
||||
var failures = Apply(project, ortho, plan, fixtureRoot);
|
||||
owner.Editor.WriteMessage($"\nPXP-IOD Apply completed: views={plan.Operations.Count}; failures={failures.Count}.");
|
||||
RuntimeDiagnostics.Write("UpdateOrthoDrawingsApplyCompleted", commandName: "PXP-IOD", resultStatus: failures.Count == 0 ? "Applied" : "PartiallyApplied", document: owner, details: new Dictionary<string, object?> { ["FailureCount"] = failures.Count, ["Failures"] = failures.ToArray() });
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
RuntimeDiagnostics.Write("UpdateOrthoDrawingsFailed", "ERROR", commandName: "PXP-IOD", resultStatus: exception.Message, exception: exception, document: owner);
|
||||
owner.Editor.WriteMessage("\nPXP-IOD: " + exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static IReadOnlyList<OrthoBatchDrawing> ReadDrawings(PlantProject project, OrthoProject ortho)
|
||||
{
|
||||
var root = Path.GetDirectoryName(project.FileName) ?? throw new InvalidOperationException("ORTHO_BATCH_PROJECT_ROOT_UNAVAILABLE");
|
||||
return ortho.DwgSheetDefinitions.AsArray().Select(sheet =>
|
||||
{
|
||||
var path = UIUtils.GetDwgSheetDefResolvedPath(sheet);
|
||||
var relative = Path.GetRelativePath(root, path);
|
||||
return new OrthoBatchDrawing(sheet.DwgGuid.ToString(), Path.GetFileName(path), relative, sheet.DwgViewDefs.AsArray().Select(view => view.ViewName).ToArray());
|
||||
}).OrderBy(value => value.RelativePath, StringComparer.OrdinalIgnoreCase).ToArray();
|
||||
}
|
||||
|
||||
private static IReadOnlyList<OrthoBatchSelection> PromptSelections(Editor editor, IReadOnlyList<OrthoBatchDrawing> drawings)
|
||||
{
|
||||
var result = new List<OrthoBatchSelection>();
|
||||
foreach (var drawing in drawings)
|
||||
{
|
||||
var prompt = new PromptKeywordOptions($"\nUpdate Ortho drawing {drawing.RelativePath}?") { AllowNone = true };
|
||||
prompt.Keywords.Add("Yes"); prompt.Keywords.Add("No"); prompt.Keywords.Default = "No";
|
||||
var answer = editor.GetKeywords(prompt);
|
||||
if (answer.Status == PromptStatus.Cancel) return Array.Empty<OrthoBatchSelection>();
|
||||
if (string.Equals(answer.StringResult, "Yes", StringComparison.OrdinalIgnoreCase)) result.Add(new(drawing.RelativePath, drawing.Views));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> Apply(PlantProject project, OrthoProject ortho, OrthoBatchPlan plan, string fixtureRoot)
|
||||
{
|
||||
var projectRoot = Path.GetDirectoryName(project.FileName)!;
|
||||
var failures = new List<string>();
|
||||
foreach (var group in plan.Operations.GroupBy(value => value.RelativePath, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
var path = Path.GetFullPath(Path.Combine(projectRoot, group.Key));
|
||||
if (!path.StartsWith(fixtureRoot, StringComparison.OrdinalIgnoreCase)) { failures.Add(group.Key + ":OUTSIDE_FIXTURE"); continue; }
|
||||
Document? document = AcadApplication.DocumentManager.Cast<Document>().FirstOrDefault(value => string.Equals(Path.GetFullPath(value.Name), path, StringComparison.OrdinalIgnoreCase));
|
||||
var wasOpen = document is not null;
|
||||
try
|
||||
{
|
||||
if (document is null) { UIUtils.OpenDocument(path, out document, false); document.Database.CloseInput(true); }
|
||||
AcadApplication.DocumentManager.MdiActiveDocument = document;
|
||||
using (document.LockDocument()) foreach (var operation in group) UpdateView(document, ortho, operation.ViewName);
|
||||
if (wasOpen) document.Database.SaveAs(document.Name, true, DwgVersion.Current, document.Database.SecurityParameters); else document.CloseAndSave(path);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
failures.Add(group.Key + ":" + exception.GetType().Name);
|
||||
if (!wasOpen && document is not null) try { document.CloseAndDiscard(); } catch { }
|
||||
}
|
||||
}
|
||||
return failures;
|
||||
}
|
||||
|
||||
private static void UpdateView(Document document, OrthoProject ortho, string viewName)
|
||||
{
|
||||
if (!UIUtils.GetDwgSheetFromOrthoDrawingName(document.Database.Filename, out IDwgSheetDef sheet) || !UIUtils.GetViewDefByViewName(viewName, sheet, out IDwgViewDef definition))
|
||||
throw new InvalidOperationException("ORTHO_BATCH_VIEW_NOT_FOUND:" + viewName);
|
||||
var viewportId = FindViewport(document.Database, definition);
|
||||
var frozen = ReadFrozenLayers(document.Database, viewportId); var off = new StringCollection();
|
||||
if (!UIUtils.IsValidViewpport(viewportId, out double width, out double height, out double scale, out Point3d center) || !definition.SetLayout(width, height, ref center))
|
||||
throw new InvalidOperationException("ORTHO_BATCH_VIEW_LAYOUT_INVALID:" + viewName);
|
||||
definition.ViewScale = scale;
|
||||
var resolved = new StringCollection(); resolved.AddRange(UIUtils.GetResolvedRefModels(definition, false).Select(value => value.ModelFileName).ToArray());
|
||||
var models = new ArrayList(); UIUtils.UpdateFileCollectionWithXrefs(resolved, ref models, out Extents3d extents);
|
||||
definition.DwgQuery.ModelFiles = models.ToArray(typeof(ModelFile)) as ModelFile[];
|
||||
definition.DwgQuery.Execute(frozen, off);
|
||||
UIUtils.CreateOrUpdateOrthoView(null!, sheet, viewName, ortho, false, true, Dwg2dGen.NewDwgSetClipper(definition, 0), 0, frozen, off, new Vector3d(), 0, new Vector3d(), null!);
|
||||
definition.ViewCreationTime = DateTime.UtcNow.ToString("O");
|
||||
}
|
||||
|
||||
private static ObjectId FindViewport(Database database, IDwgViewDef definition)
|
||||
{
|
||||
using var transaction = database.TransactionManager.StartOpenCloseTransaction();
|
||||
var layouts = (DBDictionary)transaction.GetObject(database.LayoutDictionaryId, OpenMode.ForRead);
|
||||
foreach (DBDictionaryEntry entry in layouts)
|
||||
{
|
||||
var layout = (Layout)transaction.GetObject(entry.Value, OpenMode.ForRead);
|
||||
if (layout.ModelType) continue;
|
||||
var paper = (BlockTableRecord)transaction.GetObject(layout.BlockTableRecordId, OpenMode.ForRead);
|
||||
foreach (ObjectId id in paper)
|
||||
if (transaction.GetObject(id, OpenMode.ForRead) is Viewport viewport && viewport.Number > 1 &&
|
||||
Math.Abs(viewport.CenterPoint.X - definition.CenterPoint.X) < .001 && Math.Abs(viewport.CenterPoint.Y - definition.CenterPoint.Y) < .001 &&
|
||||
Math.Abs(viewport.Width - definition.ViewWidth) < .001 && Math.Abs(viewport.Height - definition.ViewHeight) < .001) return id;
|
||||
}
|
||||
throw new InvalidOperationException("ORTHO_BATCH_VIEWPORT_NOT_FOUND:" + definition.ViewName);
|
||||
}
|
||||
|
||||
private static StringCollection ReadFrozenLayers(Database database, ObjectId viewportId)
|
||||
{
|
||||
var result = new StringCollection();
|
||||
using var transaction = database.TransactionManager.StartOpenCloseTransaction();
|
||||
var viewport = (Viewport)transaction.GetObject(viewportId, OpenMode.ForRead);
|
||||
foreach (ObjectId id in viewport.GetFrozenLayers()) if (transaction.GetObject(id, OpenMode.ForRead) is LayerTableRecord layer) result.Add(layer.Name);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string ValidateFixture()
|
||||
{
|
||||
var marker = Environment.GetEnvironmentVariable("P_REC_ANNOT_ORTHO_BATCH_FIXTURE_MARKER");
|
||||
if (string.IsNullOrWhiteSpace(marker) || !File.Exists(marker) || !string.Equals(File.ReadAllText(marker).Trim(), "P_REC_ANNOT_TEST_FIXTURE=ORTHO_BATCH_APPLY", StringComparison.Ordinal)) throw new InvalidOperationException("ORTHO_BATCH_TEST_FIXTURE_REQUIRED");
|
||||
return Path.GetDirectoryName(Path.GetFullPath(marker))! + Path.DirectorySeparatorChar;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user