8.9 KiB
TwinCAT XAR fixture project
This folder holds the TwinCAT 3 XAE project that the XAR VM runs for the
integration-tests suite (tests/.../TwinCAT.IntegrationTests/*.cs).
Status today: stub. The .tsproj isn't committed yet; once the XAR
VM is up + a project with the required state exists, export via
File → Export + drop it here as OtOpcUaTwinCatFixture.tsproj + its
PLC .library / .plcproj companions.
Why .tsproj, not the binary bootproject
TwinCAT ships two project forms: the XAE .tsproj (XML, source of
truth) and the compiled bootproject that the XAR runtime actually
loads. Ship the .tsproj because:
- Text format — reviewable in PR diffs, diffable in git
- Rebuildable across TC3 engineering versions (the XAE tool rebuilds
the bootproject from
.tsprojon "Activate Configuration") - Doesn't carry per-install state (target AmsNetId, source licensing)
Reconstruction workflow on the VM:
- Open TC3 XAE (Visual Studio shell)
- File → Open →
OtOpcUaTwinCatFixture.tsproj - Target system → the VM's AmsNetId (set in System Manager → Routes)
- Build → Build Solution (produces the bootproject)
- Activate Configuration → Run Mode (deploys to XAR + starts the runtime)
Required project state
The smoke tests in TwinCAT3SmokeTests.cs depend on this exact GVL +
PLC setup. Missing or renamed symbols surface as ADS DeviceSymbolNotFound
or wrong-type read failures, not silent skips.
Global Variable List: GVL_Fixture
VAR_GLOBAL
// Monotonically-increasing counter; MAIN increments each cycle.
// Seed value 1234 picked so the smoke test can assert ">= 1234" without
// synchronising with the initial cycle.
nCounter : DINT := 1234;
// Scratch REAL for write-then-read round-trip test. Smoke test writes
// 42.5 + reads back.
rSetpoint : REAL := 0.0;
// Readable boolean with seed value TRUE. Reserved for future
// expansion (e.g. discovery / symbol-browse tests).
bFlag : BOOL := TRUE;
END_VAR
PLC program: MAIN
PROGRAM MAIN
VAR
END_VAR
// One-line program: increment the fixture counter every cycle.
// The native-notification smoke test subscribes to GVL_Fixture.nCounter
// + observes the monotonic changes without a write path.
GVL_Fixture.nCounter := GVL_Fixture.nCounter + 1;
Task
PlcTask— cyclic, 10 ms interval, priority 20- Assigned to
MAIN
Note (PR 3.1 / #313):
GVL_Fixture.nCounterdoubles as the coalescing-test driver forTwinCATMaxDelayTests. The 10 ms cycle + per-cycle increment inMAINmeans a no-coalescing subscriber sees ~100 events / s; withMaxDelayMs=500the test asserts ≤ 3 events / s. No new project state required.
Performance scenarios
PR 2.1 (ADS Sum-read / Sum-write) ships an opt-in perf-tier integration test
(TwinCATSumCommandPerfTests.Driver_sum_read_1000_tags_beats_loop_baseline_by_5x)
that reads 1000 DINTs in one shot and asserts the bulk path beats the per-tag
loop by ≥ 5×. The fixture state required by that test is:
Global Variable List: GVL_Perf
VAR_GLOBAL
// 1000-DINT array — exercised by the bulk Sum-read benchmark.
aTags : ARRAY[1..1000] OF DINT;
fbPerfChurn : FB_PerfChurn;
END_VAR
The XAE-form GVL ships at PLC/GVLs/GVL_Perf.TcGVL; import it into the PLC
project alongside GVL_Fixture.
POU: FB_PerfChurn
FUNCTION_BLOCK FB_PerfChurn
VAR
nIndex : INT := 1;
END_VAR
GVL_Perf.aTags[nIndex] := GVL_Perf.aTags[nIndex] + 1;
nIndex := nIndex + 1;
IF nIndex > 1000 THEN
nIndex := 1;
END_IF
The XAE-form POU ships at PLC/POUs/FB_PerfChurn.TcPOU. Wire it into MAIN
so a value rotates each cycle:
PROGRAM MAIN
VAR
END_VAR
// existing GVL_Fixture line:
GVL_Fixture.nCounter := GVL_Fixture.nCounter + 1;
// PR 2.1 — keep aTags moving so caches don't short-circuit the read.
GVL_Perf.fbPerfChurn();
Running the perf tier
$env:TWINCAT_TARGET_HOST = '10.0.0.42'
$env:TWINCAT_TARGET_NETID = '5.23.91.23.1.1'
$env:TWINCAT_PERF = '1'
dotnet test tests\ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests `
--filter "Category=Performance"
Without TWINCAT_PERF=1 the perf test skips via [TwinCATPerfFact] even when
the runtime is reachable — perf runs are opt-in to keep the default integration
pass fast.
Runtime ID
- TC3 PLC runtime 1 (AMS port
851) — the smoke-test fixture defaults to this. Use runtime 2 / port852only if the single runtime is already taken by another project on the same VM.
XAR VM setup (one-time)
Full bootstrap lives in docs/v2/dev-environment.md. The TwinCAT-specific
steps:
- Create the Hyper-V VM — Gen 2, Windows 10/11 64-bit, 4 GB RAM,
2 CPUs. External virtual switch so the dev box can reach
<vm-ip>:48898. - Install TwinCAT 3 XAE + XAR — free download from Beckhoff
(
www.beckhoff.com/en-en/products/automation/twincat/). Activate the 7-day trial on first boot. - Note the VM's AmsNetId — shown in the TwinCAT system tray icon →
Properties → AMS NetId (format like
5.23.91.23.1.1). - Configure bilateral ADS route:
- On the VM: System Manager → Routes → Add Route → dev box's AmsNetId + IP
- On the dev box: edit
%TC_INSTALLPATH%\Target\StaticRoutes.xml(or use the dev box's own TwinCAT System Manager if installed) to add the VM's AmsNetId + IP
- Import this project per the reconstruction workflow above.
- Hit Activate Configuration + Run Mode. The runtime starts; the
system tray icon goes green; port
48898is live.
License rotation
The XAR trial expires every 7 days. When it lapses:
- The runtime goes silent (red tray icon, ADS port
48898stops responding to new connections). - Integration tests skip with the reason message pointing at this folder's README.
- Operator runs
C:\TwinCAT\3.1\Target\StartUp\TcActivate.exe /reactivateon the VM console (not RDP — the trial activation wants the interactive-login desktop).
Options to eliminate the manual step:
- Scheduled task that runs the reactivate every 6 days at 02:00 — documented in the Beckhoff forums as working for some TC3 builds, not officially supported.
- Paid runtime license (~$1k one-time per runtime, per CPU) — kills the rotation permanently, worth it if the integration host is long-lived.
Online-change test scenario
PR 2.3 (proactive Symbol-Version invalidation listener) ships an
operator-gated integration test
(TwinCATSymbolVersionTests.Driver_invalidates_handle_cache_on_symbol_version_bump)
that verifies AdsTwinCATClient's AdsSymbolVersionChanged listener
wipes the handle cache when the PLC re-initialises after an online
change. The test polls for up to 60 s waiting for the operator to
trigger the change from XAE.
The fixture state (GVL_Perf + aTags[1..1000]) is the same one used by
the Sum-read perf test — no new project state required.
Manual workflow
With the XAR runtime live + the test process polling:
- Open the project in XAE on the dev box (or wherever XAE runs).
- Add a dummy variable to
GVL_Perf— any new declaration triggers a symbol-table rebuild. Example: appendbSymVerProbe : BOOL := FALSE;to the GVL. - Login (
Ctrl+F8) — XAE prompts to load the change. - Activate Configuration (Yellow-arrow button, or
TwinCAT → Activate Configuration). The runtime re-initialises; the symbol-version counter increments;AdsTwinCATClient.OnAdsSymbolVersionChangedfires; the handle cache wipes; the test polls observeSymbolVersionBumps > 0+ asserts the post-bump read recreates handles.
The test skips by default — opt in by setting
TWINCAT_MANUAL_ONLINE_CHANGE=1 alongside the standard
TWINCAT_TARGET_HOST / TWINCAT_TARGET_NETID env vars before kicking
off the test run.
$env:TWINCAT_TARGET_HOST = '10.0.0.42'
$env:TWINCAT_TARGET_NETID = '5.23.91.23.1.1'
$env:TWINCAT_MANUAL_ONLINE_CHANGE = '1'
dotnet test tests\ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests `
--filter "FullyQualifiedName~TwinCATSymbolVersionTests"
How to run the TwinCAT-tier tests
On the dev box:
$env:TWINCAT_TARGET_HOST = '10.0.0.42' # replace with the VM IP
$env:TWINCAT_TARGET_NETID = '5.23.91.23.1.1' # replace with the VM AmsNetId
# $env:TWINCAT_TARGET_PORT = '852' # only if not using PLC runtime 1
dotnet test tests\ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests
With any of those env vars unset, all three smoke tests skip cleanly via
[TwinCATFact]; unit suite (TwinCAT.Tests) runs unchanged.
See also
docs/drivers/TwinCAT-Test-Fixture.md— coverage mapdocs/v2/dev-environment.md§Integration host — VM + route + license-rotation notes- Beckhoff Information System → TwinCAT 3 → Product overview + ADS + PLC reference (licensed; internal link only)