0
0
Fork 0
Codesys-MCP-SP21-plus/src/scripts/read_running_version_online.py
Karstein Phobic Nyvold Kvistad e29bb7e534 feat(read_running_version_online): read _MCP_PROJECT_VERSION from running PLC
New MCP tool that reads the running project's version from a
connected PLC over the CODESYS online protocol (port 11740 / gateway).
Returns the value of `_MCP_PROJECT_VERSION.sVersion` -- the runtime
anchor that bump_project_version maintains automatically (commit
00d2dd8). Closes the loop on the version-tracking convention:

  bump_project_version           writes _MCP_PROJECT_VERSION.sVersion
                                 into the project (compiled into the
                                 boot application on next download)
  read_running_version_online    reads the same symbol back from the
                                 running PLC over the online protocol

Pairs with the existing connect_to_device + read_variable pattern
but with sharpened error messages tailored to the version-read use
case:
  - missing GVL                  -> 'has bump_project_version run on
                                     this project? Or has the boot
                                     application not been downloaded
                                     since the bump?'
  - read returns None            -> 'variable exists in project but
                                     not in boot app -- download_to_device
                                     after the last bump.'
  - missing online API           -> typed clearly, suggests SP-version
                                     drift.

Implementation: ensure_project_open + ensure_online_connection +
online_app.read_value('_MCP_PROJECT_VERSION.sVersion'); strips quote
characters from the returned STRING; sanity-checks the shape against
\b\d+\.\d+\.\d+\.\d+\b and warns if it doesn't match the
4-part convention. TS handler extracts the matched RUNNING_VERSION
line from the script output and surfaces it as the headline of the
tool response.

SSH transport variant (read_running_version_ssh) lands as a separate
later commit once a real PFC is reachable to test against.
2026-04-26 00:33:22 +02:00

80 lines
3.4 KiB
Python

import sys, scriptengine as script_engine, os, traceback, re
# Reads the running project's version from the PLC over the CODESYS online
# protocol (port 11740 on a soft PLC; gateway-resolved on real hardware).
# Looks at the standard runtime anchor maintained by bump_project_version:
#
# _MCP_PROJECT_VERSION.sVersion : STRING
#
# Returns the string value plus a sanity check (matches X.Y.Z.W shape).
# Soft-fails if:
# - the project hasn't been bumped yet (GVL missing) -> clear hint
# - the application isn't downloaded -> clear hint
# - the connection drops mid-read -> error surfaced
VARIABLE_PATH = "_MCP_PROJECT_VERSION.sVersion"
VERSION_PATTERN = re.compile(r"\b(\d+\.\d+\.\d+\.\d+)\b")
try:
print("DEBUG: read_running_version_online: Project='%s'" % PROJECT_FILE_PATH)
primary_project = ensure_project_open(PROJECT_FILE_PATH)
online_app, target_app = ensure_online_connection(primary_project)
app_name = getattr(target_app, 'get_name', lambda: 'Unknown')()
print("DEBUG: connected to application '%s'" % app_name)
# Read the version anchor
raw_value = None
if hasattr(online_app, 'read_value'):
try:
result = online_app.read_value(VARIABLE_PATH)
if result is not None:
if hasattr(result, 'value'):
raw_value = result.value
else:
raw_value = result
except Exception as e:
msg = str(e)
if 'not found' in msg.lower() or 'unknown' in msg.lower() or 'symbol' in msg.lower():
raise RuntimeError(
"Variable '%s' not found on the running PLC. "
"Either bump_project_version has never been run on this project "
"(run it first to create the GVL), or the running boot application "
"predates the GVL (download_to_device after the bump to publish "
"the new symbol). Underlying error: %s" % (VARIABLE_PATH, e)
)
raise
elif hasattr(online_app, 'read'):
try:
raw_value = online_app.read(VARIABLE_PATH)
except Exception as e:
raise RuntimeError(
"Failed to read '%s' via .read(): %s" % (VARIABLE_PATH, e))
else:
raise TypeError(
"Online application object does not expose read_value() or read(). "
"This SP/install may have a different online API surface.")
if raw_value is None:
raise RuntimeError(
"read_value returned None for '%s'. Often means the variable exists in "
"the project but the running boot application doesn't include it -- "
"did you download_to_device after the last bump?" % VARIABLE_PATH)
version_str = str(raw_value).strip().strip("'\"")
matches_shape = bool(VERSION_PATTERN.match(version_str))
print("RUNNING_VERSION: %s" % version_str)
print("Variable: %s" % VARIABLE_PATH)
print("Application: %s" % app_name)
print("Shape check (X.Y.Z.W): %s" % ("OK" if matches_shape else "WARN -- value does not look like a 4-part version"))
print("SCRIPT_SUCCESS: read_running_version_online complete.")
sys.exit(0)
except Exception as e:
detailed = traceback.format_exc()
msg = "Error reading running version from project '%s': %s\n%s" % (
PROJECT_FILE_PATH, e, detailed)
print(msg)
print("SCRIPT_ERROR: %s" % msg)
sys.exit(1)