VirtualTag → VirtualTag dependencies may never update: VT outputs don't go through DependencyMuxActor #791
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found while revising #787 (
f201f16f): VirtualTag outputs are not published throughDependencyMuxActor, which only carries driver publishes. So a VirtualTag whose dependency list names another VirtualTag's path appears never to receive that upstream's values or status, whether the upstream is enabled or disabled (BadOutOfService).This was read from code, not run. To do:
BadOutOfServicewhen A is disabled.ScriptStackGuardcovers recursion inside one evaluation, not cross-VT feedback loops), or reject VT→VT dependencies at deploy time with a clearDraftValidatorerror. Document which indocs/VirtualTags.md.ctx.GetTag()reads of a VT path inside scripts too: does a script read return the VT's current value?Fixed in
000d7f55(master).Step 1: confirmed broken.
Host.Tests/Engines/VirtualTagToVirtualTagFlowTestsruns a realDependencyMuxActor, a realVirtualTagHostActorand the productionRoslynVirtualTagEvaluator. VT B reads VT A by its key (ctx.GetTag("EQ-…/A")). On the old master A evaluated and published 10, but B never published anything: the test timed out at line 66. VT outputs only went to their OPC UA node, and the mux only carried driver values. A VT has no raw path. Its only identity is its UNS NodeId identifier{EquipmentId}/{Name}. The AdminUI completion offered the bare leaf name, which never resolved to anything.Step 2: chose routing, not rejection. Routing turned out to be small and contained, so there was no reason to fall back to rejecting VT→VT at deploy time.
VirtualTagHostActorstill publishes each result to its node exactly as before, so the publish path stays single-source. It also sends every real result to the mux asVirtualTagOutput, keyed by the same string. Re-asserts are not routed. A disabled VT'sBadOutOfServiceis routed too.DependencyMuxActorpins VT outputs. They are retained even before any dependent registers, never enter the replay grace, and a driverSourceRemovedcannot evict them. When a key leaves the deployed set,VirtualTagOutputsRemovedreleases it.DraftValidatoradds a blockingVirtualTagDependencyCycleerror. It covers A→B→A, a self-read, longer loops, and loops through a Calculation tag, with{{equip}}refs resolved. Disabled VTs are not graph nodes.EquipReferenceUnresolvednow names the key form when{{equip}}/<name>is actually a VT.ScriptTagCatalognow lists VTs by their key.VirtualTagNoEffectiveTriggeralready counts literal reads, so a VT whose only dependency is another VT passes. That is now correct, because the upstream value actually arrives.docs/VirtualTags.md.Evidence.
ctx.GetTagreturns A's current value.VirtualTagOutputRoutingTests(6) andDraftValidatorVirtualTagCycleTests(7).AKKA_TEST_TIMEFACTOR=3, all passing: Runtime 1001, Configuration 241, Core.VirtualTags 16, Host 270, OpcUaServer.Tests 1027, OpcUaServer.IntegrationTests 133, AdminUI 1968.