@codesys/mcp-toolkit 1.2.1 → 1.3.0

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.
@@ -0,0 +1,278 @@
1
+ import sys, os, json, time, tempfile
2
+ import scriptengine as script_engine
3
+
4
+ # Define markers for script results
5
+ SCRIPT_SUCCESS_MARKER = "SCRIPT_SUCCESS"
6
+ SCRIPT_ERROR_MARKER = "SCRIPT_ERROR"
7
+
8
+ # Session state and command files
9
+ SESSION_DIR = os.path.join(tempfile.gettempdir(), "codesys_session")
10
+ SESSION_STATE_FILE = os.path.join(SESSION_DIR, "session_state.json")
11
+ COMMAND_FILE = os.path.join(SESSION_DIR, "command.json")
12
+ RESULT_FILE = os.path.join(SESSION_DIR, "result.json")
13
+ SESSION_LOCK_FILE = os.path.join(SESSION_DIR, "session.lock")
14
+ POLL_INTERVAL = 0.5 # seconds
15
+
16
+ def setup_session_dir():
17
+ """Create the session directory and initial files"""
18
+ try:
19
+ # Create session directory
20
+ if not os.path.exists(SESSION_DIR):
21
+ os.makedirs(SESSION_DIR)
22
+
23
+ # Initialize session state
24
+ session_state = {
25
+ "active": True,
26
+ "session_id": str(time.time()),
27
+ "start_time": time.time(),
28
+ "status": "running",
29
+ "project_open": False,
30
+ "project_name": "",
31
+ "project_path": ""
32
+ }
33
+
34
+ # Write session state
35
+ with open(SESSION_STATE_FILE, 'w') as f:
36
+ json.dump(session_state, f)
37
+
38
+ # Create empty command file
39
+ with open(COMMAND_FILE, 'w') as f:
40
+ json.dump({"status": "ready", "command": None, "timestamp": time.time()}, f)
41
+
42
+ # Create empty result file
43
+ with open(RESULT_FILE, 'w') as f:
44
+ json.dump({"status": "none", "result": None, "timestamp": time.time()}, f)
45
+
46
+ # Create lock file
47
+ with open(SESSION_LOCK_FILE, 'w') as f:
48
+ f.write(str(time.time()))
49
+
50
+ print(f"Session directory setup at: {SESSION_DIR}")
51
+ return True
52
+ except Exception as e:
53
+ print(f"Failed to set up session directory: {e}")
54
+ print(f"{SCRIPT_ERROR_MARKER}: Session setup failed - {str(e)}")
55
+ return False
56
+
57
+ def update_session_state(status="running"):
58
+ """Update the session state file with current CODESYS state"""
59
+ try:
60
+ # Get current state
61
+ project_open = False
62
+ project_name = "No project open"
63
+ project_path = "N/A"
64
+
65
+ primary_project = script_engine.projects.primary
66
+ if primary_project:
67
+ project_open = True
68
+ try:
69
+ project_path = os.path.normcase(os.path.abspath(primary_project.path))
70
+ try:
71
+ project_name = primary_project.get_name()
72
+ if not project_name:
73
+ project_name = f"Unnamed (path: {os.path.basename(project_path)})"
74
+ except:
75
+ project_name = f"Unnamed (path: {os.path.basename(project_path)})"
76
+ except Exception as e_path:
77
+ project_path = f"N/A (Error: {e_path})"
78
+ project_name = "Unnamed (Path Error)"
79
+
80
+ # Read current state file
81
+ with open(SESSION_STATE_FILE, 'r') as f:
82
+ session_state = json.load(f)
83
+
84
+ # Update state
85
+ session_state["project_open"] = project_open
86
+ session_state["project_name"] = project_name
87
+ session_state["project_path"] = project_path
88
+ session_state["last_update"] = time.time()
89
+ session_state["status"] = status
90
+
91
+ # Write updated state
92
+ with open(SESSION_STATE_FILE, 'w') as f:
93
+ json.dump(session_state, f)
94
+
95
+ return True
96
+ except Exception as e:
97
+ print(f"Failed to update session state: {e}")
98
+ return False
99
+
100
+ def execute_command(command_data):
101
+ """Execute a command"""
102
+ try:
103
+ cmd_type = command_data.get("type", "execute_script")
104
+ script = command_data.get("script", "")
105
+ cmd_id = command_data.get("id", "unknown")
106
+
107
+ result = {
108
+ "id": cmd_id,
109
+ "success": False,
110
+ "output": "",
111
+ "error": ""
112
+ }
113
+
114
+ if cmd_type == "execute_script" and script:
115
+ # Execute the script in the current CODESYS context
116
+ try:
117
+ # Capture output by redirecting stdout/stderr
118
+ import io
119
+ from contextlib import redirect_stdout, redirect_stderr
120
+
121
+ stdout_buffer = io.StringIO()
122
+ stderr_buffer = io.StringIO()
123
+
124
+ with redirect_stdout(stdout_buffer), redirect_stderr(stderr_buffer):
125
+ # Execute the script
126
+ exec(script, globals())
127
+
128
+ # Get the captured output
129
+ stdout_output = stdout_buffer.getvalue()
130
+ stderr_output = stderr_buffer.getvalue()
131
+
132
+ # Check for success/error markers
133
+ if SCRIPT_SUCCESS_MARKER in stdout_output or SCRIPT_SUCCESS_MARKER in stderr_output:
134
+ result["success"] = True
135
+ elif SCRIPT_ERROR_MARKER in stdout_output or SCRIPT_ERROR_MARKER in stderr_output:
136
+ result["success"] = False
137
+ else:
138
+ # No explicit markers, assume success
139
+ result["success"] = True
140
+
141
+ result["output"] = stdout_output
142
+ if stderr_output:
143
+ result["error"] = stderr_output
144
+
145
+ except Exception as e:
146
+ result["success"] = False
147
+ result["error"] = f"Error executing script: {str(e)}"
148
+ result["output"] = f"{SCRIPT_ERROR_MARKER}: {str(e)}"
149
+
150
+ elif cmd_type == "stop":
151
+ # Stop command received
152
+ result["success"] = True
153
+ result["output"] = f"{SCRIPT_SUCCESS_MARKER}: Session stopping"
154
+
155
+ # Update session state to mark as stopping
156
+ update_session_state("stopping")
157
+
158
+ # Return the result and signal to stop the loop
159
+ return result, True
160
+
161
+ else:
162
+ result["success"] = False
163
+ result["error"] = f"Unknown command type: {cmd_type}"
164
+ result["output"] = f"{SCRIPT_ERROR_MARKER}: Unknown command type: {cmd_type}"
165
+
166
+ # Update session state after command execution
167
+ update_session_state()
168
+
169
+ return result, False
170
+
171
+ except Exception as e:
172
+ return {
173
+ "id": command_data.get("id", "unknown"),
174
+ "success": False,
175
+ "output": f"{SCRIPT_ERROR_MARKER}: Command execution failed",
176
+ "error": str(e)
177
+ }, False
178
+
179
+ def check_command():
180
+ """Check if there is a new command to execute"""
181
+ try:
182
+ # Read the command file
183
+ with open(COMMAND_FILE, 'r') as f:
184
+ command_data = json.load(f)
185
+
186
+ # Check if there's a new command
187
+ if command_data.get("status") == "pending" and command_data.get("command"):
188
+ # Extract the command
189
+ command = command_data.get("command")
190
+
191
+ # Mark as processing
192
+ command_data["status"] = "processing"
193
+ with open(COMMAND_FILE, 'w') as f:
194
+ json.dump(command_data, f)
195
+
196
+ # Execute the command
197
+ result, stop_requested = execute_command(command)
198
+
199
+ # Write the result
200
+ result_data = {
201
+ "status": "complete",
202
+ "result": result,
203
+ "timestamp": time.time()
204
+ }
205
+
206
+ with open(RESULT_FILE, 'w') as f:
207
+ json.dump(result_data, f)
208
+
209
+ # Reset command file
210
+ command_data = {
211
+ "status": "ready",
212
+ "command": None,
213
+ "timestamp": time.time()
214
+ }
215
+
216
+ with open(COMMAND_FILE, 'w') as f:
217
+ json.dump(command_data, f)
218
+
219
+ return stop_requested
220
+
221
+ return False
222
+ except Exception as e:
223
+ print(f"Error checking command: {e}")
224
+ return False
225
+
226
+ def command_loop():
227
+ """Main loop to check for and execute commands"""
228
+ stop_requested = False
229
+
230
+ print(f"Starting command loop...")
231
+ print(f"Command file: {COMMAND_FILE}")
232
+ print(f"Result file: {RESULT_FILE}")
233
+
234
+ while not stop_requested:
235
+ try:
236
+ # Update session lock file to indicate we're alive
237
+ with open(SESSION_LOCK_FILE, 'w') as f:
238
+ f.write(str(time.time()))
239
+
240
+ # Check for new commands
241
+ stop_requested = check_command()
242
+
243
+ # Sleep for a bit
244
+ time.sleep(POLL_INTERVAL)
245
+
246
+ except Exception as e:
247
+ print(f"Error in command loop: {e}")
248
+ # Sleep for a bit
249
+ time.sleep(POLL_INTERVAL)
250
+
251
+ print("Command loop stopped.")
252
+
253
+ # Main script
254
+ def main():
255
+ try:
256
+ # Setup session directory and files
257
+ if not setup_session_dir():
258
+ sys.exit(1)
259
+
260
+ # Update initial session state
261
+ update_session_state()
262
+
263
+ # Start command loop
264
+ print(f"{SCRIPT_SUCCESS_MARKER}: CODESYS session started")
265
+ command_loop()
266
+
267
+ # Update final state
268
+ update_session_state("stopped")
269
+
270
+ print(f"{SCRIPT_SUCCESS_MARKER}: CODESYS session stopped cleanly")
271
+ sys.exit(0)
272
+ except Exception as e:
273
+ print(f"Error in session: {e}")
274
+ print(f"{SCRIPT_ERROR_MARKER}: Session failed - {str(e)}")
275
+ sys.exit(1)
276
+
277
+ if __name__ == "__main__":
278
+ main()
@@ -0,0 +1,117 @@
1
+ import sys, os, json, time
2
+ import tempfile
3
+
4
+ # Define markers for script results
5
+ SCRIPT_SUCCESS_MARKER = "SCRIPT_SUCCESS"
6
+ SCRIPT_ERROR_MARKER = "SCRIPT_ERROR"
7
+
8
+ # Session state and command files
9
+ SESSION_DIR = os.path.join(tempfile.gettempdir(), "codesys_session")
10
+ SESSION_STATE_FILE = os.path.join(SESSION_DIR, "session_state.json")
11
+ COMMAND_FILE = os.path.join(SESSION_DIR, "command.json")
12
+ RESULT_FILE = os.path.join(SESSION_DIR, "result.json")
13
+ SESSION_LOCK_FILE = os.path.join(SESSION_DIR, "session.lock")
14
+
15
+ def stop_session():
16
+ """Stop a running CODESYS session"""
17
+ try:
18
+ # Check if session directory exists
19
+ if not os.path.exists(SESSION_DIR):
20
+ print(f"{SCRIPT_ERROR_MARKER}: No session directory found at {SESSION_DIR}")
21
+ return False
22
+
23
+ # Check if session state file exists
24
+ if not os.path.exists(SESSION_STATE_FILE):
25
+ print(f"{SCRIPT_ERROR_MARKER}: No session state file found at {SESSION_STATE_FILE}")
26
+ return False
27
+
28
+ # Read session state
29
+ with open(SESSION_STATE_FILE, 'r') as f:
30
+ try:
31
+ session_state = json.load(f)
32
+ except json.JSONDecodeError:
33
+ print(f"{SCRIPT_ERROR_MARKER}: Invalid session state file")
34
+ return False
35
+
36
+ # Check if session is active
37
+ if not session_state.get('active', False):
38
+ print(f"{SCRIPT_SUCCESS_MARKER}: Session is already stopped")
39
+ return True
40
+
41
+ # Check if command file exists
42
+ if not os.path.exists(COMMAND_FILE):
43
+ print(f"{SCRIPT_ERROR_MARKER}: Command file not found at {COMMAND_FILE}")
44
+ return False
45
+
46
+ # Create stop command
47
+ stop_command = {
48
+ "id": f"stop_{int(time.time())}",
49
+ "type": "stop",
50
+ "timestamp": time.time()
51
+ }
52
+
53
+ # Write command to file
54
+ command_data = {
55
+ "status": "pending",
56
+ "command": stop_command,
57
+ "timestamp": time.time()
58
+ }
59
+
60
+ with open(COMMAND_FILE, 'w') as f:
61
+ json.dump(command_data, f)
62
+
63
+ print(f"Stop command sent to session")
64
+
65
+ # Wait for result (with timeout)
66
+ timeout = 10 # seconds
67
+ start_time = time.time()
68
+ result_received = False
69
+
70
+ while not result_received and (time.time() - start_time) < timeout:
71
+ # Check if result file exists
72
+ if not os.path.exists(RESULT_FILE):
73
+ time.sleep(0.5)
74
+ continue
75
+
76
+ try:
77
+ with open(RESULT_FILE, 'r') as f:
78
+ result_data = json.load(f)
79
+
80
+ if result_data.get("status") == "complete" and result_data.get("result"):
81
+ result = result_data.get("result")
82
+ if result.get('id') == stop_command['id']:
83
+ result_received = True
84
+ print(f"Session stop result: {result.get('output', 'No output')}")
85
+ break
86
+ except Exception as e:
87
+ print(f"Error reading result: {e}")
88
+ time.sleep(0.5)
89
+
90
+ # Update session state to indicate it's stopped
91
+ with open(SESSION_STATE_FILE, 'r') as f:
92
+ session_state = json.load(f)
93
+
94
+ session_state['active'] = False
95
+ session_state['status'] = "stopped"
96
+ session_state['end_time'] = time.time()
97
+
98
+ with open(SESSION_STATE_FILE, 'w') as f:
99
+ json.dump(session_state, f)
100
+
101
+ if result_received:
102
+ print(f"{SCRIPT_SUCCESS_MARKER}: Session stopped successfully")
103
+ else:
104
+ print(f"{SCRIPT_ERROR_MARKER}: Timeout waiting for session to stop")
105
+
106
+ return result_received
107
+
108
+ except Exception as e:
109
+ print(f"{SCRIPT_ERROR_MARKER}: Error stopping session - {str(e)}")
110
+ return False
111
+
112
+ # Main script
113
+ if __name__ == "__main__":
114
+ if stop_session():
115
+ sys.exit(0)
116
+ else:
117
+ sys.exit(1)