feat: read Plant 3D Ortho context and viewports
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Autodesk.AutoCAD.ApplicationServices;
|
||||
using Autodesk.ProcessPower.DataLinks;
|
||||
using Autodesk.ProcessPower.P3dProjectParts;
|
||||
using Autodesk.ProcessPower.PlantInstance;
|
||||
using Autodesk.ProcessPower.ProjectManager;
|
||||
using PRecAnnot.Abstractions;
|
||||
using AcadApplication = Autodesk.AutoCAD.ApplicationServices.Application;
|
||||
|
||||
namespace PRecAnnot.Plant3D2026;
|
||||
|
||||
internal sealed class PlantRuntimeContextReader : IPlantRuntimeContextReader
|
||||
{
|
||||
public PlantRuntimeContext ReadCurrentContext()
|
||||
{
|
||||
var document = AcadApplication.DocumentManager.MdiActiveDocument;
|
||||
if (document is null)
|
||||
{
|
||||
return Empty(false, false, "NO_ACTIVE_DOCUMENT", "Активный документ отсутствует.");
|
||||
}
|
||||
|
||||
PlantProject? project;
|
||||
try { project = PlantApplication.CurrentProject; }
|
||||
catch (Exception exception)
|
||||
{
|
||||
return Empty(true, false, "PLANT_PROJECT_READ_FAILED", exception.GetType().Name);
|
||||
}
|
||||
|
||||
if (project is null)
|
||||
{
|
||||
return Empty(true, false, "NO_PLANT_PROJECT", "Активный Plant 3D-проект не найден.");
|
||||
}
|
||||
|
||||
OrthoProject? orthoProject;
|
||||
try { orthoProject = PlantApplication.ProjectPart("Ortho") as OrthoProject; }
|
||||
catch (Exception exception)
|
||||
{
|
||||
return Empty(true, true, "ORTHO_PROJECT_PART_READ_FAILED", exception.GetType().Name);
|
||||
}
|
||||
|
||||
if (orthoProject is null)
|
||||
{
|
||||
return Empty(true, true, "ORTHO_PROJECT_PART_UNAVAILABLE", "Ortho project part недоступен.", OrthoDetectionStatus.Unsupported, "ProjectPart(Ortho)");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string resolvedPath;
|
||||
var isOrtho = OrthoDocUtils.IsValidOrthoDrawing(document.Name, orthoProject, out resolvedPath);
|
||||
if (!isOrtho)
|
||||
{
|
||||
return Empty(true, true, "NOT_ORTHO_DRAWING", "OrthoDocUtils.IsValidOrthoDrawing вернул false.", OrthoDetectionStatus.NotOrtho, "Ortho project part", "OrthoDocUtils.IsValidOrthoDrawing=false");
|
||||
}
|
||||
|
||||
var viewports = new PlantOrthoViewportReader(orthoProject, document.Name).ReadViewports(CancellationToken.None);
|
||||
return new PlantRuntimeContext
|
||||
{
|
||||
HasActiveDocument = true,
|
||||
HasPlantProject = true,
|
||||
IsOrthoDrawing = true,
|
||||
DrawingName = Path.GetFileName(document.Name),
|
||||
ProjectDisplayName = project.Name,
|
||||
Viewports = viewports,
|
||||
Diagnostics = Array.Empty<RuntimeDiagnostic>(),
|
||||
Detection = new OrthoDetectionResult
|
||||
{
|
||||
Status = OrthoDetectionStatus.Confirmed,
|
||||
Evidence = new[] { "ProjectPart(Ortho)", "OrthoDocUtils.IsValidOrthoDrawing=true", "DwgSheetDefinitions" },
|
||||
Diagnostics = Array.Empty<RuntimeDiagnostic>()
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
return Empty(true, true, "ORTHO_DETECTION_FAILED", exception.GetType().Name, OrthoDetectionStatus.Failed, "ProjectPart(Ortho)");
|
||||
}
|
||||
}
|
||||
|
||||
private static PlantRuntimeContext Empty(bool hasDocument, bool hasProject, string code, string message, OrthoDetectionStatus status = OrthoDetectionStatus.NotOrtho, params string[] evidence) => new()
|
||||
{
|
||||
HasActiveDocument = hasDocument,
|
||||
HasPlantProject = hasProject,
|
||||
IsOrthoDrawing = false,
|
||||
Diagnostics = new[] { new RuntimeDiagnostic { Code = code, Message = message } },
|
||||
Detection = new OrthoDetectionResult { Status = status, Evidence = evidence, Diagnostics = new[] { new RuntimeDiagnostic { Code = code, Message = message } } }
|
||||
};
|
||||
}
|
||||
|
||||
internal sealed class PlantOrthoViewportReader : IOrthoViewportReader
|
||||
{
|
||||
private readonly OrthoProject project;
|
||||
private readonly string drawingName;
|
||||
|
||||
public PlantOrthoViewportReader(OrthoProject project, string drawingName)
|
||||
{
|
||||
this.project = project;
|
||||
this.drawingName = Path.GetFileName(drawingName);
|
||||
}
|
||||
|
||||
public IReadOnlyList<OrthoViewportInfo> ReadViewports(CancellationToken cancellationToken)
|
||||
{
|
||||
var result = new List<OrthoViewportInfo>();
|
||||
foreach (var sheet in project.DwgSheetDefinitions.AsArray())
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (!string.Equals(Path.GetFileName(sheet.DwgName), drawingName, StringComparison.OrdinalIgnoreCase)) continue;
|
||||
|
||||
foreach (var view in sheet.DwgViewDefs.AsArray())
|
||||
{
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var id = StableId(sheet.DwgGuid.ToString(), view.ViewName);
|
||||
result.Add(new OrthoViewportInfo
|
||||
{
|
||||
Id = id,
|
||||
DisplayName = view.ViewName,
|
||||
CenterX = view.CenterPoint.X,
|
||||
CenterY = view.CenterPoint.Y,
|
||||
Width = view.ViewWidth,
|
||||
Height = view.ViewHeight,
|
||||
Scale = view.ViewScale,
|
||||
TwistAngle = 0,
|
||||
IsOn = true,
|
||||
IsLocked = false,
|
||||
SourceDrawingName = sheet.DwgName,
|
||||
ViewName = view.ViewName
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
// One malformed view must not abort the remaining read-only analysis.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.OrderByDescending(x => x.CenterY).ThenBy(x => x.CenterX).ThenBy(x => x.Id, StringComparer.Ordinal).ToArray();
|
||||
}
|
||||
|
||||
private static string StableId(string sheetId, string viewName)
|
||||
{
|
||||
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(sheetId + "|" + viewName));
|
||||
return "vp-" + Convert.ToHexString(hash)[..20];
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlantObjectReader : IPlantObjectReader
|
||||
{
|
||||
public IReadOnlyList<PlantObjectSnapshot> ReadObjects(OrthoViewportInfo viewport, CancellationToken cancellationToken)
|
||||
{
|
||||
var document = AcadApplication.DocumentManager.MdiActiveDocument;
|
||||
if (document is null) return Array.Empty<PlantObjectSnapshot>();
|
||||
|
||||
DataLinksManager? dataLinks;
|
||||
try { dataLinks = PlantApplication.ProjectPart("Piping")?.DataLinksManager; }
|
||||
catch { return Array.Empty<PlantObjectSnapshot>(); }
|
||||
if (dataLinks is null) return Array.Empty<PlantObjectSnapshot>();
|
||||
|
||||
var snapshots = new List<PlantObjectSnapshot>();
|
||||
foreach (var rowId in dataLinks.SelectAcPpRowIds(document.Database))
|
||||
{
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var className = dataLinks.GetObjectClassname(rowId);
|
||||
var properties = dataLinks.GetAllProperties(rowId, true)
|
||||
.GroupBy(x => x.Key, StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(x => x.Key, x => x.Last().Value, StringComparer.OrdinalIgnoreCase);
|
||||
string? Value(params string[] names) => properties.FirstOrDefault(x => names.Any(name => string.Equals(name, x.Key, StringComparison.OrdinalIgnoreCase))).Value;
|
||||
snapshots.Add(new PlantObjectSnapshot
|
||||
{
|
||||
SnapshotId = StableId(Path.GetFileName(document.Name), rowId, className),
|
||||
ObjectClass = className ?? string.Empty,
|
||||
Tag = Value("Tag", "TAG"),
|
||||
LineNumber = Value("Line Number", "LineNumber", "LINENUMBER"),
|
||||
SupportNumber = Value("Support Number", "SupportNumber"),
|
||||
EquipmentNumber = Value("Equipment Number", "EquipmentNumber"),
|
||||
Geometry = new GeometrySnapshot(),
|
||||
Properties = properties.ToDictionary(x => x.Key, x => (string?)x.Value, StringComparer.OrdinalIgnoreCase),
|
||||
Diagnostics = new[] { "GEOMETRY_NOT_MAPPED" }
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Keep the read path best-effort; no write operation is attempted.
|
||||
}
|
||||
}
|
||||
return snapshots.OrderBy(x => x.SnapshotId, StringComparer.Ordinal).ToArray();
|
||||
}
|
||||
|
||||
private static string StableId(string drawingName, int rowId, string? className)
|
||||
{
|
||||
var hash = SHA256.HashData(Encoding.UTF8.GetBytes($"{drawingName}|{rowId}|{className}"));
|
||||
return "obj-" + Convert.ToHexString(hash)[..24];
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class RuntimeWriteGuard(PRecAnnotExecutionMode mode) : IRuntimeWriteGuard
|
||||
{
|
||||
public void EnsureWriteAllowed(string operation)
|
||||
{
|
||||
if (mode == PRecAnnotExecutionMode.Apply) throw new InvalidOperationException("APPLY_MODE_NOT_IMPLEMENTED");
|
||||
throw new InvalidOperationException("SMOKE_WRITE_BLOCKED");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user