Skip to content

fix: improve debug forcing for coils, contacts, and variables#608

Merged
thiagoralves merged 4 commits into
developmentfrom
fix/investigate-multiples-issues
Feb 17, 2026
Merged

fix: improve debug forcing for coils, contacts, and variables#608
thiagoralves merged 4 commits into
developmentfrom
fix/investigate-multiples-issues

Conversation

@JoaoGSP
Copy link
Copy Markdown
Member

@JoaoGSP JoaoGSP commented Feb 16, 2026

Summary

  • Fix debug forcing for ladder coils, contacts, and variables to use immediate forced values instead of waiting for the 200ms poll delay
  • Fix forced variable color display to correctly respect the negated variant logic
  • Fix composite key generation for variables inside function block instances by resolving the full FB instance path (programName:fbVariableName.variableName)
  • Add diagnostic console.warn logs when debug variable index resolution fails

Resolves

Test plan

  • Open a ladder diagram with coils, contacts, and variables
  • Start the debugger and force a boolean variable — verify the color updates immediately without delay
  • Force a negated coil/contact — verify the color reflects the negated state correctly
  • Debug a function block instance — verify forcing variables inside the FB works correctly
  • Check the console for any new warning messages when variable indexes fail to resolve

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Forced variables in the graphical debugger show immediate values ('0' or '1') without polling delays.
    • Debugger color indication for forced variables is now consistent across components.
  • Bug Fixes

    • Added runtime diagnostic warnings when debug variable indices cannot be resolved.
  • Refactor

    • Consolidated composite-key construction across ladder and function-block debugger components via a shared hook.
  • Chores

    • Exported the new composite-key hook, improved default handling for record schemas, and tightened POU parsing/serialization (trim end markers and append end keyword).

Use immediate forced values from debugForcedVariables instead of polled
values for coils and contacts, eliminating the 200ms delay. Respect
negated variant when displaying forced state colors. Fix composite key
generation in variable element to support function block instance context.
Add debug logging for unresolved variable indexes.

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

coderabbitai Bot commented Feb 16, 2026

Walkthrough

Replaces inline composite-key construction with a new hook (useDebugCompositeKey), centralizes forced-variable resolution (prefer debugForcedVariables over polled values), consolidates forced/non-forced color logic, removes per-file FB-instance context plumbing, and adds runtime warnings when debug index resolution fails.

Changes

Cohort / File(s) Summary
Ladder UI — forced-value & color logic
src/renderer/components/_atoms/graphical-editor/ladder/coil.tsx, src/renderer/components/_atoms/graphical-editor/ladder/contact.tsx
Use useDebugCompositeKey(); derive composite keys via hook; prefer debugForcedVariables for forced state (map → '1'/'0') before falling back to debugVariableValues; unify isForced + displayState color logic; remove redundant FB-context key construction.
Variable components — composite-key refactor
src/renderer/components/_atoms/graphical-editor/ladder/variable.tsx, src/renderer/components/_atoms/graphical-editor/fbd/variable.tsx
Replaced hard-coded composite-key construction with useDebugCompositeKey(); updated force/release/lookup handlers to use getCompositeKey(variableName); narrowed workspace destructuring.
Graphical editor molecules / rung body
src/renderer/components/_molecules/graphical-editor/fbd/index.tsx, src/renderer/components/_molecules/graphical-editor/ladder/rung/body.tsx
Removed local FB-instance context and related callbacks; switched to useDebugCompositeKey; simplified POU type checks and dropped fbSelectedInstance/fbDebugInstances from props/destructuring.
Hook exports
src/renderer/hooks/index.ts
Re-exported the new useDebugCompositeKey hook from the hooks index.
Hook implementation
src/renderer/hooks/use-debug-composite-key.ts
New hook: computes memoized composite-key builder using editor meta, POU type and FB instance context; returns getCompositeKey(variableName).
Workspace diagnostics
src/renderer/screens/workspace-screen.tsx
Added runtime warnings when composite-key → debug index resolution fails (in nested variable processing, program variable handling, and force-variable path); early-return on failed force-variable index lookup.
Types & utils
src/types/PLC/open-plc.ts, src/utils/default-zod-schema-values.ts
Defaulted pous to [] in PLC schema; added handling for ZodRecord in default schema values (returns {}).
POU text parse/serialize
src/utils/PLC/pou-text-parser.ts, src/utils/PLC/pou-text-serializer.ts
Parser now trims trailing END_* keyword from body; serializer appends two newlines before the end keyword and includes the end keyword in output.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • Gustavohsdp

Poem

🐰
I stitched the keys that used to roam,
FB or program — each finds home.
Forces answer first, colors sing,
Warnings nudge when lookups swing.
Hopping on — the debugger hums along. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: improving debug forcing for coils, contacts, and variables with immediate value updates.
Description check ✅ Passed The description provides a comprehensive summary, references three Jira issues, and includes a detailed test plan, but does not follow the provided template structure (missing explicit sections like 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
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/investigate-multiples-issues

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.

🧹 Nitpick comments (2)
src/renderer/screens/workspace-screen.tsx (1)

288-292: Diagnostic warnings may be noisy during normal operation.

These console.warn calls fire for every unresolvable variable during the setup phase. If debugVariableIndexes is populated asynchronously or incrementally, these warnings will fire on initial runs before indexes are fully available, potentially flooding the dev console. Consider gating them behind a debug/verbose flag, or batching them into a single summary warning.

Also applies to: 440-444

src/renderer/components/_atoms/graphical-editor/ladder/contact.tsx (1)

108-122: getCompositeKey is duplicated verbatim in contact.tsx, coil.tsx, and variable.tsx.

Consider extracting this helper into a shared utility (e.g., src/utils/graphical-editor/composite-key.ts) to eliminate the triple copy-paste. All three copies accept the same store values and perform identical logic.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request improves the debug forcing functionality for ladder diagram elements (coils, contacts, and variables) by addressing three key issues: immediate visual feedback when forcing variables, correct color display for negated variants, and proper composite key generation for variables inside function block instances.

Changes:

  • Added diagnostic console.warn logs when debug variable index resolution fails to help troubleshoot debugging issues
  • Fixed composite key generation for ladder variables inside function block instances to properly resolve the full FB instance path
  • Fixed forced variable display to use immediate values from debugForcedVariables instead of waiting for the 200ms polling delay
  • Fixed negated coil/contact color display to correctly calculate displayState before determining debug colors

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/renderer/screens/workspace-screen.tsx Added console.warn diagnostics for failed variable index lookups in nested variables, program variables, and force operations
src/renderer/components/_atoms/graphical-editor/ladder/variable.tsx Added getCompositeKey helper to properly resolve FB instance paths, updated all composite key usages to support FB instances
src/renderer/components/_atoms/graphical-editor/ladder/contact.tsx Refactored debug color logic to use immediate forced values and apply negated variant logic before color selection
src/renderer/components/_atoms/graphical-editor/ladder/coil.tsx Refactored debug color logic to use immediate forced values and apply negated variant logic before color selection
package-lock.json Removed unused bcryptjs dependency

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Consolidate 6 duplicated getCompositeKey implementations (4 plain
functions in atom components, 2 useCallback-wrapped in molecule
components) into a single shared hook with proper memoization.

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/components/_molecules/graphical-editor/fbd/index.tsx (1)

217-225: ⚠️ Potential issue | 🟠 Major

Missing getCompositeKey in styledEdges dependency array — stale edges when FB instance changes.

getNodeOutputState calls getCompositeKey internally, but getCompositeKey is not listed in the useMemo dependencies. If the user switches the selected FB debug instance, getCompositeKey will return different keys, but styledEdges won't recompute.

Note that rung/body.tsx (Line 237) correctly includes getCompositeKey in its equivalent styledEdges dependency array.

Proposed fix
   ], [
     rungLocal.edges,
     rungLocal.nodes,
     isDebuggerVisible,
     debugVariableValues,
     debugForcedVariables,
     editor.meta.name,
     project,
+    getCompositeKey,
   ])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/_molecules/graphical-editor/fbd/index.tsx` around
lines 217 - 225, The styledEdges useMemo does not include getCompositeKey in its
dependency array, causing stale edge styling when the FB debug instance changes;
update the dependency list for the useMemo that computes styledEdges to include
getCompositeKey (the same way rung/body.tsx does) so that getNodeOutputState
(which calls getCompositeKey) triggers recomputation when the composite key
logic changes or the FB instance switches.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/renderer/components/_molecules/graphical-editor/fbd/index.tsx`:
- Around line 217-225: The styledEdges useMemo does not include getCompositeKey
in its dependency array, causing stale edge styling when the FB debug instance
changes; update the dependency list for the useMemo that computes styledEdges to
include getCompositeKey (the same way rung/body.tsx does) so that
getNodeOutputState (which calls getCompositeKey) triggers recomputation when the
composite key logic changes or the FB instance switches.

thiagoralves and others added 2 commits February 17, 2026 08:40
Web editor exports project.json without data.pous (POUs are stored as
separate files), causing Zod validation to fail and the entire project
data to be replaced with defaults. This lost the project name and
introduced null for debugVariables.pous (ZodRecord was unhandled).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The hybrid POU serializer (Python/C++) was not appending the END keyword
(END_FUNCTION_BLOCK, END_PROGRAM, END_FUNCTION) to saved files, breaking
compatibility with openplc-web which correctly includes it. The parser
also did not strip the END keyword on load, causing it to appear in the
code editor when opening projects saved by openplc-web.

- Serializer: append END keyword for hybrid POUs, matching textual/graphical
- Parser: strip trailing END keyword from body, matching textual/graphical

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/utils/PLC/pou-text-parser.ts (1)

244-255: Reuse getEndKeyword utility instead of duplicating the keyword map.

getEndKeyword from pou-file-extensions.ts already maps POU types to their END_* keywords and is imported in the sibling serializer file. The same endKeywords map is also duplicated in parseTextualPouFromString (line 118) and parseGraphicalPouFromString (line 367) within this file.

Regarding the static analysis ReDoS warning on line 253: this is a false positive since endKeyword is always one of three hardcoded constant strings (END_PROGRAM, END_FUNCTION, END_FUNCTION_BLOCK).

♻️ Suggested refactor
-    // Strip the trailing END keyword from the body content, matching how textual/graphical parsers handle it
-    const endKeywords: Record<string, string> = {
-      program: 'END_PROGRAM',
-      function: 'END_FUNCTION',
-      'function-block': 'END_FUNCTION_BLOCK',
-    }
-    const endKeyword = endKeywords[type]
-    let bodyContent = remainingContent.slice(bodyStartIndex).trim()
-    if (endKeyword) {
-      const endKeywordRegex = new RegExp(`\\s*\\b${endKeyword}\\b\\s*$`, 'i')
-      bodyContent = bodyContent.replace(endKeywordRegex, '').trim()
-    }
+    // Strip the trailing END keyword from the body content, matching how textual/graphical parsers handle it
+    const endKeyword = getEndKeyword(type)
+    let bodyContent = remainingContent.slice(bodyStartIndex).trim()
+    const endKeywordRegex = new RegExp(`\\s*\\b${endKeyword}\\b\\s*$`, 'i')
+    bodyContent = bodyContent.replace(endKeywordRegex, '').trim()

getEndKeyword is already imported in the serializer; add it to the parser's import:

-import { getLanguageFromExtension } from './pou-file-extensions'
+import { getEndKeyword, getLanguageFromExtension } from './pou-file-extensions'

The same consolidation could be applied to parseTextualPouFromString and parseGraphicalPouFromString to eliminate all three duplicated maps.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/PLC/pou-text-parser.ts` around lines 244 - 255, The duplicated
endKeywords map should be replaced by the shared getEndKeyword utility: import
and call getEndKeyword(type) instead of defining the endKeywords object in
pou-text-parser.ts, and remove the duplicated maps in parseTextualPouFromString
and parseGraphicalPouFromString as well; update the code that computes
endKeyword (currently in the block around bodyContent/bodyStartIndex) to use
getEndKeyword(type) and keep the existing regex trimming logic (the ReDoS
warning is a false positive since the value is one of the known constants).
🤖 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/utils/PLC/pou-text-parser.ts`:
- Around line 244-255: The duplicated endKeywords map should be replaced by the
shared getEndKeyword utility: import and call getEndKeyword(type) instead of
defining the endKeywords object in pou-text-parser.ts, and remove the duplicated
maps in parseTextualPouFromString and parseGraphicalPouFromString as well;
update the code that computes endKeyword (currently in the block around
bodyContent/bodyStartIndex) to use getEndKeyword(type) and keep the existing
regex trimming logic (the ReDoS warning is a false positive since the value is
one of the known constants).

@thiagoralves thiagoralves merged commit 2dc8796 into development Feb 17, 2026
10 checks passed
@thiagoralves thiagoralves deleted the fix/investigate-multiples-issues branch February 17, 2026 20:48
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.

3 participants