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.
80 lines
3.4 KiB
Python
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)
|