Completes the Generic / HmEG-aware / App-specific separation started in
f6b6e74. The legacy EgPlugin / EngineBridge / EngineBridge.Client /
EngineBridge.Probe modules are moved into their proper tiers, namespaces
and csproj/sln entries are renamed, and the HmegDirectStateProvider
lambdas are finally populated with real handles from the EgBim plugin
host. A new Recordingtest.Architecture.Tests project enforces the tier
rule at build time.
Moves (git mv + csproj/RootNamespace/AssemblyName rename + sln):
src/Recordingtest.EgPlugin
-> src/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost
src/Recordingtest.EngineBridge
-> src/Hmeg/Recordingtest.Hmeg.Catalog
src/Recordingtest.EngineBridge.Client
-> src/Hmeg/Recordingtest.Hmeg.Bridge.Client
src/Recordingtest.EngineBridge.Probe
-> src/Hmeg/Recordingtest.Hmeg.Catalog.Probe
tests/Recordingtest.EgPlugin.Tests
-> tests/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost.Tests
tests/Recordingtest.EngineBridge.Tests
-> tests/Hmeg/Recordingtest.Hmeg.Catalog.Tests
tests/Recordingtest.EngineBridge.IntegrationTests
-> tests/Hmeg/Recordingtest.Hmeg.Catalog.IntegrationTests
Namespace rename applied across all .cs files and csproj RootNamespace:
Recordingtest.EgPlugin -> Recordingtest.Sut.EgBim.PluginHost
Recordingtest.EngineBridge -> Recordingtest.Hmeg.Catalog
Recordingtest.EngineBridge.Client -> Recordingtest.Hmeg.Bridge.Client
Recordingtest.EngineBridge.Probe -> Recordingtest.Hmeg.Catalog.Probe
New: tests/Recordingtest.Architecture.Tests/
DependencyGraphTests walks Assembly.GetReferencedAssemblies() for each
tier and fails if a forbidden reference leaks in:
- Generic modules must not reference HmEG or any app-specific DLL
- HmEG-aware modules must not reference app-specific DLLs
- Recordingtest.Hmeg.Bridge must reference HmEG (positive check)
11 tests, all passing. Prevents future drift from CLAUDE.md §8.1.
Engine-bridge v3 wire-up (HmEgBridgePlugin.BuildProvider):
Previously the HmegDirectStateProvider lambdas returned null and the
chain fell through to reflection. They now call directly into the
EditorPlugin base class that HmEgBridgePlugin inherits:
spaceProvider = () => RootSpace
// AppManager.ViewportManager.RootSpace
viewportProvider = () => View
// EGViewport : Control, HmEGViewport
documentPathProvider = () => AppManager?.FileManager?.CurrentFile
Every lambda is wrapped in try/catch so plugin construction still
cannot throw back into the SUT. Editor02.HmEGAppManager.dll added as
a reference on Recordingtest.Sut.EgBim.PluginHost.csproj — app-
specific tier, which is allowed by the architecture tests.
Entry points were confirmed from read-only review of the SUT sources at
D:\GiteaAll\EG-BIM_Modeler\EditorPluginInterface\EditorPlugin.cs
D:\GiteaAll\EG-BIM_Modeler\HmEGApplicationManagementLibrary\HmEGAppManager.cs
D:\GiteaAll\EG-BIM_Modeler\HmEGApplicationManagementLibrary\SubManager\FileManager.cs
closing out Q1/Q2/Q6/Q7 from docs/hmeg-api-survey.md.
Tests: 115 -> 126 (+11 Architecture), 0 failures.
Next step: live verification of /scene /camera /selection with a real
SUT session; any discrepancy in HmegDirectStateProvider reflection will
be tightened after observing real HmEG camera field names.
Ref: #10 follow-up, #14 follow-up, docs/contracts/generic-sut-split.md.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
109 lines
3.8 KiB
C#
109 lines
3.8 KiB
C#
using Editor.PluginInterface;
|
|
using HmEG;
|
|
using Recordingtest.Bridge;
|
|
using Recordingtest.Hmeg.Bridge;
|
|
|
|
namespace Recordingtest.Sut.EgBim.PluginHost;
|
|
|
|
/// <summary>
|
|
/// MEF/PluginLoader-discovered plugin. Inherits the SUT's <c>EditorPlugin</c>
|
|
/// abstract base (which itself implements <c>HmEG.IPlugin</c>), and on construction
|
|
/// boots a localhost HTTP bridge that exposes HmEG state to recordingtest.
|
|
/// </summary>
|
|
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.
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Choose the best available <see cref="IEngineStateProvider"/>:
|
|
///
|
|
/// 1. <c>HmegDirectStateProvider</c> — 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. <c>ReflectionEngineStateProvider</c> — fallback that uses
|
|
/// <c>IAppManagerAccessor</c> 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.
|
|
/// </summary>
|
|
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<Space?> spaceProvider = () =>
|
|
{
|
|
try { return RootSpace; }
|
|
catch { return null; }
|
|
};
|
|
|
|
Func<HmEGViewport?> 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<string?> 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;
|
|
}
|
|
}
|