feat(mtconnect): one agent client per driver — a second /raw Device is a deploy error (#628)

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 14:29:50 -04:00
parent da33f27544
commit dcc827ca8c
5 changed files with 144 additions and 14 deletions
@@ -5,6 +5,7 @@ using ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors;
using ZB.MOM.WW.OtOpcUa.Commons.Types;
using ZB.MOM.WW.OtOpcUa.Configuration;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Validation;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Uns;
@@ -615,9 +616,16 @@ public sealed class RawTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
await using var db = await dbFactory.CreateDbContextAsync(ct);
if (!await db.DriverInstances.AnyAsync(d => d.DriverInstanceId == driverInstanceId, ct))
var owner = await db.DriverInstances.FirstOrDefaultAsync(d => d.DriverInstanceId == driverInstanceId, ct);
if (owner is null)
return new UnsMutationResult(false, $"Driver '{driverInstanceId}' not found.");
// #628: MTConnect owns one agent client per driver — mirror the deploy gate
// (DraftValidator.ValidateMTConnectSingleDevice) so the second Device is refused where it is authored.
if (string.Equals(owner.DriverType, DriverTypeNames.MTConnect, StringComparison.Ordinal)
&& await db.Devices.AnyAsync(dev => dev.DriverInstanceId == driverInstanceId, ct))
return new UnsMutationResult(false, DraftValidator.MTConnectMultipleDevicesMessage(owner.Name));
if (await db.Devices.AnyAsync(dev => dev.DriverInstanceId == driverInstanceId && dev.Name == name, ct))
return new UnsMutationResult(false, $"A device named '{name}' already exists on this driver.");