fix: defer Plant activation until document is quiescent

This commit is contained in:
codex
2026-08-03 17:19:01 +03:00
parent c9c4113e86
commit 32607e2f34
2 changed files with 42 additions and 12 deletions
@@ -139,6 +139,8 @@ internal sealed class FirstLimitedApplyAutomationCoordinator
private CoordinatorState previousState = CoordinatorState.Created;
private bool documentHandlersSubscribed;
private bool lifecycleHandlersSubscribed;
private bool fixtureDocumentActivated;
private bool projectActivationStarted;
private bool exitQueued;
private bool exitHandlerSubscribed;
private string? exceptionText;
@@ -155,24 +157,24 @@ internal sealed class FirstLimitedApplyAutomationCoordinator
return;
}
if (string.IsNullOrWhiteSpace(config.OrthoDrawingPath)) throw new InvalidOperationException("AUTOMATION_ORTHO_PATH_REQUIRED");
transitions.Add("ProjectActivationStarted");
var projectXml = Path.Combine(Path.GetDirectoryName(config.FixtureMarkerPath)!, "Project.xml");
var project = PlantProject.LoadProject(projectXml, false, string.Empty, string.Empty);
PlantApplication.SetCurrentProject(project, true);
transitions.Add("ProjectActivated");
// A first startup Idle is sufficient to arm automation, but not to
// call Plant APIs. Establish an AutoCAD document first and wait for
// its activation plus a quiescent callback before project activation.
AcadApplication.DocumentManager.DocumentActivated += OnDocumentActivated;
AcadApplication.Idle += OnIdle;
lifecycleHandlersSubscribed = true;
if (!string.IsNullOrWhiteSpace(config.ModelDrawingPath)) automationOpenedDocuments.Add(AcadApplication.DocumentManager.Open(config.ModelDrawingPath, false));
transitions.Add("DocumentOpenStarted");
RuntimeDiagnostics.Write("AutomationBeforeDocumentOpen", resultStatus: "PostStartupReady");
document = AcadApplication.DocumentManager.Open(config.OrthoDrawingPath, false);
automationOpenedDocuments.Add(document);
AcadApplication.DocumentManager.MdiActiveDocument = document;
transitions.Add("DocumentOpened");
RuntimeDiagnostics.Write("AutomationAfterDocumentOpen", resultStatus: "WaitingForDocumentActivation", document: document);
document.CommandEnded += OnCommandEnded;
document.CommandFailed += OnCommandFailed;
document.CommandCancelled += OnCommandCancelled;
AcadApplication.DocumentManager.DocumentActivated += OnDocumentActivated;
AcadApplication.Idle += OnIdle;
documentHandlersSubscribed = true;
lifecycleHandlersSubscribed = true;
Transition(CoordinatorState.Running, "AutomationRunning");
}
catch (System.Exception ex) { FailFromCallback("Start", ex, "AUTOMATION_START_FAILED"); }
@@ -188,23 +190,35 @@ internal sealed class FirstLimitedApplyAutomationCoordinator
RequestControlledExit(idleSafe: true);
}
private void OnIdle(object? sender, EventArgs e) => RunCallback("Idle", BeginContextVerification);
private void OnIdle(object? sender, EventArgs e) => RunCallback("Idle", TryActivateProjectAndVerifyContext);
private void OnDocumentActivated(object sender, DocumentCollectionEventArgs e)
{
RunCallback("DocumentActivated", () =>
{
if (document is null || e.Document is null) return;
if (string.Equals(e.Document.Name, document.Name, StringComparison.OrdinalIgnoreCase)) BeginContextVerification();
if (!string.Equals(e.Document.Name, document.Name, StringComparison.OrdinalIgnoreCase)) return;
fixtureDocumentActivated = true;
transitions.Add("FixtureDocumentActivated");
TryActivateProjectAndVerifyContext();
});
}
private void BeginContextVerification()
private void TryActivateProjectAndVerifyContext()
{
if (IsTerminal || document is null) return;
if (IsTerminal || document is null || !fixtureDocumentActivated || projectActivationStarted) return;
if (!AcadApplication.IsQuiescent) return;
projectActivationStarted = true;
AcadApplication.Idle -= OnIdle;
AcadApplication.DocumentManager.DocumentActivated -= OnDocumentActivated;
lifecycleHandlersSubscribed = false;
try
{
transitions.Add("ProjectActivationStarted");
RuntimeDiagnostics.Write("AutomationBeforeProjectActivation", resultStatus: "DocumentActivatedAndQuiescent", document: document);
var projectXml = Path.Combine(Path.GetDirectoryName(config!.FixtureMarkerPath)!, "Project.xml");
var project = PlantProject.LoadProject(projectXml, false, string.Empty, string.Empty);
PlantApplication.SetCurrentProject(project, true);
transitions.Add("ProjectActivated");
RuntimeDiagnostics.Write("AutomationAfterProjectActivation", resultStatus: "ProjectAvailable", document: document);
transitions.Add("ContextVerificationStarted");
var context = new PlantRuntimeContextReader().ReadCurrentContext(document);
if (!context.HasPlantProject || !context.IsOrthoDrawing || context.Viewports.Count != 1) { Complete(false, $"AUTOMATION_ORTHO_CONTEXT_INVALID:P={context.HasPlantProject};O={context.IsOrthoDrawing};V={context.Viewports.Count}"); return; }