Auto: twincat-3.2 — cycle-time / jitter / PLC-state diagnostics
Closes #314
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.TwinCAT;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// PR 3.2 / issue #314 — wire-level coverage for the cycle-time / jitter / online-change
|
||||
/// diagnostics surfaced through the probe loop. Skipped via <see cref="TwinCATFactAttribute"/>
|
||||
/// when the XAR VM isn't reachable. The four well-known system symbols
|
||||
/// (<c>TwinCAT_SystemInfoVarList._AppInfo.OnlineChangeCnt</c>, <c>_AppInfo.AppName</c>,
|
||||
/// <c>_TaskInfo[1].CycleTime</c>, <c>_TaskInfo[1].LastExecTime</c>) are always exported by
|
||||
/// a live TC3 PLC runtime — no extra fixture state required beyond the existing
|
||||
/// <c>GVL_Fixture</c> / <c>MAIN</c> setup documented in <c>TwinCatProject/README.md</c>.
|
||||
/// </summary>
|
||||
[Collection("TwinCATXar")]
|
||||
[Trait("Category", "Integration")]
|
||||
[Trait("Simulator", "TwinCAT-XAR")]
|
||||
public sealed class TwinCATDiagnosticsIntegrationTests(TwinCATXarFixture sim)
|
||||
{
|
||||
[TwinCATFact]
|
||||
public async Task Probe_loop_surfaces_cycle_time_and_online_change_count()
|
||||
{
|
||||
if (sim.SkipReason is not null) Assert.Skip(sim.SkipReason);
|
||||
|
||||
// Probe interval kept tight (250 ms) so the test doesn't have to wait the default 5 s
|
||||
// between probe ticks. The four system symbols are scalar UDINTs / a STRING(80), so
|
||||
// the diagnostics sample completes well within the 250 ms budget on any healthy
|
||||
// runtime.
|
||||
var options = new TwinCATDriverOptions
|
||||
{
|
||||
Devices = [
|
||||
new TwinCATDeviceOptions(
|
||||
HostAddress: $"ads://{sim.TargetNetId}:{sim.AmsPort}",
|
||||
DeviceName: "XAR-VM"),
|
||||
],
|
||||
// No Tags — diagnostics doesn't need any user-declared symbols.
|
||||
Tags = [],
|
||||
UseNativeNotifications = false,
|
||||
Timeout = TimeSpan.FromSeconds(5),
|
||||
Probe = new TwinCATProbeOptions
|
||||
{
|
||||
Enabled = true,
|
||||
Interval = TimeSpan.FromMilliseconds(250),
|
||||
},
|
||||
};
|
||||
|
||||
await using var drv = new TwinCATDriver(options, driverInstanceId: "tc3-diag-probe");
|
||||
await drv.InitializeAsync("{}", TestContext.Current.CancellationToken);
|
||||
|
||||
// Wait up to 5 s for the probe loop to fire at least once + populate the snapshot.
|
||||
// 250 ms cycle × 20 attempts ≈ 5 s budget — generous enough for AMS handshake on a
|
||||
// freshly-restarted runtime.
|
||||
var hostAddress = $"ads://{sim.TargetNetId}:{sim.AmsPort}";
|
||||
TwinCATDeviceDiagnostics? snap = null;
|
||||
for (var i = 0; i < 20 && snap is null; i++)
|
||||
{
|
||||
await Task.Delay(250, TestContext.Current.CancellationToken);
|
||||
snap = drv.GetDeviceDiagnostics(hostAddress);
|
||||
}
|
||||
|
||||
snap.ShouldNotBeNull(
|
||||
"probe loop must populate TwinCATDeviceDiagnostics within ~5 s on a reachable XAR runtime");
|
||||
snap.CycleTimeMs.ShouldBeGreaterThan(0,
|
||||
"TwinCAT_SystemInfoVarList._TaskInfo[1].CycleTime is non-zero on every running PLC task");
|
||||
snap.OnlineChangeCnt.ShouldBeGreaterThanOrEqualTo(0u,
|
||||
"OnlineChangeCnt is a UDINT counter — non-negative by type, present on every TC3 runtime");
|
||||
// AppName is best-effort but on a project-loaded runtime it should be non-empty.
|
||||
snap.AppName.ShouldNotBeNull("running PLC project always reports an AppName");
|
||||
|
||||
// The cross-driver DriverHealth.Diagnostics dictionary should also reflect the same
|
||||
// values so the driver-diagnostics RPC has a consistent surface.
|
||||
var dict = drv.GetHealth().DiagnosticsOrEmpty;
|
||||
dict["TwinCAT.CycleTimeMs"].ShouldBe(snap.CycleTimeMs);
|
||||
dict["TwinCAT.OnlineChangeCnt"].ShouldBe(snap.OnlineChangeCnt);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user