using Editor.PluginInterface; using HmEG; using Recordingtest.Bridge; using Recordingtest.Hmeg.Bridge; namespace Recordingtest.Sut.EgBim.PluginHost; /// /// MEF/PluginLoader-discovered plugin. Inherits the SUT's EditorPlugin /// abstract base (which itself implements HmEG.IPlugin), and on construction /// boots a localhost HTTP bridge that exposes HmEG state to recordingtest. /// public sealed class HmEgBridgePlugin : EditorPlugin, IDisposable { private BridgeHttpServer? _server; public HmEgBridgePlugin() { StartBridge(); } public override string Name => "Recordingtest.Sut.EgBim.PluginHost"; public override string Description => "recordingtest engine-bridge v3 (HTTP sidecar)"; protected override void Initialize() { // Construction already started the bridge; Initialize is a no-op safeguard. } private void StartBridge() { try { var port = PortResolver.Resolve(); var provider = BuildProvider(); var router = new StateRouter(provider, port); _server = new BridgeHttpServer(router, port); _server.Start(); } catch { // never throw out of plugin construction; SUT must remain stable. } } /// /// Choose the best available : /// /// 1. HmegDirectStateProvider — HmEG-aware tier, calls into HmEG /// public API. Reusable across any HmEG-hosting WPF app. Active /// space/viewport handles are resolved via lambdas; the EgBim /// app-specific entry-point lookup lives in this method only. /// 2. ReflectionEngineStateProvider — fallback that uses /// IAppManagerAccessor reflection on Editor.AppManager.AppManager. /// Used when the direct provider can't resolve any handle. /// /// Both providers are wrapped to never throw; the SUT must remain stable. /// private IEngineStateProvider BuildProvider() { // EG-BIM Modeler entry points (resolved via EditorPlugin base): // RootSpace = AppManager.ViewportManager.RootSpace // View (EGViewport : HmEGViewport) — plugin-injected active viewport // AppManager.FileManager.CurrentFile — document path on disk // // Each lambda is wrapped in try/catch so plugin construction never // throws even if the host state is in flight at boot time. Func spaceProvider = () => { try { return RootSpace; } catch { return null; } }; Func viewportProvider = () => { try { // EGViewport implements HmEGViewport; the base class exposes it // as EGViewport on the Obsolete View property. We catch and // return null to survive the obsolete warning at runtime. #pragma warning disable CS0618 // Obsolete API on EditorPlugin.View return View; #pragma warning restore CS0618 } catch { return null; } }; Func documentPathProvider = () => { try { var path = AppManager?.FileManager?.CurrentFile; return string.IsNullOrEmpty(path) ? null : path; } catch { return null; } }; var direct = new HmegDirectStateProvider(spaceProvider, viewportProvider, documentPathProvider); var fallback = new ReflectionEngineStateProvider(this); return new ChainedEngineStateProvider(direct, fallback); } public void Dispose() { try { _server?.Dispose(); } catch { } _server = null; } }