From 115f44fcb921fd9e8e43d4033c11d59c2255d428 Mon Sep 17 00:00:00 2001 From: codex Date: Tue, 4 Aug 2026 10:07:03 +0300 Subject: [PATCH] feat: drive Preview automation through AutoCAD script --- Launcher.Next.Autodesk/AutodeskAdapters.cs | 9 ++++++++- Launcher.Next.Core/Contracts.cs | 2 +- .../PilotExecutionService.cs | 14 ++++++++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Launcher.Next.Autodesk/AutodeskAdapters.cs b/Launcher.Next.Autodesk/AutodeskAdapters.cs index aabba50..cc8edf1 100644 --- a/Launcher.Next.Autodesk/AutodeskAdapters.cs +++ b/Launcher.Next.Autodesk/AutodeskAdapters.cs @@ -16,7 +16,14 @@ public sealed class StartupScriptBuilder : IStartupScriptBuilder { public string BuildNetloadScript(string root, string entry) { var full = Path.GetFullPath(Path.Combine(root, entry)); if (!full.StartsWith(Path.GetFullPath(root) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) || !File.Exists(full)) throw new InvalidOperationException("UNTRUSTED_ENTRY_PATH"); return $"_.NETLOAD\n\"{full.Replace("\"", "\\\"")}\"\n"; } - public string BuildAutomationScript(string root, string entry) => BuildNetloadScript(root, entry) + "PHP_REC_AUTOMATION_START\n"; + public string BuildAutomationScript(string root, string entry, string fixtureOrthoDwg) + { + var fixture = Path.GetFullPath(fixtureOrthoDwg); + if (!File.Exists(fixture)) throw new InvalidOperationException("AUTOMATION_FIXTURE_NOT_FOUND"); + var quote = (string value) => "\"" + value.Replace("\"", "\\\"") + "\""; + return "_.OPEN\n" + quote(fixture) + "\n" + BuildNetloadScript(root, entry) + + "PHP_REC_AUTOMATION_PREPARE\nPXP-OA\nPXP-OA\nPHP_REC_AUTOMATION_FINALIZE\n"; + } } public sealed class DryRunAutodeskProcessLauncher : IAutodeskProcessLauncher { diff --git a/Launcher.Next.Core/Contracts.cs b/Launcher.Next.Core/Contracts.cs index d077fe7..80efd12 100644 --- a/Launcher.Next.Core/Contracts.cs +++ b/Launcher.Next.Core/Contracts.cs @@ -28,7 +28,7 @@ public interface ILocalPackageStore { Task GetCurrentVersionAsync(strin public interface IPackageActivator { Task ActivateAsync(string packageId, string version, CancellationToken cancellationToken); } public interface IRollbackManager { Task RollbackAsync(string packageId, CancellationToken cancellationToken); } public interface IAutodeskProcessLauncher { PreparedLaunch Prepare(LaunchRequest request); } -public interface IStartupScriptBuilder { string BuildNetloadScript(string validatedPackageRoot, string entryAssembly); string BuildAutomationScript(string validatedPackageRoot, string entryAssembly); } +public interface IStartupScriptBuilder { string BuildNetloadScript(string validatedPackageRoot, string entryAssembly); string BuildAutomationScript(string validatedPackageRoot, string entryAssembly, string fixtureOrthoDwg); } public interface ILauncherConfigurationProvider { LauncherMode GetMode(); } public interface ILauncherTelemetry { void Write(string eventType, IReadOnlyDictionary? fields = null); } public interface IClock { DateTimeOffset UtcNow { get; } } diff --git a/Launcher.Next.Infrastructure/PilotExecutionService.cs b/Launcher.Next.Infrastructure/PilotExecutionService.cs index d883d03..f3287e5 100644 --- a/Launcher.Next.Infrastructure/PilotExecutionService.cs +++ b/Launcher.Next.Infrastructure/PilotExecutionService.cs @@ -40,9 +40,10 @@ public sealed class PilotExecutionService( if (!safety.IsAllowed) return new(null, safety); var futureEntry = Path.Combine(request.PilotRoot, "packages", request.Manifest.PackageId, "versions", request.Manifest.Version, request.Manifest.EntryAssembly); + var fixture = request.Configuration.AutomationMode is null ? null : ReadFixtureOrtho(request.Configuration.AutomationConfigurationPath); var content = request.Configuration.AutomationMode is null ? $"_.NETLOAD\n\"{futureEntry.Replace("\"", "\\\"")}\"\n" - : $"_.NETLOAD\n\"{futureEntry.Replace("\"", "\\\"")}\"\nPHP_REC_AUTOMATION_START\n"; + : $"_.OPEN\n\"{fixture!.Replace("\"", "\\\"")}\"\n_.NETLOAD\n\"{futureEntry.Replace("\"", "\\\"")}\"\nPHP_REC_AUTOMATION_PREPARE\nPXP-OA\nPXP-OA\nPHP_REC_AUTOMATION_FINALIZE\n"; var scriptHash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(content))); var plan = PilotPlanFactory.Create(request.Product, profile, request.Manifest, request.ManifestHash, request.PilotRoot, request.StartupScriptPath, scriptHash, safety); var environment = new Dictionary { ["P_REC_ANNOT_RUNTIME_MODE"] = request.Configuration.PluginExecutionMode.ToString() }; @@ -50,6 +51,7 @@ public sealed class PilotExecutionService( environment["P_REC_ANNOT_APPLY_PERMIT_PATH"] = request.Configuration.ApplyPermitPath; if (request.Configuration.AutomationMode is not null) { + environment["P_REC_ANNOT_LAUNCHER_MODE"] = "NextPilot"; environment["P_REC_ANNOT_AUTOMATION"] = request.Configuration.AutomationMode; environment["P_REC_ANNOT_AUTOMATION_CONFIG"] = request.Configuration.AutomationConfigurationPath!; } @@ -104,7 +106,7 @@ public sealed class PilotExecutionService( Directory.CreateDirectory(Path.GetDirectoryName(scriptPath)!); var script = request.Preparation.Configuration.AutomationMode is null ? scriptBuilder.BuildNetloadScript(versionDirectory, request.Preparation.Manifest.EntryAssembly) - : scriptBuilder.BuildAutomationScript(versionDirectory, request.Preparation.Manifest.EntryAssembly); + : scriptBuilder.BuildAutomationScript(versionDirectory, request.Preparation.Manifest.EntryAssembly, ReadFixtureOrtho(request.Preparation.Configuration.AutomationConfigurationPath)); await File.WriteAllTextAsync(scriptPath, script, cancellationToken); await WriteSession(plan.PilotRoot, (await ReadSession(plan.PilotRoot, cancellationToken))! with { State = "ProcessStarting", UpdatedAtUtc = DateTimeOffset.UtcNow }, cancellationToken); var result = new GuardedAutodeskProcessLauncher(gate, probe, invoker).Start(context, plan with { StartupScriptPath = scriptPath, Process = plan.Process with { Arguments = ["/product", "PLNT3D", "/language", "ru-RU", "/b", scriptPath], WorkingDirectory = plan.PilotRoot } }, request.Permit, permits); @@ -124,6 +126,14 @@ public sealed class PilotExecutionService( } } + private static string ReadFixtureOrtho(string? automationConfigurationPath) + { + if (string.IsNullOrWhiteSpace(automationConfigurationPath) || !File.Exists(automationConfigurationPath)) throw new InvalidOperationException("AUTOMATION_CONFIG_MISSING"); + using var json = JsonDocument.Parse(File.ReadAllText(automationConfigurationPath)); + if (!json.RootElement.TryGetProperty("orthoDrawingPath", out var value) || string.IsNullOrWhiteSpace(value.GetString())) throw new InvalidOperationException("AUTOMATION_ORTHO_PATH_REQUIRED"); + return Path.GetFullPath(value.GetString()!); + } + public async Task RollbackAsync(PilotRollbackRequest request, CancellationToken cancellationToken) { try