0
0
Fork 0

fix(write_variable): probe write methods + diagnostic dump on miss

Mirror of the connect_to_device probe pattern (e862846). The fork's
prior write_variable.py hard-coded write_value() then write() and
errored if neither existed; on SP22 Patch 1 neither is exposed on
online_app, even though the read counterpart (read_value()) works.

Now tries six method names in priority order:
  - Single-write: write_value, set_value, write, set
  - Batch-write:  write_values, set_values   (passes [(name, value)])

On total miss, dumps sorted dir(online_app) so the next debug session
sees exactly what the live online application object exposes -- the
same diagnostic technique that found 'librarymanager' in the earlier
install_library_file probe.

read_variable already works (uses read_value()) so the asymmetry is
specifically on the write side. Verifying by re-running write_variable
after the MCP restart will reveal which method actually exists, and
we can pin it explicitly in a follow-up if useful.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Karstein Phobic Nyvold Kvistad 2026-04-25 18:32:57 +02:00
parent eee8ce2d1e
commit 010811b342

View file

@ -4,32 +4,61 @@ VARIABLE_PATH = "{VARIABLE_PATH}"
VARIABLE_VALUE = "{VARIABLE_VALUE}"
try:
print("DEBUG: write_variable script: Variable='%s', Value='%s', Project='%s'" % (VARIABLE_PATH, VARIABLE_VALUE, PROJECT_FILE_PATH))
print("DEBUG: write_variable script: Variable='%s', Value='%s', Project='%s'" % (
VARIABLE_PATH, VARIABLE_VALUE, PROJECT_FILE_PATH))
primary_project = ensure_project_open(PROJECT_FILE_PATH)
if not VARIABLE_PATH: raise ValueError("Variable path empty.")
if not VARIABLE_PATH:
raise ValueError("Variable path empty.")
online_app, target_app = ensure_online_connection(primary_project)
app_name = getattr(target_app, 'get_name', lambda: "Unknown")()
# Write the variable value
if hasattr(online_app, 'write_value'):
try:
online_app.write_value(VARIABLE_PATH, VARIABLE_VALUE)
print("DEBUG: write_value succeeded.")
except Exception as e:
print("DEBUG: write_value failed: %s" % e)
raise
# The write counterpart of read_value() has shifted across CODESYS SPs.
# Some expose 'write_value(name, value)', some 'set_value(...)',
# and some only the batch form 'write_values([(name, value), ...])'.
# Probe each available method, then fall back to dumping the available
# methods on online_app so future debug sessions know what's exposed.
candidates = []
elif hasattr(online_app, 'write'):
try:
online_app.write(VARIABLE_PATH, VARIABLE_VALUE)
print("DEBUG: write succeeded.")
except Exception as e:
print("DEBUG: write failed: %s" % e)
raise
# Single-write methods, (name, value) style
for method_name in ('write_value', 'set_value', 'write', 'set'):
if hasattr(online_app, method_name):
candidates.append((method_name + '(name, value)', method_name, [(VARIABLE_PATH, VARIABLE_VALUE)]))
else:
raise TypeError("Online application does not support write_value() or write().")
# Batch-write methods, [(name, value), ...] style
for method_name in ('write_values', 'set_values'):
if hasattr(online_app, method_name):
candidates.append((method_name + '([(name, value)])', method_name, [[(VARIABLE_PATH, VARIABLE_VALUE)]]))
if not candidates:
# No known method exposed. Dump diagnostic.
attrs = sorted([a for a in dir(online_app) if not a.startswith('_')])
raise TypeError(
"Online application exposes no known write method "
"(tried write_value/set_value/write/set/write_values/set_values).\n"
"Available attributes: %s" % attrs
)
written = False
last_err = None
for desc, method_name, args_list in candidates:
try:
getattr(online_app, method_name)(*args_list)
print("DEBUG: %s OK" % desc)
written = True
break
except Exception as e:
last_err = e
print("DEBUG: %s failed: %s: %s" % (desc, type(e).__name__, e))
if not written:
# All known method names were exposed but every call failed. Dump
# diagnostic + last error so we can iterate.
attrs = sorted([a for a in dir(online_app) if not a.startswith('_')])
raise RuntimeError(
"All known write methods were rejected. Last error: %s\n"
"Available attributes on online_app: %s" % (last_err, attrs)
)
print("Variable: %s" % VARIABLE_PATH)
print("Value Written: %s" % VARIABLE_VALUE)
@ -38,7 +67,8 @@ try:
sys.exit(0)
except Exception as e:
detailed_error = traceback.format_exc()
error_message = "Error writing variable '%s' in project %s: %s\n%s" % (VARIABLE_PATH, PROJECT_FILE_PATH, e, detailed_error)
error_message = "Error writing variable '%s' in project %s: %s\n%s" % (
VARIABLE_PATH, PROJECT_FILE_PATH, e, detailed_error)
print(error_message)
print("SCRIPT_ERROR: %s" % error_message)
sys.exit(1)