@codesys/mcp-toolkit 1.5.8 → 2.0.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.
- package/CHANGELOG.md +153 -189
- package/LICENSE +21 -21
- package/README.md +269 -152
- package/dist/actions.d.ts +28 -0
- package/dist/actions.js +98 -0
- package/dist/actions.js.map +1 -0
- package/dist/bridge/bridge.d.ts +105 -0
- package/dist/bridge/bridge.js +400 -0
- package/dist/bridge/bridge.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +196 -0
- package/dist/cli.js.map +1 -0
- package/dist/codesys/cache.d.ts +48 -0
- package/dist/codesys/cache.js +82 -0
- package/dist/codesys/cache.js.map +1 -0
- package/dist/codesys/changes.d.ts +57 -0
- package/dist/codesys/changes.js +133 -0
- package/dist/codesys/changes.js.map +1 -0
- package/dist/codesys/decl.d.ts +45 -0
- package/dist/codesys/decl.js +251 -0
- package/dist/codesys/decl.js.map +1 -0
- package/dist/codesys/devices.d.ts +34 -0
- package/dist/codesys/devices.js +82 -0
- package/dist/codesys/devices.js.map +1 -0
- package/dist/codesys/diff.d.ts +13 -0
- package/dist/codesys/diff.js +61 -0
- package/dist/codesys/diff.js.map +1 -0
- package/dist/codesys/install.d.ts +40 -0
- package/dist/codesys/install.js +144 -0
- package/dist/codesys/install.js.map +1 -0
- package/dist/codesys/library-browse.d.ts +19 -0
- package/dist/codesys/library-browse.js +57 -0
- package/dist/codesys/library-browse.js.map +1 -0
- package/dist/codesys/outline.d.ts +32 -0
- package/dist/codesys/outline.js +219 -0
- package/dist/codesys/outline.js.map +1 -0
- package/dist/codesys/simulation.d.ts +34 -0
- package/dist/codesys/simulation.js +57 -0
- package/dist/codesys/simulation.js.map +1 -0
- package/dist/codesys/st.d.ts +36 -0
- package/dist/codesys/st.js +69 -0
- package/dist/codesys/st.js.map +1 -0
- package/dist/daemon.d.ts +46 -0
- package/dist/daemon.js +173 -0
- package/dist/daemon.js.map +1 -0
- package/dist/driver.d.ts +25 -0
- package/dist/driver.js +33 -0
- package/dist/driver.js.map +1 -0
- package/dist/panel/sim-panel.html +357 -0
- package/dist/panel/viewer.html +126 -0
- package/dist/panel-types.d.ts +20 -0
- package/dist/panel-types.js +3 -0
- package/dist/panel-types.js.map +1 -0
- package/dist/panel.d.ts +27 -0
- package/dist/panel.js +208 -0
- package/dist/panel.js.map +1 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +33 -974
- package/dist/server.js.map +1 -0
- package/dist/tools.d.ts +103 -0
- package/dist/tools.js +793 -0
- package/dist/tools.js.map +1 -0
- package/dist/viewer.d.ts +14 -0
- package/dist/viewer.js +125 -0
- package/dist/viewer.js.map +1 -0
- package/package.json +66 -51
- package/watcher/mcp_compat.py +147 -0
- package/watcher/mcp_ops.py +1178 -0
- package/watcher/mcp_sim.py +246 -0
- package/watcher/watcher.py +255 -0
- package/.claude/settings.local.json +0 -29
- package/CLAUDE.md +0 -26
- package/CONTRIBUTING.md +0 -72
- package/README.md.enhanced +0 -155
- package/codesys-mcp-toolkit-1.2.0.tgz +0 -0
- package/codesys-mcp-toolkit-1.2.1.tgz +0 -0
- package/codesys-mcp-toolkit-1.5.7.tgz +0 -0
- package/codesys-mcp-toolkit-1.5.8.tgz +0 -0
- package/dist/bin.js +0 -75
- package/dist/check_session_paths.js +0 -149
- package/dist/codesys_interop.js +0 -435
- package/dist/direct_session_command.js +0 -293
- package/dist/scriptLoader.js +0 -165
- package/dist/scripts/check_status.py +0 -21
- package/dist/scripts/compile_project.py +0 -65
- package/dist/scripts/create_method.py +0 -80
- package/dist/scripts/create_pou.py +0 -102
- package/dist/scripts/create_project.py +0 -116
- package/dist/scripts/create_property.py +0 -78
- package/dist/scripts/ensure_project_open.py +0 -154
- package/dist/scripts/find_object_by_path.py +0 -111
- package/dist/scripts/get_pou_code.py +0 -91
- package/dist/scripts/get_project_structure.py +0 -72
- package/dist/scripts/open_project.py +0 -25
- package/dist/scripts/save_project.py +0 -29
- package/dist/scripts/session_status.py +0 -281
- package/dist/scripts/set_pou_code.py +0 -109
- package/dist/scripts/start_session.py +0 -809
- package/dist/scripts/stop_session.py +0 -324
- package/dist/sessionManager.js +0 -798
- package/docs/configuration.md +0 -141
- package/docs/index.md +0 -26
- package/docs/installation.md +0 -84
- package/docs/session_management.md +0 -110
- package/examples/README.md +0 -78
- package/examples/sample_config.json +0 -32
- package/replacements_session_status.txt +0 -16
- package/scriptDoc.txt +0 -8679
- package/scripts/build.js +0 -25
- package/scripts/fix_fstrings.py +0 -76
package/dist/codesys_interop.js
DELETED
|
@@ -1,435 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* CODESYS Interop Module
|
|
4
|
-
* Handles direct interaction with the CODESYS executable via command-line scripts.
|
|
5
|
-
*
|
|
6
|
-
* This module manages:
|
|
7
|
-
* - Creating temporary Python script files
|
|
8
|
-
* - Executing them via CODESYS's scripting engine
|
|
9
|
-
* - Capturing and processing results
|
|
10
|
-
* - Supporting both one-off script execution and persistent sessions
|
|
11
|
-
*
|
|
12
|
-
* IMPORTANT: Path handling for Windows is critical - paths with spaces require
|
|
13
|
-
* special handling to avoid the 'C:\Program' not recognized error.
|
|
14
|
-
*/
|
|
15
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
-
if (k2 === undefined) k2 = k;
|
|
17
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
-
}
|
|
21
|
-
Object.defineProperty(o, k2, desc);
|
|
22
|
-
}) : (function(o, m, k, k2) {
|
|
23
|
-
if (k2 === undefined) k2 = k;
|
|
24
|
-
o[k2] = m[k];
|
|
25
|
-
}));
|
|
26
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
-
}) : function(o, v) {
|
|
29
|
-
o["default"] = v;
|
|
30
|
-
});
|
|
31
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
-
var ownKeys = function(o) {
|
|
33
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
-
var ar = [];
|
|
35
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
-
return ar;
|
|
37
|
-
};
|
|
38
|
-
return ownKeys(o);
|
|
39
|
-
};
|
|
40
|
-
return function (mod) {
|
|
41
|
-
if (mod && mod.__esModule) return mod;
|
|
42
|
-
var result = {};
|
|
43
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
-
__setModuleDefault(result, mod);
|
|
45
|
-
return result;
|
|
46
|
-
};
|
|
47
|
-
})();
|
|
48
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
49
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
50
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
51
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
52
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
53
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
54
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
55
|
-
});
|
|
56
|
-
};
|
|
57
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
|
-
exports.executeCodesysScript = executeCodesysScript;
|
|
59
|
-
exports.checkSessionStatus = checkSessionStatus;
|
|
60
|
-
exports.startSession = startSession;
|
|
61
|
-
exports.stopSession = stopSession;
|
|
62
|
-
const child_process_1 = require("child_process");
|
|
63
|
-
const promises_1 = require("fs/promises");
|
|
64
|
-
const path = __importStar(require("path"));
|
|
65
|
-
const os = __importStar(require("os"));
|
|
66
|
-
const fs = __importStar(require("fs")); // Import fs for existsSync check
|
|
67
|
-
// Session manager import
|
|
68
|
-
const sessionManager_1 = require("./sessionManager");
|
|
69
|
-
// Define expected success/error markers from the Python scripts
|
|
70
|
-
const SCRIPT_SUCCESS_MARKER = 'SCRIPT_SUCCESS';
|
|
71
|
-
const SCRIPT_ERROR_MARKER = 'SCRIPT_ERROR';
|
|
72
|
-
/**
|
|
73
|
-
* Executes a CODESYS Python script using the command line interface.
|
|
74
|
-
*
|
|
75
|
-
* @param scriptContent The Python script code to execute.
|
|
76
|
-
* @param codesysExePath The full path to the CODESYS.exe executable (can contain spaces).
|
|
77
|
-
* @param codesysProfileName The name of the CODESYS profile to use for scripting.
|
|
78
|
-
* @param sessionManager Optional session manager for persistent session execution
|
|
79
|
-
* @returns A promise resolving to an object containing the success status and the script's output.
|
|
80
|
-
*
|
|
81
|
-
* NOTE: Using shell: true with careful command string quoting to handle
|
|
82
|
-
* CODESYS argument parsing quirks when launched non-interactively.
|
|
83
|
-
*/
|
|
84
|
-
function executeCodesysScript(scriptContent, codesysExePath, codesysProfileName, sessionManager) {
|
|
85
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
86
|
-
// --- Pre-checks ---
|
|
87
|
-
if (!codesysExePath)
|
|
88
|
-
throw new Error('CODESYS executable path was not provided.');
|
|
89
|
-
if (!codesysProfileName)
|
|
90
|
-
throw new Error('CODESYS profile name was not provided.');
|
|
91
|
-
if (!fs.existsSync(codesysExePath))
|
|
92
|
-
throw new Error(`CODESYS executable not found at provided path: ${codesysExePath}`);
|
|
93
|
-
// --- End Pre-checks ---
|
|
94
|
-
// Session management feature is now fully controlled by the presence of sessionManager
|
|
95
|
-
// No need for a separate flag as server.ts will set sessionManager to undefined if disabled
|
|
96
|
-
// --- Check if we should use the persistent session ---
|
|
97
|
-
if (sessionManager) {
|
|
98
|
-
const sessionState = sessionManager.getState();
|
|
99
|
-
// Check if the session is active
|
|
100
|
-
if (sessionState === sessionManager_1.SessionState.RUNNING || sessionState === sessionManager_1.SessionState.BUSY) {
|
|
101
|
-
process.stderr.write(`INTEROP: Using persistent session for script execution\n`);
|
|
102
|
-
try {
|
|
103
|
-
// Execute the script in the session
|
|
104
|
-
const result = yield sessionManager.executeScript(scriptContent);
|
|
105
|
-
return {
|
|
106
|
-
success: result.success,
|
|
107
|
-
output: result.output
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
catch (error) {
|
|
111
|
-
process.stderr.write(`INTEROP: Error executing script in persistent session: ${error.message}\n`);
|
|
112
|
-
process.stderr.write(`INTEROP: Falling back to one-time execution mode\n`);
|
|
113
|
-
// Continue with the non-persistent approach as fallback
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
else if (sessionState === sessionManager_1.SessionState.STOPPED || sessionState === sessionManager_1.SessionState.ERROR) {
|
|
117
|
-
// Try to start the session if it's stopped or in error state
|
|
118
|
-
process.stderr.write(`INTEROP: Session is in ${sessionState} state. Attempting to start it.\n`);
|
|
119
|
-
try {
|
|
120
|
-
yield sessionManager.startSession();
|
|
121
|
-
const newState = sessionManager.getState();
|
|
122
|
-
if (newState === sessionManager_1.SessionState.RUNNING) {
|
|
123
|
-
process.stderr.write(`INTEROP: Session started successfully. Using it for script execution.\n`);
|
|
124
|
-
// Now that the session is running, execute the script
|
|
125
|
-
const result = yield sessionManager.executeScript(scriptContent);
|
|
126
|
-
return {
|
|
127
|
-
success: result.success,
|
|
128
|
-
output: result.output
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
process.stderr.write(`INTEROP: Failed to start session (state: ${newState}). Using one-time execution.\n`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
process.stderr.write(`INTEROP: Error starting session: ${error.message}\n`);
|
|
137
|
-
process.stderr.write(`INTEROP: Using one-time execution as fallback.\n`);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
process.stderr.write(`INTEROP: Session not in RUNNING/BUSY state (current: ${sessionState}). Using one-time execution.\n`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
process.stderr.write(`INTEROP: No session manager provided. Using one-time execution mode.\n`);
|
|
146
|
-
}
|
|
147
|
-
// --- Standard one-time script execution ---
|
|
148
|
-
const tempDir = os.tmpdir();
|
|
149
|
-
const tempFileName = `codesys_script_${Date.now()}_${Math.random().toString(36).substring(2, 9)}.py`;
|
|
150
|
-
const tempFilePath = path.join(tempDir, tempFileName); // Path module uses OS-specific separators ('\' on Windows)
|
|
151
|
-
let output = '';
|
|
152
|
-
let stderrOutput = '';
|
|
153
|
-
let success = false;
|
|
154
|
-
let exitCode = null;
|
|
155
|
-
const codesysDir = path.dirname(codesysExePath); // Directory containing CODESYS.exe
|
|
156
|
-
try {
|
|
157
|
-
const normalizedScriptContent = scriptContent.replace(/\r\n/g, '\n'); // Normalize line endings
|
|
158
|
-
// <<< --- ADDED SCRIPT CONTENT LOGGING --- >>>
|
|
159
|
-
process.stderr.write(`INTEROP: Script content to be written (first 500 chars):\n`);
|
|
160
|
-
process.stderr.write(`------ START SCRIPT (TEMP FILE) -----\n`);
|
|
161
|
-
process.stderr.write(`${normalizedScriptContent.substring(0, 500)}\n`); // Log the first 500 chars
|
|
162
|
-
process.stderr.write(`------ END SCRIPT SNIPPET (TEMP FILE) -----\n`);
|
|
163
|
-
// <<< --- END SCRIPT CONTENT LOGGING --- >>>
|
|
164
|
-
yield (0, promises_1.writeFile)(tempFilePath, normalizedScriptContent, 'latin1'); // Write the normalized content
|
|
165
|
-
process.stderr.write(`INTEROP: Temp script written: ${tempFilePath}\n`);
|
|
166
|
-
// --- Construct command string for shell: true ---
|
|
167
|
-
// Quote the executable path itself
|
|
168
|
-
const quotedExePath = `"${codesysExePath}"`;
|
|
169
|
-
// Format arguments exactly as CODESYS seems to want: --option="Value With Spaces"
|
|
170
|
-
// The outer quotes are for the shell parser.
|
|
171
|
-
const profileArg = `--profile="${codesysProfileName}"`;
|
|
172
|
-
const scriptArg = `--runscript="${tempFilePath}"`; // tempFilePath from path.join has correct backslashes for Win
|
|
173
|
-
// Combine into a single string for the shell
|
|
174
|
-
const fullCommandString = `${quotedExePath} ${profileArg} --noUI ${scriptArg}`;
|
|
175
|
-
// Example result: "\"C:\\Program Files\\...\\CODESYS.exe\" --profile=\"CODESYS V3.5 SP21\" --noUI --runscript=\"C:\\Users\\...\\script.py\""
|
|
176
|
-
// --- End command string construction ---
|
|
177
|
-
process.stderr.write(`INTEROP: Spawning command (shell:true): ${fullCommandString}\n`);
|
|
178
|
-
process.stderr.write(`INTEROP ENV: CWD before spawn: ${process.cwd()}\n`);
|
|
179
|
-
process.stderr.write(`INTEROP ENV: Forcing CWD for spawn: ${codesysDir}\n`); // Re-enabled CWD change
|
|
180
|
-
// --- Create modified environment (Re-enabled) ---
|
|
181
|
-
const spawnEnv = Object.assign({}, process.env);
|
|
182
|
-
const pathSeparator = os.platform() === 'win32' ? ';' : ':';
|
|
183
|
-
const originalPath = spawnEnv.PATH || '';
|
|
184
|
-
spawnEnv.PATH = `${codesysDir}${pathSeparator}${originalPath}`; // Prepend CODESYS dir to PATH
|
|
185
|
-
process.stderr.write(`INTEROP ENV: MODIFIED PATH for spawn (prepended): ${spawnEnv.PATH.substring(0, 100)}...\n`); // Re-enabled ENV change
|
|
186
|
-
// --- End modified environment ---
|
|
187
|
-
const spawnResult = yield new Promise((resolve) => {
|
|
188
|
-
let stdoutData = '';
|
|
189
|
-
let stderrData = '';
|
|
190
|
-
const controller = new AbortController();
|
|
191
|
-
const timeoutSignal = controller.signal;
|
|
192
|
-
const timeoutDuration = 60000; // 60 seconds
|
|
193
|
-
// Pass the single command string, empty args array, and shell: true
|
|
194
|
-
const childProcess = (0, child_process_1.spawn)(fullCommandString, [], {
|
|
195
|
-
windowsHide: true,
|
|
196
|
-
signal: timeoutSignal,
|
|
197
|
-
cwd: codesysDir, // Re-enabled CWD change
|
|
198
|
-
env: spawnEnv, // Re-enabled ENV change
|
|
199
|
-
shell: true // USE shell: true
|
|
200
|
-
});
|
|
201
|
-
const timeoutId = setTimeout(() => {
|
|
202
|
-
process.stderr.write('INTEROP: Process timeout reached.\n');
|
|
203
|
-
controller.abort();
|
|
204
|
-
}, timeoutDuration);
|
|
205
|
-
// --- Event Listeners (stdout, stderr, error, close, abort) ---
|
|
206
|
-
childProcess.stdout.on('data', (data) => {
|
|
207
|
-
const chunk = data.toString();
|
|
208
|
-
stdoutData += chunk;
|
|
209
|
-
process.stderr.write(`INTEROP stdout chunk: ${chunk.length > 50 ? chunk.substring(0, 50) + '...' : chunk}\n`);
|
|
210
|
-
});
|
|
211
|
-
childProcess.stderr.on('data', (data) => {
|
|
212
|
-
const chunk = data.toString();
|
|
213
|
-
stderrData += chunk;
|
|
214
|
-
// Check for specific error patterns
|
|
215
|
-
if (chunk.includes('--profile="profile name"')) {
|
|
216
|
-
process.stderr.write(`>>>> INTEROP STDERR DETECTED Profile Error Message: ${chunk}\n`);
|
|
217
|
-
}
|
|
218
|
-
else if (chunk.includes('is not recognized')) {
|
|
219
|
-
process.stderr.write(`>>>> INTEROP STDERR DETECTED 'not recognized' (shell issue?): ${chunk}\n`);
|
|
220
|
-
}
|
|
221
|
-
else if (chunk.includes('SyntaxErrorException')) {
|
|
222
|
-
process.stderr.write(`>>>> INTEROP STDERR DETECTED Syntax Error: ${chunk}\n`);
|
|
223
|
-
} // More specific check
|
|
224
|
-
else {
|
|
225
|
-
process.stderr.write(`INTEROP stderr chunk: ${chunk}\n`);
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
childProcess.on('error', (spawnError) => {
|
|
229
|
-
var _a;
|
|
230
|
-
clearTimeout(timeoutId);
|
|
231
|
-
process.stderr.write(`INTEROP SPAWN ERROR (shell:true): ${spawnError.message}\n`);
|
|
232
|
-
resolve({ code: (_a = spawnError.errno) !== null && _a !== void 0 ? _a : 1, stdout: stdoutData, stderr: stderrData, error: spawnError });
|
|
233
|
-
});
|
|
234
|
-
childProcess.on('close', (code) => {
|
|
235
|
-
clearTimeout(timeoutId);
|
|
236
|
-
process.stderr.write(`INTEROP: Process closed code: ${code}\n`);
|
|
237
|
-
resolve({ code: code, stdout: stdoutData, stderr: stderrData });
|
|
238
|
-
});
|
|
239
|
-
timeoutSignal.addEventListener('abort', () => {
|
|
240
|
-
process.stderr.write('INTEROP: Abort signal received, attempting to kill process.\n');
|
|
241
|
-
if (!childProcess.killed) {
|
|
242
|
-
if (!childProcess.kill('SIGTERM')) { // Try graceful termination first
|
|
243
|
-
process.stderr.write('INTEROP: SIGTERM failed, attempting SIGKILL in 2s.\n');
|
|
244
|
-
setTimeout(() => { if (!childProcess.killed)
|
|
245
|
-
childProcess.kill('SIGKILL'); }, 2000);
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
process.stderr.write('INTEROP: SIGTERM sent.\n');
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
resolve({ code: null, stdout: stdoutData, stderr: stderrData + "\nTIMEOUT: Process aborted due to timeout." });
|
|
252
|
-
}, { once: true });
|
|
253
|
-
// --- End Event Listeners ---
|
|
254
|
-
});
|
|
255
|
-
output = spawnResult.stdout;
|
|
256
|
-
stderrOutput = spawnResult.stderr;
|
|
257
|
-
exitCode = spawnResult.code;
|
|
258
|
-
// --- Success Determination Logic ---
|
|
259
|
-
success = false; // Assume failure unless proven otherwise
|
|
260
|
-
if (spawnResult.error) {
|
|
261
|
-
process.stderr.write(`INTEROP: Failure determined by spawn error: ${spawnResult.error.message}\n`);
|
|
262
|
-
if (!stderrOutput.includes(SCRIPT_ERROR_MARKER))
|
|
263
|
-
stderrOutput = `SCRIPT_ERROR: Spawn failed: ${spawnResult.error.message}\n${stderrOutput}`;
|
|
264
|
-
}
|
|
265
|
-
else if (stderrOutput.includes('is not recognized as an internal or external command')) {
|
|
266
|
-
process.stderr.write("INTEROP: Failure determined by 'not recognized' error in stderr (shell:true quoting issue likely).\n");
|
|
267
|
-
if (!stderrOutput.includes(SCRIPT_ERROR_MARKER))
|
|
268
|
-
stderrOutput = `SCRIPT_ERROR: Shell execution failed: ${stderrOutput}`;
|
|
269
|
-
}
|
|
270
|
-
else if (stderrOutput.includes('--profile="profile name"')) {
|
|
271
|
-
process.stderr.write("INTEROP: Failure determined by CODESYS profile error message in stderr.\n");
|
|
272
|
-
if (!stderrOutput.includes(SCRIPT_ERROR_MARKER))
|
|
273
|
-
stderrOutput = `SCRIPT_ERROR: ${stderrOutput}`;
|
|
274
|
-
}
|
|
275
|
-
else if (stderrOutput.includes('SyntaxErrorException')) { // Check for syntax error specifically
|
|
276
|
-
process.stderr.write("INTEROP: Failure determined by CODESYS Script Syntax Error.\n");
|
|
277
|
-
if (!stderrOutput.includes(SCRIPT_ERROR_MARKER))
|
|
278
|
-
stderrOutput = `SCRIPT_ERROR: ${stderrOutput}`; // Include the syntax error details
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
// No spawn error, no shell error, no profile error, no syntax error -> check markers/exit code
|
|
282
|
-
process.stderr.write(`INTEROP: Checking markers/exit code (Code: ${exitCode})...\n`);
|
|
283
|
-
if (output.includes(SCRIPT_SUCCESS_MARKER) || stderrOutput.includes(SCRIPT_SUCCESS_MARKER)) {
|
|
284
|
-
success = true;
|
|
285
|
-
process.stderr.write("INTEROP: Success determined by SUCCESS marker.\n");
|
|
286
|
-
}
|
|
287
|
-
else if (output.includes(SCRIPT_ERROR_MARKER) || stderrOutput.includes(SCRIPT_ERROR_MARKER)) {
|
|
288
|
-
success = false; // Explicit error marker found
|
|
289
|
-
process.stderr.write("INTEROP: Failure determined by ERROR marker.\n");
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
// No markers found, rely solely on exit code
|
|
293
|
-
success = exitCode === 0;
|
|
294
|
-
if (success) {
|
|
295
|
-
process.stderr.write(`INTEROP: Success determined by exit code 0 (no markers found).\n`);
|
|
296
|
-
}
|
|
297
|
-
else {
|
|
298
|
-
process.stderr.write(`INTEROP: Failure determined by non-zero exit code ${exitCode} (no markers found).\n`);
|
|
299
|
-
// Add generic failure message if stderr doesn't already contain SCRIPT_ERROR
|
|
300
|
-
if (!stderrOutput.includes(SCRIPT_ERROR_MARKER))
|
|
301
|
-
stderrOutput = `SCRIPT_ERROR: Process failed with exit code ${exitCode} (no markers found).\n${stderrOutput}`;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
// --- End Success Determination ---
|
|
306
|
-
}
|
|
307
|
-
catch (error) {
|
|
308
|
-
process.stderr.write(`INTEROP: Error during setup: ${error.message}\n${error.stack}\n`);
|
|
309
|
-
stderrOutput = `SCRIPT_ERROR: Failed during script execution setup: ${error.message}`;
|
|
310
|
-
success = false;
|
|
311
|
-
}
|
|
312
|
-
finally {
|
|
313
|
-
// Cleanup: Attempt to delete the temporary script file
|
|
314
|
-
try {
|
|
315
|
-
yield (0, promises_1.unlink)(tempFilePath);
|
|
316
|
-
process.stderr.write(`INTEROP: Temp script deleted: ${tempFilePath}\n`);
|
|
317
|
-
}
|
|
318
|
-
catch (cleanupError) {
|
|
319
|
-
process.stderr.write(`INTEROP: Failed to delete temp file ${tempFilePath}: ${cleanupError.message}\n`);
|
|
320
|
-
if (success)
|
|
321
|
-
stderrOutput += `\nWARNING: Failed to delete temporary script file ${tempFilePath}. ${cleanupError.message}`;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
// Final output processing
|
|
325
|
-
// Combine stderr and stdout only on failure to preserve clean success output
|
|
326
|
-
const finalOutput = success ? output : `${stderrOutput}\n${output}`.trim();
|
|
327
|
-
process.stderr.write(`INTEROP: Final Success: ${success}\n`);
|
|
328
|
-
process.stderr.write(`INTEROP: Final Output Length: ${finalOutput.length}\n---\n`);
|
|
329
|
-
return { success, output: finalOutput };
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Checks if a session is currently active
|
|
334
|
-
*
|
|
335
|
-
* @param codesysExePath The full path to the CODESYS.exe executable
|
|
336
|
-
* @param codesysProfileName The name of the CODESYS profile to use for scripting
|
|
337
|
-
* @param sessionManager The session manager instance
|
|
338
|
-
* @returns Promise resolving to an object with session status
|
|
339
|
-
*/
|
|
340
|
-
function checkSessionStatus(codesysExePath, codesysProfileName, sessionManager) {
|
|
341
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
342
|
-
if (!sessionManager) {
|
|
343
|
-
return { active: false, info: null };
|
|
344
|
-
}
|
|
345
|
-
try {
|
|
346
|
-
// Check if session is active
|
|
347
|
-
const isActive = yield sessionManager.isSessionActive();
|
|
348
|
-
if (!isActive) {
|
|
349
|
-
return { active: false, info: null };
|
|
350
|
-
}
|
|
351
|
-
// Get session information
|
|
352
|
-
const sessionInfo = yield sessionManager.getSessionInfo();
|
|
353
|
-
return {
|
|
354
|
-
active: true,
|
|
355
|
-
info: sessionInfo
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
catch (error) {
|
|
359
|
-
process.stderr.write(`INTEROP: Error checking session status: ${error.message}\n`);
|
|
360
|
-
return { active: false, info: null };
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Starts a new persistent CODESYS session
|
|
366
|
-
*
|
|
367
|
-
* @param codesysExePath The full path to the CODESYS.exe executable
|
|
368
|
-
* @param codesysProfileName The name of the CODESYS profile to use for scripting
|
|
369
|
-
* @param sessionManager The session manager instance
|
|
370
|
-
* @returns Promise resolving when the session has started
|
|
371
|
-
*/
|
|
372
|
-
function startSession(codesysExePath, codesysProfileName, sessionManager) {
|
|
373
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
374
|
-
if (!sessionManager) {
|
|
375
|
-
return { success: false, message: "No session manager provided" };
|
|
376
|
-
}
|
|
377
|
-
try {
|
|
378
|
-
// Check if session is already active
|
|
379
|
-
const isActive = yield sessionManager.isSessionActive();
|
|
380
|
-
if (isActive) {
|
|
381
|
-
return { success: true, message: "Session is already active" };
|
|
382
|
-
}
|
|
383
|
-
// Start a new session
|
|
384
|
-
yield sessionManager.startSession();
|
|
385
|
-
// Check if session started successfully
|
|
386
|
-
const statusAfterStart = yield sessionManager.isSessionActive();
|
|
387
|
-
if (statusAfterStart) {
|
|
388
|
-
return { success: true, message: "Session started successfully" };
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
return { success: false, message: "Failed to start session" };
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
catch (error) {
|
|
395
|
-
process.stderr.write(`INTEROP: Error starting session: ${error.message}\n`);
|
|
396
|
-
return { success: false, message: `Error starting session: ${error.message}` };
|
|
397
|
-
}
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
|
-
* Stops an active CODESYS session
|
|
402
|
-
*
|
|
403
|
-
* @param codesysExePath The full path to the CODESYS.exe executable
|
|
404
|
-
* @param codesysProfileName The name of the CODESYS profile to use for scripting
|
|
405
|
-
* @param sessionManager The session manager instance
|
|
406
|
-
* @returns Promise resolving when the session has stopped
|
|
407
|
-
*/
|
|
408
|
-
function stopSession(codesysExePath, codesysProfileName, sessionManager) {
|
|
409
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
410
|
-
if (!sessionManager) {
|
|
411
|
-
return { success: false, message: "No session manager provided" };
|
|
412
|
-
}
|
|
413
|
-
try {
|
|
414
|
-
// Check if session is active
|
|
415
|
-
const isActive = yield sessionManager.isSessionActive();
|
|
416
|
-
if (!isActive) {
|
|
417
|
-
return { success: true, message: "No active session to stop" };
|
|
418
|
-
}
|
|
419
|
-
// Stop the session
|
|
420
|
-
yield sessionManager.stopSession();
|
|
421
|
-
// Verify session stopped
|
|
422
|
-
const statusAfterStop = yield sessionManager.isSessionActive();
|
|
423
|
-
if (!statusAfterStop) {
|
|
424
|
-
return { success: true, message: "Session stopped successfully" };
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
return { success: false, message: "Failed to stop session" };
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
catch (error) {
|
|
431
|
-
process.stderr.write(`INTEROP: Error stopping session: ${error.message}\n`);
|
|
432
|
-
return { success: false, message: `Error stopping session: ${error.message}` };
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
}
|