Fix debugger polling to fetch only visible variables#646
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughCache and centralize derivation of composite debug/polling variable keys per active POU/FB context, add editor-model-aware rescan triggers, extend variable detection to editor source (ST/IL), bracketed/nested paths, and FB/_TMP handling; invalidate cache on context changes and teardown. (≈34 words) Changes
Sequence Diagram(s)sequenceDiagram
participant Editor as Editor (Monaco)
participant Workspace as WorkspaceScreen
participant Poll as PollingService
participant Runtime as PLC Runtime
participant Store as VariableStore
Editor->>Workspace: notify active model / POU / modelVersion change
Workspace->>Workspace: validate/compute diagramVarKeysCache (composite keys from diagram + source)
Workspace->>Poll: register/batch composite keys to poll
Poll->>Runtime: request values for composite keys
Runtime-->>Poll: return polled values
Poll->>Store: write values mapped to composite keys (including _TMP & nested keys)
Store->>Workspace: notify updated values
Workspace->>Editor: render debug badges / inline values
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/renderer/screens/workspace-screen.tsx (2)
1179-1191: Avoid duplicating composite-key rules.
makeCompositeKeyForCurrentPou()duplicates the same format logic already used byuseDebugCompositeKey. A shared pure utility would reduce key-format drift risk between polling and badge lookups.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/screens/workspace-screen.tsx` around lines 1179 - 1191, makeCompositeKeyForCurrentPou duplicates the composite-key formatting already implemented in useDebugCompositeKey; extract the shared formatting logic into a pure utility (e.g., formatDebugCompositeKey or buildCompositeKey) and replace the implementations in makeCompositeKeyForCurrentPou and useDebugCompositeKey to call that utility, ensuring the utility accepts the necessary inputs (POU info, fbTypeKey/selectedInstance or variableName) and returns the canonical string used for both polling and badge lookups to avoid drift.
1247-1263: Poll only block outputs that can actually render badges.
outputVarscurrently includes all outputs, but connected outputs are not always badge-visible in the UI. Filtering to truly visible outputs would better match the visible-only polling goal and trim unnecessary reads.Also applies to: 1307-1324
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/screens/workspace-screen.tsx` around lines 1247 - 1263, outputVars currently includes all outputs but we should only poll ones that can render badges; create/use a predicate (e.g., isBadgeVisibleOutput(outVar)) that returns true for outputs that are actually badge-visible and replace the existing outputVars usage with outputVars.filter(isBadgeVisibleOutput) before iterating in both the 'function-block' branch and the 'function' branch (where tmpName is built), and likewise update the other occurrence at lines 1307-1324; ensure you call makeCompositeKeyForCurrentPou only for those filtered outputs and add the resulting keys to debugVariableKeys as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/renderer/screens/workspace-screen.tsx`:
- Around line 1319-1326: The code adds `_TMP_` composite keys only during one
traversal path, causing FBD-bodied custom function block (variantType ===
'function') outputs to miss being registered and thus never resolving indexes
via variableInfoMapRef; update the traversal that handles custom FBs (the branch
that processes variantType === 'function' using variantName,
blockData.numericId, and outputVars) to always call
makeCompositeKeyForCurrentPou for each
`_TMP_${variantName.toUpperCase()}${numericId}_${outVar.name}` and add the
resulting compositeKey to debugVariableKeys (the same way LD bodies do),
ensuring FBD-bodied custom FBs also register `_TMP_` outputs so
variableInfoMapRef lookups can resolve indexes and badges render correctly.
---
Nitpick comments:
In `@src/renderer/screens/workspace-screen.tsx`:
- Around line 1179-1191: makeCompositeKeyForCurrentPou duplicates the
composite-key formatting already implemented in useDebugCompositeKey; extract
the shared formatting logic into a pure utility (e.g., formatDebugCompositeKey
or buildCompositeKey) and replace the implementations in
makeCompositeKeyForCurrentPou and useDebugCompositeKey to call that utility,
ensuring the utility accepts the necessary inputs (POU info,
fbTypeKey/selectedInstance or variableName) and returns the canonical string
used for both polling and badge lookups to avoid drift.
- Around line 1247-1263: outputVars currently includes all outputs but we should
only poll ones that can render badges; create/use a predicate (e.g.,
isBadgeVisibleOutput(outVar)) that returns true for outputs that are actually
badge-visible and replace the existing outputVars usage with
outputVars.filter(isBadgeVisibleOutput) before iterating in both the
'function-block' branch and the 'function' branch (where tmpName is built), and
likewise update the other occurrence at lines 1307-1324; ensure you call
makeCompositeKeyForCurrentPou only for those filtered outputs and add the
resulting keys to debugVariableKeys as before.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/renderer/screens/workspace-screen.tsx
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/renderer/screens/workspace-screen.tsx (1)
1344-1351:⚠️ Potential issue | 🟠 MajorFBD function
_TMP_keys can still be enqueued without resolvable indexes in custom FB internals.These lines add
_TMP_...keys for visible FBD function outputs, but index registration for nested custom FB internals is still LD-only (visitFbInstancebranch at Line 883). For FBD-bodied custom FBs, keys may never appear invariableInfoMap, so badges remain empty.🔧 Proposed fix direction
- // 3. Process function temps in ladder (if this FB has a ladder body) - if (fbPou.data.body.language === 'ld') { + // 3. Process function temps in ladder/FBD (if this FB has those bodies) + const registerFunctionTempsInNodes = (nodes: Array<{ type?: string; data: object }>) => { + nodes.forEach((node) => { + if (node.type !== 'block') return + // existing function `_TMP_` extraction + getFieldIndexFromMapWithFallback(...) + addVariableInfo(...) + }) + } + + if (fbPou.data.body.language === 'ld') { const fbLadderFlow = ladderFlows.find((flow) => flow.name === fbPou.data.name) if (fbLadderFlow) { - // existing LD traversal... + fbLadderFlow.rungs.forEach((rung) => registerFunctionTempsInNodes(rung.nodes)) } + } else if (fbPou.data.body.language === 'fbd') { + const fbFbdFlow = fbdFlows.find((flow) => flow.name === fbPou.data.name) + if (fbFbdFlow) { + registerFunctionTempsInNodes(fbFbdFlow.rung.nodes) + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/screens/workspace-screen.tsx` around lines 1344 - 1351, The code is adding `_TMP_...` composite keys for FBD function outputs even when nested custom FB internals (FBD-bodied FBs) never register indexes, causing badges to remain empty; update the block that handles variantType === 'function' (using variantName, blockData.numericId, debugVariableKeys, makeCompositeKeyForCurrentPou) to only add tmp keys when the index can be resolved (e.g., check variableInfoMap.has(compositeKey) or that makeCompositeKeyForCurrentPou returns an index-aware key), or alternatively invoke the same index-registration logic used by visitFbInstance for FBD-bodied custom FBs so those internals register their variable indexes before adding to debugVariableKeys; ensure the guard prevents enqueueing `_TMP_` keys that will never appear in variableInfoMap.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/renderer/screens/workspace-screen.tsx`:
- Around line 1344-1351: The code is adding `_TMP_...` composite keys for FBD
function outputs even when nested custom FB internals (FBD-bodied FBs) never
register indexes, causing badges to remain empty; update the block that handles
variantType === 'function' (using variantName, blockData.numericId,
debugVariableKeys, makeCompositeKeyForCurrentPou) to only add tmp keys when the
index can be resolved (e.g., check variableInfoMap.has(compositeKey) or that
makeCompositeKeyForCurrentPou returns an index-aware key), or alternatively
invoke the same index-registration logic used by visitFbInstance for FBD-bodied
custom FBs so those internals register their variable indexes before adding to
debugVariableKeys; ensure the guard prevents enqueueing `_TMP_` keys that will
never appear in variableInfoMap.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
Run ID: 6de37e52-fb09-488a-84a8-bf327cff59a9
📒 Files selected for processing (1)
src/renderer/screens/workspace-screen.tsx
Add an ST/IL section to the debugger polling tick that scans the POU source text for variable references and adds them to debugVariableKeys. This follows the same pattern as the LD/FBD sections — only variables that actually appear in the code are polled, not every declared variable. Also polls FB instance sub-variables (e.g., TON0.ET, TON0.Q) when the instance name appears in the source text. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… mixing When multiple ST/IL function blocks are open in tabs, switching between them causes the debugVarPositions memo to run with the new POU name but the old Monaco model (since @monaco-editor/react reuses the component with keepCurrentModel). This resets editorMounted to false on name change so the memo returns null until handleEditorDidMount fires with the correct model. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/renderer/screens/workspace-screen.tsx (1)
1385-1412: Consider indexing variableInfoMap by prefix for faster FB child lookups.This loop iterates all entries in
variableInfoMapReffor every FB instance found in source text. With many FB instances and a large variable map, this becomes O(fbInstances × totalVariables) per poll tick.A prefix-based index (e.g.,
Map<string, VariableInfo[]>keyed byfbInstance.nameorselectedInstance.fbVariableName.fbInstance.name) could reduce this to O(fbInstances + matchingVariables).💡 Suggested optimization direction
// Build prefix index once when variableInfoMap is constructed const varInfoByPrefix = new Map<string, VariableInfo[]>() variableInfoMap.forEach((varInfos, _index) => { for (const varInfo of varInfos) { const firstDot = varInfo.variable.name.indexOf('.') if (firstDot > 0) { const prefix = varInfo.variable.name.substring(0, firstDot) const key = `${varInfo.pouName}:${prefix}` const existing = varInfoByPrefix.get(key) || [] existing.push(varInfo) varInfoByPrefix.set(key, existing) } } }) // Then in polling, lookup by prefix instead of full iteration const prefixKey = currentPou.type === 'function-block' ? `${selectedInstance.programName}:${selectedInstance.fbVariableName}.${fbInstance.name}` : `${currentPou.data.name}:${fbInstance.name}` const matchingVars = varInfoByPrefix.get(prefixKey) || []🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/screens/workspace-screen.tsx` around lines 1385 - 1412, The loop over Array.from(variableInfoMapRef.current.entries()) is O(totalVariables) per FB instance causing heavy CPU load; build a prefix index (e.g., varInfoByPrefix: Map<string, VariableInfo[]>) when variableInfoMapRef is constructed using keys like `${pouName}:${prefix}` where prefix is the leading segment of varInfo.variable.name, then replace the full-iteration in the polling logic (the block using currentPou, fbSelectedInstance, fbDebugInstances, fbInstance and writing into debugVariableKeys) with a lookup into varInfoByPrefix (compute the same prefixKey for function-block and program POUs) and iterate only the returned matchingVars to add `${varInfo.pouName}:${varInfo.variable.name}` to debugVariableKeys.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/renderer/screens/workspace-screen.tsx`:
- Around line 1385-1412: The loop over
Array.from(variableInfoMapRef.current.entries()) is O(totalVariables) per FB
instance causing heavy CPU load; build a prefix index (e.g., varInfoByPrefix:
Map<string, VariableInfo[]>) when variableInfoMapRef is constructed using keys
like `${pouName}:${prefix}` where prefix is the leading segment of
varInfo.variable.name, then replace the full-iteration in the polling logic (the
block using currentPou, fbSelectedInstance, fbDebugInstances, fbInstance and
writing into debugVariableKeys) with a lookup into varInfoByPrefix (compute the
same prefixKey for function-block and program POUs) and iterate only the
returned matchingVars to add `${varInfo.pouName}:${varInfo.variable.name}` to
debugVariableKeys.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 029de1ba-8858-45af-a855-6e964e7ebf1c
📒 Files selected for processing (2)
src/renderer/components/_features/[workspace]/editor/monaco/index.tsxsrc/renderer/screens/workspace-screen.tsx
The previous fix (resetting editorMounted on name change) didn't work
because @monaco-editor/react's onMount only fires once on initial mount,
not on subsequent path/model switches with keepCurrentModel={true}.
New approach:
- Listen to editor.onDidChangeModel to detect when the library switches
models (tab change), then bump a modelVersion counter to trigger the
debugVarPositions memo recomputation.
- Add a URI guard inside the memo that verifies the current model matches
the expected POU path, preventing position scanning against a stale
model during the render before the model switch completes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The LD/FBD node scanning and ST/IL regex matching produce the same result every 50ms poll tick since the editor is read-only during debug. This caches the computed variable key set in a ref, keyed by POU name, language, and FB instance context. The cache is only invalidated when the user switches POUs or changes FB instance selection. Also hoists the FB instance context resolution (fbSelectedInstance, fbDebugInstances lookups) outside the inner loops in the ST/IL FB children section, so it's resolved once per cache rebuild instead of once per varInfo iteration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/renderer/screens/workspace-screen.tsx (1)
1392-1401:⚠️ Potential issue | 🟠 MajorFBD
_TMP_polling keys can still fail to resolve for custom FB bodies.You correctly add
_TMP_...keys for FBD blocks here, but index registration still appears LD-only invisitFbInstance(function temp handling starts at Line 894 and branches only onlanguage === 'ld'). That can leave FBD-bodied custom FB function outputs withoutvariableInfoMapRefentries, so badges remain empty.🔧 Fix direction
- // 3. Process function temps in ladder (if this FB has a ladder body) - if (fbPou.data.body.language === 'ld') { - // existing LD handling... - } + // 3. Process function temps in ladder/FBD + const registerFbFunctionTemps = (nodes: Array<{ type?: string; data: object }>) => { + // mirror existing LD block-function temp registration: + // - compute _TMP_${blockName}${numericId}_${output} + // - resolve via getFieldIndexFromMapWithFallback(debugVariableIndexes, debugPathPrefix, tmpName) + // - add VariableInfo with name `${variablePathPrefix}.${tmpName}` + } + + if (fbPou.data.body.language === 'ld') { + // iterate ladder rungs and call registerFbFunctionTemps(rung.nodes) + } else if (fbPou.data.body.language === 'fbd') { + const fbFbdFlow = fbdFlows.find((flow) => flow.name === fbPou.data.name) + if (fbFbdFlow) { + registerFbFunctionTemps(fbFbdFlow.rung.nodes) + } + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/screens/workspace-screen.tsx` around lines 1392 - 1401, The `_TMP_...` composite keys are being added in the output key loop (variantType/variantName/blockData.numericId/outputVars/makeCompositeKey/newKeys) but visitFbInstance only registers temp variable entries when language === 'ld', leaving FBD-bodied custom FB outputs unregistered in variableInfoMapRef and badges empty; update visitFbInstance (the function handling temp variables around the language === 'ld' branch) to also register those `_TMP_` variableInfoMapRef entries for FBD-bodied instances (either by changing the condition to include 'fbd' or by detecting custom FB bodies and running the same registration logic) so that the composite keys created earlier have corresponding variableInfoMapRef entries.
🧹 Nitpick comments (1)
src/renderer/screens/workspace-screen.tsx (1)
1294-1334: Consider extracting shared block-output key derivation for LD/FBD.The function-block/function output handling is duplicated across LD and FBD branches with nearly identical logic. A small shared helper would reduce drift risk and make future fixes (e.g.,
_TMP_behavior) safer.Also applies to: 1362-1401
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/screens/workspace-screen.tsx` around lines 1294 - 1334, Extract the duplicated logic that derives composite keys for block outputs into a single helper (e.g., deriveBlockOutputCompositeKeys) and call it from both LD and FBD branches instead of repeating the code; the helper should accept the block data (the object currently typed as blockData), the makeCompositeKey function, and a collector (e.g., newKeys Set) and implement the two cases currently inlined: when variant.type === 'function-block' use blockData.variable?.name to build keys from outputVars, and when variant.type === 'function' use variant.name and blockData.numericId to build `_TMP_${variantName.toUpperCase()}${numericId}_${outVar.name}` keys; replace both the code around makeCompositeKey/newKeys (lines referencing outputVars, instanceName, variantName, numericId) with a single call to the new helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/renderer/screens/workspace-screen.tsx`:
- Line 1234: The forEach callback is implicitly returning the result of
debugVariableKeys.add(key), triggering the lint rule; update the callbacks (the
one using cached.keys.forEach and the similar callback around
debugVariableKeys.add at the other occurrence) to use an explicit block body
that performs the add without returning a value (e.g., change the arrow callback
to { debugVariableKeys.add(key); }) or replace with a for...of loop so the
callback does not return a value.
---
Duplicate comments:
In `@src/renderer/screens/workspace-screen.tsx`:
- Around line 1392-1401: The `_TMP_...` composite keys are being added in the
output key loop
(variantType/variantName/blockData.numericId/outputVars/makeCompositeKey/newKeys)
but visitFbInstance only registers temp variable entries when language === 'ld',
leaving FBD-bodied custom FB outputs unregistered in variableInfoMapRef and
badges empty; update visitFbInstance (the function handling temp variables
around the language === 'ld' branch) to also register those `_TMP_`
variableInfoMapRef entries for FBD-bodied instances (either by changing the
condition to include 'fbd' or by detecting custom FB bodies and running the same
registration logic) so that the composite keys created earlier have
corresponding variableInfoMapRef entries.
---
Nitpick comments:
In `@src/renderer/screens/workspace-screen.tsx`:
- Around line 1294-1334: Extract the duplicated logic that derives composite
keys for block outputs into a single helper (e.g.,
deriveBlockOutputCompositeKeys) and call it from both LD and FBD branches
instead of repeating the code; the helper should accept the block data (the
object currently typed as blockData), the makeCompositeKey function, and a
collector (e.g., newKeys Set) and implement the two cases currently inlined:
when variant.type === 'function-block' use blockData.variable?.name to build
keys from outputVars, and when variant.type === 'function' use variant.name and
blockData.numericId to build
`_TMP_${variantName.toUpperCase()}${numericId}_${outVar.name}` keys; replace
both the code around makeCompositeKey/newKeys (lines referencing outputVars,
instanceName, variantName, numericId) with a single call to the new helper.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 7035c3c4-dd9c-4e0b-a7c7-8e42adfc71a9
📒 Files selected for processing (1)
src/renderer/screens/workspace-screen.tsx
| cached.fbContextKey === fbContextKey | ||
| ) { | ||
| // Reuse cached keys | ||
| cached.keys.forEach((key) => debugVariableKeys.add(key)) |
There was a problem hiding this comment.
Fix forEach callbacks that implicitly return a value (Biome error).
At Line 1234 and Line 1460, Set.add(...) is returned from the callback expression, which triggers lint/suspicious/useIterableCallbackReturn.
🔧 Minimal fix
- cached.keys.forEach((key) => debugVariableKeys.add(key))
+ cached.keys.forEach((key) => {
+ debugVariableKeys.add(key)
+ })
@@
- newKeys.forEach((key) => debugVariableKeys.add(key))
+ newKeys.forEach((key) => {
+ debugVariableKeys.add(key)
+ })Also applies to: 1460-1460
🧰 Tools
🪛 Biome (2.4.6)
[error] 1234-1234: This callback passed to forEach() iterable method should not return a value.
(lint/suspicious/useIterableCallbackReturn)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/screens/workspace-screen.tsx` at line 1234, The forEach callback
is implicitly returning the result of debugVariableKeys.add(key), triggering the
lint rule; update the callbacks (the one using cached.keys.forEach and the
similar callback around debugVariableKeys.add at the other occurrence) to use an
explicit block body that performs the add without returning a value (e.g.,
change the arrow callback to { debugVariableKeys.add(key); }) or replace with a
for...of loop so the callback does not return a value.
8817969
into
Autonomy-Logic:development
Reimplements the optimization from PR #646 using the refactored port architecture. The debug polling hook now filters variables to only poll what is actively needed: watched (debug=true), forced, graph-listed, diagram-visible (LD/FBD node scan), source-visible (ST/IL regex), and expansion-aware nested children. Diagram/source scan results are cached per {pouName, language, fbContext} since the editor is read-only during debug. This reduces polling traffic by 100-500x on large projects, restoring interactive update speed on Modbus RTU serial targets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When two textual POUs (ST/IL) are open in tabs, debug badges showed in wrong positions or disappeared because the debugVarPositions memo scanned a stale model during tab switches. Ports the remaining fixes from PR #646 (commits 039d382 and 681f519): - Add modelVersion state bumped by onDidChangeModel listener to detect when Monaco swaps models on tab change with keepCurrentModel - Add URI guard in debugVarPositions memo to prevent scanning a model that doesn't match the current POU during the React/Monaco race - Add modelVersion to the memo dependency array so it re-scans after the model swap completes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Commit message
fix(debugger): poll only visible/used variables (avoid polling entire POU)
PR: Reduce debugger polling to visible variables
Problem
Online debugging over Modbus RTU (USB serial @ 115200) was updating very slowly (~5s) even for small projects. The debugger was polling a very large number of variables (e.g. 691) every cycle, which makes the update rate unusable for step-by-step logic debugging.
Root cause: the renderer polling loop in the workspace screen explicitly added all variables from the active POU into the poll set (mainly to support numeric debug badges), causing a project-wide flood regardless of what the user is actually viewing.
What changed
Updated the renderer-side debugger polling logic to request only the variables that are actually needed, while keeping all existing debugger features.
Changed file:
New polling behavior (how it works)
Each polling tick builds a set of composite keys and requests only those debug indexes:
Always included (existing behavior preserved)
debug === true(and graphed variables continue to be included)
Newly included: only what’s on the active diagram (LD + FBD)
To keep diagram indicators/badges working without polling everything:
DebugValueBadgeand force context menus work)instanceName.outputName_TMP_<FUNCNAME><numericId>_<OUTNAME>Removed (core fix)
Why this improves update rate
RTU performance is dominated by the number of variables being read. Polling 691 variables guarantees slow refresh regardless of batch size.
With this change, the polled set scales with:
This dramatically reduces reads for small projects and restores interactive update speed.
Compatibility / No regression intent
No functionality was removed:
Summary by CodeRabbit