461133235a
FOCAS, AbLegacy, AbCip, TwinCAT, S7 and OpcUaClient orphaned the previous probe/recycle/fixed-tree loops and clients when re-initialised on a live driver. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
51 lines
2.1 KiB
C#
51 lines
2.1 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.PlcFamilies;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests;
|
|
|
|
/// <summary>
|
|
/// #630 — <c>InitializeAsync</c> on a live driver (<c>DriverInstanceActor.AdoptConfigDuringInit</c> runs
|
|
/// it with no teardown first) overwrote <c>_devices[host]</c> and orphaned the previous state's
|
|
/// probe loop + runtime. It now releases them first.
|
|
/// </summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class AbLegacyReinitializeReleasesStateTests
|
|
{
|
|
/// <summary>Initialize twice: the first state's probe loop has stopped and its probe runtime is disposed.</summary>
|
|
[Fact]
|
|
public async Task SecondInitialize_StopsFirstStatesLoop_AndDisposesItsRuntime()
|
|
{
|
|
const string host = "ab://10.0.0.5/1,0";
|
|
var factory = new FakeAbLegacyTagFactory();
|
|
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
|
{
|
|
Devices = [new AbLegacyDeviceOptions(host, AbLegacyPlcFamily.Slc500)],
|
|
Probe = new AbLegacyProbeOptions { Enabled = true, Interval = TimeSpan.FromMilliseconds(20), ProbeAddress = "S:0" },
|
|
}, "drv-reinit", factory);
|
|
await drv.InitializeAsync("{}", CancellationToken.None);
|
|
|
|
var first = drv.GetDeviceState(host)!;
|
|
// Positive control: the first state's probe loop is running and has created + initialised its runtime.
|
|
var deadline = DateTime.UtcNow.AddSeconds(5);
|
|
while (!(factory.Tags.TryGetValue("S:0", out var t) && t.InitializeCount > 0))
|
|
{
|
|
if (DateTime.UtcNow > deadline) throw new TimeoutException("probe never started");
|
|
await Task.Delay(10);
|
|
}
|
|
var firstTag = factory.Tags["S:0"];
|
|
var firstLoop = first.ProbeTask;
|
|
firstLoop.ShouldNotBeNull();
|
|
firstLoop!.IsCompleted.ShouldBeFalse();
|
|
firstTag.Disposed.ShouldBeFalse();
|
|
|
|
await drv.InitializeAsync("{}", CancellationToken.None);
|
|
|
|
firstLoop.IsCompleted.ShouldBeTrue(); // joined
|
|
firstTag.Disposed.ShouldBeTrue();
|
|
drv.GetDeviceState(host).ShouldNotBeSameAs(first);
|
|
|
|
await drv.ShutdownAsync(CancellationToken.None);
|
|
}
|
|
}
|