Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36178374db | ||
|
|
b33351117b | ||
|
|
56dcce42f6 | ||
|
|
ea3a9e157e | ||
|
|
837ac1d994 | ||
|
|
142b5f8b93 | ||
|
|
dcf95d7e3a | ||
|
|
c733a27cbb | ||
|
|
9a7a6cd678 |
@@ -1,4 +1,6 @@
|
||||
artifacts/
|
||||
.local-dotnet-sdk/
|
||||
.local-tools/
|
||||
bin/
|
||||
obj/
|
||||
.vs/
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Deterministic>true</Deterministic>
|
||||
<DebugType>portable</DebugType>
|
||||
<Plant3D2026InstallDir Condition="'$(Plant3D2026InstallDir)' == ''">$(ProgramFiles)\Autodesk\AutoCAD 2026</Plant3D2026InstallDir>
|
||||
<AutoCAD2026ManagedDir Condition="'$(AutoCAD2026ManagedDir)' == '' and '$(Plant3D2026InstallDir)' != ''">$(Plant3D2026InstallDir)</AutoCAD2026ManagedDir>
|
||||
<Plant3D2026ManagedDir Condition="'$(Plant3D2026ManagedDir)' == '' and '$(Plant3D2026InstallDir)' != ''">$(Plant3D2026InstallDir)\PLNT3D</Plant3D2026ManagedDir>
|
||||
<Plant3D2026InstallDir Condition="'$(Plant3D2026InstallDir)' == '' and '$(AutoCAD2026ManagedDir)' != ''">$(AutoCAD2026ManagedDir)</Plant3D2026InstallDir>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,3 +1,29 @@
|
||||
# Build
|
||||
|
||||
SDK 8.0.423 is installed. Plant 3D 2026 is not installed on the audit machine, so Autodesk references are currently absent and the adapter is a deliberate blocker. Build Core/tests independently first; after installation pass `Plant3D2026InstallDir` and validate exact assembly/member compatibility. No Autodesk DLL is copied to Git or bundle. `SeriesMin`, `SeriesMax` and the exact platform token are intentionally absent from the manifest until verified against the installed product and official package contract.
|
||||
The workstation has .NET SDK 8.0.423 and Autodesk AutoCAD Plant 3D 2026 installed. The paths are deliberately supplied at build time; no machine-specific Autodesk path is stored in project files.
|
||||
|
||||
Detected assembly inventory:
|
||||
|
||||
| Assembly | Assembly version | Build input |
|
||||
|---|---:|---|
|
||||
| AcMgd.dll | 25.1.0.0 | `AutoCAD2026ManagedDir` |
|
||||
| AcDbMgd.dll | 25.1.0.0 | `AutoCAD2026ManagedDir` |
|
||||
| AcCoreMgd.dll | 25.1.0.0 | `AutoCAD2026ManagedDir` |
|
||||
| AdWindows.dll | 5.2.1.1 | `AutoCAD2026ManagedDir` |
|
||||
| PnP3dObjectsMgd.dll | 17.0.60.1 | `Plant3D2026ManagedDir` |
|
||||
| PnPProjectManagerMgd.dll | 17.0.60.1 | `Plant3D2026ManagedDir` |
|
||||
|
||||
The actual build used:
|
||||
|
||||
```powershell
|
||||
dotnet restore .\Plant3D2026\P_Rec_Annot\PRecAnnot2026.sln
|
||||
dotnet build .\Plant3D2026\P_Rec_Annot\PRecAnnot2026.sln --configuration Release `
|
||||
/p:AutoCAD2026ManagedDir="<AutoCAD managed directory>" `
|
||||
/p:Plant3D2026ManagedDir="<Plant 3D managed directory>"
|
||||
```
|
||||
|
||||
`PRecAnnot.Plant3D2026` now compiles against the real AutoCAD/Plant 3D 2026 metadata. The adapter has real `IExtensionApplication`, `CommandMethod`, `PlantApplication.CurrentProject`, `Document`, `Editor`, and `Autodesk.Windows` Ribbon references. The previous conditional foundation-only adapter was removed. Autodesk references are `Private=false` and `CopyLocal=false`.
|
||||
|
||||
Release emits one non-fatal WindowsBase reference conflict caused by Autodesk's legacy reference surface versus the .NET 8 reference pack. It does not copy Autodesk assemblies to output. Autodesk DLLs are excluded from Git, NuGet, bundle, and publish output.
|
||||
|
||||
The local smoke bundle is created at `Plant3D2026\P_Rec_Annot\artifacts\P_Rec_Annot.bundle`. Deploy is NOT PERFORMED.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"executionMode": "DryRun",
|
||||
"runtimeExecuted": false,
|
||||
"plantProjectDetected": null,
|
||||
"orthoDetection": "NotExecuted",
|
||||
"viewportCount": null,
|
||||
"dataLinksAvailable": null,
|
||||
"objectsRead": null,
|
||||
"plan": {
|
||||
"create": null,
|
||||
"update": null,
|
||||
"skip": null,
|
||||
"conflict": null,
|
||||
"unsupported": null
|
||||
},
|
||||
"planDeterministic": true,
|
||||
"dbmodBefore": null,
|
||||
"dbmodAfter": null,
|
||||
"dwgModified": null,
|
||||
"dcfModified": null,
|
||||
"projectXmlModified": null,
|
||||
"unhandledExceptions": null,
|
||||
"blocker": "No controlled test Ortho DWG/project path was supplied or discovered; runtime DryRun was not claimed."
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"product": "Autodesk AutoCAD Plant 3D 2026",
|
||||
"manualNetload": true,
|
||||
"bundleLoaded": true,
|
||||
"automaticAppLoaderChecked": false,
|
||||
"smokeModeConfirmedByOperator": true,
|
||||
"observedCommandLogRuntimeMode": "Normal",
|
||||
"assemblyInitializeCountObserved": 1,
|
||||
"commands": {
|
||||
"PHP_REC_ANNOT": { "registered": true, "executed": true, "usageEvents": 1, "result": "PreflightAccepted" },
|
||||
"PXP-AAV": { "registered": true, "executed": true, "usageEvents": 1, "result": "PreflightAccepted" },
|
||||
"PXP-OA": { "registered": true, "executed": true, "usageEvents": 1, "result": "PreflightAccepted" }
|
||||
},
|
||||
"ribbon": { "created": true, "duplicateCheckedExhaustively": false },
|
||||
"plantContext": { "projectDetected": true, "projectDisplayName": "DefaultProject", "observedOrthoDetected": false, "dataLinksAvailable": true },
|
||||
"writesBlocked": true,
|
||||
"dbmodChecked": false,
|
||||
"dwgModified": false,
|
||||
"dcfModified": false,
|
||||
"projectXmlModified": false,
|
||||
"telemetryEventsExpected": 3,
|
||||
"telemetryEventsActual": 3,
|
||||
"unhandledExceptions": 0,
|
||||
"temporaryInstallationRemoved": true,
|
||||
"productionDeploy": false
|
||||
}
|
||||
@@ -1,3 +1,18 @@
|
||||
# Migration dashboard
|
||||
|
||||
The active command set is mapped in `command-migration-matrix.md`; all currently published target descriptors are `BLOCKED_WITH_REASON` because Plant 3D 2026 assemblies/runtime are unavailable. Coverage is not reported as a percentage until every active use case has a runtime result. Current status: mapped 3 foundation descriptors, implemented Core planning/registry/telemetry contracts, runtime verified 0, native replacements approved 0, retirements approved 0, blockers 3.
|
||||
The first real Plant 3D 2026 build is complete against the workstation assemblies (AutoCAD managed 25.1.0.0; Plant managed 17.0.60.1; AdWindows 5.2.1.1). Core remains Autodesk-free.
|
||||
|
||||
| Area | Status |
|
||||
|---|---|
|
||||
| `PHP_REC_ANNOT` | Registered real `CommandMethod`; primary diagnostic entry point |
|
||||
| `PXP-AAV` | Registered real `CommandMethod`; diagnostic alias |
|
||||
| `PXP-OA` | Registered real `CommandMethod`; diagnostic alias |
|
||||
| Ribbon | Implemented with `Autodesk.Windows`; idempotent tab/panel guard |
|
||||
| Adapter | Compiled against real 2026 assemblies |
|
||||
| Bundle | Local smoke package built and checked for Autodesk DLL exclusion |
|
||||
| Runtime smoke | Manual NETLOAD confirmed on Plant 3D 2026; automatic AppLoader and Ortho DryRun remain unverified |
|
||||
| Ortho read path | Compiled against `OrthoDocUtils`, `OrthoProject`, `DwgSheetDefinitions`, `DwgViewDefs`, and `DataLinksManager`; runtime fixture pending |
|
||||
| Dry-run plan | Deterministic Core builder with DTO boundary and Apply guard; no annotation writes |
|
||||
| Production deploy | NOT PERFORMED |
|
||||
|
||||
Fixed API assumptions: foundation-only lifecycle became `IExtensionApplication`; command descriptors became actual `CommandMethod` registrations; Ortho detection uses `OrthoDocUtils.IsValidOrthoDrawing` plus the Ortho project part; viewport metadata uses `DwgSheetDefinitions`/`DwgViewDefs`; object properties use read-only `DataLinksManager.SelectAcPpRowIds` and `GetAllProperties`; Core receives only immutable DTOs. Remaining blockers are a controlled Ortho fixture, geometry/object-to-viewport mapping, legacy source provenance, and future Apply implementation.
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# PXP-AAV read-path analysis
|
||||
|
||||
Status: traced to the existing command registration and the shared dry-run plan path; annotation writes are not implemented in this slice.
|
||||
|
||||
| RuleId | LegacyCommand | LegacyProject | LegacyFile | ObservedBehavior | TargetUseCase | TargetClass | Test | Status |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
| PXP-AAV-001 | PXP-AAV | legacy audit | `docs/legacy-rule-provenance.csv` | legacy provenance is recorded, but the requested legacy checkout is absent on this workstation | read objects visible to an Ortho view and build a dry-run plan | adapter DTO -> `AnnotationPlan` | `AnnotationPlan_InputOrderDoesNotChangeResult` | partial |
|
||||
|
||||
The implementation deliberately does not claim behavioral equivalence. The next required input is a read-only copy of the legacy source or a golden test Ortho drawing with expected diagnostics.
|
||||
@@ -0,0 +1,9 @@
|
||||
# PXP-OA read-path analysis
|
||||
|
||||
Status: command is a real `CommandMethod` and uses the same safe context/read-plan pipeline. No Ortho annotation is created, updated, or deleted.
|
||||
|
||||
| RuleId | LegacyCommand | LegacyProject | LegacyFile | ObservedBehavior | TargetUseCase | TargetClass | Test | Status |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
| PXP-OA-001 | PXP-OA | legacy audit | `docs/legacy-rule-provenance.csv` | legacy provenance is available, but source checkout was not found locally | Ortho context and DataLinks read diagnostics | adapter DTO -> `AnnotationPlan` | `ApplyMode_IsBlocked` | partial |
|
||||
|
||||
Exact object filters, annotation text, placement rules, and existing-annotation detection remain unconfirmed until the legacy source or a controlled golden fixture is supplied.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Plant 3D 2026 runtime smoke results
|
||||
|
||||
## Scope
|
||||
|
||||
The smoke test used a temporary per-user bundle installation only. Production deploy was not performed. The temporary installation was removed after AutoCAD closed.
|
||||
|
||||
## Confirmed
|
||||
|
||||
- Manual `NETLOAD` loaded the P_Rec_Annot entry assembly in AutoCAD Plant 3D 2026.
|
||||
- `PHP_REC_ANNOT`, `PXP-AAV`, and `PXP-OA` were accepted by the command line.
|
||||
- The active Plant project was detected as `DefaultProject`.
|
||||
- The user confirmed Smoke mode behavior and no DWG mutation during the manual smoke interaction.
|
||||
- The runtime log contains one assembly initialization and one normal termination for the observed process.
|
||||
- The runtime log records Ribbon creation with status `Created`.
|
||||
- The runtime log records exactly one `CommandUsageEvent` for each of the three commands.
|
||||
- The observed Plant context reported `DataLinksAvailable=true` and `IsOrthoDrawing=false` for `Floor Plan Sample.dwg`.
|
||||
- No unhandled exception was reported in the captured runtime log.
|
||||
|
||||
## Not confirmed
|
||||
|
||||
- Automatic AppLoader loading without `NETLOAD` was not tested.
|
||||
- DBMOD and byte-level DWG/DCF/Project.xml comparisons were not captured by the smoke evidence.
|
||||
- Ribbon duplicate behavior across workspace/document lifecycle was not exhaustively tested.
|
||||
- A command invocation logged as `RuntimeMode=Smoke` is not present in the captured JSONL; the three detailed command records have `RuntimeMode=Normal`.
|
||||
|
||||
## Evidence handling
|
||||
|
||||
The detailed runtime JSONL remains local under `%LOCALAPPDATA%\Promhimproekt\PRecAnnot2026\Logs\` and is not committed. No DWG, DCF, Project.xml, Autodesk DLL, user identity, project path, or full local runtime path is included in the repository.
|
||||
|
||||
Temporary installation: PERFORMED AND REMOVED.
|
||||
Production deploy: NOT PERFORMED.
|
||||
@@ -1,3 +1,19 @@
|
||||
# Runtime test plan
|
||||
|
||||
On a controlled Plant 3D 2026 test machine: install the bundle outside production, start Plant, verify exactly one Ribbon tab/panel, open a test project and Ortho DWG, run `PHP_REC_ANNOT`, inspect dry-run counts, confirm one transaction, save/reopen/audit, repeat command, test no duplicates and Undo, then compare entities/text/layers/positions/links with the 2019 golden sample. This plan is prepared but not executed.
|
||||
Runtime smoke is ready but has not been executed in AutoCAD. The local package is `Plant3D2026\P_Rec_Annot\artifacts\P_Rec_Annot.bundle`.
|
||||
|
||||
Next-step installation command (intentionally not executed):
|
||||
|
||||
```powershell
|
||||
Copy-Item -Recurse -Force .\Plant3D2026\P_Rec_Annot\artifacts\P_Rec_Annot.bundle `
|
||||
"$env:APPDATA\Autodesk\ApplicationPlugins\P_Rec_Annot.bundle"
|
||||
```
|
||||
|
||||
In a controlled Plant 3D 2026 session, verify that the entry assembly loads, the `Промхимпроект` tab and `Аннотации` panel occur once, and the `Аннотировать` button invokes `PHP_REC_ANNOT`. Open a Plant project and active DWG, run all three commands, verify the project guard and diagnostic-only behavior, and confirm no unhandled exception. Repeat initialization and confirm no Ribbon duplicate. DWG mutation, Ortho viewport/data-link behavior, undo, repeat-run idempotence, and golden-DWG comparison remain runtime blockers for the implementation wave.
|
||||
|
||||
Deploy status: NOT PERFORMED.
|
||||
# Current slice: Ortho read / dry-run
|
||||
|
||||
`P_REC_ANNOT_RUNTIME_MODE=Smoke` reads context only. `DryRun` reads Ortho view metadata and DataLinks properties, builds an immutable deterministic `AnnotationPlan`, and never applies it. `Apply` is blocked with `APPLY_MODE_NOT_IMPLEMENTED`; unknown values resolve to `DryRun`.
|
||||
|
||||
The read adapter uses `OrthoDocUtils.IsValidOrthoDrawing`, `OrthoProject.DwgSheetDefinitions`, `IDwgViewDef`, and read-only `DataLinksManager` methods. The adapter does not expose AutoCAD or Plant objects to Core and contains no write calls. A runtime DryRun result is not published until a controlled test Ortho DWG is available; see `docs/evidence/ortho-dry-run-summary.json`.
|
||||
|
||||
@@ -1,5 +1,117 @@
|
||||
namespace PRecAnnot.Abstractions;
|
||||
|
||||
public sealed record RuntimeDiagnostic
|
||||
{
|
||||
public required string Code { get; init; }
|
||||
public required string Message { get; init; }
|
||||
public string Severity { get; init; } = "Warning";
|
||||
}
|
||||
|
||||
public enum OrthoDetectionStatus { Confirmed, NotOrtho, Ambiguous, Unsupported, Failed }
|
||||
|
||||
public sealed record OrthoDetectionResult
|
||||
{
|
||||
public required OrthoDetectionStatus Status { get; init; }
|
||||
public required IReadOnlyList<string> Evidence { get; init; }
|
||||
public required IReadOnlyList<RuntimeDiagnostic> Diagnostics { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct Point2Dto(double X, double Y);
|
||||
public readonly record struct Point3Dto(double X, double Y, double Z);
|
||||
public readonly record struct Vector2Dto(double X, double Y);
|
||||
public sealed record Bounds2Dto(Point2Dto Minimum, Point2Dto Maximum);
|
||||
|
||||
public sealed record GeometrySnapshot
|
||||
{
|
||||
public Point3Dto? WorldPosition { get; init; }
|
||||
public Point2Dto? PaperPosition { get; init; }
|
||||
public Bounds2Dto? ProjectedBounds { get; init; }
|
||||
public double? Rotation { get; init; }
|
||||
public double? Elevation { get; init; }
|
||||
}
|
||||
|
||||
public sealed record OrthoViewportInfo
|
||||
{
|
||||
public required string Id { get; init; }
|
||||
public required string DisplayName { get; init; }
|
||||
public double CenterX { get; init; }
|
||||
public double CenterY { get; init; }
|
||||
public double Width { get; init; }
|
||||
public double Height { get; init; }
|
||||
public double Scale { get; init; }
|
||||
public double TwistAngle { get; init; }
|
||||
public bool IsOn { get; init; }
|
||||
public bool IsLocked { get; init; }
|
||||
public string? SourceDrawingName { get; init; }
|
||||
public string? ViewName { get; init; }
|
||||
}
|
||||
|
||||
public sealed record PlantObjectSnapshot
|
||||
{
|
||||
public required string SnapshotId { get; init; }
|
||||
public required string ObjectClass { get; init; }
|
||||
public string? Tag { get; init; }
|
||||
public string? LineNumber { get; init; }
|
||||
public string? SupportNumber { get; init; }
|
||||
public string? EquipmentNumber { get; init; }
|
||||
public required GeometrySnapshot Geometry { get; init; }
|
||||
public IReadOnlyDictionary<string, string?> Properties { get; init; } = new Dictionary<string, string?>();
|
||||
public IReadOnlyList<string> Diagnostics { get; init; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
public sealed record PlantRuntimeContext
|
||||
{
|
||||
public bool HasActiveDocument { get; init; }
|
||||
public bool HasPlantProject { get; init; }
|
||||
public bool IsOrthoDrawing { get; init; }
|
||||
public string? DrawingName { get; init; }
|
||||
public string? ProjectDisplayName { get; init; }
|
||||
public IReadOnlyList<OrthoViewportInfo> Viewports { get; init; } = Array.Empty<OrthoViewportInfo>();
|
||||
public IReadOnlyList<RuntimeDiagnostic> Diagnostics { get; init; } = Array.Empty<RuntimeDiagnostic>();
|
||||
public OrthoDetectionResult Detection { get; init; } = new() { Status = OrthoDetectionStatus.Unsupported, Evidence = Array.Empty<string>(), Diagnostics = Array.Empty<RuntimeDiagnostic>() };
|
||||
}
|
||||
|
||||
public interface IPlantRuntimeContextReader { PlantRuntimeContext ReadCurrentContext(); }
|
||||
public interface IOrthoViewportReader { IReadOnlyList<OrthoViewportInfo> ReadViewports(CancellationToken cancellationToken); }
|
||||
|
||||
public enum AnnotationOperationKind { Create, Update, Skip, Conflict, Unsupported }
|
||||
|
||||
public sealed record AnnotationOperationPlan
|
||||
{
|
||||
public required string SourceSnapshotId { get; init; }
|
||||
public required AnnotationOperationKind Kind { get; init; }
|
||||
public string? AnnotationType { get; init; }
|
||||
public string? Text { get; init; }
|
||||
public Point2Dto? Position { get; init; }
|
||||
public double? Rotation { get; init; }
|
||||
public string? ExistingAnnotationId { get; init; }
|
||||
public required IReadOnlyList<string> Reasons { get; init; }
|
||||
}
|
||||
|
||||
public sealed record AnnotationPlanSummary
|
||||
{
|
||||
public int ObjectsExamined { get; init; }
|
||||
public int CreateCount { get; init; }
|
||||
public int UpdateCount { get; init; }
|
||||
public int SkipCount { get; init; }
|
||||
public int ConflictCount { get; init; }
|
||||
public int UnsupportedCount { get; init; }
|
||||
public int WarningCount { get; init; }
|
||||
public int ErrorCount { get; init; }
|
||||
}
|
||||
|
||||
public sealed record AnnotationPlanningRequest
|
||||
{
|
||||
public required string CommandName { get; init; }
|
||||
public required OrthoViewportInfo Viewport { get; init; }
|
||||
public IReadOnlyList<PlantObjectSnapshot> Objects { get; init; } = Array.Empty<PlantObjectSnapshot>();
|
||||
public IReadOnlyList<ExistingAnnotation> Existing { get; init; } = Array.Empty<ExistingAnnotation>();
|
||||
}
|
||||
|
||||
public interface IAnnotationPlanBuilder { AnnotationPlan Build(AnnotationPlanningRequest request); }
|
||||
public enum PRecAnnotExecutionMode { Smoke, DryRun, Apply }
|
||||
public interface IRuntimeWriteGuard { void EnsureWriteAllowed(string operation); }
|
||||
|
||||
public sealed record OrthoViewportRef(string DrawingId, string ViewportId);
|
||||
|
||||
public sealed record PlantObjectData
|
||||
@@ -36,6 +148,11 @@ public sealed record AnnotationIntent
|
||||
|
||||
public sealed record AnnotationPlan
|
||||
{
|
||||
public string PlanId { get; init; } = string.Empty;
|
||||
public string CommandName { get; init; } = string.Empty;
|
||||
public IReadOnlyList<AnnotationOperationPlan> Operations { get; init; } = Array.Empty<AnnotationOperationPlan>();
|
||||
public IReadOnlyList<RuntimeDiagnostic> Diagnostics { get; init; } = Array.Empty<RuntimeDiagnostic>();
|
||||
public AnnotationPlanSummary Summary { get; init; } = new();
|
||||
public required IReadOnlyList<AnnotationIntent> Create { get; init; }
|
||||
public required IReadOnlyList<AnnotationIntent> Update { get; init; }
|
||||
public required IReadOnlyList<string> Delete { get; init; }
|
||||
@@ -58,7 +175,7 @@ public interface IOrthoDrawingContext
|
||||
|
||||
public interface IPlantObjectReader
|
||||
{
|
||||
IReadOnlyList<PlantObjectData> ReadObjects(OrthoViewportRef viewport, CancellationToken cancellationToken);
|
||||
IReadOnlyList<PlantObjectSnapshot> ReadObjects(OrthoViewportInfo viewport, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public interface IAnnotationRepository
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using PRecAnnot.Abstractions;
|
||||
|
||||
namespace PRecAnnot.Core;
|
||||
|
||||
public sealed class AnnotationPlanBuilder : IAnnotationPlanBuilder
|
||||
{
|
||||
public AnnotationPlan Build(AnnotationPlanningRequest request)
|
||||
{
|
||||
var objects = request.Objects.OrderBy(x => x.SnapshotId, StringComparer.Ordinal).ToArray();
|
||||
var existing = request.Existing.ToDictionary(x => x.StableKey, StringComparer.OrdinalIgnoreCase);
|
||||
var operations = new List<AnnotationOperationPlan>(objects.Length);
|
||||
var creates = new List<AnnotationIntent>();
|
||||
var updates = new List<AnnotationIntent>();
|
||||
var diagnostics = new List<RuntimeDiagnostic>();
|
||||
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var item in objects)
|
||||
{
|
||||
if (!seen.Add(item.SnapshotId))
|
||||
{
|
||||
operations.Add(new AnnotationOperationPlan { SourceSnapshotId = item.SnapshotId, Kind = AnnotationOperationKind.Conflict, Reasons = new[] { "duplicate-snapshot-id" } });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(item.ObjectClass))
|
||||
{
|
||||
operations.Add(new AnnotationOperationPlan { SourceSnapshotId = item.SnapshotId, Kind = AnnotationOperationKind.Unsupported, Reasons = new[] { "missing-object-class" } });
|
||||
continue;
|
||||
}
|
||||
|
||||
var text = string.IsNullOrWhiteSpace(item.LineNumber) ? item.Tag?.Trim() : item.LineNumber!.Trim();
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
operations.Add(new AnnotationOperationPlan { SourceSnapshotId = item.SnapshotId, Kind = AnnotationOperationKind.Skip, Reasons = new[] { "missing-line-number-or-tag" } });
|
||||
continue;
|
||||
}
|
||||
|
||||
var position = item.Geometry.PaperPosition;
|
||||
if (position is null)
|
||||
{
|
||||
diagnostics.Add(new RuntimeDiagnostic { Code = "MISSING_GEOMETRY", Message = $"No paper position for {item.SnapshotId}." });
|
||||
}
|
||||
|
||||
var stableKey = $"{request.Viewport.SourceDrawingName ?? string.Empty}:{request.Viewport.Id}:{item.SnapshotId}";
|
||||
var existingAnnotation = existing.TryGetValue(stableKey, out var match) ? match : existing.TryGetValue(item.SnapshotId, out var directMatch) ? directMatch : null;
|
||||
var kind = existingAnnotation is null ? AnnotationOperationKind.Create : AnnotationOperationKind.Update;
|
||||
operations.Add(new AnnotationOperationPlan
|
||||
{
|
||||
SourceSnapshotId = item.SnapshotId,
|
||||
Kind = kind,
|
||||
AnnotationType = "PlantObject",
|
||||
Text = text,
|
||||
Position = position,
|
||||
Rotation = item.Geometry.Rotation,
|
||||
ExistingAnnotationId = existingAnnotation?.StableKey,
|
||||
Reasons = Array.Empty<string>()
|
||||
});
|
||||
|
||||
var intent = new AnnotationIntent
|
||||
{
|
||||
StableKey = stableKey,
|
||||
Text = text,
|
||||
Position = position is { } point ? new AnnotationPoint(point.X, point.Y) : new AnnotationPoint(0, 0),
|
||||
Rotation = item.Geometry.Rotation ?? 0,
|
||||
ClassName = item.ObjectClass,
|
||||
Layer = "PXP_ANNOTATION"
|
||||
};
|
||||
if (kind == AnnotationOperationKind.Create) creates.Add(intent); else updates.Add(intent);
|
||||
}
|
||||
|
||||
var summary = new AnnotationPlanSummary
|
||||
{
|
||||
ObjectsExamined = objects.Length,
|
||||
CreateCount = operations.Count(x => x.Kind == AnnotationOperationKind.Create),
|
||||
UpdateCount = operations.Count(x => x.Kind == AnnotationOperationKind.Update),
|
||||
SkipCount = operations.Count(x => x.Kind == AnnotationOperationKind.Skip),
|
||||
ConflictCount = operations.Count(x => x.Kind == AnnotationOperationKind.Conflict),
|
||||
UnsupportedCount = operations.Count(x => x.Kind == AnnotationOperationKind.Unsupported),
|
||||
WarningCount = diagnostics.Count(x => !string.Equals(x.Severity, "Error", StringComparison.OrdinalIgnoreCase)),
|
||||
ErrorCount = diagnostics.Count(x => string.Equals(x.Severity, "Error", StringComparison.OrdinalIgnoreCase))
|
||||
};
|
||||
|
||||
return new AnnotationPlan
|
||||
{
|
||||
PlanId = BuildPlanId(request, objects),
|
||||
CommandName = request.CommandName,
|
||||
Operations = operations,
|
||||
Diagnostics = diagnostics,
|
||||
Summary = summary,
|
||||
Create = creates,
|
||||
Update = updates,
|
||||
Delete = Array.Empty<string>(),
|
||||
SkipReasons = operations.Where(x => x.Kind == AnnotationOperationKind.Skip).SelectMany(x => x.Reasons).ToArray(),
|
||||
ExaminedCount = objects.Length,
|
||||
IsDeterministic = true
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildPlanId(AnnotationPlanningRequest request, IReadOnlyList<PlantObjectSnapshot> objects)
|
||||
{
|
||||
var canonical = new StringBuilder(request.CommandName).Append('|').Append(request.Viewport.SourceDrawingName).Append('|').Append(request.Viewport.Id);
|
||||
foreach (var item in objects)
|
||||
canonical.Append('|').Append(item.SnapshotId).Append('|').Append(item.ObjectClass).Append('|').Append(item.Tag).Append('|').Append(item.LineNumber).Append('|').Append(item.Geometry.PaperPosition);
|
||||
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(canonical.ToString()));
|
||||
return "plan-" + Convert.ToHexString(hash)[..24];
|
||||
}
|
||||
}
|
||||
@@ -6,43 +6,21 @@ public sealed class AnnotationRuleEngine : IAnnotationRuleEngine
|
||||
{
|
||||
public AnnotationPlan BuildPlan(AnnotationRequest request)
|
||||
{
|
||||
var existing = request.Existing.ToDictionary(x => x.StableKey, StringComparer.OrdinalIgnoreCase);
|
||||
var create = new List<AnnotationIntent>();
|
||||
var update = new List<AnnotationIntent>();
|
||||
var skipped = new List<string>();
|
||||
var objects = request.Objects.Select(item => new PlantObjectSnapshot
|
||||
{
|
||||
SnapshotId = item.ObjectId,
|
||||
ObjectClass = item.ClassName,
|
||||
Tag = item.Tag,
|
||||
LineNumber = item.LineNumber,
|
||||
Geometry = new GeometrySnapshot { PaperPosition = new Point2Dto(item.Position.X, item.Position.Y), Rotation = item.Rotation }
|
||||
}).ToArray();
|
||||
|
||||
foreach (var item in request.Objects.OrderBy(x => x.ObjectId, StringComparer.Ordinal))
|
||||
return new AnnotationPlanBuilder().Build(new AnnotationPlanningRequest
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(item.LineNumber) && string.IsNullOrWhiteSpace(item.Tag))
|
||||
{
|
||||
skipped.Add($"{item.ObjectId}:missing-line-number-or-tag");
|
||||
continue;
|
||||
CommandName = "PHP_REC_ANNOT",
|
||||
Viewport = new OrthoViewportInfo { Id = request.Viewport.ViewportId, DisplayName = request.Viewport.ViewportId, SourceDrawingName = request.Viewport.DrawingId },
|
||||
Objects = objects,
|
||||
Existing = request.Existing
|
||||
});
|
||||
}
|
||||
|
||||
var text = BuildText(item);
|
||||
var intent = new AnnotationIntent
|
||||
{
|
||||
StableKey = $"{request.Viewport.DrawingId}:{request.Viewport.ViewportId}:{item.ObjectId}",
|
||||
Text = text,
|
||||
Position = item.Position,
|
||||
Rotation = item.Rotation,
|
||||
ClassName = item.ClassName,
|
||||
Layer = "PXP_ANNOTATION"
|
||||
};
|
||||
|
||||
if (existing.ContainsKey(intent.StableKey)) update.Add(intent); else create.Add(intent);
|
||||
}
|
||||
|
||||
return new AnnotationPlan
|
||||
{
|
||||
Create = create,
|
||||
Update = update,
|
||||
Delete = Array.Empty<string>(),
|
||||
SkipReasons = skipped,
|
||||
ExaminedCount = request.Objects.Count
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildText(PlantObjectData item) =>
|
||||
string.IsNullOrWhiteSpace(item.LineNumber) ? item.Tag!.Trim() : item.LineNumber.Trim();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using PRecAnnot.Abstractions;
|
||||
|
||||
namespace PRecAnnot.Core;
|
||||
|
||||
public sealed class ExecutionModeWriteGuard(PRecAnnotExecutionMode mode) : IRuntimeWriteGuard
|
||||
{
|
||||
public void EnsureWriteAllowed(string operation) => throw new InvalidOperationException(mode == PRecAnnotExecutionMode.Apply ? "APPLY_MODE_NOT_IMPLEMENTED" : "SMOKE_WRITE_BLOCKED");
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using PRecAnnot.Abstractions;
|
||||
|
||||
namespace PRecAnnot.Core;
|
||||
|
||||
public static class OrthoDetectionClassifier
|
||||
{
|
||||
public static OrthoDetectionResult Classify(bool hasPlantProject, bool hasOrthoPart, bool apiConfirmed, bool metadataAmbiguous = false)
|
||||
{
|
||||
if (!hasPlantProject) return Result(OrthoDetectionStatus.NotOrtho, "NO_PLANT_PROJECT", "Active Plant project is absent.");
|
||||
if (metadataAmbiguous) return Result(OrthoDetectionStatus.Ambiguous, "AMBIGUOUS_ORTHO_METADATA", "Plant metadata is insufficient to confirm Ortho.");
|
||||
if (!hasOrthoPart) return Result(OrthoDetectionStatus.Unsupported, "ORTHO_PART_UNAVAILABLE", "Ortho project part is unavailable.");
|
||||
return apiConfirmed
|
||||
? Result(OrthoDetectionStatus.Confirmed, "ORTHO_API_CONFIRMED", "OrthoDocUtils confirmed the active drawing.")
|
||||
: Result(OrthoDetectionStatus.NotOrtho, "ORTHO_API_NEGATIVE", "OrthoDocUtils did not confirm the active drawing.");
|
||||
}
|
||||
|
||||
private static OrthoDetectionResult Result(OrthoDetectionStatus status, string code, string message) => new()
|
||||
{
|
||||
Status = status,
|
||||
Evidence = new[] { code },
|
||||
Diagnostics = new[] { new RuntimeDiagnostic { Code = code, Message = message } }
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
using System.Diagnostics;
|
||||
using Autodesk.AutoCAD.ApplicationServices;
|
||||
using Autodesk.AutoCAD.Runtime;
|
||||
using Autodesk.Windows;
|
||||
using PRecAnnot.Abstractions;
|
||||
using PRecAnnot.Core;
|
||||
using AcadApplication = Autodesk.AutoCAD.ApplicationServices.Application;
|
||||
|
||||
namespace PRecAnnot.Plant3D2026;
|
||||
|
||||
public sealed class EntryPoint : IExtensionApplication
|
||||
{
|
||||
public void Initialize()
|
||||
{
|
||||
RuntimeDiagnostics.Write("AssemblyInitializeStarted");
|
||||
RibbonIntegration.Register();
|
||||
RuntimeDiagnostics.Write("AssemblyInitializeCompleted");
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
RuntimeDiagnostics.Write("AssemblyTerminateStarted");
|
||||
RibbonIntegration.Terminate();
|
||||
RuntimeDiagnostics.Write("AssemblyTerminateCompleted");
|
||||
}
|
||||
|
||||
[CommandMethod("PHP_REC_ANNOT")]
|
||||
public void PrimaryCommand() => RunDiagnostic("PHP_REC_ANNOT");
|
||||
|
||||
[CommandMethod("PXP-AAV")]
|
||||
public void AutoAnnotateViewCommand() => RunDiagnostic("PXP-AAV");
|
||||
|
||||
[CommandMethod("PXP-OA")]
|
||||
public void OrthoAnnotateCommand() => RunDiagnostic("PXP-OA");
|
||||
|
||||
private static void RunDiagnostic(string commandName)
|
||||
{
|
||||
var operationId = Guid.NewGuid().ToString("N");
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
Document? document = null;
|
||||
RuntimeDiagnostics.Write("CommandStarted", operationId: operationId, commandName: commandName);
|
||||
try
|
||||
{
|
||||
document = AcadApplication.DocumentManager.MdiActiveDocument;
|
||||
var mode = ParseMode(RuntimeDiagnostics.RuntimeMode);
|
||||
if (mode == PRecAnnotExecutionMode.Apply) throw new InvalidOperationException("APPLY_MODE_NOT_IMPLEMENTED");
|
||||
|
||||
var context = new PlantRuntimeContextReader().ReadCurrentContext();
|
||||
var details = new Dictionary<string, object?>
|
||||
{
|
||||
["ExecutionMode"] = mode.ToString(),
|
||||
["IsPlantProjectDetected"] = context.HasPlantProject,
|
||||
["IsPlantProjectOpen"] = context.HasPlantProject,
|
||||
["IsOrthoDrawing"] = context.IsOrthoDrawing,
|
||||
["ViewportCount"] = context.Viewports.Count,
|
||||
["DataLinksAvailable"] = DataLinksAvailable(context.HasPlantProject)
|
||||
};
|
||||
|
||||
if (document is null || !context.HasPlantProject)
|
||||
{
|
||||
RuntimeDiagnostics.Write("CommandPreflightCompleted", operationId: operationId, commandName: commandName, resultStatus: "PreflightRejected", document: document, details: details);
|
||||
WriteEditor(document, "\nP_Rec_Annot: Активный Plant 3D-проект не найден; DWG не изменён.");
|
||||
WriteUsage(operationId, commandName, "PreflightRejected", document, details, stopwatch.ElapsedMilliseconds);
|
||||
RuntimeDiagnostics.Write("CommandCompleted", operationId: operationId, commandName: commandName, resultStatus: "PreflightRejected", document: document, details: details);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == PRecAnnotExecutionMode.Smoke || !context.IsOrthoDrawing)
|
||||
{
|
||||
var status = context.IsOrthoDrawing ? "PreflightAccepted" : "NotOrtho";
|
||||
RuntimeDiagnostics.Write("CommandPreflightCompleted", operationId: operationId, commandName: commandName, resultStatus: status, document: document, details: details);
|
||||
WriteEditor(document, $"\nP_Rec_Annot {commandName}: проект '{context.ProjectDisplayName}' найден; тип чертежа: {(context.IsOrthoDrawing ? "Ortho" : "NotOrtho")}; DWG не изменён.");
|
||||
WriteUsage(operationId, commandName, status, document, details, stopwatch.ElapsedMilliseconds);
|
||||
RuntimeDiagnostics.Write("CommandCompleted", operationId: operationId, commandName: commandName, resultStatus: status, document: document, details: details);
|
||||
return;
|
||||
}
|
||||
|
||||
var builder = new AnnotationPlanBuilder();
|
||||
var plans = new List<AnnotationPlan>();
|
||||
foreach (var viewport in context.Viewports)
|
||||
{
|
||||
var objects = new PlantObjectReader().ReadObjects(viewport, CancellationToken.None);
|
||||
plans.Add(builder.Build(new AnnotationPlanningRequest { CommandName = commandName, Viewport = viewport, Objects = objects }));
|
||||
}
|
||||
|
||||
var summary = new
|
||||
{
|
||||
ObjectsExamined = plans.Sum(x => x.Summary.ObjectsExamined),
|
||||
CreateCount = plans.Sum(x => x.Summary.CreateCount),
|
||||
UpdateCount = plans.Sum(x => x.Summary.UpdateCount),
|
||||
SkipCount = plans.Sum(x => x.Summary.SkipCount),
|
||||
ConflictCount = plans.Sum(x => x.Summary.ConflictCount),
|
||||
UnsupportedCount = plans.Sum(x => x.Summary.UnsupportedCount),
|
||||
WarningCount = plans.Sum(x => x.Summary.WarningCount),
|
||||
ErrorCount = plans.Sum(x => x.Summary.ErrorCount)
|
||||
};
|
||||
details["ObjectsExamined"] = summary.ObjectsExamined;
|
||||
details["PlanCreateCount"] = summary.CreateCount;
|
||||
details["PlanUpdateCount"] = summary.UpdateCount;
|
||||
details["PlanSkipCount"] = summary.SkipCount;
|
||||
details["PlanConflictCount"] = summary.ConflictCount;
|
||||
details["PlanUnsupportedCount"] = summary.UnsupportedCount;
|
||||
details["ErrorCount"] = summary.ErrorCount;
|
||||
RuntimeDiagnostics.Write("CommandPreflightCompleted", operationId: operationId, commandName: commandName, resultStatus: "DryRunCompleted", document: document, details: details);
|
||||
WriteEditor(document, $"\nP_Rec_Annot — диагностический план\nПроект: {context.ProjectDisplayName}\nТип чертежа: Ortho\nViewport: {context.Viewports.Count}\nОбъектов найдено: {summary.ObjectsExamined}\nБудет создано: {summary.CreateCount}\nБудет обновлено: {summary.UpdateCount}\nБудет пропущено: {summary.SkipCount}\nКонфликтов: {summary.ConflictCount}\nОшибок: {summary.ErrorCount}\nDWG не изменён.");
|
||||
WriteUsage(operationId, commandName, "DryRunCompleted", document, details, stopwatch.ElapsedMilliseconds);
|
||||
RuntimeDiagnostics.Write("CommandCompleted", operationId: operationId, commandName: commandName, resultStatus: "DryRunCompleted", document: document, details: details);
|
||||
}
|
||||
catch (System.Exception exception)
|
||||
{
|
||||
RuntimeDiagnostics.Write("CommandFailed", "ERROR", operationId, commandName, exception.Message, exception, document);
|
||||
WriteUsage(operationId, commandName, exception.Message, document, null, stopwatch.ElapsedMilliseconds, exception);
|
||||
WriteEditor(document, $"\nP_Rec_Annot: безопасная диагностика завершилась ошибкой: {exception.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static PRecAnnotExecutionMode ParseMode(string value) =>
|
||||
value.Equals("Smoke", StringComparison.OrdinalIgnoreCase) ? PRecAnnotExecutionMode.Smoke :
|
||||
value.Equals("Apply", StringComparison.OrdinalIgnoreCase) ? PRecAnnotExecutionMode.Apply : PRecAnnotExecutionMode.DryRun;
|
||||
|
||||
private static bool DataLinksAvailable(bool hasPlantProject)
|
||||
{
|
||||
try { return hasPlantProject && Autodesk.ProcessPower.PlantInstance.PlantApplication.ProjectPart("Piping")?.DataLinksManager is not null; }
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
private static void WriteUsage(string operationId, string commandName, string status, Document? document, IReadOnlyDictionary<string, object?>? details, long duration, System.Exception? exception = null)
|
||||
{
|
||||
var eventDetails = new Dictionary<string, object?>
|
||||
{
|
||||
["ObjectsExamined"] = 0, ["AnnotationsCreated"] = 0, ["AnnotationsUpdated"] = 0,
|
||||
["AnnotationsDeleted"] = 0, ["AnnotationsSkipped"] = 0, ["WarningCount"] = 0,
|
||||
["ErrorCount"] = exception is null ? 0 : 1, ["DurationMilliseconds"] = duration
|
||||
};
|
||||
if (details is not null) foreach (var item in details) eventDetails[item.Key] = item.Value;
|
||||
RuntimeDiagnostics.Write("CommandUsageEvent", exception is null ? "INFO" : "ERROR", operationId, commandName, status, exception, document, eventDetails);
|
||||
}
|
||||
|
||||
private static void WriteEditor(Document? document, string message)
|
||||
{
|
||||
try { document?.Editor.WriteMessage(message); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
internal static class RibbonIntegration
|
||||
{
|
||||
private const string TabId = "PRecAnnot.Plant3D2026";
|
||||
private const string PanelId = "PRecAnnot.Plant3D2026.Annotations";
|
||||
private const string ButtonId = "PRecAnnot.Plant3D2026.PHP_REC_ANNOT";
|
||||
private static bool deferred;
|
||||
|
||||
public static void Register()
|
||||
{
|
||||
RuntimeDiagnostics.Write("RibbonInitializationStarted");
|
||||
try
|
||||
{
|
||||
if (ComponentManager.Ribbon is null)
|
||||
{
|
||||
if (!deferred) { ComponentManager.ItemInitialized += OnItemInitialized; deferred = true; }
|
||||
RuntimeDiagnostics.Write("RibbonInitializationCompleted", resultStatus: "Deferred");
|
||||
return;
|
||||
}
|
||||
RegisterNow();
|
||||
}
|
||||
catch (System.Exception exception) { RuntimeDiagnostics.Write("RibbonInitializationCompleted", "ERROR", exception: exception, resultStatus: "Failed"); }
|
||||
}
|
||||
|
||||
private static void OnItemInitialized(object? sender, EventArgs args)
|
||||
{
|
||||
if (ComponentManager.Ribbon is null) return;
|
||||
ComponentManager.ItemInitialized -= OnItemInitialized;
|
||||
deferred = false;
|
||||
Register();
|
||||
}
|
||||
|
||||
private static void RegisterNow()
|
||||
{
|
||||
var ribbon = ComponentManager.Ribbon!;
|
||||
var tab = ribbon.Tabs.FirstOrDefault(x => string.Equals(x.Id, TabId, StringComparison.OrdinalIgnoreCase));
|
||||
if (tab is null) { tab = new RibbonTab { Id = TabId, Title = "Промхимпроект" }; ribbon.Tabs.Add(tab); }
|
||||
var panel = tab.Panels.FirstOrDefault(x => string.Equals(x.Source?.Id, PanelId, StringComparison.OrdinalIgnoreCase));
|
||||
if (panel is not null) { RuntimeDiagnostics.Write("RibbonAlreadyExists", resultStatus: "AlreadyExists"); RuntimeDiagnostics.Write("RibbonInitializationCompleted", resultStatus: "AlreadyExists"); return; }
|
||||
var source = new RibbonPanelSource { Id = PanelId, Title = "Аннотации" };
|
||||
source.Items.Add(new RibbonButton { Id = ButtonId, Text = "Аннотировать", ShowText = true, CommandParameter = "PHP_REC_ANNOT " });
|
||||
tab.Panels.Add(new RibbonPanel { Source = source });
|
||||
RuntimeDiagnostics.Write("RibbonInitializationCompleted", resultStatus: "Created");
|
||||
}
|
||||
|
||||
public static void Terminate()
|
||||
{
|
||||
if (!deferred) return;
|
||||
ComponentManager.ItemInitialized -= OnItemInitialized;
|
||||
deferred = false;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
namespace PRecAnnot.Plant3D2026;
|
||||
|
||||
/// <summary>
|
||||
/// Autodesk-facing composition-root lifecycle. The concrete IExtensionApplication
|
||||
/// implementation is enabled only after the Plant 3D 2026 SDK reference contract
|
||||
/// is verified on the target installation.
|
||||
/// </summary>
|
||||
public sealed class EntryPointLifecycle : IDisposable
|
||||
{
|
||||
private int _initialized;
|
||||
public bool IsInitialized => Volatile.Read(ref _initialized) == 1;
|
||||
public bool Initialize() => Interlocked.Exchange(ref _initialized, 1) == 0;
|
||||
public void Terminate() => Interlocked.Exchange(ref _initialized, 0);
|
||||
public void Dispose() => Terminate();
|
||||
}
|
||||
@@ -1,15 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup><TargetFramework>net8.0-windows</TargetFramework><PlatformTarget>x64</PlatformTarget><UseWindowsForms>true</UseWindowsForms></PropertyGroup>
|
||||
<PropertyGroup><TargetFramework>net8.0-windows</TargetFramework><PlatformTarget>x64</PlatformTarget><UseWindowsForms>true</UseWindowsForms><EnableDefaultApplicationDefinition>false</EnableDefaultApplicationDefinition></PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PRecAnnot.Abstractions\PRecAnnot.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\PRecAnnot.Application\PRecAnnot.Application.csproj" />
|
||||
<ProjectReference Include="..\PRecAnnot.Core\PRecAnnot.Core.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="Exists('$(Plant3D2026InstallDir)')">
|
||||
<Reference Include="AcMgd"><HintPath>$(Plant3D2026InstallDir)\AcMgd.dll</HintPath><Private>false</Private></Reference>
|
||||
<Reference Include="AcDbMgd"><HintPath>$(Plant3D2026InstallDir)\AcDbMgd.dll</HintPath><Private>false</Private></Reference>
|
||||
<Reference Include="AcCoreMgd"><HintPath>$(Plant3D2026InstallDir)\AcCoreMgd.dll</HintPath><Private>false</Private></Reference>
|
||||
<Reference Include="PnPProjectManagerMgd"><HintPath>$(Plant3D2026InstallDir)\PLNT3D\PnPProjectManagerMgd.dll</HintPath><Private>false</Private></Reference>
|
||||
<Reference Include="PnPDataLinks"><HintPath>$(Plant3D2026InstallDir)\PLNT3D\PnPDataLinks.dll</HintPath><Private>false</Private></Reference>
|
||||
<ItemGroup Condition="Exists('$(AutoCAD2026ManagedDir)') and Exists('$(Plant3D2026ManagedDir)')">
|
||||
<Reference Include="AcMgd"><HintPath>$(AutoCAD2026ManagedDir)\AcMgd.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
<Reference Include="AcDbMgd"><HintPath>$(AutoCAD2026ManagedDir)\AcDbMgd.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
<Reference Include="AcCoreMgd"><HintPath>$(AutoCAD2026ManagedDir)\AcCoreMgd.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
<Reference Include="AdWindows"><HintPath>$(AutoCAD2026ManagedDir)\AdWindows.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
<Reference Include="PnPDataLinks"><HintPath>$(AutoCAD2026ManagedDir)\PnPDataLinks.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
<Reference Include="PnPDataObjects"><HintPath>$(AutoCAD2026ManagedDir)\PnPDataObjects.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
<Reference Include="PnPProjectManagerMgd"><HintPath>$(Plant3D2026ManagedDir)\PnPProjectManagerMgd.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
<Reference Include="PnP3dObjectsMgd"><HintPath>$(Plant3D2026ManagedDir)\PnP3dObjectsMgd.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
<Reference Include="PnP3dOrthoProjectPart"><HintPath>$(Plant3D2026ManagedDir)\PnP3dOrthoProjectPart.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
<Reference Include="PnPDwg2dDef"><HintPath>$(Plant3D2026ManagedDir)\PnPDwg2dDef.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -4,7 +4,7 @@ public sealed record Plant3D2026Availability(bool IsAvailable, string InstallDir
|
||||
{
|
||||
public static Plant3D2026Availability Detect(string installDirectory)
|
||||
{
|
||||
var expected = new[] { "AcMgd.dll", "AcDbMgd.dll", "AcCoreMgd.dll", Path.Combine("PLNT3D", "PnPProjectManagerMgd.dll"), Path.Combine("PLNT3D", "PnPDataLinks.dll") };
|
||||
var expected = new[] { "AcMgd.dll", "AcDbMgd.dll", "AcCoreMgd.dll", "PnPDataLinks.dll", Path.Combine("PLNT3D", "PnPProjectManagerMgd.dll"), Path.Combine("PLNT3D", "PnP3dObjectsMgd.dll") };
|
||||
var missing = expected.Where(x => !File.Exists(Path.Combine(installDirectory, x))).ToArray();
|
||||
return new(missing.Length == 0, installDirectory, missing);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using Autodesk.AutoCAD.ApplicationServices;
|
||||
using Autodesk.ProcessPower.PlantInstance;
|
||||
|
||||
namespace PRecAnnot.Plant3D2026;
|
||||
|
||||
internal static class RuntimeDiagnostics
|
||||
{
|
||||
private static readonly object Gate = new();
|
||||
private static readonly string LogDirectory = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"Promhimproekt", "PRecAnnot2026", "Logs");
|
||||
|
||||
public static string RuntimeMode => Environment.GetEnvironmentVariable("P_REC_ANNOT_RUNTIME_MODE") ?? "Normal";
|
||||
|
||||
public static void Write(
|
||||
string eventType,
|
||||
string level = "INFO",
|
||||
string? operationId = null,
|
||||
string? commandName = null,
|
||||
string? resultStatus = null,
|
||||
System.Exception? exception = null,
|
||||
Document? document = null,
|
||||
IReadOnlyDictionary<string, object?>? details = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var payload = new Dictionary<string, object?>(StringComparer.Ordinal)
|
||||
{
|
||||
["Timestamp"] = DateTimeOffset.UtcNow,
|
||||
["Level"] = level,
|
||||
["OperationId"] = operationId,
|
||||
["EventType"] = eventType,
|
||||
["AssemblyVersion"] = typeof(EntryPoint).Assembly.GetName().Version?.ToString(),
|
||||
["AutoCADVersion"] = typeof(Document).Assembly.GetName().Version?.ToString(),
|
||||
["PlantVersion"] = typeof(PlantApplication).Assembly.GetName().Version?.ToString(),
|
||||
["ProcessId"] = Environment.ProcessId,
|
||||
["ThreadId"] = Environment.CurrentManagedThreadId,
|
||||
["ApartmentState"] = Thread.CurrentThread.GetApartmentState().ToString(),
|
||||
["ActiveDocumentName"] = SafeDocumentName(document),
|
||||
["CommandName"] = commandName,
|
||||
["ResultStatus"] = resultStatus,
|
||||
["ExceptionType"] = exception?.GetType().FullName,
|
||||
["ExceptionMessage"] = exception?.Message,
|
||||
["StackTrace"] = level.Equals("ERROR", StringComparison.OrdinalIgnoreCase) ? exception?.StackTrace : null,
|
||||
["RuntimeMode"] = RuntimeMode
|
||||
};
|
||||
|
||||
if (details is not null)
|
||||
foreach (var item in details) payload[item.Key] = item.Value;
|
||||
|
||||
var line = JsonSerializer.Serialize(payload) + Environment.NewLine;
|
||||
lock (Gate)
|
||||
{
|
||||
Directory.CreateDirectory(LogDirectory);
|
||||
File.AppendAllText(Path.Combine(LogDirectory, "runtime.jsonl"), line);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Runtime diagnostics must never prevent AutoCAD command execution.
|
||||
}
|
||||
}
|
||||
|
||||
private static string? SafeDocumentName(Document? document)
|
||||
{
|
||||
try { return document is null ? null : Path.GetFileName(document.Name); }
|
||||
catch { return null; }
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SmokeWriteBlockedException() : InvalidOperationException("SMOKE_WRITE_BLOCKED");
|
||||
|
||||
internal static class SmokeGuard
|
||||
{
|
||||
public static bool IsSmoke => string.Equals(RuntimeDiagnostics.RuntimeMode, "Smoke", StringComparison.OrdinalIgnoreCase);
|
||||
public static void EnsureReadOnly() { if (IsSmoke) throw new SmokeWriteBlockedException(); }
|
||||
}
|
||||
@@ -7,6 +7,16 @@ namespace PRecAnnot.Core.Tests;
|
||||
|
||||
public sealed class FoundationTests
|
||||
{
|
||||
private static string ProjectRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
var directory = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "PRecAnnot2026.sln"))) directory = directory.Parent;
|
||||
return directory?.FullName ?? throw new DirectoryNotFoundException("PRecAnnot2026.sln was not found.");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact] public void Plan_IsDeterministic()
|
||||
{
|
||||
var engine = new AnnotationRuleEngine();
|
||||
@@ -34,4 +44,103 @@ public sealed class FoundationTests
|
||||
var ribbon = new RibbonRegistration(); var registry = CommandRegistry.Foundation();
|
||||
Assert.True(ribbon.Register(registry)); Assert.False(ribbon.Register(registry)); Assert.Equal(3, ribbon.RegisteredCount);
|
||||
}
|
||||
|
||||
[Fact] public void PlantAdapter_LoadsAgainst2026Assemblies()
|
||||
{
|
||||
var adapter = Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "bin", "Release", "net8.0-windows", "PRecAnnot.Plant3D2026.dll");
|
||||
var assembly = System.Reflection.AssemblyName.GetAssemblyName(adapter);
|
||||
var project = File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "PRecAnnot.Plant3D2026.csproj"));
|
||||
Assert.NotNull(assembly);
|
||||
Assert.Contains("AcMgd", project);
|
||||
Assert.Contains("PnPProjectManagerMgd", project);
|
||||
}
|
||||
|
||||
[Fact] public void CommandClass_HasCommandMethodAttributes()
|
||||
{
|
||||
var source = File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "EntryPoint.cs"));
|
||||
Assert.Contains("[CommandMethod(\"PHP_REC_ANNOT\")]", source);
|
||||
Assert.Contains("[CommandMethod(\"PXP-AAV\")]", source);
|
||||
Assert.Contains("[CommandMethod(\"PXP-OA\")]", source);
|
||||
}
|
||||
|
||||
[Fact] public void PrimaryCommand_IsRegistered() => Assert.Contains("[CommandMethod(\"PHP_REC_ANNOT\")]", File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "EntryPoint.cs")));
|
||||
[Fact] public void LegacyAliases_AreRegistered() => Assert.True(File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "EntryPoint.cs")).Contains("PXP-AAV") && File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "EntryPoint.cs")).Contains("PXP-OA"));
|
||||
[Fact] public void RibbonCommand_ReferencesRegisteredCommand() => Assert.Contains("PHP_REC_ANNOT ", File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "EntryPoint.cs")));
|
||||
|
||||
[Fact] public void AutodeskReferences_AreNotCopyLocal()
|
||||
{
|
||||
var project = File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "PRecAnnot.Plant3D2026.csproj"));
|
||||
Assert.DoesNotContain("<Private>true</Private>", project);
|
||||
Assert.DoesNotContain("<CopyLocal>true</CopyLocal>", project);
|
||||
}
|
||||
|
||||
[Fact] public void Bundle_DoesNotContainAutodeskAssemblies()
|
||||
{
|
||||
var root = Path.Combine(ProjectRoot, "artifacts", "P_Rec_Annot.bundle");
|
||||
if (!Directory.Exists(root)) return;
|
||||
Assert.DoesNotContain(Directory.EnumerateFiles(root, "*.dll", SearchOption.AllDirectories), path => Path.GetFileName(path).StartsWith("PnP", StringComparison.OrdinalIgnoreCase) || new[] { "AcMgd.dll", "AcDbMgd.dll", "AcCoreMgd.dll", "AdWindows.dll" }.Contains(Path.GetFileName(path), StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
[Fact] public void Core_HasNoAutodeskReferences()
|
||||
{
|
||||
var references = typeof(AnnotationRequest).Assembly.GetReferencedAssemblies();
|
||||
Assert.DoesNotContain(references, x => x.Name?.StartsWith("Autodesk", StringComparison.OrdinalIgnoreCase) == true || x.Name?.StartsWith("Ac", StringComparison.OrdinalIgnoreCase) == true);
|
||||
}
|
||||
|
||||
[Fact] public void OrthoDetection_NoPlantProject_ReturnsNotOrtho() => Assert.Equal(OrthoDetectionStatus.NotOrtho, OrthoDetectionClassifier.Classify(false, false, false).Status);
|
||||
[Fact] public void OrthoDetection_NormalDwg_ReturnsNotOrtho() => Assert.Equal(OrthoDetectionStatus.NotOrtho, OrthoDetectionClassifier.Classify(true, true, false).Status);
|
||||
[Fact] public void OrthoDetection_ConfirmedOrtho_ReturnsConfirmed() => Assert.Equal(OrthoDetectionStatus.Confirmed, OrthoDetectionClassifier.Classify(true, true, true).Status);
|
||||
[Fact] public void OrthoDetection_AmbiguousMetadata_ReturnsAmbiguous() => Assert.Equal(OrthoDetectionStatus.Ambiguous, OrthoDetectionClassifier.Classify(true, true, false, true).Status);
|
||||
|
||||
[Fact] public void AnnotationPlan_InputOrderDoesNotChangeResult()
|
||||
{
|
||||
var viewport = new OrthoViewportInfo { Id = "vp", DisplayName = "A", SourceDrawingName = "test.dwg" };
|
||||
var objects = new[] { Snapshot("b", "Pipe", "L-2"), Snapshot("a", "Valve", "V-1") };
|
||||
var builder = new AnnotationPlanBuilder();
|
||||
var first = builder.Build(new AnnotationPlanningRequest { CommandName = "PHP_REC_ANNOT", Viewport = viewport, Objects = objects });
|
||||
var second = builder.Build(new AnnotationPlanningRequest { CommandName = "PHP_REC_ANNOT", Viewport = viewport, Objects = objects.Reverse().ToArray() });
|
||||
Assert.Equal(first.PlanId, second.PlanId);
|
||||
Assert.Equal(first.Summary, second.Summary);
|
||||
Assert.Equal(first.Operations.Select(x => x.SourceSnapshotId), second.Operations.Select(x => x.SourceSnapshotId));
|
||||
}
|
||||
|
||||
[Fact] public void AnnotationPlan_MissingLineNumber_IsSkipped()
|
||||
{
|
||||
var plan = Build(Snapshot("a", "Pipe", null, null));
|
||||
Assert.Equal(AnnotationOperationKind.Skip, Assert.Single(plan.Operations).Kind);
|
||||
}
|
||||
|
||||
[Fact] public void AnnotationPlan_UnsupportedObject_IsMarkedUnsupported()
|
||||
{
|
||||
var plan = Build(Snapshot("a", "", "L-1"));
|
||||
Assert.Equal(AnnotationOperationKind.Unsupported, Assert.Single(plan.Operations).Kind);
|
||||
}
|
||||
|
||||
[Fact] public void AnnotationPlan_ExistingAnnotation_BecomesUpdate()
|
||||
{
|
||||
var viewport = new OrthoViewportInfo { Id = "vp", DisplayName = "A", SourceDrawingName = "test.dwg" };
|
||||
var item = Snapshot("a", "Pipe", "L-1");
|
||||
var plan = new AnnotationPlanBuilder().Build(new AnnotationPlanningRequest { CommandName = "PHP_REC_ANNOT", Viewport = viewport, Objects = new[] { item }, Existing = new[] { new ExistingAnnotation("a", "old", new(0, 0)) } });
|
||||
Assert.Equal(AnnotationOperationKind.Update, Assert.Single(plan.Operations).Kind);
|
||||
}
|
||||
|
||||
[Fact] public void AnnotationPlan_DuplicateCandidate_BecomesConflict()
|
||||
{
|
||||
var plan = Build(Snapshot("a", "Pipe", "L-1"), Snapshot("a", "Pipe", "L-1"));
|
||||
Assert.Equal(AnnotationOperationKind.Conflict, plan.Operations.Last().Kind);
|
||||
}
|
||||
|
||||
[Fact] public void ApplyMode_IsBlocked() => Assert.Throws<InvalidOperationException>(() => new ExecutionModeWriteGuard(PRecAnnotExecutionMode.Apply).EnsureWriteAllowed("test"));
|
||||
|
||||
[Fact] public void DryRunPath_HasNoForWriteOperations()
|
||||
{
|
||||
var source = File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "PlantReadPath.cs"));
|
||||
Assert.DoesNotContain("ForWrite", source);
|
||||
Assert.DoesNotContain("AppendEntity", source);
|
||||
Assert.DoesNotContain("AcceptChanges", source);
|
||||
Assert.DoesNotContain("SetProperties", source);
|
||||
}
|
||||
|
||||
private static AnnotationPlan Build(params PlantObjectSnapshot[] objects) => new AnnotationPlanBuilder().Build(new AnnotationPlanningRequest { CommandName = "PHP_REC_ANNOT", Viewport = new OrthoViewportInfo { Id = "vp", DisplayName = "A", SourceDrawingName = "test.dwg" }, Objects = objects });
|
||||
private static PlantObjectSnapshot Snapshot(string id, string type, string? line, string? tag = "TAG") => new() { SnapshotId = id, ObjectClass = type, LineNumber = line, Tag = tag, Geometry = new GeometrySnapshot { PaperPosition = new Point2Dto(1, 2) } };
|
||||
}
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
param([ValidateSet('Debug','Release')][string]$Configuration='Release',[string]$Plant3D2026InstallDir)
|
||||
param([ValidateSet('Debug','Release')][string]$Configuration='Release',[Parameter(Mandatory=$true)][string]$AutoCAD2026ManagedDir,[Parameter(Mandatory=$true)][string]$Plant3D2026ManagedDir)
|
||||
$ErrorActionPreference='Stop'
|
||||
dotnet build (Join-Path $PSScriptRoot '..\PRecAnnot2026.sln') --configuration $Configuration /p:Plant3D2026InstallDir=$Plant3D2026InstallDir
|
||||
dotnet build (Join-Path $PSScriptRoot '..\PRecAnnot2026.sln') --configuration $Configuration /p:AutoCAD2026ManagedDir=$AutoCAD2026ManagedDir /p:Plant3D2026ManagedDir=$Plant3D2026ManagedDir
|
||||
if($LASTEXITCODE -ne 0){ throw "Build failed: $LASTEXITCODE" }
|
||||
$bundleRoot=Join-Path $PSScriptRoot '..\artifacts\P_Rec_Annot.bundle'
|
||||
$contents=Join-Path $bundleRoot 'Contents\Windows'
|
||||
New-Item -ItemType Directory -Force -Path $contents | Out-Null
|
||||
$output=Join-Path $PSScriptRoot "..\src\PRecAnnot.Plant3D2026\bin\$Configuration\net8.0-windows"
|
||||
Get-ChildItem $output -File -Filter '*.dll' | Copy-Item -Destination $contents -Force
|
||||
Copy-Item (Join-Path $PSScriptRoot '..\bundle\P_Rec_Annot.bundle\PackageContents.xml') $bundleRoot -Force
|
||||
Write-Host 'Bundle assembly build completed; deployment is intentionally not performed.'
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
param([string]$BundleRoot=(Join-Path $PSScriptRoot '..\artifacts\P_Rec_Annot.bundle'))
|
||||
$ErrorActionPreference='Stop'
|
||||
$package=Join-Path $PSScriptRoot '..\bundle\P_Rec_Annot.bundle\PackageContents.xml'
|
||||
$package=Join-Path $BundleRoot 'PackageContents.xml'
|
||||
[xml]$xml=Get-Content -LiteralPath $package
|
||||
$entry=$xml.ApplicationPackage.Components.ComponentEntry
|
||||
if(@($entry).Count -ne 1){throw 'Bundle must have exactly one entry assembly.'}
|
||||
if($entry.ModuleName -match '_netload|OrthoLoader|Settings.xml'){throw 'Legacy loading chain is forbidden.'}
|
||||
if($entry.ModuleName -notmatch 'PRecAnnot\.Plant3D2026\.dll'){throw 'Unexpected entry assembly.'}
|
||||
if(-not (Test-Path (Join-Path $BundleRoot 'Contents\Windows\PRecAnnot.Plant3D2026.dll'))){throw 'Entry assembly is missing.'}
|
||||
$forbidden='^(AcMgd|AcDbMgd|AcCoreMgd|AdWindows|PnP.*)\.dll$'
|
||||
$forbiddenFiles=Get-ChildItem $BundleRoot -Recurse -File | Where-Object { $_.Name -match $forbidden -or $_.Extension -in '.pdb','.cs' -or $_.FullName -match '\\(bin|obj)\\' }
|
||||
if($forbiddenFiles){throw "Bundle contains forbidden files: $($forbiddenFiles.FullName -join ', ')"}
|
||||
Write-Host 'Bundle manifest is structurally valid.'
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Launcher source architecture audit
|
||||
|
||||
Static, read-only audit of three independent nested Git repositories under `C:\dev\launcher\git` and the linked bootstrap repository `C:\dev\p_serv_starter`. No launcher executable, helper, build, installer, registry action, profile change, or AutoCAD process was run.
|
||||
|
||||
The three projects are all independent WinForms `WinExe` applications, not a three-project solution: `All_Serv_Install`, `All_Serv_InstallUpdater`, and `All_Serv_Load`. Each solution contains exactly one project and has Debug/Release AnyCPU `Build.0` enabled. There are no project references between them.
|
||||
|
||||
The current primary installer is `All_Serv_Install.exe` (v1.1.0.0). It presents a Form1 UI, reads server-side settings, copies selected corporate application directories to local targets, then starts a configured external executable. `All_Serv_InstallUpdater.exe` is a visible but short-lived self-updater: it updates the local installer with MD5 checks and starts `All_Serv_Install.exe`. `All_Serv_Load.exe` is an earlier separate WinForms loader (v1.0.0.0); source evidence does not establish that it is part of the current primary workflow.
|
||||
|
||||
No static source evidence shows a direct reference to `app_netload.lsp`, `OrthoLoader.dll`, or `P_Rec_Annot` in the updater. The installer configuration does establish `P_Rec_Annot` deployment: catalog `P_Rec_Annot`, install file `OrthoLoader.dll`, source DriveA, target `C:\AutoCAD User Support\Applications`.
|
||||
|
||||
`P_Serv_Starter.exe` is the confirmed external bootstrap selected by the Plant 3D installer pattern. Its source creates `_app_netload.lsp` from the installer `Settings.xml`, copies `launcher_starter.lsp` to the local Applications directory, and launches the configured local `start_plant_3d.bat`. The LISP renames the generated file to `app_netload.lsp`, adds the local Applications directory to SupportPath, and loads it. The source comment attributes LISP auto-start to `acaddoc.lsp`, but that file and the expected local installation were absent on this workstation during the audit; automatic startup remains `UNKNOWN`.
|
||||
@@ -0,0 +1,16 @@
|
||||
# Application synchronization analysis
|
||||
|
||||
`All_Serv_Install` is a synchronizing installer, not merely a launcher. It reads server `Settings.xml`, selects products/plugins, and recursively copies application directories from configured network roots to local targets. Its copy path includes directory deletion and file deletion (`Form1.cs:396-645`), so it was not executed for this audit.
|
||||
|
||||
For Plant 3D_2019, the configuration specifies DriveA and DriveQ network sources and local target `C:\AutoCAD User Support\Applications`. The catalog `P_Rec_Annot` is selected with install entry `OrthoLoader.dll`; `download_list_plant.xml` maps the P_Rec_Annot plugin ID to a real area. This proves the intended local deployment source and target, but not the state of a particular workstation installation.
|
||||
|
||||
`All_Serv_InstallUpdater` is a separate local updater. Its `Form1_Load` computes MD5 checksums, compares a server control-sums manifest, deletes stale local files, copies changed files, writes a local control-sums file and starts `All_Serv_Install.exe`. It references `N:\BIM\save\All_Serv_Install` and `N:\BIM\data_write\All_Serv_Install`; it can write both local and server test/control files. It was not executed.
|
||||
|
||||
| Component | Source | Target | Version check | Destructive operations | Role | Confidence |
|
||||
|---|---|---|---|---|---|---|
|
||||
| All_Serv_Install | configured network DriveA/DriveQ | configured local target | configuration-driven selection; no content hash established | recursive copy, delete files/directories | application synchronizer | CONFIRMED |
|
||||
| All_Serv_InstallUpdater | server installer directories | updater own local directory | MD5 control sums | deletes stale local files; writes control sums | installer updater | CONFIRMED |
|
||||
| P_Serv_Starter | local installed executable + installer Settings.xml | local Applications | none | overwrites `_app_netload.lsp` and `launcher_starter.lsp` | per-launch bootstrap | CONFIRMED |
|
||||
|
||||
Offline behavior is `UNKNOWN`: both installer and updater require configured paths at the point they run, but source evidence does not show an explicit offline fallback.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# Architecture summary
|
||||
|
||||
## Project architecture
|
||||
|
||||
```text
|
||||
All_Serv_InstallUpdater.exe (independent repository / updater)
|
||||
-> starts local All_Serv_Install.exe
|
||||
|
||||
All_Serv_Install.exe (independent repository / visible installer and synchronizer)
|
||||
-> starts configured P_Serv_Starter.exe
|
||||
|
||||
P_Serv_Starter.exe (separate repository / Plant bootstrap)
|
||||
-> generates local _app_netload.lsp
|
||||
-> copies launcher_starter.lsp
|
||||
-> starts configured local start_plant_3d.bat
|
||||
|
||||
All_Serv_Load.exe (independent repository)
|
||||
-> separate historical/alternate loader; no source dependency edge to the above
|
||||
```
|
||||
|
||||
There are no ProjectReference edges between the three projects beneath `C:\dev\launcher\git`; each is its own one-project solution. `P_Serv_Starter` is a fourth independent repository selected by configuration, not a project reference. This explains why a user can experience one product despite multiple executables: the updater can hand off to the visible installer, which synchronizes selected applications and delegates Plant startup to a hidden bootstrap. Actual deployed shortcut/entrypoint evidence was not present, so the updater's use in a specific installation remains `INFERRED`.
|
||||
|
||||
## Roles and status
|
||||
|
||||
| Project | Output | Role | Status | Evidence |
|
||||
|---|---|---|---|---|
|
||||
| All_Serv_Install | WinExe | visible catalog UI, synchronizer and configured process launcher | ACTIVE_PRIMARY | Form1 startup and copy/Process.Start code |
|
||||
| All_Serv_InstallUpdater | WinExe | installer self-updater then handoff | ACTIVE_UPDATER | Form1_Load MD5/copy/start code |
|
||||
| All_Serv_Load | WinExe | independent alternate/historical loader | UNKNOWN | independent Form1 and 2019 batch script; no current deployment proof |
|
||||
| P_Serv_Starter | Exe | Plant session bootstrap | ACTIVE_BOOTSTRAP | selected in installer XML; writes loader and starts batch |
|
||||
|
||||
## Runtime chain
|
||||
|
||||
The confirmed chain ends at execution of the local Plant start batch. The gap between that batch and `launcher_starter.lsp` automatic loading is `UNKNOWN`: no local batch, `acaddoc.lsp`, or Applications files were found. Once `launcher_starter.lsp` runs, all following load steps are source-confirmed.
|
||||
|
||||
## Answers to the requested questions
|
||||
|
||||
1. The three launcher projects are independent WinForms executables: installer, updater and alternate loader.
|
||||
2. `All_Serv_Install` creates the confirmed visible catalog UI.
|
||||
3. Three EXE artifacts are declared by the launcher repositories; P_Serv_Starter adds one external bootstrap EXE.
|
||||
4. No launcher project is a class library; `launcher_starter.lsp` is a copied runtime content artifact.
|
||||
5. Updater/helper/bootstrap behavior is confirmed in source; actual installed-process count is unknown.
|
||||
6. Plant launch is delegated by `All_Serv_Install` to configured `P_Serv_Starter.exe`; the bootstrap starts a local batch. A current Plant executable/profile/argument string is not proven.
|
||||
7. `app_netload.lsp` and `OrthoLoader.dll` are connected through P_Serv_Starter generated LISP and installer plugin metadata.
|
||||
8. The installer synchronizes corporate application folders to a local Applications cache; it does not load P_Rec_Annot directly.
|
||||
9. No source proof warrants modifying LAUNCHER for the new plugin yet. First validate one local entry assembly through the established catalog/bootstrap contract in a non-production pilot.
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# AutoCAD/Plant launch chain
|
||||
|
||||
## Confirmed static chain
|
||||
|
||||
```text
|
||||
User
|
||||
-> All_Serv_Install.exe Form1
|
||||
-> copy selected Plant 3D application folders to local Applications target
|
||||
-> Process.Start(P_Serv_Starter.exe, five configured arguments)
|
||||
-> P_Serv_Starter creates local _app_netload.lsp and copies launcher_starter.lsp
|
||||
-> P_Serv_Starter starts C:\AutoCAD User Support\p3d-support\start_plant_3d.bat
|
||||
-> [UNKNOWN: installed batch contents and auto-load registration]
|
||||
-> Plant 3D startup
|
||||
-> launcher_starter.lsp (when loaded)
|
||||
-> local app_netload.lsp
|
||||
-> _netload local Applications\P_Rec_Annot\OrthoLoader.dll
|
||||
-> OrthoLoader Settings.xml -> sequential ._netload configured modules
|
||||
```
|
||||
|
||||
The final two steps are confirmed by the prior P_Rec_Annot legacy audit. The activation of `launcher_starter.lsp` is not directly implemented in `P_Serv_Starter`: `Program.cs:181` comments that `acaddoc.lsp` renames the generated file, while the copied LISP itself performs the rename and `load`. No matching installed `acaddoc.lsp`, batch file, or Applications directory existed on this workstation at audit time. Therefore automatic execution is `UNKNOWN`, not asserted.
|
||||
|
||||
## Source evidence
|
||||
|
||||
| Claim | Evidence | Confidence |
|
||||
|---|---|---|
|
||||
| User-visible installer starts configured object | `All_Serv_Install/Form1.cs:195-230` | CONFIRMED |
|
||||
| Plant pattern object is `P_Serv_Starter.exe` | `All_Serv_Install/Settings.xml`, Plant 3D pattern | CONFIRMED |
|
||||
| Five arguments include product, Applications target, settings, project and working directory | `Settings.xml` Plant pattern and `P_Serv_Starter/Program.cs:12-23` | CONFIRMED |
|
||||
| Bootstrap generates `_app_netload.lsp` | `P_Serv_Starter/Program.cs:58-182` | CONFIRMED |
|
||||
| Generated DLL modules use AutoLISP `_netload` | `P_Serv_Starter/Program.cs:146-151` | CONFIRMED |
|
||||
| Bootstrap copies LISP and starts local batch file | `P_Serv_Starter/Program.cs:202-216,49-54` | CONFIRMED |
|
||||
| Generated LISP is renamed, SupportPath updated and loaded | `P_Serv_Starter/launcher_starter.lsp:74-96` | CONFIRMED |
|
||||
| Actual installed batch launches a specific Plant version | no installed batch was present | UNKNOWN |
|
||||
|
||||
## Historical source-only alternative
|
||||
|
||||
`All_Serv_Load/Addins/start_plant_3d.bat` contains a direct AutoCAD 2019 command using `/product PLNT3D /language "ru-RU"`, preceded by PlantCleaner and PnPPersistentCache cleanup. It is evidence only of that project's historical script; it is not evidence that current users start Plant 3D 2026 this way. No `/ld`, profile, LISP or script argument is present in that script.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
Artifact,SourceProject,Type,AssemblyName,AssemblyVersion,TargetFramework,Architecture,ReferencedBy,VisibleToUser,RequiredAtRuntime,LikelyRole,Confidence
|
||||
All_Serv_Install.exe,All_Serv_Install,WinExe,All_Serv_Install,1.1.0.0,.NETFramework v4.7,AnyCPU,None,Yes,Yes,PRIMARY_INSTALLER,CONFIRMED
|
||||
All_Serv_InstallUpdater.exe,All_Serv_InstallUpdater,WinExe,All_Serv_InstallUpdater,3.0.0.0,.NETFramework v4.7,AnyCPU,None,Briefly,Only when updater is launched,UPDATER_HELPER,CONFIRMED
|
||||
All_Serv_Load.exe,All_Serv_Load,WinExe,All_Serv_Load,1.0.0.0,.NETFramework v4.8,AnyCPU,None,Possible,Unknown,SEPARATE_LEGACY_LOADER,INFERRED
|
||||
P_Serv_Starter.exe,P_Serv_Starter,Exe,P_Serv_Starter,3.0.1.0,.NETFramework v4.7,AnyCPU,Configured external process from All_Serv_Install,No,Yes for Plant launch bootstrap,BOOTSTRAP,CONFIRMED
|
||||
launcher_starter.lsp,P_Serv_Starter,Content,LISP script,N/A,N/A,N/A,Copied by P_Serv_Starter,No,Yes for generated loader activation,LISP_BOOTSTRAP,CONFIRMED
|
||||
|
||||
|
@@ -0,0 +1,7 @@
|
||||
Solution,Configuration,Platform,Project,BuildEnabled,Notes
|
||||
All_Serv_Install.sln,Debug,Any CPU,All_Serv_Install,yes,Only project in solution
|
||||
All_Serv_Install.sln,Release,Any CPU,All_Serv_Install,yes,Only project in solution
|
||||
All_Serv_InstallUpdater.sln,Debug,Any CPU,All_Serv_InstallUpdater,yes,Only project in solution
|
||||
All_Serv_InstallUpdater.sln,Release,Any CPU,All_Serv_InstallUpdater,yes,Only project in solution
|
||||
All_Serv_Load.sln,Debug,Any CPU,All_Serv_Load,yes,Only project in solution
|
||||
All_Serv_Load.sln,Release,Any CPU,All_Serv_Load,yes,Only project in solution
|
||||
|
@@ -0,0 +1,10 @@
|
||||
Setting,OwnerProject,Source,DefaultValue,RuntimeValueSource,Purpose,Sensitive,Required,Fallback,UsedBy,Confidence
|
||||
InstallerSettings,All_Serv_Install,N:\BIM\save\All_Serv_Install\Settings.xml,None,hard-coded source path,product/plugin catalog and process pattern,No,Yes,None,Form1,CONFIRMED
|
||||
PlantApplicationTarget,All_Serv_Install,Settings.xml Plant pattern,C:\AutoCAD User Support\Applications,XML target,local corporate applications directory,No,Yes,None,copy and P_Serv_Starter arguments,CONFIRMED
|
||||
PlantBootstrapExecutable,All_Serv_Install,Settings.xml Plant pattern,C:\CAD User Support\All_Serv_Install\P_Serv_Starter.exe,XML object,external bootstrap process,No,Yes,None,Form1 Process.Start,CONFIRMED
|
||||
PluginCatalog,All_Serv_Install,Settings.xml plugin entry,P_Rec_Annot,XML plugin metadata,subdirectory to deploy,No,Optional by selection,skip if unselected,copy and generated loader,CONFIRMED
|
||||
PluginInstall,All_Serv_Install,Settings.xml plugin entry,OrthoLoader.dll,XML plugin metadata,first P_Rec_Annot assembly to load,No,Optional by selection,skip if catalog unavailable,P_Serv_Starter generated LISP,CONFIRMED
|
||||
GeneratedLoader,P_Serv_Starter,arguments plus installer Settings.xml,_app_netload.lsp,computed at runtime,per-session module loading commands,Contains project path in first comment,Yes,abort on error,generated LISP,CONFIRMED
|
||||
PlantStartScript,P_Serv_Starter,Program.cs,C:\AutoCAD User Support\p3d-support\start_plant_3d.bat,hard-coded source constant,launch Plant process,No,Yes,none,Process.Start,CONFIRMED
|
||||
UpdaterServer,All_Serv_InstallUpdater,Form1.cs,N:\BIM\data_write\All_Serv_Install,hard-coded source constant,hash/copy source,No,Yes,none,Form1_Load,CONFIRMED
|
||||
|
||||
|
@@ -0,0 +1,7 @@
|
||||
Artifact,ExpectedInstalledLocation,SourceProject,SourceAssemblyVersion,InstalledBinaryFound,InstalledMetadataMatch,MatchStatus,Notes
|
||||
All_Serv_Install.exe,C:\CAD User Support\All_Serv_Install\All_Serv_Install.exe,All_Serv_Install,1.1.0.0,False,N/A,NO_DEPLOYED_BINARY_FOUND,Expected location came from Settings.xml object directory
|
||||
P_Serv_Starter.exe,C:\CAD User Support\All_Serv_Install\P_Serv_Starter.exe,P_Serv_Starter,3.0.1.0,False,N/A,NO_DEPLOYED_BINARY_FOUND,Expected location came from Settings.xml object
|
||||
All_Serv_InstallUpdater.exe,UNKNOWN,All_Serv_InstallUpdater,3.0.0.0,False,N/A,NO_DEPLOYED_BINARY_FOUND,No installed location in inspected source
|
||||
All_Serv_Load.exe,UNKNOWN,All_Serv_Load,1.0.0.0,False,N/A,NO_DEPLOYED_BINARY_FOUND,No installed location in inspected source
|
||||
start_plant_3d.bat,C:\AutoCAD User Support\p3d-support\start_plant_3d.bat,P_Serv_Starter dependency,N/A,False,N/A,NO_DEPLOYED_FILE_FOUND,Not present at expected path during audit
|
||||
|
||||
|
@@ -0,0 +1,4 @@
|
||||
EntryPoint,Project,File,Lines,ApplicationType,CommandLineArguments,CreatesVisibleWindow,CanRunHeadless,StartsOtherProcesses,SingleInstanceMechanism,ExitBehavior,Confidence
|
||||
Program.Main,All_Serv_Install,All_Serv_Install/Program.cs,13-18,WinForms,none parsed,yes Form1,no,yes configured object,none,Application exits after launching configured object,CONFIRMED
|
||||
Program.Main,All_Serv_InstallUpdater,All_Serv_InstallUpdater/Program.cs,13-18,WinForms,none parsed,yes Form1,no,yes All_Serv_Install.exe,none,exits after updater starts installer,CONFIRMED
|
||||
Program.Main,All_Serv_Load,All_Serv_Load/All_Serv_Load/Program.cs,12-17,WinForms,none parsed,yes Form1,no,yes configured object,none,Application exits after launching configured object,CONFIRMED
|
||||
|
@@ -0,0 +1,11 @@
|
||||
Generated: 2026-07-31
|
||||
Method: static read-only source and metadata inspection; no build or executable launch.
|
||||
|
||||
Launcher sources:
|
||||
- C:\dev\launcher\git\All_Serv_Install @ 29d163322a2dc01983e15bc26f62f787fb402a04 (main; clean)
|
||||
- C:\dev\launcher\git\All_Serv_InstallUpdater @ 1e6f2e78a1ca0005ee704dbeb464e73a478c3390 (main; clean)
|
||||
- C:\dev\launcher\git\All_Serv_Load @ bd38b87f903b02533dbb659d9776cd32dd9e2a3e (main; clean)
|
||||
- C:\dev\p_serv_starter @ 8219520aad1a5ccc2cd96611f2a71fef61cbf9b1 (main; clean)
|
||||
|
||||
Local deployment probes were metadata-only. Expected launcher/bootstrap files and the relevant AutoCAD User Support scripts were absent at the configured locations at audit time.
|
||||
No source, binaries, user configuration content, registry export, DWG, DCF, Project.xml or network files are included in this audit.
|
||||
@@ -0,0 +1,24 @@
|
||||
# LAUNCHER to P_Rec_Annot
|
||||
|
||||
## Proven loading contract
|
||||
|
||||
| Stage | Evidence | Finding | Confidence |
|
||||
|---|---|---|---|
|
||||
| Deployment selection | `All_Serv_Install/Settings.xml`; `download_lists/download_list_plant.xml` | `P_Rec_Annot` is an installer plugin catalog; its configured install file is `OrthoLoader.dll`. | CONFIRMED |
|
||||
| Local target | Plant configuration target | P_Rec_Annot is intended to be copied under `C:\AutoCAD User Support\Applications\P_Rec_Annot`. | CONFIRMED |
|
||||
| Bootstrap selection | installer Plant pattern object/arguments | `All_Serv_Install` invokes `P_Serv_Starter.exe` after install selection. | CONFIRMED |
|
||||
| Generated first load | `P_Serv_Starter/Program.cs:140-174` | For each installed DLL catalog, generated LISP emits `(command "_netload" attribute)`. Therefore `OrthoLoader.dll` is loaded from the local Applications path when its catalog exists. | CONFIRMED |
|
||||
| Generated LISP activation | `launcher_starter.lsp:74-96` | It renames `_app_netload.lsp`, adds Applications to SupportPath and `(load ...)`s `app_netload.lsp`. | CONFIRMED |
|
||||
| Automatic LISP invocation | source comment in `Program.cs:181`; no installed startup file | `acaddoc.lsp` is named by a comment, but its installed content and registration were not available. | UNKNOWN |
|
||||
| Secondary module load | legacy audit, `OrthoLoader.Ribbon.Initialize` / `OrthoLoader.Initialize` | OrthoLoader reads adjacent Settings.xml and sequentially calls `._netload` for 20 configured modules. | CONFIRMED |
|
||||
|
||||
## P_Rec_Annot-specific implication
|
||||
|
||||
The historic contract is not a direct network `NETLOAD`. It is a local synchronized application directory followed by a generated, per-session AutoLISP loader. A generated LISP includes the Plant project path in its first comment and conditionally emits loads only for existing local plugin catalogs. New code must not reproduce that behavior before deciding whether it is compatible, necessary and safe for Plant 3D 2026.
|
||||
|
||||
The prior legacy audit established that `OrthoLoader.dll` is the initial assembly for P_Rec_Annot. This launcher audit establishes how the installer/bootstrap is intended to make that assembly loadable. Neither audit proves the deployed local files existed or auto-loaded on this workstation.
|
||||
|
||||
## Integration recommendation
|
||||
|
||||
Use one versioned, locally synchronized entry artifact only after a compatibility decision. The minimal pilot should retain the existing installer catalog/target contract, make the bootstrap load exactly one new entry assembly, and use an explicitly reviewed startup mechanism. Do not rely on an unproven AppLoader bundle or add new global `TRUSTEDPATHS`/profile mutations. The generated loader and any project-path comment are legacy behavior, not requirements for the new P_Rec_Annot until runtime validation establishes a need.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
ParentProject,ParentFile,ParentMethod,Executable,Arguments,WorkingDirectory,UseShellExecute,WaitForExit,Conditions,ErrorHandling,Confidence
|
||||
All_Serv_Install,All_Serv_Install/Form1.cs,button launch handler around 195-230,configured Pattern.Object,Predictable quoted Pattern.Attrib values including project path,not set,default,not set,after successful plugin copy,none visible around Process.Start,CONFIRMED
|
||||
All_Serv_InstallUpdater,All_Serv_InstallUpdater/Form1.cs,Form1_Load 28-48,local All_Serv_Install.exe,none,not set,default,not set,after successful MD5 update,closes on update failure,CONFIRMED
|
||||
All_Serv_Load,All_Serv_Load/Form1.cs,launch handler around 180-205,configured Pattern.Object,configured attributes plus selected project path,not set,default,not set,after successful plugin copy,none visible around Process.Start,CONFIRMED
|
||||
P_Serv_Starter,P_Serv_Starter/Program.cs,Main 15-54,C:\AutoCAD User Support\p3d-support\start_plant_3d.bat,none,changed to configured project directory before start,default,not set,after local loader generation and LISP copy,shows message and returns for loader/copy errors,CONFIRMED
|
||||
start_plant_3d.bat,All_Serv_Load/Addins/start_plant_3d.bat,script,C:\Program Files\Autodesk\AutoCAD 2019\acad.exe,/product PLNT3D /language ru-RU,not set,START default,no,after PlantCleaner,script has no error checks,CONFIRMED
|
||||
|
@@ -0,0 +1,15 @@
|
||||
# Project dependency graph
|
||||
|
||||
```text
|
||||
All_Serv_InstallUpdater.exe
|
||||
-> local All_Serv_Install.exe (Process.Start)
|
||||
|
||||
All_Serv_Install.exe
|
||||
-> configured external object: P_Serv_Starter.exe for Plant 3D 2019
|
||||
-> copies selected application catalogs from configured network sources
|
||||
|
||||
All_Serv_Load.exe
|
||||
-> configured external object (separate legacy workflow)
|
||||
```
|
||||
|
||||
No `ProjectReference` exists in any of the three projects. Each solution has a single buildable project. `P_Serv_Starter.exe` is referenced only as a configured external object, not supplied by any audited launcher project.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Repositories
|
||||
|
||||
| Repository | HEAD | Branch | Dirty | Remote | Submodules | Confidence |
|
||||
|---|---|---|---|---|---|---|
|
||||
| All_Serv_Install | `29d163322a2dc01983e15bc26f62f787fb402a04` | main | no | GitHub `pxpgitorg/All_Serv_Install` | none | CONFIRMED |
|
||||
| All_Serv_InstallUpdater | `1e6f2e78a1ca0005ee704dbeb464e73a478c3390` | main | no | GitHub `pxpgitorg/All_Serv_InstallUpdater` | none | CONFIRMED |
|
||||
| All_Serv_Load | `bd38b87f903b02533dbb659d9776cd32dd9e2a3e` | main | no | GitHub `pxpgitorg/All_Serv_Load` | none | CONFIRMED |
|
||||
| P_Serv_Starter | `8219520aad1a5ccc2cd96611f2a71fef61cbf9b1` | main | no | GitHub `pxpgitorg/P_Serv_Starter` | none | CONFIRMED |
|
||||
|
||||
The launcher parent directory is not a Git repository; it contains three nested repositories. `P_Serv_Starter` is a fourth, separately supplied repository outside that parent and is referenced by installer configuration. No launcher or bootstrap source was modified.
|
||||
@@ -0,0 +1,5 @@
|
||||
Project,ProjectPath,ProjectGuid,ProjectType,Sdk,OutputType,TargetFramework,PlatformTarget,AssemblyName,RootNamespace,StartupObject,EntryPoint,ApplicationIcon,ApplicationManifest,ProjectReferences,PackageReferences,DirectDllReferences,BuildOutput,PublishOutput,IsStartupProject,HasMainMethod,HasAppXaml,HasFormsEntryPoint,HasPostBuildActions,LikelyRole,Status
|
||||
All_Serv_Install,All_Serv_Install/All_Serv_Install.csproj,{FE649634-187B-410E-9A53-A8C41134F53D},C# classic MSBuild,,WinExe,v4.7,AnyCPU,All_Serv_Install,All_Serv_Install,,Program.Main,,,none,none,System assemblies,bin/Debug or bin/Release,none,only solution project,yes,no,yes,no,primary corporate application installer/synchronizer,ACTIVE_PRIMARY
|
||||
All_Serv_InstallUpdater,All_Serv_InstallUpdater/All_Serv_InstallUpdater.csproj,{6E075C69-20B5-4613-8BE1-5C41A615C70F},C# classic MSBuild,,WinExe,v4.7,AnyCPU,All_Serv_InstallUpdater,All_Serv_InstallUpdater,,Program.Main,,,none,none,System assemblies,bin/Debug or bin/Release,none,only solution project,yes,no,yes,no,self-updater for All_Serv_Install,ACTIVE_UPDATER
|
||||
All_Serv_Load,All_Serv_Load/All_Serv_Load/All_Serv_Load.csproj,{CCEF88B4-9157-4F50-AA7F-E0A6F3A665E3},C# classic MSBuild,,WinExe,v4.8,AnyCPU,All_Serv_Load,All_Serv_Load,,Program.Main,,,none,none,System assemblies,bin/Debug or bin/Release,none,only solution project,yes,no,yes,no,earlier standalone loader/install UI,UNKNOWN
|
||||
P_Serv_Starter,P_Serv_Starter/P_Serv_Starter.csproj,{DBA449EE-5DEC-454B-9EF1-E71BF949D880},C# classic MSBuild,,Exe,v4.7,AnyCPU,P_Serv_Starter,P_Serv_Starter,,Program.Main,,,none,none,System assemblies,bin/Debug or bin/Release,none,only solution project,yes,no,no,no,external Plant bootstrap selected by installer configuration,ACTIVE_BOOTSTRAP
|
||||
|
@@ -0,0 +1,84 @@
|
||||
param(
|
||||
[string]$DeployedRoot = '\\phpsrv03\NBIM\save\Apps\Plant3D\DriveA\P_Rec_Annot',
|
||||
[string]$SourceRoot = '\\phpsrv03\NBIM\code\CS\P_Rec_Annot\v1.0.4\repo',
|
||||
[string]$OutputRoot = (Split-Path -Parent $PSCommandPath)
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Get-PeArchitecture([string]$Path) {
|
||||
if ([IO.Path]::GetExtension($Path) -notin '.dll', '.exe') { return $null }
|
||||
try {
|
||||
$stream = [IO.File]::Open($Path, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::ReadWrite)
|
||||
try {
|
||||
$reader = [IO.BinaryReader]::new($stream)
|
||||
$stream.Position = 0x3c; $offset = $reader.ReadInt32(); $stream.Position = $offset + 4
|
||||
$machine = $reader.ReadUInt16()
|
||||
switch ($machine) { 0x14c { 'x86' } 0x8664 { 'x64' } 0x1c4 { 'ARM' } default { 'Unknown' } }
|
||||
} finally { $stream.Dispose() }
|
||||
} catch { 'Unreadable' }
|
||||
}
|
||||
|
||||
function Get-AssemblyMetadata([string]$Path) {
|
||||
$result = [ordered]@{ AssemblyName = $null; AssemblyVersion = $null; TargetFramework = $null }
|
||||
if ([IO.Path]::GetExtension($Path) -ne '.dll') { return [pscustomobject]$result }
|
||||
try {
|
||||
$name = [Reflection.AssemblyName]::GetAssemblyName($Path)
|
||||
$result.AssemblyName = $name.Name; $result.AssemblyVersion = $name.Version.ToString()
|
||||
try {
|
||||
$assembly = [Reflection.Assembly]::ReflectionOnlyLoadFrom($Path)
|
||||
$attribute = $assembly.GetCustomAttributesData() | Where-Object { $_.AttributeType.FullName -eq 'System.Runtime.Versioning.TargetFrameworkAttribute' } | Select-Object -First 1
|
||||
if ($attribute) { $result.TargetFramework = [string]$attribute.ConstructorArguments[0].Value }
|
||||
} catch { }
|
||||
} catch { }
|
||||
[pscustomobject]$result
|
||||
}
|
||||
|
||||
$inventory = foreach ($file in Get-ChildItem -LiteralPath $DeployedRoot -Recurse -File) {
|
||||
$hash = Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256
|
||||
$version = try { [Diagnostics.FileVersionInfo]::GetVersionInfo($file.FullName) } catch { $null }
|
||||
$meta = Get-AssemblyMetadata $file.FullName
|
||||
[pscustomobject]@{
|
||||
RelativePath = $file.FullName.Substring($DeployedRoot.Length).TrimStart('\')
|
||||
Name = $file.Name; Extension = $file.Extension; Length = $file.Length; LastWriteTimeUtc = $file.LastWriteTimeUtc.ToString('o')
|
||||
Sha256 = $hash.Hash; FileVersion = $version.FileVersion; ProductVersion = $version.ProductVersion
|
||||
AssemblyName = $meta.AssemblyName; AssemblyVersion = $meta.AssemblyVersion; TargetFramework = $meta.TargetFramework
|
||||
PeArchitecture = Get-PeArchitecture $file.FullName
|
||||
}
|
||||
}
|
||||
$inventory | Sort-Object RelativePath | Export-Csv -LiteralPath (Join-Path $OutputRoot 'deployed-files.csv') -NoTypeInformation -Encoding utf8
|
||||
|
||||
$settings = [xml](Get-Content -LiteralPath (Join-Path $DeployedRoot 'Settings.xml'))
|
||||
$sequence = 0
|
||||
$configuration = foreach ($item in $settings.Main.LoadAssemblies.LoadAssembly) {
|
||||
$sequence++
|
||||
$configured = [string]$item.assemblyname; $resolved = Join-Path $DeployedRoot $configured
|
||||
$entry = $inventory | Where-Object Name -eq $configured | Select-Object -First 1
|
||||
[pscustomobject]@{
|
||||
Sequence = $sequence; ConfigFile = 'Settings.xml'; ConfiguredPath = $configured; ResolvedPath = ('<deployed-root>\' + $configured)
|
||||
Assembly = $entry.AssemblyName; Exists = Test-Path -LiteralPath $resolved; Sha256 = $entry.Sha256
|
||||
LoadMethod = 'acDoc.Editor.Command("._netload", fullPath)'; Required = $true
|
||||
FailureBehavior = 'Outer loader catch stops remaining sequence and shows MessageBox'; Notes = 'Path is combined with OrthoLoader assembly directory.'
|
||||
}
|
||||
}
|
||||
$configuration | Export-Csv -LiteralPath (Join-Path $OutputRoot 'configuration-map.csv') -NoTypeInformation -Encoding utf8
|
||||
|
||||
$projects = Get-ChildItem -LiteralPath $SourceRoot -Recurse -File -Filter *.csproj | ForEach-Object {
|
||||
$content = Get-Content -LiteralPath $_.FullName -Raw
|
||||
$assemblyName = if ($content -match '<AssemblyName>([^<]+)</AssemblyName>') { $Matches[1] } else { [IO.Path]::GetFileNameWithoutExtension($_.Name) }
|
||||
$framework = if ($content -match '<TargetFrameworkVersion>([^<]+)</TargetFrameworkVersion>') { $Matches[1] } else { $null }
|
||||
$platform = if ($content -match '<PlatformTarget>([^<]+)</PlatformTarget>') { $Matches[1] } else { $null }
|
||||
[pscustomobject]@{ AssemblyName = $assemblyName; Csproj = $_.FullName.Substring($SourceRoot.Length).TrimStart('\'); TargetFramework = $framework; PlatformTarget = $platform }
|
||||
}
|
||||
$sourceMap = foreach ($dll in $inventory | Where-Object Extension -eq '.dll') {
|
||||
$matches = @($projects | Where-Object { $_.AssemblyName -eq $dll.AssemblyName -or $_.AssemblyName -eq [IO.Path]::GetFileNameWithoutExtension($dll.Name) })
|
||||
$releaseCandidate = Join-Path (Join-Path $SourceRoot 'FilesRelease') $dll.Name
|
||||
$exactReleaseMatch = (Test-Path -LiteralPath $releaseCandidate) -and ((Get-FileHash -LiteralPath $releaseCandidate -Algorithm SHA256).Hash -eq $dll.Sha256)
|
||||
$status = if ($exactReleaseMatch) { 'EXACT_SOURCE_MATCH' } elseif ($matches.Count -eq 1) { 'LIKELY_SOURCE_MATCH' } elseif ($matches.Count -gt 1) { 'MULTIPLE_CANDIDATES' } elseif ($dll.Name -match '^PnP|^Ac') { 'AUTODESK_DEPENDENCY' } else { 'NO_SOURCE_FOUND' }
|
||||
$project = $matches | Select-Object -First 1
|
||||
[pscustomobject]@{ DeployedDll=$dll.Name; DeployedSha256=$dll.Sha256; FileVersion=$dll.FileVersion; AssemblyVersion=$dll.AssemblyVersion; LegacyProject=if($project){[IO.Path]::GetFileNameWithoutExtension($project.Csproj)}else{$null}; LegacyCsproj=if($project){$project.Csproj}else{$null}; LegacyOutputName=if($project){$project.AssemblyName}else{$null}; TargetFramework=if($project){$project.TargetFramework}else{$null}; PlatformTarget=if($project){$project.PlatformTarget}else{$null}; CommandCount=$null; RibbonRole=if($dll.AssemblyName -eq 'OrthoLoader'){'Central ribbon/loader'}else{$null}; ConfigurationSource=if($configuration.ConfiguredPath -contains $dll.Name){'Settings.xml'}else{$null}; MatchStatus=$status }
|
||||
}
|
||||
$sourceMap | Sort-Object DeployedDll | Export-Csv -LiteralPath (Join-Path $OutputRoot 'deployed-to-source-map.csv') -NoTypeInformation -Encoding utf8
|
||||
|
||||
"GeneratedUtc=$([DateTime]::UtcNow.ToString('o'))`nDeployedRoot=<network root redacted to report context>`nSourceRoot=<network source root redacted to report context>`nFiles=$($inventory.Count)" | Set-Content -LiteralPath (Join-Path $OutputRoot 'evidence\generation.txt') -Encoding utf8
|
||||
@@ -0,0 +1,19 @@
|
||||
# Legacy P_Rec_Annot loading audit
|
||||
|
||||
Scope: read-only inspection of the deployed network directory and the v1.0.4 source checkout. No legacy DLL was executed, copied, or deployed.
|
||||
|
||||
The deployed root has 52 files, including 22 managed DLLs. `Settings.xml` enumerates 20 application modules. The inventory and its SHA-256 values are in `deployed-files.csv`; generated report paths use `<deployed-root>` rather than embedding a new runtime configuration path.
|
||||
|
||||
High-confidence conclusion: the central loader is `OrthoLoader.dll`. `OrthoLoader.Ribbon.Initialize()` invokes `OrthoLoader.Initialize()`. The latter reads adjacent `Settings.xml` and calls `acDoc.Editor.Command("._netload", fullPath)` sequentially for the configured modules. The bootstrap that initially loads `OrthoLoader.dll` is not present in either audited directory; it remains unknown. Therefore the audit proves same-directory loading, but cannot prove whether an external launcher first NETLOADed the UNC copy or a local copy.
|
||||
|
||||
Source-to-binary mapping is `EXACT_SOURCE_MATCH` for 15 deployed DLLs by SHA-256 against `FilesRelease`. Six newer deployed DLLs are `LIKELY_SOURCE_MATCH` by assembly/project identity and metadata but differ byte-for-byte from the checked-in release output. `InsertElevation.dll` is `MULTIPLE_CANDIDATES` because archival projects share the assembly identity.
|
||||
|
||||
## Potential test data
|
||||
|
||||
| RelativePath | Type | LikelyPurpose | ContainsProjectData | SafeForTest | Reason |
|
||||
|---|---|---|---|---|---|
|
||||
| `Template.dwg` | DWG | legacy command template | Unknown | No | Located beside production deployment; no authorization to open or copy it was established. |
|
||||
| `test.txt` | text | unknown/empty marker | No content | Not useful | Zero bytes. |
|
||||
| `Справка*.pdf/docx` | documentation | user instructions | Unknown | Review only | Not a CAD fixture. |
|
||||
|
||||
No `Project.xml`, `.dcf`, or explicitly labelled sample/demo Ortho DWG was found in the deployed root.
|
||||
@@ -0,0 +1,21 @@
|
||||
"Sequence","ConfigFile","ConfiguredPath","ResolvedPath","Assembly","Exists","Sha256","LoadMethod","Required","FailureBehavior","Notes"
|
||||
"1","Settings.xml","AnnotateLineNumbers.dll","<deployed-root>\AnnotateLineNumbers.dll","AnnotateLineNumbers","True","799BC277ED98BD870F01824180B5A945B24F016DFD9956A8CCC630AFE80FDFA4","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"2","Settings.xml","AutoAnnotateView.dll","<deployed-root>\AutoAnnotateView.dll","AutoAnnotateView","True","EAE55411EC84AC747A3816EE65CD095648282BE44235351E7C9ED1C188BCB611","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"3","Settings.xml","AutoLinenumberConnections.dll","<deployed-root>\AutoLinenumberConnections.dll","AutoLinenumberConnections","True","0F6B2A759F2A63B9152B480B6968BDA20F42AD1D922F67CA6E6BFD31A0715F35","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"4","Settings.xml","CheckOrthoView.dll","<deployed-root>\CheckOrthoView.dll","CheckOrthoView","True","89DB5E537C2BAEC292CDD8A0A43819119850119D54958E8CC3C43E2136AB24A0","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"5","Settings.xml","EnablerLoader.dll","<deployed-root>\EnablerLoader.dll","EnablerLoader","True","745D625C27F92842F2EFF3C515DB48BB9FC0FE59B6E4559ACD696BCC93FF4C51","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"6","Settings.xml","ExtentsIntersect.dll","<deployed-root>\ExtentsIntersect.dll","ExtentsIntersect","True","59D2776E083D4BE2FBFBE4F43009EAA9CE0E81A1752815B45C510F80654169D9","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"7","Settings.xml","GetComponentGeometry.dll","<deployed-root>\GetComponentGeometry.dll","GetComponentGeometry","True","9EA8C48FEC4D36D32CC6AC88B78C434662C16D4484F86EFAA46ECC309E9F9142","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"8","Settings.xml","GetLinenumberAnnotationText.dll","<deployed-root>\GetLinenumberAnnotationText.dll","GetLinenumberAnnotationText","True","047E721DBE380061D9ABDD92BC5475440E3B7ADB2BD0F544D538BA24B121D30E","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"9","Settings.xml","GetPlantScriptFolderPath.dll","<deployed-root>\GetPlantScriptFolderPath.dll","GetPlantScriptFolderPath","True","5080A167A1DAF6C19BE08F6CAB678D490736B5E07E121A918A2915F0D9D8BF65","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"10","Settings.xml","GetScriptParameters.dll","<deployed-root>\GetScriptParameters.dll","GetScriptParameters","True","7FF7B0D26BE8D89FF4A5A4FA1C6EC17BDA1506BCCB78AFE0E650137CD7BA5E87","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"11","Settings.xml","InsertElevation.dll","<deployed-root>\InsertElevation.dll","InsertElevation","True","B2D8B98F353D3DDF3A75A848E6922DC30478A1E5F98B372F24A049690492CA82","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"12","Settings.xml","LoadedAssemblies.dll","<deployed-root>\LoadedAssemblies.dll","LoadedAssemblies","True","277EB83C587A984DC2F558B8A7C86AA98E195A77AFA6FADD7DB83F2B801C0E4E","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"13","Settings.xml","OrthoAutoAnnotate.dll","<deployed-root>\OrthoAutoAnnotate.dll","OrthoAutoAnnotate","True","2E6BB2EBA0A98AA3550DFA4E661832FC7C3EE3DFF49BFC147BD4DFAD7A8596C6","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"14","Settings.xml","P3DCopyMissingObjectsToOrthoDrawing.dll","<deployed-root>\P3DCopyMissingObjectsToOrthoDrawing.dll","P3DCopyMissingObjectsToOrthoDrawing","True","6C879AFC7F35EC25E5E2FFF9FF0EE6EFEB155670DBFD5D0CDE9A0F629C5727B2","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"15","Settings.xml","P3DNewMa.dll","<deployed-root>\P3DNewMa.dll","P3DNewMa","True","97246EE9B24FD373551EF380C5AC0CA78CF7ED6A92581D5331B54445A2A948F8","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"16","Settings.xml","P3DSetLayerForViewport.dll","<deployed-root>\P3DSetLayerForViewport.dll","P3DSetLayerForViewport","True","A28E58FDE4EBAA26450CA646C5A85F98C1A04AA42C0BFC5413D718243ACBF42B","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"17","Settings.xml","PHPAnnotations.dll","<deployed-root>\PHPAnnotations.dll","PHPAnnotations","True","816A693F1B775C5506043DD0461055F248D3897E96AC8285DAE29C5B71FFCA73","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"18","Settings.xml","SwapAttributesMenuItem.dll","<deployed-root>\SwapAttributesMenuItem.dll","SwapAttributesMenuItem","True","674C865E34F9C877561E03E2077F7F2C6852C3C8B6A0503ACAA7619F3E80A460","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"19","Settings.xml","UpdateOrthoDrawings.dll","<deployed-root>\UpdateOrthoDrawings.dll","UpdateOrthoDrawings","True","1492FFF1F56853DF4A858CD3E926640C1E4920B5E3F5CFCC12F4389855E630BA","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
"20","Settings.xml","UpdateOrthoViewBlocks.dll","<deployed-root>\UpdateOrthoViewBlocks.dll","UpdateOrthoViewBlocks","True","5022D4D66D79E7E7D82F9FCB647DF8E1FE658A4694EB7794E363D5EA5517F5A9","acDoc.Editor.Command(""._netload"", fullPath)","True","Outer loader catch stops remaining sequence and shows MessageBox","Path is combined with OrthoLoader assembly directory."
|
||||
|
@@ -0,0 +1,53 @@
|
||||
"RelativePath","Name","Extension","Length","LastWriteTimeUtc","Sha256","FileVersion","ProductVersion","AssemblyName","AssemblyVersion","TargetFramework","PeArchitecture"
|
||||
"AnnotateLineNumbers.dll","AnnotateLineNumbers.dll",".dll","126976","2024-12-13T07:41:34.0000000Z","799BC277ED98BD870F01824180B5A945B24F016DFD9956A8CCC630AFE80FDFA4","1.0.4.1","v1.0.4","AnnotateLineNumbers","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"AutoAnnotateView.dll","AutoAnnotateView.dll",".dll","49664","2025-02-10T07:47:43.4844455Z","EAE55411EC84AC747A3816EE65CD095648282BE44235351E7C9ED1C188BCB611","1.0.4.6","v1.0.4","AutoAnnotateView","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"AutoElevation.dll","AutoElevation.dll",".dll","28160","2025-01-16T08:13:32.2554341Z","A8766B73B30F8F44213769EB7824F82F73ED418DE0EF49695E00A3EB7FC28885","1.0.4.5","v1.0.4","AutoElevation","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"AutoLinenumberConnections.dll","AutoLinenumberConnections.dll",".dll","41472","2024-12-13T07:41:34.0000000Z","0F6B2A759F2A63B9152B480B6968BDA20F42AD1D922F67CA6E6BFD31A0715F35","1.0.4.1","v1.0.4","AutoLinenumberConnections","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"b455ee6eaed79d76f084846a60ce3284","b455ee6eaed79d76f084846a60ce3284","","0","2023-12-29T11:13:44.6409171Z","E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",,,,,,
|
||||
"CheckOrthoView.dll","CheckOrthoView.dll",".dll","14848","2024-12-13T07:41:34.0000000Z","89DB5E537C2BAEC292CDD8A0A43819119850119D54958E8CC3C43E2136AB24A0","1.0.4.1","v1.0.4","CheckOrthoView","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"EnablerLoader.dll","EnablerLoader.dll",".dll","6144","2024-12-13T07:41:34.0000000Z","745D625C27F92842F2EFF3C515DB48BB9FC0FE59B6E4559ACD696BCC93FF4C51","1.0.4.1","v1.0.4","EnablerLoader","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"ExtentsIntersect.dll","ExtentsIntersect.dll",".dll","6656","2024-12-13T07:41:34.0000000Z","59D2776E083D4BE2FBFBE4F43009EAA9CE0E81A1752815B45C510F80654169D9","1.0.4.1","v1.0.4","ExtentsIntersect","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"GetComponentGeometry.dll","GetComponentGeometry.dll",".dll","10240","2024-12-13T07:41:34.0000000Z","9EA8C48FEC4D36D32CC6AC88B78C434662C16D4484F86EFAA46ECC309E9F9142","1.0.4.1","v1.0.4","GetComponentGeometry","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"GetComponentGeometry.xml","GetComponentGeometry.xml",".xml","1239","2025-01-14T11:26:24.5054138Z","70169C3A3AAAFE5A2282E01D5B9B7C99F2F4FF9B7514553434302674ABE46C98",,,,,,
|
||||
"GetLinenumberAnnotationText.dll","GetLinenumberAnnotationText.dll",".dll","23040","2024-12-13T07:41:34.0000000Z","047E721DBE380061D9ABDD92BC5475440E3B7ADB2BD0F544D538BA24B121D30E","1.0.4.1","v1.0.4","GetLinenumberAnnotationText","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"GetPlantScriptFolderPath.dll","GetPlantScriptFolderPath.dll",".dll","4608","2024-12-13T07:41:34.0000000Z","5080A167A1DAF6C19BE08F6CAB678D490736B5E07E121A918A2915F0D9D8BF65","1.0.4.1","v1.0.4","GetPlantScriptFolderPath","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"GetScriptParameters.dll","GetScriptParameters.dll",".dll","6656","2024-12-13T07:41:34.0000000Z","7FF7B0D26BE8D89FF4A5A4FA1C6EC17BDA1506BCCB78AFE0E650137CD7BA5E87","1.0.4.1","v1.0.4","GetScriptParameters","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"ico\button_BJ.png","button_BJ.png",".png","1235","2024-12-13T07:41:34.0000000Z","414F0A23E917D41A82C697B0E9CDDDF9BFCACD1627310BD5717AEA172418AA51",,,,,,
|
||||
"ico\button_CAC.png","button_CAC.png",".png","1943","2024-12-13T07:41:34.0000000Z","FA2B8877F8B41C70BA62C737C40374394D6840A53DFE2D0B0F83463C4817C138",,,,,,
|
||||
"ico\button_PXP-AAV.png","button_PXP-AAV.png",".png","1905","2024-12-13T07:41:34.0000000Z","6AB171963B553B434B5AA4BFBE91993B1782606AAF9B486FA23BB27BC1F0E4AA",,,,,,
|
||||
"ico\button_PXP-AC.png","button_PXP-AC.png",".png","1751","2024-12-13T07:41:34.0000000Z","60A6BD62EF3E50BDDCD00D9C281F2399E6C9D42E0C2DAA1742273C0DC48FE9C3",,,,,,
|
||||
"ico\button_PXP-AE.png","button_PXP-AE.png",".png","2001","2024-12-13T07:41:34.0000000Z","F1DCF0E908F5AF38CCD13ABA58B824E5B186430414499C0F623C1D5EEAD8DAD7",,,,,,
|
||||
"ico\button_PXP-ALC.png","button_PXP-ALC.png",".png","1740","2024-12-13T07:41:34.0000000Z","19DB2449B66B95965155E6E3BC313BD52000FE38C95B0FAD90EF07DF53EA9CC9",,,,,,
|
||||
"ico\button_PXP-COV.png","button_PXP-COV.png",".png","2007","2024-12-13T07:41:34.0000000Z","529BDD045961C244FBD0DE177B899AF9E7CE0F4506B3463C56D2704B3BF207B5",,,,,,
|
||||
"ico\button_PXP-DAV.png","button_PXP-DAV.png",".png","1249","2025-01-28T06:29:03.3724433Z","A7932F2ABDAC652EA3A901A221AB3BE2BC80FCB76FB203D44FC63AF3681F51E3",,,,,,
|
||||
"ico\button_PXP-DJ.png","button_PXP-DJ.png",".png","1470","2024-12-13T07:41:34.0000000Z","127957752DC2B6573CD2FD9A2791914BA22802E07E9B7E23CD3AE1FFF9E4FFC4",,,,,,
|
||||
"ico\button_PXP-ELN.png","button_PXP-ELN.png",".png","1927","2024-12-13T07:41:34.0000000Z","DF32E6A465D5C196A17F3F96473315590DF2D71995F63D2D241D7599B62C99BB",,,,,,
|
||||
"ico\button_PXP-ESN.png","button_PXP-ESN.png",".png","1636","2024-12-13T07:41:34.0000000Z","0F089184CFD428DA3620F9C0AD2DCBFC21B31ED34FA301E8D5D45923783C0CC7",,,,,,
|
||||
"ico\button_PXP-IE.png","button_PXP-IE.png",".png","1885","2024-12-13T07:41:34.0000000Z","F1C6373BFAF82E1908AF371D5F21133D27E0B84AFC706A703530855A2046FE43",,,,,,
|
||||
"ico\button_PXP-IOD.png","button_PXP-IOD.png",".png","1942","2024-12-13T07:41:34.0000000Z","494E90C899A0BB5BB365B0E1452E92A68359610C5A38A37D09F520A39BDB3899",,,,,,
|
||||
"ico\button_PXP-LN.png","button_PXP-LN.png",".png","2024","2024-12-13T07:41:34.0000000Z","F3305ED6D266E29FA475D4C8A7DF38D2D2A1776A4CE379B876B6CFA278B2FE5D",,,,,,
|
||||
"ico\button_PXP-MA.png","button_PXP-MA.png",".png","1907","2024-12-13T07:41:34.0000000Z","9386249121644DCF4B9C70491F561E8B6E5BAAE87EBB2C76A84E46B10A60C8C3",,,,,,
|
||||
"ico\button_PXP-OA.png","button_PXP-OA.png",".png","1999","2024-12-13T07:41:34.0000000Z","EAA2AD7EE5C6B7F3716AF1D7690C76AE59229AE9B6BA02009F5009CD9307F4E8",,,,,,
|
||||
"ico\button_PXP-OSI.png","button_PXP-OSI.png",".png","1508","2024-12-13T07:41:34.0000000Z","422D10B6EBDD76085CF9C5985391CCA060464B200D6792D604658AAE47F12CEE",,,,,,
|
||||
"ico\button_PXP-RA.png","button_PXP-RA.png",".png","1826","2024-12-13T07:41:34.0000000Z","1DB3F39C6CCA4EABE79D81DE6DCF9900649CDC9064F2EBF5E45F9AD849B8317F",,,,,,
|
||||
"ico\button_PXP-RMP.png","button_PXP-RMP.png",".png","1759","2024-12-13T07:41:34.0000000Z","156026EC6DEB7BDBE720047409E9A25615D650D8634C2F0BAF49A71E27135258",,,,,,
|
||||
"ico\button_PXP-ULN.png","button_PXP-ULN.png",".png","1928","2024-12-13T07:41:34.0000000Z","B37EBB460691EAA7BC3A91DF05C27FE7CC1F0E9F9D69BDA7703956EAA7AA785C",,,,,,
|
||||
"ico\button_PXP-UOB.png","button_PXP-UOB.png",".png","1569","2024-12-13T07:41:34.0000000Z","3BC16B9923CCD34CF10F403CD65732B217E255E89539174841E7F30D9DFD2DCD",,,,,,
|
||||
"InsertElevation.dll","InsertElevation.dll",".dll","24576","2025-01-16T08:13:32.2504470Z","B2D8B98F353D3DDF3A75A848E6922DC30478A1E5F98B372F24A049690492CA82","1.0.4.5","v1.0.4","InsertElevation","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"LoadedAssemblies.dll","LoadedAssemblies.dll",".dll","4608","2024-12-13T07:41:34.0000000Z","277EB83C587A984DC2F558B8A7C86AA98E195A77AFA6FADD7DB83F2B801C0E4E","1.0.4.1","v1.0.4","LoadedAssemblies","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"OrthoAutoAnnotate.dll","OrthoAutoAnnotate.dll",".dll","108544","2025-02-10T07:45:33.3280516Z","2E6BB2EBA0A98AA3550DFA4E661832FC7C3EE3DFF49BFC147BD4DFAD7A8596C6","1.0.4.6","v1.0.4","OrthoAutoAnnotate","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"OrthoLoader.dll","OrthoLoader.dll",".dll","18944","2025-02-24T06:59:01.9094468Z","D418F2CBDD134C0156E2D332BD7F9ED471B9A1E0581FE42EDF98BBE01CB3345D","1.0.4.7","v1.0.4","OrthoLoader","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"P3DCopyMissingObjectsToOrthoDrawing.dll","P3DCopyMissingObjectsToOrthoDrawing.dll",".dll","14336","2024-12-13T07:41:34.0000000Z","6C879AFC7F35EC25E5E2FFF9FF0EE6EFEB155670DBFD5D0CDE9A0F629C5727B2","1.0.4.1","v1.0.4","P3DCopyMissingObjectsToOrthoDrawing","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"P3DNewMa.dll","P3DNewMa.dll",".dll","103936","2025-02-24T06:59:03.1350320Z","97246EE9B24FD373551EF380C5AC0CA78CF7ED6A92581D5331B54445A2A948F8","1.0.4.7","v1.0.4","P3DNewMa","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"P3DSetLayerForViewport.dll","P3DSetLayerForViewport.dll",".dll","4608","2024-12-13T07:41:34.0000000Z","A28E58FDE4EBAA26450CA646C5A85F98C1A04AA42C0BFC5413D718243ACBF42B","1.0.4.1","v1.0.4","P3DSetLayerForViewport","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"PHPAnnotations.dll","PHPAnnotations.dll",".dll","69120","2024-12-13T07:41:34.0000000Z","816A693F1B775C5506043DD0461055F248D3897E96AC8285DAE29C5B71FFCA73","1.0.4.1","v1.0.4","PHPAnnotations","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"Settings.xml","Settings.xml",".xml","136576","2025-01-31T07:28:24.1891507Z","AFEDC7526923ACE1DC60374F98F171809B36992262A37E1F11828F2B8D9799CB",,,,,,
|
||||
"SwapAttributesMenuItem.dll","SwapAttributesMenuItem.dll",".dll","11264","2024-12-13T07:41:34.0000000Z","674C865E34F9C877561E03E2077F7F2C6852C3C8B6A0503ACAA7619F3E80A460","1.0.4.1","v1.0.4","SwapAttributesMenuItem","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"Template.dwg","Template.dwg",".dwg","148438","2025-01-30T13:16:13.0000000Z","EA279A30F113660CF53585451083BAEB0A01CA58C8F9A767C4B7C2A5E66ECCA3",,,,,,
|
||||
"test.txt","test.txt",".txt","0","2025-01-16T09:56:52.0597137Z","E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855",,,,,,
|
||||
"UpdateOrthoDrawings.dll","UpdateOrthoDrawings.dll",".dll","45568","2025-02-24T06:59:02.4829428Z","1492FFF1F56853DF4A858CD3E926640C1E4920B5E3F5CFCC12F4389855E630BA","1.0.4.2","v1.0.4","UpdateOrthoDrawings","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"UpdateOrthoViewBlocks.dll","UpdateOrthoViewBlocks.dll",".dll","15360","2024-12-13T07:41:34.0000000Z","5022D4D66D79E7E7D82F9FCB647DF8E1FE658A4694EB7794E363D5EA5517F5A9","1.0.4.1","v1.0.4","UpdateOrthoViewBlocks","1.0.4.0",".NETFramework,Version=v4.7","x64"
|
||||
"Справка по работе с палитрой.docx","Справка по работе с палитрой.docx",".docx","855301","2025-02-05T09:53:08.1186958Z","B6DC65BA17C4EA79DBCCC501909BEDBF56EB6A9F1E24BFAD1B01158B6EC639D0",,,,,,
|
||||
"Справка по работе с палитрой.pdf","Справка по работе с палитрой.pdf",".pdf","744035","2025-02-05T09:53:21.3752687Z","332CA70D3264C28BC712CA790157D0CD03BE7E07F42603DAF9247600271B7B75",,,,,,
|
||||
"Справка.docx","Справка.docx",".docx","87889","2025-02-06T05:13:36.4991443Z","14BA9BD252FB6DF37C4D151787E8799AB1703847C1DEC24F3EFE4D6805B0EB12",,,,,,
|
||||
"Справка.pdf","Справка.pdf",".pdf","154074","2025-02-06T05:14:24.2809131Z","EB840514755A9F56ABC8351B8EC41233FE2512118DB2D4BA0ACD46E74D2C7DC9",,,,,,
|
||||
|
@@ -0,0 +1,23 @@
|
||||
"DeployedDll","DeployedSha256","FileVersion","AssemblyVersion","LegacyProject","LegacyCsproj","LegacyOutputName","TargetFramework","PlatformTarget","CommandCount","RibbonRole","ConfigurationSource","MatchStatus"
|
||||
"AnnotateLineNumbers.dll","799BC277ED98BD870F01824180B5A945B24F016DFD9956A8CCC630AFE80FDFA4","1.0.4.1","1.0.4.0","AnnotateLineNumbers","AnnotateLineNumbers\AnnotateLineNumbers.csproj","AnnotateLineNumbers","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"AutoAnnotateView.dll","EAE55411EC84AC747A3816EE65CD095648282BE44235351E7C9ED1C188BCB611","1.0.4.6","1.0.4.0","AutoAnnotateView","AutoAnnotateView\AutoAnnotateView.csproj","AutoAnnotateView","v4.7","x64",,,"Settings.xml","LIKELY_SOURCE_MATCH"
|
||||
"AutoElevation.dll","A8766B73B30F8F44213769EB7824F82F73ED418DE0EF49695E00A3EB7FC28885","1.0.4.5","1.0.4.0","AutoElevation","AutoElevation\AutoElevation.csproj","AutoElevation","v4.7","x64",,,,"LIKELY_SOURCE_MATCH"
|
||||
"AutoLinenumberConnections.dll","0F6B2A759F2A63B9152B480B6968BDA20F42AD1D922F67CA6E6BFD31A0715F35","1.0.4.1","1.0.4.0","AutoLinenumberConnections","AutoLinenumberConnections\AutoLinenumberConnections.csproj","AutoLinenumberConnections","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"CheckOrthoView.dll","89DB5E537C2BAEC292CDD8A0A43819119850119D54958E8CC3C43E2136AB24A0","1.0.4.1","1.0.4.0","CheckOrthoView","CheckOrthoView\CheckOrthoView.csproj","CheckOrthoView","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"EnablerLoader.dll","745D625C27F92842F2EFF3C515DB48BB9FC0FE59B6E4559ACD696BCC93FF4C51","1.0.4.1","1.0.4.0","EnablerLoader","EnablerLoader\EnablerLoader.csproj","EnablerLoader","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"ExtentsIntersect.dll","59D2776E083D4BE2FBFBE4F43009EAA9CE0E81A1752815B45C510F80654169D9","1.0.4.1","1.0.4.0","ExtentsIntersect","ExtentsIntersect\ExtentsIntersect.csproj","ExtentsIntersect","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"GetComponentGeometry.dll","9EA8C48FEC4D36D32CC6AC88B78C434662C16D4484F86EFAA46ECC309E9F9142","1.0.4.1","1.0.4.0","GetComponentGeometry","GetComponentGeometry\GetComponentGeometry.csproj","GetComponentGeometry","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"GetLinenumberAnnotationText.dll","047E721DBE380061D9ABDD92BC5475440E3B7ADB2BD0F544D538BA24B121D30E","1.0.4.1","1.0.4.0","GetLinenumberAnnotationText","GetLinenumberAnnotationText\GetLinenumberAnnotationText.csproj","GetLinenumberAnnotationText","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"GetPlantScriptFolderPath.dll","5080A167A1DAF6C19BE08F6CAB678D490736B5E07E121A918A2915F0D9D8BF65","1.0.4.1","1.0.4.0","GetPlantScriptFolderPath","GetPlantScriptFolderPath\GetPlantScriptFolderPath.csproj","GetPlantScriptFolderPath","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"GetScriptParameters.dll","7FF7B0D26BE8D89FF4A5A4FA1C6EC17BDA1506BCCB78AFE0E650137CD7BA5E87","1.0.4.1","1.0.4.0","GetScriptParameters","GetScriptParameters\GetScriptParameters.csproj","GetScriptParameters","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"InsertElevation.dll","B2D8B98F353D3DDF3A75A848E6922DC30478A1E5F98B372F24A049690492CA82","1.0.4.5","1.0.4.0","InsertElevation","InsertElevation\InsertElevation.csproj","InsertElevation","v4.7","x64",,,"Settings.xml","MULTIPLE_CANDIDATES"
|
||||
"LoadedAssemblies.dll","277EB83C587A984DC2F558B8A7C86AA98E195A77AFA6FADD7DB83F2B801C0E4E","1.0.4.1","1.0.4.0","LoadedAssemblies","LoadedAssemblies\LoadedAssemblies.csproj","LoadedAssemblies","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"OrthoAutoAnnotate.dll","2E6BB2EBA0A98AA3550DFA4E661832FC7C3EE3DFF49BFC147BD4DFAD7A8596C6","1.0.4.6","1.0.4.0","OrthoAutoAnnotate","OrthoAutoAnnotate\OrthoAutoAnnotate.csproj","OrthoAutoAnnotate","v4.7","x64",,,"Settings.xml","LIKELY_SOURCE_MATCH"
|
||||
"OrthoLoader.dll","D418F2CBDD134C0156E2D332BD7F9ED471B9A1E0581FE42EDF98BBE01CB3345D","1.0.4.7","1.0.4.0","OrthoLoader","OrthoLoader\OrthoLoader.csproj","OrthoLoader","v4.7","x64",,"Central ribbon/loader",,"LIKELY_SOURCE_MATCH"
|
||||
"P3DCopyMissingObjectsToOrthoDrawing.dll","6C879AFC7F35EC25E5E2FFF9FF0EE6EFEB155670DBFD5D0CDE9A0F629C5727B2","1.0.4.1","1.0.4.0","P3DCopyMissingObjectsToOrthoDrawing","P3DCopyMissingObjectsToOrthoDrawing\P3DCopyMissingObjectsToOrthoDrawing.csproj","P3DCopyMissingObjectsToOrthoDrawing","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"P3DNewMa.dll","97246EE9B24FD373551EF380C5AC0CA78CF7ED6A92581D5331B54445A2A948F8","1.0.4.7","1.0.4.0","P3DNewMa","P3DNewMa\P3DNewMa.csproj","P3DNewMa","v4.7","x64",,,"Settings.xml","LIKELY_SOURCE_MATCH"
|
||||
"P3DSetLayerForViewport.dll","A28E58FDE4EBAA26450CA646C5A85F98C1A04AA42C0BFC5413D718243ACBF42B","1.0.4.1","1.0.4.0","P3DSetLayerForViewport","P3DSetLayerForViewport\P3DSetLayerForViewport.csproj","P3DSetLayerForViewport","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"PHPAnnotations.dll","816A693F1B775C5506043DD0461055F248D3897E96AC8285DAE29C5B71FFCA73","1.0.4.1","1.0.4.0","PHPAnnotations","PHPAnnotations\PHPAnnotations.csproj","PHPAnnotations","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"SwapAttributesMenuItem.dll","674C865E34F9C877561E03E2077F7F2C6852C3C8B6A0503ACAA7619F3E80A460","1.0.4.1","1.0.4.0","SwapAttributesMenuItem","SwapAttributesMenuItem\SwapAttributesMenuItem.csproj","SwapAttributesMenuItem","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
"UpdateOrthoDrawings.dll","1492FFF1F56853DF4A858CD3E926640C1E4920B5E3F5CFCC12F4389855E630BA","1.0.4.2","1.0.4.0","UpdateOrthoDrawings","UpdateOrthoDrawings\UpdateOrthoDrawings.csproj","UpdateOrthoDrawings","v4.7","x64",,,"Settings.xml","LIKELY_SOURCE_MATCH"
|
||||
"UpdateOrthoViewBlocks.dll","5022D4D66D79E7E7D82F9FCB647DF8E1FE658A4694EB7794E363D5EA5517F5A9","1.0.4.1","1.0.4.0","UpdateOrthoViewBlocks","UpdateOrthoViewBlocks\UpdateOrthoViewBlocks.csproj","UpdateOrthoViewBlocks","v4.7","x64",,,"Settings.xml","EXACT_SOURCE_MATCH"
|
||||
|
@@ -0,0 +1,4 @@
|
||||
GeneratedUtc=2026-07-31T10:39:04.9899963Z
|
||||
DeployedRoot=<network root redacted to report context>
|
||||
SourceRoot=<network source root redacted to report context>
|
||||
Files=52
|
||||
@@ -0,0 +1,13 @@
|
||||
# Legacy versus new loading model
|
||||
|
||||
| Legacy behavior | Legacy evidence | New implementation | Compatibility requirement | Decision | Risk |
|
||||
|---|---|---|---|---|---|
|
||||
| Unknown external bootstrap loads `OrthoLoader.dll` | `loading-chain.md` | Bundle bootstrap not selected yet | Identify the workstation/project startup owner first | Defer loading-model decision | High |
|
||||
| 20 independent DLLs loaded in order | `Settings.xml`, `Loader.cs:63-81` | one entry assembly currently | Preserve required command availability, not the DLL count | Evaluate command/module migration individually | Medium |
|
||||
| DLLs loaded from OrthoLoader's directory (possibly UNC) | `Loader.cs:49,65`; bootstrap source unknown | local smoke bundle only | Decide offline/update policy with operations owner | Do not hard-code UNC in new code | High |
|
||||
| Settings.xml controls order | `Loader.cs:104-112` | MSBuild/bundle manifest currently | Preserve explicit dependency ordering where behavior requires it | Document, do not emulate blindly | Medium |
|
||||
| central PluginsOrtho Ribbon | `Ribbon.cs` | new Promhimproekt tab | Preserve actual command mapping where adopted | Separate UI migration decision | Medium |
|
||||
| project selection uses local app_netload.lsp | `Ribbon.cs:380-405` | no equivalent | Confirm security/project-selection policy | Do not reproduce hidden local control file | High |
|
||||
| network icons and documents | `Ribbon.cs:330-357`, deployed files | bundle resources | Bundle only licensed, necessary resources | Replace network runtime dependency | Medium |
|
||||
|
||||
Recommendation: do not choose AppLoader or a manual-NETLOAD model yet. First identify the missing external bootstrap and confirm whether `app_netload.lsp` is the corporate launcher contract. Once confirmed, use a single versioned entry assembly with explicit manifest/dependency validation; avoid sequential programmatic NETLOAD and UNC execution because the legacy chain is non-atomic and fails the remaining modules after one error.
|
||||
@@ -0,0 +1,29 @@
|
||||
# Proven legacy loading chain
|
||||
|
||||
```text
|
||||
Unknown external bootstrap
|
||||
-> NETLOAD of deployed OrthoLoader.dll (not evidenced in audited directories)
|
||||
-> OrthoLoader.Ribbon.Initialize()
|
||||
-> subscribe ComponentManager.ItemInitialized
|
||||
-> OrthoLoader.Initialize()
|
||||
-> adjacent Settings.xml / Main/LoadAssemblies/LoadAssembly
|
||||
-> sequential acDoc.Editor.Command("._netload", fullPath) for 20 DLLs
|
||||
-> each DLL exposes CommandMethod commands
|
||||
-> Ribbon ItemInitialized creates PluginsOrtho tab and command buttons
|
||||
```
|
||||
|
||||
| Claim | Evidence | Confidence |
|
||||
|---|---|---|
|
||||
| Central loader is `OrthoLoader.dll` | `OrthoLoader/Ribbon.cs:151-156`; deployed DLL metadata and `Settings.xml` | High |
|
||||
| Module list is read from adjacent `Settings.xml` | `OrthoLoader/Loader.cs:104-112` | High |
|
||||
| Module loading is programmatic `._netload`, in XML order | `OrthoLoader/Loader.cs:63-81`; `configuration-map.csv` | High |
|
||||
| Full DLL path is loader directory + configured filename | `OrthoLoader/Loader.cs:49, 65` | High |
|
||||
| Modules load from the same directory as OrthoLoader | `Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)` in `Loader.cs:49,65` | High |
|
||||
| Modules loaded directly from UNC | true only if external bootstrap NETLOADed the deployed UNC copy; bootstrap is outside audit scope | Unknown |
|
||||
| Missing/failed module stops later loading and shows MessageBox | single outer try/catch around the loop, `Loader.cs:63-99` | High |
|
||||
| Ribbon owner is OrthoLoader | `OrthoLoader/Ribbon.cs:16-19,151-172,193-321` | High |
|
||||
| Initial bootstrap mechanism | no LISP/CUI/ARG/REG/PackageContents file found in either audited directory | Unknown |
|
||||
|
||||
`Assembly.LoadFrom` occurs inside several legacy command implementations for secondary dependencies. It is not the mechanism used by `OrthoLoader` to load its configured modules. `EnablerLoader` separately uses `Assembly.LoadFile` for the Plant enabler from the host base directory.
|
||||
|
||||
No `PackageContents.xml`, `*.bundle`, registry demand-load registration, LISP, CUI, MNL, script, batch, or PowerShell bootstrap was found in the audited locations. This proves absence only within audit scope; it does not prove the workstation bootstrap did not exist elsewhere.
|
||||
@@ -0,0 +1,10 @@
|
||||
Dependency,Category,Evidence,Requiredness,Notes
|
||||
Settings.xml,CONFIGURATION,OrthoLoader/Loader.cs:104-112,Required,Adjacent to each loaded assembly directory
|
||||
ico/*.png,RIBBON_RESOURCE,OrthoLoader/Ribbon.cs:330-357,Optional,Missing file yields null image
|
||||
app_netload.lsp,CONFIGURATION,OrthoLoader/Ribbon.cs:380-405,Implicit,Local workstation file gates project-specific Ribbon enablement
|
||||
annotation_settings.xml,REFERENCE_DATA,README.md and annotation modules,Project-dependent,Located in Plant project data area according to README
|
||||
users_indexes_list.xml,REFERENCE_DATA,README.md and annotation modules,Project-dependent,Located in Plant project data area according to README
|
||||
Template.dwg,TEMPLATE,Deployed inventory and legacy source references,Command-dependent,Do not treat deployed copy as approved test fixture
|
||||
Plant project XML/DWG,COMMAND_TIME,UpdateOrthoDrawings and annotation source,Required by relevant command,Out of deployed plugin root
|
||||
Update report CSV,LOGGING,UpdateOrthoDrawings/Commands.cs:1004,Optional,Written beside executing assembly by legacy command
|
||||
Help PDFs,RIBBON_RESOURCE,P3DNewMa/NewMaControl.cs:6214,Optional,Opened from assembly directory
|
||||
|
@@ -0,0 +1,9 @@
|
||||
# Observed user workflow
|
||||
|
||||
After an external bootstrap loaded `OrthoLoader.dll`, the loader created a `PluginsOrtho` Ribbon tab only when `isProjectCorrect` accepted the current Plant project. That predicate reads the first line of the local file `C:\AutoCAD User Support\Applications\app_netload.lsp` and compares its encoded project root with the active Plant project (`OrthoLoader/Ribbon.cs:380-405`).
|
||||
|
||||
`PluginsOrtho` has eight panels. Button IDs are commands; clicks call `Document.SendStringToExecute(command + "\n", ...)` (`Ribbon.cs:193-321, 526-529`). Examples: `PXP-AAV` is registered by `AutoAnnotateView/Commands.cs:546`; `PXP-OA` by `OrthoAutoAnnotate/Commands.cs:6444`.
|
||||
|
||||
The deployed `ico/*.png` files are runtime Ribbon resources. They are resolved from the loader assembly directory; in this deployment that is the UNC directory. The code has a null-image fallback when an icon is absent.
|
||||
|
||||
The tab and panels use stable IDs and lookup before creation. Button collections are appended when a missing panel is created; repeated project-open events can enable existing UI or create absent UI. The event is detached after first Ribbon initialization, but `CurrentProjectOpenedEvent` is retained for the process lifetime.
|
||||
Reference in New Issue
Block a user