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

262 lines
11 KiB
Markdown

# 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](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:
```python
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:**
- https://content.helpme-codesys.com/en/ScriptingEngine/ScriptSystem.html
confirms `get_message_objects()` and `get_messages()` exist on
`system`. Return-type details aren't surfaced in the index page.
- The IronPython 2.7 stdlib `json` is documented to NOT handle
`long` -- a known limitation of the `_default_encoder`.
**Proposed fix:**
Two-part:
1. **Pre-process the message dict** to coerce any `long` to `int` or
`str` before `json.dumps`:
```python
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.
```python
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 SECOND `Standard` reference. 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:**
- https://content.helpme-codesys.com/en/ScriptingEngine/ScriptLibManObject.html
confirms two distinct methods:
- `add_library(...)` -- direct reference (specific version)
- `add_placeholder(...)` -- placeholder reference (resolves at compile)
- Library reference objects expose `name`, `namespace`,
`is_placeholder`, `is_managed` -- usable for dedup checks.
**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:**
- https://content.helpme-codesys.com/en/ScriptingEngine/ScriptObject.html
documents `rename()` -- no documented refactor variant or
"rename_with_references" companion method.
- No documented `find_references()` or "callers" API on
ScriptObject / ScriptTreeObject in the index.
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.