fix: defer Plant activation until document is quiescent
This commit is contained in:
@@ -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; }
|
||||
|
||||
@@ -82,6 +82,22 @@ public sealed class FoundationTests
|
||||
Assert.Contains("PostStartupReady", source);
|
||||
}
|
||||
|
||||
[Fact] public void AutomationProjectActivation_WaitsForFixtureDocumentAndQuiescence()
|
||||
{
|
||||
var source = File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "FirstLimitedApplyAutomation.cs"));
|
||||
var activation = source[source.IndexOf("private void TryActivateProjectAndVerifyContext", StringComparison.Ordinal)..];
|
||||
Assert.Contains("!fixtureDocumentActivated", activation);
|
||||
Assert.Contains("!AcadApplication.IsQuiescent", activation);
|
||||
Assert.True(activation.IndexOf("PlantProject.LoadProject", StringComparison.Ordinal) > activation.IndexOf("AutomationBeforeProjectActivation", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
[Fact] public void AutomationDocumentActivation_IsSubscribedBeforeFixtureOpen()
|
||||
{
|
||||
var source = File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "FirstLimitedApplyAutomation.cs"));
|
||||
Assert.True(source.IndexOf("DocumentActivated += OnDocumentActivated", StringComparison.Ordinal) < source.IndexOf("DocumentManager.Open(config.OrthoDrawingPath", StringComparison.Ordinal));
|
||||
Assert.Contains("FixtureDocumentActivated", source);
|
||||
}
|
||||
|
||||
[Fact] public void AutomationControlledExit_QueuesNativeQuitAfterPublicCommand()
|
||||
{
|
||||
var source = File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "FirstLimitedApplyAutomation.cs"));
|
||||
|
||||
Reference in New Issue
Block a user