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:
parent
eee8ce2d1e
commit
010811b342
1 changed files with 50 additions and 20 deletions
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue