@codesys/mcp-toolkit 1.1.14 → 1.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +24 -0
- package/CHANGELOG.md +4 -63
- package/CONTRIBUTING.md +72 -0
- package/codesys-mcp-toolkit-1.1.16.tgz +0 -0
- package/codesys-mcp-toolkit-1.1.3.tgz +0 -0
- package/codesys-mcp-toolkit-1.1.6.tgz +0 -0
- package/codesys-mcp-toolkit-1.1.7.tgz +0 -0
- package/dist/server.js +1037 -397
- package/package.json +3 -15
- package/src/scripts/check_status.py +0 -21
- package/src/scripts/compile_project.py +0 -65
- package/src/scripts/create_method.py +0 -80
- package/src/scripts/create_pou.py +0 -141
- package/src/scripts/create_project.py +0 -54
- package/src/scripts/create_property.py +0 -78
- package/src/scripts/ensure_project_open.py +0 -170
- package/src/scripts/find_object_by_path.py +0 -116
- package/src/scripts/get_pou_code.py +0 -91
- package/src/scripts/get_project_structure.py +0 -187
- package/src/scripts/open_project.py +0 -25
- package/src/scripts/save_project.py +0 -29
- package/src/scripts/set_pou_code.py +0 -104
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import traceback
|
|
2
|
-
# --- Find object by path function ---
|
|
3
|
-
def find_object_by_path_robust(start_node, full_path, target_type_name="object"):
|
|
4
|
-
print("DEBUG: Finding %s by path: '%s'" % (target_type_name, full_path))
|
|
5
|
-
# Handle both dot and slash separators
|
|
6
|
-
# First replace backslashes with forward slashes
|
|
7
|
-
path_with_slashes = full_path.replace('\\', '/').strip('/')
|
|
8
|
-
# Then replace dots with slashes (CODESYS sometimes uses dots)
|
|
9
|
-
normalized_path = path_with_slashes.replace('.', '/')
|
|
10
|
-
# Split into parts
|
|
11
|
-
path_parts = normalized_path.split('/')
|
|
12
|
-
if not path_parts:
|
|
13
|
-
print("ERROR: Path is empty.")
|
|
14
|
-
return None
|
|
15
|
-
|
|
16
|
-
# Determine the actual starting node (project or application)
|
|
17
|
-
project = start_node # Assume start_node is project initially
|
|
18
|
-
if not hasattr(start_node, 'active_application') and hasattr(start_node, 'project'):
|
|
19
|
-
# If start_node is not project but has project ref (e.g., an application), get the project
|
|
20
|
-
try: project = start_node.project
|
|
21
|
-
except Exception as proj_ref_err:
|
|
22
|
-
print("WARN: Could not get project reference from start_node: %s" % proj_ref_err)
|
|
23
|
-
# Proceed assuming start_node might be the project anyway or search fails
|
|
24
|
-
|
|
25
|
-
# Try to get the application object robustly if we think we have the project
|
|
26
|
-
app = None
|
|
27
|
-
if hasattr(project, 'active_application'):
|
|
28
|
-
try: app = project.active_application
|
|
29
|
-
except Exception: pass # Ignore errors getting active app
|
|
30
|
-
if not app:
|
|
31
|
-
try:
|
|
32
|
-
apps = project.find("Application", True) # Search recursively
|
|
33
|
-
if apps: app = apps[0]
|
|
34
|
-
except Exception: pass
|
|
35
|
-
|
|
36
|
-
# Check if the first path part matches the application name
|
|
37
|
-
app_name_lower = ""
|
|
38
|
-
if app:
|
|
39
|
-
try: app_name_lower = (app.get_name() or "application").lower()
|
|
40
|
-
except Exception: app_name_lower = "application" # Fallback
|
|
41
|
-
|
|
42
|
-
# Decide where to start the traversal
|
|
43
|
-
current_obj = start_node # Default to the node passed in
|
|
44
|
-
if hasattr(project, 'active_application'): # Only adjust if start_node was likely the project
|
|
45
|
-
if app and path_parts[0].lower() == app_name_lower:
|
|
46
|
-
print("DEBUG: Path starts with Application name '%s'. Beginning search there." % path_parts[0])
|
|
47
|
-
current_obj = app
|
|
48
|
-
path_parts = path_parts[1:] # Consume the app name part
|
|
49
|
-
# If path was *only* the application name
|
|
50
|
-
if not path_parts:
|
|
51
|
-
print("DEBUG: Target path is the Application object itself.")
|
|
52
|
-
return current_obj
|
|
53
|
-
else:
|
|
54
|
-
print("DEBUG: Path does not start with Application name. Starting search from project root.")
|
|
55
|
-
current_obj = project # Start search from the project root
|
|
56
|
-
else:
|
|
57
|
-
print("DEBUG: Starting search from originally provided node.")
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
# Traverse the remaining path parts
|
|
61
|
-
parent_path_str = getattr(current_obj, 'get_name', lambda: str(current_obj))() # Safer name getting
|
|
62
|
-
|
|
63
|
-
for i, part_name in enumerate(path_parts):
|
|
64
|
-
is_last_part = (i == len(path_parts) - 1)
|
|
65
|
-
print("DEBUG: Searching for part [%d/%d]: '%s' under '%s'" % (i+1, len(path_parts), part_name, parent_path_str))
|
|
66
|
-
found_in_parent = None
|
|
67
|
-
try:
|
|
68
|
-
# Prioritize non-recursive find for direct children
|
|
69
|
-
children_of_current = current_obj.get_children(False)
|
|
70
|
-
print("DEBUG: Found %d direct children under '%s'." % (len(children_of_current), parent_path_str))
|
|
71
|
-
for child in children_of_current:
|
|
72
|
-
child_name = getattr(child, 'get_name', lambda: None)() # Safer name getting
|
|
73
|
-
# print("DEBUG: Checking child: '%s'" % child_name) # Verbose
|
|
74
|
-
if child_name == part_name:
|
|
75
|
-
found_in_parent = child
|
|
76
|
-
print("DEBUG: Found direct child matching '%s'." % part_name)
|
|
77
|
-
break # Found direct child, stop searching children
|
|
78
|
-
|
|
79
|
-
# If not found directly, AND it's the last part, try recursive find from current parent
|
|
80
|
-
if not found_in_parent and is_last_part:
|
|
81
|
-
print("DEBUG: Direct find failed for last part '%s'. Trying recursive find under '%s'." % (part_name, parent_path_str))
|
|
82
|
-
found_recursive_list = current_obj.find(part_name, True) # Recursive find
|
|
83
|
-
if found_recursive_list:
|
|
84
|
-
# Maybe add a check here if multiple are found?
|
|
85
|
-
found_in_parent = found_recursive_list[0] # Take the first match
|
|
86
|
-
print("DEBUG: Found last part '%s' recursively." % part_name)
|
|
87
|
-
else:
|
|
88
|
-
print("DEBUG: Recursive find also failed for last part '%s'." % part_name)
|
|
89
|
-
|
|
90
|
-
# Update current object if found
|
|
91
|
-
if found_in_parent:
|
|
92
|
-
current_obj = found_in_parent
|
|
93
|
-
parent_path_str = getattr(current_obj, 'get_name', lambda: part_name)() # Safer name getting
|
|
94
|
-
print("DEBUG: Stepped into '%s'." % parent_path_str)
|
|
95
|
-
else:
|
|
96
|
-
# If not found at any point, the path is invalid from this parent
|
|
97
|
-
print("ERROR: Path part '%s' not found under '%s'." % (part_name, parent_path_str))
|
|
98
|
-
return None # Path broken
|
|
99
|
-
|
|
100
|
-
except Exception as find_err:
|
|
101
|
-
print("ERROR: Exception while searching for '%s' under '%s': %s" % (part_name, parent_path_str, find_err))
|
|
102
|
-
traceback.print_exc()
|
|
103
|
-
return None # Error during search
|
|
104
|
-
|
|
105
|
-
# Final verification (optional but recommended): Check if the found object's name matches the last part
|
|
106
|
-
final_expected_name = full_path.split('/')[-1]
|
|
107
|
-
found_final_name = getattr(current_obj, 'get_name', lambda: None)() # Safer name getting
|
|
108
|
-
|
|
109
|
-
if found_final_name == final_expected_name:
|
|
110
|
-
print("DEBUG: Final %s found and name verified for path '%s': %s" % (target_type_name, full_path, found_final_name))
|
|
111
|
-
return current_obj
|
|
112
|
-
else:
|
|
113
|
-
print("ERROR: Traversal ended on object '%s' but expected final name was '%s'." % (found_final_name, final_expected_name))
|
|
114
|
-
return None # Name mismatch implies target not found as expected
|
|
115
|
-
|
|
116
|
-
# --- End of find object function ---
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import sys, scriptengine as script_engine, os, traceback
|
|
2
|
-
|
|
3
|
-
# Import ensure_project_open function from the template
|
|
4
|
-
# This will be prepended by the script loader
|
|
5
|
-
def ensure_project_open(target_project_path):
|
|
6
|
-
# Function implementation will be included from ensure_project_open.py
|
|
7
|
-
pass
|
|
8
|
-
|
|
9
|
-
# Import find_object_by_path function from the template
|
|
10
|
-
# This will be prepended by the script loader
|
|
11
|
-
def find_object_by_path_robust(start_node, full_path, target_type_name="object"):
|
|
12
|
-
# Function implementation will be included from find_object_by_path.py
|
|
13
|
-
pass
|
|
14
|
-
|
|
15
|
-
POU_FULL_PATH = "{POU_FULL_PATH}"
|
|
16
|
-
CODE_START_MARKER = "### POU CODE START ###"
|
|
17
|
-
CODE_END_MARKER = "### POU CODE END ###"
|
|
18
|
-
DECL_START_MARKER = "### POU DECLARATION START ###"
|
|
19
|
-
DECL_END_MARKER = "### POU DECLARATION END ###"
|
|
20
|
-
IMPL_START_MARKER = "### POU IMPLEMENTATION START ###"
|
|
21
|
-
IMPL_END_MARKER = "### POU IMPLEMENTATION END ###"
|
|
22
|
-
|
|
23
|
-
try:
|
|
24
|
-
print("DEBUG: Getting code: POU_FULL_PATH='%s', Project='%s'" % (POU_FULL_PATH, PROJECT_FILE_PATH))
|
|
25
|
-
primary_project = ensure_project_open(PROJECT_FILE_PATH)
|
|
26
|
-
if not POU_FULL_PATH: raise ValueError("POU full path empty.")
|
|
27
|
-
|
|
28
|
-
# Find the target POU/Method/Property object
|
|
29
|
-
target_object = find_object_by_path_robust(primary_project, POU_FULL_PATH, "target object")
|
|
30
|
-
if not target_object: raise ValueError("Target object not found using path: %s" % POU_FULL_PATH)
|
|
31
|
-
|
|
32
|
-
target_name = getattr(target_object, 'get_name', lambda: POU_FULL_PATH)()
|
|
33
|
-
print("DEBUG: Found target object: %s" % target_name)
|
|
34
|
-
|
|
35
|
-
declaration_code = ""; implementation_code = ""
|
|
36
|
-
|
|
37
|
-
# --- Get Declaration Part ---
|
|
38
|
-
if hasattr(target_object, 'textual_declaration'):
|
|
39
|
-
decl_obj = target_object.textual_declaration
|
|
40
|
-
if decl_obj and hasattr(decl_obj, 'text'):
|
|
41
|
-
try:
|
|
42
|
-
declaration_code = decl_obj.text
|
|
43
|
-
print("DEBUG: Got declaration text.")
|
|
44
|
-
except Exception as decl_read_err:
|
|
45
|
-
print("ERROR: Failed to read declaration text: %s" % decl_read_err)
|
|
46
|
-
declaration_code = "/* ERROR reading declaration: %s */" % decl_read_err
|
|
47
|
-
else:
|
|
48
|
-
print("WARN: textual_declaration exists but is None or has no 'text' attribute.")
|
|
49
|
-
else:
|
|
50
|
-
print("WARN: No textual_declaration attribute.")
|
|
51
|
-
|
|
52
|
-
# --- Get Implementation Part ---
|
|
53
|
-
if hasattr(target_object, 'textual_implementation'):
|
|
54
|
-
impl_obj = target_object.textual_implementation
|
|
55
|
-
if impl_obj and hasattr(impl_obj, 'text'):
|
|
56
|
-
try:
|
|
57
|
-
implementation_code = impl_obj.text
|
|
58
|
-
print("DEBUG: Got implementation text.")
|
|
59
|
-
except Exception as impl_read_err:
|
|
60
|
-
print("ERROR: Failed to read implementation text: %s" % impl_read_err)
|
|
61
|
-
implementation_code = "/* ERROR reading implementation: %s */" % impl_read_err
|
|
62
|
-
else:
|
|
63
|
-
print("WARN: textual_implementation exists but is None or has no 'text' attribute.")
|
|
64
|
-
else:
|
|
65
|
-
print("WARN: No textual_implementation attribute.")
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
print("Code retrieved for: %s" % target_name)
|
|
69
|
-
# Print declaration between markers, ensuring markers are on separate lines
|
|
70
|
-
print("\\n" + DECL_START_MARKER)
|
|
71
|
-
print(declaration_code)
|
|
72
|
-
print(DECL_END_MARKER + "\\n")
|
|
73
|
-
# Print implementation between markers
|
|
74
|
-
print(IMPL_START_MARKER)
|
|
75
|
-
print(implementation_code)
|
|
76
|
-
print(IMPL_END_MARKER + "\\n")
|
|
77
|
-
|
|
78
|
-
# --- LEGACY MARKERS for backward compatibility if needed ---
|
|
79
|
-
# Combine both for old marker format, adding a separator line
|
|
80
|
-
# legacy_combined_code = declaration_code + "\\n\\n// Implementation\\n" + implementation_code
|
|
81
|
-
# print(CODE_START_MARKER); print(legacy_combined_code); print(CODE_END_MARKER)
|
|
82
|
-
# --- END LEGACY ---
|
|
83
|
-
|
|
84
|
-
print("SCRIPT_SUCCESS: Code retrieved.")
|
|
85
|
-
sys.exit(0)
|
|
86
|
-
except Exception as e:
|
|
87
|
-
detailed_error = traceback.format_exc()
|
|
88
|
-
error_message = "Error getting code for object '%s' in project '%s': %s\\n%s" % (POU_FULL_PATH, PROJECT_FILE_PATH, e, detailed_error)
|
|
89
|
-
print(error_message)
|
|
90
|
-
print("SCRIPT_ERROR: %s" % error_message)
|
|
91
|
-
sys.exit(1)
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import sys, scriptengine as script_engine, os, traceback
|
|
2
|
-
|
|
3
|
-
# Import ensure_project_open function from the template
|
|
4
|
-
# This will be prepended by the script loader
|
|
5
|
-
def ensure_project_open(target_project_path):
|
|
6
|
-
# Function implementation will be included from ensure_project_open.py
|
|
7
|
-
pass
|
|
8
|
-
|
|
9
|
-
# Simpler version that directly uses script_engine.projects.open
|
|
10
|
-
def direct_project_open(project_path):
|
|
11
|
-
print("DEBUG: Attempting to open project directly: %s" % project_path)
|
|
12
|
-
try:
|
|
13
|
-
# Clean up any double backslashes
|
|
14
|
-
clean_path = project_path.replace('\\\\', '\\').strip('"\'')
|
|
15
|
-
print("DEBUG: Using path: %s" % clean_path)
|
|
16
|
-
|
|
17
|
-
# First check if the project is already open
|
|
18
|
-
try:
|
|
19
|
-
current = script_engine.get_current_project()
|
|
20
|
-
if current and hasattr(current, 'get_path'):
|
|
21
|
-
current_path = current.get_path()
|
|
22
|
-
if current_path == clean_path:
|
|
23
|
-
print("DEBUG: Project already open: %s" % clean_path)
|
|
24
|
-
return current
|
|
25
|
-
else:
|
|
26
|
-
print("DEBUG: Different project open. Closing: %s" % current_path)
|
|
27
|
-
script_engine.close_project()
|
|
28
|
-
except Exception as e:
|
|
29
|
-
print("DEBUG: Error checking current project: %s" % e)
|
|
30
|
-
|
|
31
|
-
# Try to open the project
|
|
32
|
-
try:
|
|
33
|
-
# Try different ways of opening the project
|
|
34
|
-
print("DEBUG: Trying script_engine.open_project")
|
|
35
|
-
project = script_engine.open_project(clean_path)
|
|
36
|
-
if project:
|
|
37
|
-
print("DEBUG: Project opened successfully with open_project")
|
|
38
|
-
return project
|
|
39
|
-
except Exception as e:
|
|
40
|
-
print("DEBUG: Error with open_project: %s" % e)
|
|
41
|
-
|
|
42
|
-
# Try alternative method
|
|
43
|
-
try:
|
|
44
|
-
print("DEBUG: Trying script_engine.projects.open")
|
|
45
|
-
project = script_engine.projects.open(clean_path)
|
|
46
|
-
if project:
|
|
47
|
-
print("DEBUG: Project opened successfully with projects.open")
|
|
48
|
-
return project
|
|
49
|
-
except Exception as e:
|
|
50
|
-
print("DEBUG: Error with projects.open: %s" % e)
|
|
51
|
-
|
|
52
|
-
# One more attempt if available
|
|
53
|
-
try:
|
|
54
|
-
print("DEBUG: Trying primary project access")
|
|
55
|
-
project = script_engine.projects.primary
|
|
56
|
-
if project:
|
|
57
|
-
print("DEBUG: Got primary project")
|
|
58
|
-
if hasattr(project, 'get_path'):
|
|
59
|
-
path = project.get_path()
|
|
60
|
-
print("DEBUG: Primary project path: %s" % path)
|
|
61
|
-
if path == clean_path:
|
|
62
|
-
return project
|
|
63
|
-
return project # Return anyway as fallback
|
|
64
|
-
except Exception as e:
|
|
65
|
-
print("DEBUG: Error getting primary project: %s" % e)
|
|
66
|
-
|
|
67
|
-
print("DEBUG: All attempts to open project failed")
|
|
68
|
-
return None
|
|
69
|
-
|
|
70
|
-
except Exception as e:
|
|
71
|
-
print("DEBUG: Unexpected error in direct_project_open: %s" % e)
|
|
72
|
-
traceback.print_exc()
|
|
73
|
-
return None
|
|
74
|
-
|
|
75
|
-
def get_object_structure(obj, indent=0, max_depth=10): # Add max_depth
|
|
76
|
-
lines = []; indent_str = " " * indent
|
|
77
|
-
if obj is None:
|
|
78
|
-
lines.append("%s- ERROR: Null object received" % indent_str)
|
|
79
|
-
return lines
|
|
80
|
-
|
|
81
|
-
if indent > max_depth:
|
|
82
|
-
lines.append("%s- Max recursion depth reached." % indent_str)
|
|
83
|
-
return lines
|
|
84
|
-
|
|
85
|
-
try:
|
|
86
|
-
name = "Unnamed"; obj_type = type(obj).__name__
|
|
87
|
-
guid_str = ""
|
|
88
|
-
folder_str = ""
|
|
89
|
-
try:
|
|
90
|
-
name = getattr(obj, 'get_name', lambda: "Unnamed")() or "Unnamed" # Safer get_name
|
|
91
|
-
if hasattr(obj, 'guid'): guid_str = " {%s}" % obj.guid
|
|
92
|
-
if hasattr(obj, 'is_folder') and obj.is_folder: folder_str = " [Folder]"
|
|
93
|
-
except Exception as name_err:
|
|
94
|
-
print("WARN: Error getting name/guid/folder status for an object: %s" % name_err)
|
|
95
|
-
name = "!!! Error Getting Name !!!"
|
|
96
|
-
|
|
97
|
-
lines.append("%s- %s (%s)%s%s" % (indent_str, name, obj_type, folder_str, guid_str))
|
|
98
|
-
|
|
99
|
-
# Get children only if the object potentially has them
|
|
100
|
-
children = []
|
|
101
|
-
can_have_children = hasattr(obj, 'get_children') and (
|
|
102
|
-
not hasattr(obj, 'is_folder') or # If it's not clear if it's a folder (e.g., project root)
|
|
103
|
-
(hasattr(obj, 'is_folder') and obj.is_folder) or # If it is a folder
|
|
104
|
-
# Add known container types explicitly, check marker interfaces too
|
|
105
|
-
hasattr(obj, 'is_project') or hasattr(obj, 'is_application') or hasattr(obj, 'is_device') or hasattr(obj,'is_pou')
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
if can_have_children:
|
|
109
|
-
try:
|
|
110
|
-
children = obj.get_children(False)
|
|
111
|
-
print("DEBUG: '%s' has %d children" % (name, len(children)))
|
|
112
|
-
except Exception as get_child_err:
|
|
113
|
-
lines.append("%s ERROR getting children: %s" % (indent_str, get_child_err))
|
|
114
|
-
traceback.print_exc() # Now print detailed error
|
|
115
|
-
|
|
116
|
-
for child in children:
|
|
117
|
-
lines.extend(get_object_structure(child, indent + 1, max_depth)) # Recurse
|
|
118
|
-
|
|
119
|
-
except Exception as e:
|
|
120
|
-
lines.append("%s- Error processing node: %s" % (indent_str, e))
|
|
121
|
-
traceback.print_exc() # Print detailed error for this node
|
|
122
|
-
return lines
|
|
123
|
-
|
|
124
|
-
# Simple path cleanup function - CODESYS doesn't need double-escaped paths
|
|
125
|
-
def clean_path(path_str):
|
|
126
|
-
# Just remove any quotes that might be present
|
|
127
|
-
cleaned = path_str.strip('"\'')
|
|
128
|
-
print("DEBUG: Cleaned path: '%s'" % cleaned)
|
|
129
|
-
return cleaned
|
|
130
|
-
|
|
131
|
-
try:
|
|
132
|
-
# Just use the path directly without escaping
|
|
133
|
-
project_path = PROJECT_FILE_PATH.strip('"\'')
|
|
134
|
-
print("DEBUG: Getting structure for project: '%s'" % project_path)
|
|
135
|
-
|
|
136
|
-
print("DEBUG: Opening project directly...")
|
|
137
|
-
# Try our direct method first
|
|
138
|
-
primary_project = direct_project_open(project_path)
|
|
139
|
-
|
|
140
|
-
# If that fails, try the standard method
|
|
141
|
-
if not primary_project:
|
|
142
|
-
print("DEBUG: Direct open failed, trying ensure_project_open...")
|
|
143
|
-
primary_project = ensure_project_open(project_path)
|
|
144
|
-
|
|
145
|
-
if primary_project is None:
|
|
146
|
-
print("DEBUG: ERROR - Project object is None after ensure_project_open")
|
|
147
|
-
raise ValueError("Failed to open project: %s - project object is None" % project_path)
|
|
148
|
-
|
|
149
|
-
print("DEBUG: Project object type: %s" % type(primary_project).__name__)
|
|
150
|
-
project_name = os.path.basename(project_path)
|
|
151
|
-
|
|
152
|
-
if hasattr(primary_project, 'get_name'):
|
|
153
|
-
print("DEBUG: Project name from object: %s" % primary_project.get_name())
|
|
154
|
-
else:
|
|
155
|
-
print("DEBUG: Project object does not have get_name() method")
|
|
156
|
-
|
|
157
|
-
# Check if project has useful methods
|
|
158
|
-
if hasattr(primary_project, 'get_children'):
|
|
159
|
-
print("DEBUG: Project has get_children method")
|
|
160
|
-
else:
|
|
161
|
-
print("DEBUG: WARNING - Project does not have get_children method")
|
|
162
|
-
|
|
163
|
-
if hasattr(primary_project, 'active_application'):
|
|
164
|
-
print("DEBUG: Project has active_application property")
|
|
165
|
-
app = primary_project.active_application
|
|
166
|
-
if app:
|
|
167
|
-
print("DEBUG: Active application name: %s" % getattr(app, 'get_name', lambda: "Unknown")())
|
|
168
|
-
else:
|
|
169
|
-
print("DEBUG: WARNING - Project does not have active_application property")
|
|
170
|
-
|
|
171
|
-
# Use the project object obtained from ensure_project_open
|
|
172
|
-
print("DEBUG: Starting recursion for project structure with max_depth=15")
|
|
173
|
-
structure_list = get_object_structure(primary_project, max_depth=15)
|
|
174
|
-
structure_output = "\\n".join(structure_list)
|
|
175
|
-
|
|
176
|
-
# Ensure markers are printed distinctly
|
|
177
|
-
print("\\n--- PROJECT STRUCTURE START ---")
|
|
178
|
-
print(structure_output)
|
|
179
|
-
print("--- PROJECT STRUCTURE END ---\\n")
|
|
180
|
-
print("SCRIPT_SUCCESS: Project structure retrieved.")
|
|
181
|
-
sys.exit(0)
|
|
182
|
-
except Exception as e:
|
|
183
|
-
detailed_error = traceback.format_exc()
|
|
184
|
-
error_message = "Error getting structure for %s: %s\\n%s" % (PROJECT_FILE_PATH, e, detailed_error)
|
|
185
|
-
print(error_message)
|
|
186
|
-
print("SCRIPT_ERROR: %s" % error_message)
|
|
187
|
-
sys.exit(1)
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import sys, scriptengine as script_engine, os, traceback
|
|
2
|
-
|
|
3
|
-
# Import ensure_project_open function from the template
|
|
4
|
-
# This will be prepended by the script loader
|
|
5
|
-
def ensure_project_open(target_project_path):
|
|
6
|
-
# Function implementation will be included from ensure_project_open.py
|
|
7
|
-
pass
|
|
8
|
-
|
|
9
|
-
try:
|
|
10
|
-
project = ensure_project_open(PROJECT_FILE_PATH)
|
|
11
|
-
# Get name from object if possible, otherwise use path basename
|
|
12
|
-
proj_name = "Unknown"
|
|
13
|
-
try:
|
|
14
|
-
if project: proj_name = project.get_name() or os.path.basename(PROJECT_FILE_PATH)
|
|
15
|
-
else: proj_name = os.path.basename(PROJECT_FILE_PATH) + " (ensure_project_open returned None?)"
|
|
16
|
-
except Exception:
|
|
17
|
-
proj_name = os.path.basename(PROJECT_FILE_PATH) + " (name retrieval failed)"
|
|
18
|
-
print("Project Opened: %s" % proj_name)
|
|
19
|
-
print("SCRIPT_SUCCESS: Project opened successfully.")
|
|
20
|
-
sys.exit(0)
|
|
21
|
-
except Exception as e:
|
|
22
|
-
error_message = "Error opening project %s: %s" % (PROJECT_FILE_PATH, e)
|
|
23
|
-
print(error_message)
|
|
24
|
-
traceback.print_exc()
|
|
25
|
-
print("SCRIPT_ERROR: %s" % error_message); sys.exit(1)
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import sys, scriptengine as script_engine, os, traceback
|
|
2
|
-
|
|
3
|
-
# Import ensure_project_open function from the template
|
|
4
|
-
# This will be prepended by the script loader
|
|
5
|
-
def ensure_project_open(target_project_path):
|
|
6
|
-
# Function implementation will be included from ensure_project_open.py
|
|
7
|
-
pass
|
|
8
|
-
|
|
9
|
-
try:
|
|
10
|
-
primary_project = ensure_project_open(PROJECT_FILE_PATH)
|
|
11
|
-
# Get name from object if possible, otherwise use path basename
|
|
12
|
-
project_name = "Unknown"
|
|
13
|
-
try:
|
|
14
|
-
if primary_project: project_name = primary_project.get_name() or os.path.basename(PROJECT_FILE_PATH)
|
|
15
|
-
else: project_name = os.path.basename(PROJECT_FILE_PATH) + " (ensure_project_open returned None?)"
|
|
16
|
-
except Exception:
|
|
17
|
-
project_name = os.path.basename(PROJECT_FILE_PATH) + " (name retrieval failed)"
|
|
18
|
-
|
|
19
|
-
print("DEBUG: Saving project: %s (%s)" % (project_name, PROJECT_FILE_PATH))
|
|
20
|
-
primary_project.save()
|
|
21
|
-
print("DEBUG: project.save() executed.")
|
|
22
|
-
print("Project Saved: %s" % project_name)
|
|
23
|
-
print("SCRIPT_SUCCESS: Project saved successfully.")
|
|
24
|
-
sys.exit(0)
|
|
25
|
-
except Exception as e:
|
|
26
|
-
error_message = "Error saving project %s: %s" % (PROJECT_FILE_PATH, e)
|
|
27
|
-
print(error_message)
|
|
28
|
-
traceback.print_exc()
|
|
29
|
-
print("SCRIPT_ERROR: %s" % error_message); sys.exit(1)
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import sys, scriptengine as script_engine, os, traceback
|
|
2
|
-
|
|
3
|
-
# Import ensure_project_open function from the template
|
|
4
|
-
# This will be prepended by the script loader
|
|
5
|
-
def ensure_project_open(target_project_path):
|
|
6
|
-
# Function implementation will be included from ensure_project_open.py
|
|
7
|
-
pass
|
|
8
|
-
|
|
9
|
-
# Import find_object_by_path function from the template
|
|
10
|
-
# This will be prepended by the script loader
|
|
11
|
-
def find_object_by_path_robust(start_node, full_path, target_type_name="object"):
|
|
12
|
-
# Function implementation will be included from find_object_by_path.py
|
|
13
|
-
pass
|
|
14
|
-
|
|
15
|
-
POU_FULL_PATH = "{POU_FULL_PATH}" # Expecting format like "Application/MyPOU" or "Folder/SubFolder/MyPOU"
|
|
16
|
-
DECLARATION_CONTENT = """{DECLARATION_CONTENT}"""
|
|
17
|
-
IMPLEMENTATION_CONTENT = """{IMPLEMENTATION_CONTENT}"""
|
|
18
|
-
|
|
19
|
-
try:
|
|
20
|
-
print("DEBUG: set_pou_code script: POU_FULL_PATH='%s', Project='%s'" % (POU_FULL_PATH, PROJECT_FILE_PATH))
|
|
21
|
-
primary_project = ensure_project_open(PROJECT_FILE_PATH)
|
|
22
|
-
if not POU_FULL_PATH: raise ValueError("POU full path empty.")
|
|
23
|
-
|
|
24
|
-
# Find the target POU/Method/Property object
|
|
25
|
-
target_object = find_object_by_path_robust(primary_project, POU_FULL_PATH, "target object")
|
|
26
|
-
if not target_object: raise ValueError("Target object not found using path: %s" % POU_FULL_PATH)
|
|
27
|
-
|
|
28
|
-
target_name = getattr(target_object, 'get_name', lambda: POU_FULL_PATH)()
|
|
29
|
-
print("DEBUG: Found target object: %s" % target_name)
|
|
30
|
-
|
|
31
|
-
# --- Set Declaration Part ---
|
|
32
|
-
declaration_updated = False
|
|
33
|
-
# Check if the content is actually provided (might be None/empty if only impl is set)
|
|
34
|
-
has_declaration_content = 'DECLARATION_CONTENT' in locals() or 'DECLARATION_CONTENT' in globals()
|
|
35
|
-
if has_declaration_content and DECLARATION_CONTENT is not None: # Check not None
|
|
36
|
-
if hasattr(target_object, 'textual_declaration'):
|
|
37
|
-
decl_obj = target_object.textual_declaration
|
|
38
|
-
if decl_obj and hasattr(decl_obj, 'replace'):
|
|
39
|
-
try:
|
|
40
|
-
print("DEBUG: Accessing textual_declaration...")
|
|
41
|
-
decl_obj.replace(DECLARATION_CONTENT)
|
|
42
|
-
print("DEBUG: Set declaration text using replace().")
|
|
43
|
-
declaration_updated = True
|
|
44
|
-
except Exception as decl_err:
|
|
45
|
-
print("ERROR: Failed to set declaration text: %s" % decl_err)
|
|
46
|
-
traceback.print_exc() # Print stack trace for detailed error
|
|
47
|
-
else:
|
|
48
|
-
print("WARN: Target '%s' textual_declaration attribute is None or does not have replace(). Skipping declaration update." % target_name)
|
|
49
|
-
else:
|
|
50
|
-
print("WARN: Target '%s' does not have textual_declaration attribute. Skipping declaration update." % target_name)
|
|
51
|
-
else:
|
|
52
|
-
print("DEBUG: Declaration content not provided or is None. Skipping declaration update.")
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
# --- Set Implementation Part ---
|
|
56
|
-
implementation_updated = False
|
|
57
|
-
has_implementation_content = 'IMPLEMENTATION_CONTENT' in locals() or 'IMPLEMENTATION_CONTENT' in globals()
|
|
58
|
-
if has_implementation_content and IMPLEMENTATION_CONTENT is not None: # Check not None
|
|
59
|
-
if hasattr(target_object, 'textual_implementation'):
|
|
60
|
-
impl_obj = target_object.textual_implementation
|
|
61
|
-
if impl_obj and hasattr(impl_obj, 'replace'):
|
|
62
|
-
try:
|
|
63
|
-
print("DEBUG: Accessing textual_implementation...")
|
|
64
|
-
impl_obj.replace(IMPLEMENTATION_CONTENT)
|
|
65
|
-
print("DEBUG: Set implementation text using replace().")
|
|
66
|
-
implementation_updated = True
|
|
67
|
-
except Exception as impl_err:
|
|
68
|
-
print("ERROR: Failed to set implementation text: %s" % impl_err)
|
|
69
|
-
traceback.print_exc() # Print stack trace for detailed error
|
|
70
|
-
else:
|
|
71
|
-
print("WARN: Target '%s' textual_implementation attribute is None or does not have replace(). Skipping implementation update." % target_name)
|
|
72
|
-
else:
|
|
73
|
-
print("WARN: Target '%s' does not have textual_implementation attribute. Skipping implementation update." % target_name)
|
|
74
|
-
else:
|
|
75
|
-
print("DEBUG: Implementation content not provided or is None. Skipping implementation update.")
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
# --- SAVE THE PROJECT TO PERSIST THE CODE CHANGE ---
|
|
79
|
-
# Only save if something was actually updated to avoid unnecessary saves
|
|
80
|
-
if declaration_updated or implementation_updated:
|
|
81
|
-
try:
|
|
82
|
-
print("DEBUG: Saving Project (after code change)...")
|
|
83
|
-
primary_project.save() # Save the overall project file
|
|
84
|
-
print("DEBUG: Project saved successfully after code change.")
|
|
85
|
-
except Exception as save_err:
|
|
86
|
-
print("ERROR: Failed to save Project after setting code: %s" % save_err)
|
|
87
|
-
detailed_error = traceback.format_exc()
|
|
88
|
-
error_message = "Error saving Project after code change for '%s': %s\\n%s" % (target_name, save_err, detailed_error)
|
|
89
|
-
print(error_message); print("SCRIPT_ERROR: %s" % error_message); sys.exit(1)
|
|
90
|
-
else:
|
|
91
|
-
print("DEBUG: No code parts were updated, skipping project save.")
|
|
92
|
-
# --- END SAVING ---
|
|
93
|
-
|
|
94
|
-
print("Code Set For: %s" % target_name)
|
|
95
|
-
print("Path: %s" % POU_FULL_PATH)
|
|
96
|
-
print("SCRIPT_SUCCESS: Declaration and/or implementation set successfully.")
|
|
97
|
-
sys.exit(0)
|
|
98
|
-
|
|
99
|
-
except Exception as e:
|
|
100
|
-
detailed_error = traceback.format_exc()
|
|
101
|
-
error_message = "Error setting code for object '%s' in project '%s': %s\\n%s" % (POU_FULL_PATH, PROJECT_FILE_PATH, e, detailed_error)
|
|
102
|
-
print(error_message)
|
|
103
|
-
print("SCRIPT_ERROR: %s" % error_message)
|
|
104
|
-
sys.exit(1)
|