0
0
Fork 0

fix(compile_messages): discover real category GUIDs via system.get_message_categories()

The 0.6.3 fix replaced 'first-non-empty-pattern-wins' with category
iteration but used a HARDCODED list of 'well-known V3.5 GUIDs' that was
wrong. None of those GUIDs matched the actual Build category, so
compile errors stayed invisible (compile_project still reported '0
errors' even when the IDE-side download path saw them).

Diagnosed via a one-shot probe injected into compile_project on
2026-04-29: dumped attrs of script_engine.system, then called
script_engine.system.get_message_categories() (the METHOD) directly.
That returned the actual 7 category GUIDs in this CODESYS V3.5 SP22
Patch 1 install:

  05581bd1-66d3-4251-aff2-047cc8e9adf7  Offline Help
  936e1a33-3af8-47fa-b40b-903f0ae0b6cc  Application Composer
  a9b26e07-6ae1-4c06-9cd1-9ddddd397a2d  SVN
  0a6fcb64-7f24-43c6-a6d3-f70cb5d31114  (no parameterless ctor; Git)
  194b48a9-ab51-43ae-b9a9-51d3edaaddf3  Script Messages
  97f48d64-a2a3-4856-b640-75c046e37ea9  Build              <-- the one we needed
  220493a1-f49b-4416-9a3f-a545db707cbe  Additional code checks

Real fix: replace the hardcoded list in _enumerate_categories() in both
compile_project.py and get_compile_messages.py with a call to
system.get_message_categories(); label each one via
get_message_category_description(guid). Iterate per category as before.
GUIDs are now discovered at runtime so the same code works on any SP
and any locale.

Verification: injected `THIS_IS_NOT_VALID_IEC_KEYWORD;` into
PLC_PRG.implementation, ran compile_project; output now reads
"1 error(s), 2 warning(s). ERROR: Identifier
'THIS_IS_NOT_VALID_IEC_KEYWORD' not defined". Restored PLC_PRG;
0 error(s) again. End-to-end fix confirmed.

Bumped 0.6.3 -> 0.6.4.
This commit is contained in:
Karstein Phobic Nyvold Kvistad 2026-04-29 07:39:31 +02:00
parent 2c19eca4b3
commit d4e71f61ca
3 changed files with 63 additions and 60 deletions

View file

@ -1,6 +1,6 @@
{
"name": "codesys-mcp-sp21-plus",
"version": "0.6.3",
"version": "0.6.4",
"description": "Codesys-MCP-SP21+ -- fork of luke-harriman/Codesys-MCP carrying CODESYS V3.5 SP22 Patch 1 fixes (and forward-compat with later SPs): script-engine API drift, online/runtime tool auto-login, dual-SHA release classifier, set_pou_code omitted-decl wipe fix, add_library managed-overload, etc. MCP server for CODESYS with persistent UI instance and file-based IPC.",
"main": "dist/server.js",
"bin": {

View file

@ -106,36 +106,43 @@ def _build_message_entry(msg, category_name=None):
def _enumerate_categories(script_engine_arg):
"""Returns a list of (label, category_guid_or_None) tuples for every
message category the IDE exposes plus a (None, None) sentinel for the
no-filter call.
Discovery path: script_engine.system.get_message_categories() (the
METHOD) returns the live List[Guid] of category IDs registered in
this IDE session. script_engine.system.get_message_category_description(guid)
gives the human-readable label per category ('Build', 'Application
Composer', 'SVN', 'Script Messages', 'Additional code checks', etc.).
History: pre-0.6.4 the function looked at `system.message_categories`
(a property) which doesn't exist, then fell back to a hardcoded list
of GUIDs that happened to NOT include the actual Build category
(97f48d64-a2a3-4856-b640-75c046e37ea9). Result: compile errors were
invisible. Verified by diagnostic probe with intentional syntax-error
injection on 2026-04-29."""
cats = [('<default-no-filter>', None)]
se_sys = getattr(script_engine_arg, 'system', None)
if se_sys is None or not hasattr(se_sys, 'get_message_categories'):
return cats
try:
se_sys = getattr(script_engine_arg, 'system', None)
if se_sys is not None:
mc = getattr(se_sys, 'message_categories', None)
if mc is not None:
try:
for c in mc:
try:
label = (
_coerce_str(getattr(c, 'name', None))
or _coerce_str(getattr(c, 'guid', None))
or _coerce_str(c)
or '<unnamed>'
)
cats.append((label, c))
except Exception:
continue
except Exception:
pass
except Exception:
pass
well_known = [
('Compile (well-known GUID)', '90F1B997-7AB7-4B11-B637-D55D71BC4F2A'),
('Build (well-known GUID)', '7390398F-1B2F-4B30-B6E2-37F2BB7B57E0'),
('Online (well-known GUID)', '15F65557-DC73-4193-B7F2-EFF5A2A6C10C'),
('LibMan (well-known GUID)', '0B8D54FB-C68A-43F9-9B4D-79DBE1F8DF44'),
]
for lbl, g in well_known:
cats.append((lbl, g))
guids = se_sys.get_message_categories()
except Exception as e:
print("DEBUG: get_message_categories() raised: %s" % e)
return cats
if guids is None:
return cats
for g in guids:
label = None
try:
if hasattr(se_sys, 'get_message_category_description'):
label = _coerce_str(se_sys.get_message_category_description(g))
except Exception:
label = None
if not label:
label = _coerce_str(g) or '<unnamed>'
cats.append((label, g))
return cats

View file

@ -106,39 +106,35 @@ def _build_message_entry(msg, category_name=None):
def _enumerate_categories(script_engine_arg):
"""Returns a list of (label, category_obj_or_guid_or_None). Always
includes a (None, None) sentinel for the no-filter call."""
"""Returns a list of (label, category_guid_or_None) tuples for every
message category the IDE exposes plus a (None, None) sentinel for the
no-filter call.
Discovery path: script_engine.system.get_message_categories() (the
METHOD) returns the live List[Guid]. script_engine.system.get_message_category_description(guid)
gives the human-readable label per category. See compile_project.py
for the verified-on-SP22 history note."""
cats = [('<default-no-filter>', None)]
se_sys = getattr(script_engine_arg, 'system', None)
if se_sys is None or not hasattr(se_sys, 'get_message_categories'):
return cats
try:
se_sys = getattr(script_engine_arg, 'system', None)
if se_sys is not None:
mc = getattr(se_sys, 'message_categories', None)
if mc is not None:
try:
for c in mc:
try:
label = (
_coerce_str(getattr(c, 'name', None))
or _coerce_str(getattr(c, 'guid', None))
or _coerce_str(c)
or '<unnamed>'
)
cats.append((label, c))
except Exception:
continue
except Exception:
pass
except Exception:
pass
# Well-known V3.5 category GUIDs (probed as fallback strings).
well_known = [
('Compile (well-known GUID)', '90F1B997-7AB7-4B11-B637-D55D71BC4F2A'),
('Build (well-known GUID)', '7390398F-1B2F-4B30-B6E2-37F2BB7B57E0'),
('Online (well-known GUID)', '15F65557-DC73-4193-B7F2-EFF5A2A6C10C'),
('LibMan (well-known GUID)', '0B8D54FB-C68A-43F9-9B4D-79DBE1F8DF44'),
]
for lbl, g in well_known:
cats.append((lbl, g))
guids = se_sys.get_message_categories()
except Exception as e:
print("DEBUG: get_message_categories() raised: %s" % e)
return cats
if guids is None:
return cats
for g in guids:
label = None
try:
if hasattr(se_sys, 'get_message_category_description'):
label = _coerce_str(se_sys.get_message_category_description(g))
except Exception:
label = None
if not label:
label = _coerce_str(g) or '<unnamed>'
cats.append((label, g))
return cats