0
0
Fork 0
Codesys-MCP-SP21-plus/docs/open-bugs-cross-reference.md
2026-04-28 22:20:21 +02:00

11 KiB

Open bugs -- cross-referenced against the official CODESYS scripting docs

For each of the 5 remaining MCP tool bugs from function-test-2026-04-25.md, this document records:

  1. The empirical failure observed locally on SP22 Patch 1.
  2. The relevant official documentation URL on https://content.helpme-codesys.com.
  3. What the docs say (or do not say -- many pages are index-only with the actual method bodies behind JavaScript).
  4. The proposed fix path with reasoning.

This is the cross-reference pass requested per the project rule: check helpme-codesys before fixing or adding scripting code.


Bug 1: create_folder -- unexpected keyword argument 'name'

Empirical failure (src/scripts/create_folder.py line 344):

TypeError: create_folder() got an unexpected keyword argument 'name'

Fork's call site uses name=... kwarg; the underlying CODESYS API rejects the keyword.

Documentation:

  • https://content.helpme-codesys.com/en/ScriptingEngine/ScriptObject.html confirms create_folder() exists on ScriptObject (and on ScriptProject, ScriptTreeObject).
  • The page index lists the method but doesn't surface the parameter list in the WebFetch'able content. The signature is rendered from JavaScript -- inspection in a real browser or via help(obj.create_folder) inside a CODESYS scripting session would return the canonical signature.

Proposed fix: Try positional invocation first:

new_folder = parent.create_folder('TestFolder')

If positional fails too, probe with the same defensive pattern as install_library_file.py (try several kwarg names and dump dir(parent) on total miss). High confidence the positional form works -- it's the convention everywhere else in the scriptengine API for tree-creation methods (add_library('Standard'), add_placeholder('IoStandard')).

Effort: ~10 lines. Single-line change to call site, plus diagnostic.


Bug 2: compile_project / get_compile_messages -- 281474976710655L is not JSON serializable

Empirical failure in src/scripts/compile_project.py (line 330) and src/scripts/get_compile_messages.py (line 315):

TypeError: 281474976710655L is not JSON serializable

system.get_message_objects() returns a dict whose value contains an IronPython 2.7 long (0xFFFFFFFFFFFF = the Severity bitmask), and the stdlib json module's encoder rejects long.

Documentation:

Proposed fix: Two-part:

  1. Pre-process the message dict to coerce any long to int or str before json.dumps:

    def _coerce_for_json(obj):
        if isinstance(obj, (int, long)):
            return int(obj)  # downcast; or str(obj) if value > 2**63
        if isinstance(obj, dict):
            return {k: _coerce_for_json(v) for k, v in obj.items()}
        if isinstance(obj, (list, tuple)):
            return [_coerce_for_json(v) for v in obj]
        return obj
    
    messages_json = json.dumps(_coerce_for_json(messages), indent=2)
    
  2. Belt-and-suspenders: pass default=str to json.dumps so any future un-handled type degrades to its repr instead of throwing.

Both call sites (compile_project.py, get_compile_messages.py) share the same encoder path -- single helper in a new src/scripts/_json_compat.py (or inline) addresses both.

Effort: ~15 lines for the helper + 2 call sites. Single commit.


Bug 3: list_project_libraries -- "No libraries found" after successful add_library

Empirical failure in src/scripts/list_project_libraries.py: returns "No libraries found in the project (or Library Manager not found)" both before AND after a successful add_library call. The add_library operation visibly succeeds (CODESYS UI shows the new entry in Library Manager), but the read path can't find the manager.

Documentation:

  • https://content.helpme-codesys.com/en/ScriptingEngine/ScriptLibManObject.html confirms the canonical iteration API:
    • ScriptLibManObject.get_libraries() -- returns all library references in the manager. Library reference objects expose properties: name, namespace, is_placeholder, is_managed.
  • Subclasses: ScriptManagedLibraryReference (with managed_library), ScriptPlaceholderReference (with placeholder_name, default_resolution).

Likely root cause: The fork's list_project_libraries.py locates the manager via primary_project.find("Library Manager", True) and inspects the returned object directly. On SP22 Patch 1 this find may not return a ScriptLibManObject (could be returning a generic ScriptObject wrapper without get_libraries), or the find pattern doesn't match because the display name has changed.

Proposed fix: Use the same Library Manager discovery pattern as add_library.py (which DOES succeed at writing), then call .get_libraries() on the returned object. If the read path mirrors the write path's discovery, the behaviour will be consistent.

lib_manager = None
found = primary_project.find("Library Manager", True)
if found:
    lib_manager = found[0]
# fallback: walk children looking for one with 'library' + 'manager' in name
...
if hasattr(lib_manager, 'get_libraries'):
    refs = list(lib_manager.get_libraries())
else:
    raise RuntimeError("Library Manager object has no get_libraries() method")

for ref in refs:
    name = ref.name if hasattr(ref, 'name') else getattr(ref, 'get_name', lambda: '?')()
    namespace = ref.namespace if hasattr(ref, 'namespace') else ''
    version = getattr(ref, 'version', '?')
    is_placeholder = bool(getattr(ref, 'is_placeholder', False))
    print("  %s.%s = %s%s" % (namespace, name, version, " [placeholder]" if is_placeholder else ""))

Effort: ~30 lines. New iteration + formatting; reuse find pattern from add_library.py.


Bug 4: add_library -- creates duplicate, doesn't add as * (System) placeholder

Empirical failure in src/scripts/add_library.py: calling add_library('Standard') against a project that already has `Standard,

  • (System)adds a SECONDStandardreference. The new reference is not a* (System)placeholder so it pulls in unresolved transitive deps (e.g.IoStandard 3.1.3.1` yellow-warning seen during this session's manual cleanup of MCPTest).

Documentation:

Proposed fix: Three behavioural changes to add_library.py:

  1. Pre-check existing references. Before calling add, iterate lib_manager.get_libraries() and check if a reference with the same name (case-insensitive) already exists. If yes:

    • Default behaviour: silently no-op with a confirmation message ("Library 'Standard' already referenced as direct/placeholder").
    • Optional force=true param: add anyway (current behaviour).
  2. Default to placeholder add (add_placeholder). The standard convention in modern CODESYS projects is LibName, * (System). The Standard template ships its libraries this way. Add a new optional direct=true parameter to opt out and use add_library instead.

  3. Detect partial-success state. If neither add_library nor add_placeholder is exposed on the located object, emit a clear error rather than silent-fail.

Effort: ~40 lines. New tool param signature change is mildly breaking (existing callers get placeholder by default) -- bump the fork minor version and document in release notes.


Bug 5: rename_object -- updates own decl but not callers

Empirical observation (this session, MCPTest 2026-04-25): rename_object Application/ST_Sample -> ST_SampleRenamed correctly updated the struct's own internal TYPE ST_Sample : line to TYPE ST_SampleRenamed :, BUT Application/PLC_PRG's declaration still referenced s : ST_Sample; -- the old name -- after the rename, breaking the project.

Documentation:

CODESYS UI's Rename command is a project-wide refactor (it walks every POU and rewrites references) -- that capability is presumably implemented in the IDE layer above scripting and not exposed via scriptengine.

Proposed fix: Brute-force text replace, gated behind an optional flag:

  1. Add updateReferences=true (default) to the rename_object tool.
  2. After the rename succeeds, iterate every POU/DUT/GVL via the existing get_all_pou_code enumeration pattern.
  3. For each, regex-replace \bOldName\b -> \bNewName\b in both declaration and implementation code (word-boundary safe for global identifiers like FB/DUT/GVL names).
  4. set_pou_code back the updated content for any POU whose code actually changed.

Risk: false positives for names that happen to match in comments or strings; for type names this is rare. Document the risk in the tool description.

Alternative: split into a separate update_references_to_renamed_object tool so the responsibility stays narrow and rename_object keeps its current minimal-surface behaviour.

Effort: ~80 lines. Separate Python helper script + Node-side wiring. Highest LOC of the five but most user-visible win since rename is a common operation.


Order-of-attack recommendation

  1. #2 (JSON long) -- 15 LOC, single helper, unblocks meaningful compiler diagnostics. Highest value/effort ratio.
  2. #1 (create_folder) -- 10 LOC, trivial fix attempt with high probability of success (positional call).
  3. #4 (add_library duplication / placeholder) -- 40 LOC, moderately breaking but matches modern CODESYS convention.
  4. #3 (list_project_libraries) -- 30 LOC, depends on #4 to be verifiable end-to-end (need a working add to test the read).
  5. #5 (rename_object refactor) -- 80 LOC, biggest, but lowest coupling to other fixes.

Each is its own commit per the project rule. Each commit message should cite the relevant docs URL inline.