Skip to content

Fix debugger polling to fetch only visible variables#646

Merged
thiagoralves merged 7 commits into
Autonomy-Logic:developmentfrom
MatthewReed303:development
Mar 13, 2026
Merged

Fix debugger polling to fetch only visible variables#646
thiagoralves merged 7 commits into
Autonomy-Logic:developmentfrom
MatthewReed303:development

Conversation

@MatthewReed303
Copy link
Copy Markdown
Contributor

@MatthewReed303 MatthewReed303 commented Mar 4, 2026

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:

  • src/renderer/screens/workspace-screen.tsx

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)

  • Watched variables: variables flagged with debug === true
  • Forced variables: always polled so the debugger panel remains accurate
  • Nested/expanded variables: children are polled only when their watched ancestor is expanded
    (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:

  • LD
    • contacts/coils (BOOL variables used on the diagram)
    • variable nodes (so DebugValueBadge and force context menus work)
    • block output badges (functions + function blocks), by generating the same names the UI uses:
      • FB outputs: instanceName.outputName
      • Function outputs: _TMP_<FUNCNAME><numericId>_<OUTNAME>
  • FBD
    • variable nodes
    • block output badges (functions + function blocks), same naming logic as above

Removed (core fix)

  • Removed the “poll all variables of the active POU” logic that caused hundreds of unrelated variables to be fetched every cycle.

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:

  • what’s visible in the active LD/FBD diagram, plus
  • what the user has explicitly watched / graphed / expanded / forced

This dramatically reduces reads for small projects and restores interactive update speed.

Compatibility / No regression intent

No functionality was removed:

  • watch list still works
  • graph list still works
  • expand-to-fetch-children still works
  • forcing still works (and forced vars remain polled)
  • diagram debug badges still work, without polling unrelated variables

Summary by CodeRabbit

  • Refactor
    • Centralized and cached polling/key derivation across program, FB, FBD and ST/IL contexts to reduce per-tick work and ensure cache invalidation on context changes and teardown.
    • Track editor model switches to recompute debug scans when the active file/model changes.
  • New Features
    • Polling now includes variables referenced in editor source, FB outputs, temps, and deeply nested/bracketed paths.
  • Bug Fixes
    • More reliable handling of non-BOOL values, array/bracketed paths, and diagram-exposed outputs for correct updates.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 4, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Cache 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

Cohort / File(s) Summary
Workspace polling & composite-key logic
src/renderer/screens/workspace-screen.tsx
Added diagramVarKeysCache to avoid per-tick regex/rescan, rebuilds cache when invalidated; unified composite key derivation across LD/FBD/ST/IL, FB instance context resolution, _TMP_ temp mapping, bracketed/dotted nested paths, deepest-watched-ancestor handling, and cleanup on teardown/disconnect.
Editor model / debug position rescan
src/renderer/components/_features/[workspace]/editor/monaco/index.tsx
Track Monaco model switches with modelVersion, guard debug-position scans by current model URI, and wire modelVersion into debug position recomputation to avoid scanning stale editor models.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • JoaoGSP

Poem

🐰 I hopped through code and cached the keys,
From FBs to brackets and tiny TMP seas.
I mapped each dot and ancestor true,
Polling carrots brought back values new,
Debug carrots crunch — a victory chew! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: reducing debugger polling to only visible variables instead of all POU variables, directly addressing the core performance issue.
Description check ✅ Passed The PR description comprehensively covers the problem, solution, and impact with detailed explanations of new polling behavior and compatibility. However, the DOD checklist is missing from the description provided.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 by useDebugCompositeKey. 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.

outputVars currently 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.

📥 Commits

Reviewing files that changed from the base of the PR and between e414315 and b7309b6.

📒 Files selected for processing (1)
  • src/renderer/screens/workspace-screen.tsx

Comment thread src/renderer/screens/workspace-screen.tsx Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/renderer/screens/workspace-screen.tsx (1)

1344-1351: ⚠️ Potential issue | 🟠 Major

FBD 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 (visitFbInstance branch at Line 883). For FBD-bodied custom FBs, keys may never appear in variableInfoMap, 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

📥 Commits

Reviewing files that changed from the base of the PR and between b7309b6 and 975b793.

📒 Files selected for processing (1)
  • src/renderer/screens/workspace-screen.tsx

thiagoralves and others added 3 commits March 12, 2026 18:03
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>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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 variableInfoMapRef for 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 by fbInstance.name or selectedInstance.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

📥 Commits

Reviewing files that changed from the base of the PR and between 975b793 and 039d382.

📒 Files selected for processing (2)
  • src/renderer/components/_features/[workspace]/editor/monaco/index.tsx
  • src/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>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/renderer/screens/workspace-screen.tsx (1)

1392-1401: ⚠️ Potential issue | 🟠 Major

FBD _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 in visitFbInstance (function temp handling starts at Line 894 and branches only on language === 'ld'). That can leave FBD-bodied custom FB function outputs without variableInfoMapRef entries, 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

📥 Commits

Reviewing files that changed from the base of the PR and between 681f519 and dd2c38a.

📒 Files selected for processing (1)
  • src/renderer/screens/workspace-screen.tsx

cached.fbContextKey === fbContextKey
) {
// Reuse cached keys
cached.keys.forEach((key) => debugVariableKeys.add(key))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

@thiagoralves thiagoralves merged commit 8817969 into Autonomy-Logic:development Mar 13, 2026
10 of 11 checks passed
thiagoralves pushed a commit that referenced this pull request Mar 30, 2026
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>
thiagoralves pushed a commit that referenced this pull request Mar 30, 2026
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>
@thiagoralves thiagoralves mentioned this pull request Mar 31, 2026
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants