Skip to content

feat: real-time non-BOOL value display on FBD and LD diagrams#636

Merged
thiagoralves merged 4 commits into
developmentfrom
feat/debug-value-visualization
Feb 27, 2026
Merged

feat: real-time non-BOOL value display on FBD and LD diagrams#636
thiagoralves merged 4 commits into
developmentfrom
feat/debug-value-visualization

Conversation

@thiagoralves
Copy link
Copy Markdown
Contributor

@thiagoralves thiagoralves commented Feb 27, 2026

Summary

  • Adds CODESYS-style real-time value badges on FBD and LD diagrams during debugging
  • Non-BOOL variables (INT, REAL, STRING, etc.) show their current value in a green badge next to the variable node
  • BOOL variables are unaffected — they keep their existing color indicator system
  • All variables of the active POU tab are polled; switching tabs automatically updates the polling set

Implementation

  • debug-value-badge.tsx — New shared component used by both FBD and LD editors. Reads from debugVariableValues store, renders only for non-BOOL types, supports right/left/below positioning
  • FBD variable.tsx — Badge positioned to the right of input variables, left of output variables, below inout variables
  • LD variable.tsx — Badge positioned to the right of input variables, left of output variables
  • workspace-screen.tsx — Polls all variables of the active POU (not just BOOLs), enabling non-BOOL values to be available for display. FB POU instance context is respected

Architecture notes

  • Design supports future extension to block output nodes (next PR)
  • No code duplication — single shared DebugValueBadge component
  • Minimal changes: 4 files, ~50 lines of new code

Test plan

  • Open an FBD program with INT variables, start debugger → verify green value badges appear next to non-BOOL variable nodes
  • Open an LD program with INT variables, start debugger → verify green value badges appear
  • Force a variable value → verify the badge updates in real-time
  • BOOL variables should NOT show a badge (they use color indicators)
  • Switch between POU tabs → verify old values disappear, new tab variables start polling
  • Stop debugger → verify all badges disappear
  • Test with function block POUs and selected instances
  • Test dark mode appearance

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Real-time debug value badges shown next to variable nodes (non-BOOL types) when the debugger is active.
    • Debug badges added for block outputs/connectors with contextual positioning to avoid overlap when outputs are connected.
    • Polling expanded to include non-BOOL base-type outputs and temporary non-BOOL variables, broadening real-time visibility across function-blocks and program POUs.
    • ENO and execution-control handling extended to apply to base-type outputs.
  • Chores

    • Added a lowercase base-type type alias to public types.

Show live debug values (INT, REAL, STRING, etc.) in green badges next to
variable nodes during debugging, similar to CODESYS. BOOL variables are
excluded since they already have dedicated color indicators.

- Add shared DebugValueBadge component for both FBD and LD editors
- Integrate badge into FBD variable nodes (right/left/below positioning)
- Integrate badge into LD variable nodes (right/left positioning)
- Poll all variables of the active POU (not just BOOLs) so non-BOOL
  values are available for display; tab switching naturally updates
  the polling set

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 27, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent 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 6a06cd5 and aabaab6.

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

Walkthrough

Adds DebugValueBadge and BlockOutputDebugBadges UI components, integrates them into FBD and Ladder variable/block renders, and extends workspace-screen debugger polling from BOOL-only to include non-BOOL base-type variables using composite keys for real-time debug values.

Changes

Cohort / File(s) Summary
New DebugValueBadge
src/renderer/components/_atoms/graphical-editor/debug-value-badge.tsx
New React component and props type that reads debugVariableValues from the global store and renders positioned badges for non-BOOL variable values when the debugger is active.
Variable components (FBD & Ladder)
src/renderer/components/_atoms/graphical-editor/fbd/variable.tsx, src/renderer/components/_atoms/graphical-editor/ladder/variable.tsx
Import and conditionally render DebugValueBadge when debugger is visible; ladder adds relative container and extra placement; FBD chooses badge position by variable variant.
Block output badges component
src/renderer/components/_atoms/graphical-editor/block-output-debug-badges.tsx
New BlockOutputDebugBadges that computes per-output composite keys (FB vs TMP) and renders DebugValueBadge for unconnected outputs.
Block integration (FBD & Ladder)
src/renderer/components/_atoms/graphical-editor/fbd/block.tsx, src/renderer/components/_atoms/graphical-editor/ladder/block.tsx
Compute connectedOutputNames (memoized) and inject BlockOutputDebugBadges into block render paths so output badges appear per-connector and skip connected outputs.
Workspace debugger polling
src/renderer/screens/workspace-screen.tsx
Expanded polling from BOOL-only to base-type (non-BOOL) outputs: added PLCBaseTypesLowercase, new composite-key builder makeCompositeKeyForCurrentPou, FB vs program key handling, and registration/polling for non-BOOL/forced variables.
Type export
src/types/PLC/units/base-types.ts
Added type PLCBaseTypesLowercase = Lowercase<PLCBaseTypes> and exported it alongside existing types.

Sequence Diagram

sequenceDiagram
    participant WS as rgba(52,152,219,0.5) workspace-screen
    participant Store as rgba(46,204,113,0.5) GlobalStore
    participant UI as rgba(155,89,182,0.5) Block/Variable components
    participant Badge as rgba(241,196,15,0.5) DebugValueBadge

    WS->>WS: Build composite keys for active POU variables (include non-BOOL)
    WS->>Store: Poll values for composite keys
    Store-->>Store: Update debugVariableValues map

    UI->>Store: Read debugger visibility & compositeKey helper
    alt Debugger visible & compositeKey exists
        UI->>Badge: Instantiate with compositeKey, variableType, position
        Badge->>Store: Lookup debugVariableValues[compositeKey]
        Store-->>Badge: Return value (if present)
        Badge-->>UI: Render positioned badge with value
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR 608 — overlaps in composite-key construction and debug UI changes for non-BOOL/FB variables.
  • PR 635 — related modifications to workspace-screen polling and expanded variable polling for FBD/POU variables.
  • PR 618 — related debugger polling/scheduling changes affecting workspace-screen composite-key handling.

Suggested labels

enhancement

Suggested reviewers

  • JoaoGSP
  • JulioSergioFS

Poem

🐇 I hop through nodes with a curious cheer,

tiny badges glow when the debugger is near.
Non-BOOL whispers I fetch and show,
values peek out where connectors go.
Hooray — realtime hops in a row! 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main feature: real-time display of non-BOOL variable values on FBD and LD diagrams during debugging.
Description check ✅ Passed The description is comprehensive and covers summary, implementation, architecture notes, and test plan; however, it lacks formal sections matching the template (References, References to issues/Jira, DOD checklist).
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 docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/debug-value-visualization

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

🤖 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 1392-1408: The FB POU branch incorrectly compares
currentPou.data.name (FB type) to varInfo.pouName (the program where the FB is
instantiated), so FB variables are always skipped; change the FB handling in the
loop to follow the working pattern used elsewhere: obtain the FB instance
context from currentPou (e.g., fbInstanceCtx), match varInfo.pouName against
fbInstanceCtx.programName, and for matching program entries only add variables
whose names start with the FB instance path (the instance prefix), then convert
those to the appropriate composite key (using makeCompositeKeyForCurrentPou or
the same composite-key logic used at lines 1154-1168) before adding to
debugVariableKeys; keep the non-FB branch as-is.

ℹ️ 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 df5cf8d and 69e074f.

📒 Files selected for processing (4)
  • src/renderer/components/_atoms/graphical-editor/debug-value-badge.tsx
  • src/renderer/components/_atoms/graphical-editor/fbd/variable.tsx
  • src/renderer/components/_atoms/graphical-editor/ladder/variable.tsx
  • src/renderer/screens/workspace-screen.tsx

Comment thread src/renderer/screens/workspace-screen.tsx
…l base types

- Register all base-type function outputs (not just BOOL) so non-BOOL
  _TMP_ variables like ADD's INT output get polled during debugging
- Create shared BlockOutputDebugBadges component used by both FBD and LD
  block nodes to show real-time values next to output connectors
- Skip badge rendering for outputs connected to variable nodes to avoid
  duplicate badges (LD uses connectedVariables, FBD uses edge analysis)

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.

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

1432-1448: ⚠️ Potential issue | 🟠 Major

FB active-POU polling still filters with the wrong POU identity.

At Line 1436, varInfo.pouName is compared to currentPou.data.name. For FB tabs, currentPou.data.name is the FB type, while varInfo.pouName is the hosting program (instance context). This causes FB variables to be skipped, so non-BOOL badges in FB POU context won’t get polled.

🐛 Suggested fix
-        if (currentPou) {
-          const activePouName = currentPou.data.name
-          Array.from(variableInfoMapRef.current.entries()).forEach(([_, varInfos]) => {
-            for (const varInfo of varInfos) {
-              if (varInfo.pouName !== activePouName) continue
-              // For FB POUs, only add variables that match the selected instance context
-              if (currentPou.type === 'function-block') {
-                const compositeKey = makeCompositeKeyForCurrentPou(varInfo.variable.name)
-                if (compositeKey) {
-                  debugVariableKeys.add(compositeKey)
-                }
-              } else {
-                debugVariableKeys.add(`${varInfo.pouName}:${varInfo.variable.name}`)
-              }
-            }
-          })
-        }
+        if (currentPou) {
+          if (currentPou.type === 'function-block') {
+            const fbTypeKey = currentPou.data.name.toUpperCase()
+            const selectedKey = fbSelectedInstance.get(fbTypeKey)
+            const selectedInstance = (fbDebugInstances.get(fbTypeKey) || []).find((inst) => inst.key === selectedKey)
+            if (selectedInstance) {
+              Array.from(variableInfoMapRef.current.entries()).forEach(([_, varInfos]) => {
+                for (const varInfo of varInfos) {
+                  if (varInfo.pouName !== selectedInstance.programName) continue
+                  if (!varInfo.variable.name.startsWith(`${selectedInstance.fbVariableName}.`)) continue
+                  debugVariableKeys.add(`${varInfo.pouName}:${varInfo.variable.name}`)
+                }
+              })
+            }
+          } else {
+            const activePouName = currentPou.data.name
+            Array.from(variableInfoMapRef.current.entries()).forEach(([_, varInfos]) => {
+              for (const varInfo of varInfos) {
+                if (varInfo.pouName !== activePouName) continue
+                debugVariableKeys.add(`${varInfo.pouName}:${varInfo.variable.name}`)
+              }
+            })
+          }
+        }
🤖 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 1432 - 1448, The loop
filters out FB variables because it compares varInfo.pouName to
currentPou.data.name (which is the FB type) instead of the hosting
program/instance identity; change how activePouName is derived: compute
activePouName differently when currentPou.type === 'function-block' by using the
FB's hosting program/instance identity from currentPou.data (the instance/host
field) rather than data.name, and keep the existing branch that calls
makeCompositeKeyForCurrentPou(varInfo.variable.name) and adds compositeKey — for
non-FB POUs keep comparing varInfo.pouName to currentPou.data.name and adding
`${varInfo.pouName}:${varInfo.variable.name}` as before.
🤖 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 1432-1448: The loop filters out FB variables because it compares
varInfo.pouName to currentPou.data.name (which is the FB type) instead of the
hosting program/instance identity; change how activePouName is derived: compute
activePouName differently when currentPou.type === 'function-block' by using the
FB's hosting program/instance identity from currentPou.data (the instance/host
field) rather than data.name, and keep the existing branch that calls
makeCompositeKeyForCurrentPou(varInfo.variable.name) and adds compositeKey — for
non-FB POUs keep comparing varInfo.pouName to currentPou.data.name and adding
`${varInfo.pouName}:${varInfo.variable.name}` 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 69e074f and c533d4d.

📒 Files selected for processing (4)
  • src/renderer/components/_atoms/graphical-editor/block-output-debug-badges.tsx
  • src/renderer/components/_atoms/graphical-editor/fbd/block.tsx
  • src/renderer/components/_atoms/graphical-editor/ladder/block.tsx
  • src/renderer/screens/workspace-screen.tsx

thiagoralves and others added 2 commits February 27, 2026 12:56
…line union casts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…and instance path

The non-BOOL polling loop compared varInfo.pouName (the hosting program, e.g.
"main") against currentPou.data.name (the FB type name, e.g.
"IRRIGATION_CONTROLLER"), so all FB variables were silently skipped. It also
passed the already-qualified variable name into makeCompositeKeyForCurrentPou,
which would double-prefix the instance path.

Fix follows the proven BOOL polling pattern: resolve the selected FB instance
context, then filter by programName + instance path prefix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@thiagoralves thiagoralves merged commit 9e30b23 into development Feb 27, 2026
8 of 9 checks passed
@thiagoralves thiagoralves deleted the feat/debug-value-visualization branch February 27, 2026 18:13
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.

1 participant