@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
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/** Find CODESYS installations, their profiles and project templates. */
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
4
|
+
const isCodesysExe = (exe) => basename(exe).toLowerCase() === "codesys.exe";
|
|
5
|
+
/**
|
|
6
|
+
* The folder holding an install's Profiles and Templates. CODESYS keeps them
|
|
7
|
+
* two levels above the program (`<root>\CODESYS\Common\CODESYS.exe`). Products
|
|
8
|
+
* built on CODESYS can lay this out differently, so look a few levels up.
|
|
9
|
+
*/
|
|
10
|
+
function installBase(exe) {
|
|
11
|
+
let dir = dirname(exe);
|
|
12
|
+
for (let level = 0; level < 3; level++) {
|
|
13
|
+
for (const base of [dir, join(dir, "CODESYS")])
|
|
14
|
+
if (existsSync(join(base, "Profiles")))
|
|
15
|
+
return base;
|
|
16
|
+
dir = dirname(dir);
|
|
17
|
+
}
|
|
18
|
+
return dirname(dirname(exe));
|
|
19
|
+
}
|
|
20
|
+
/** The workspace folder: created when missing, with a clear error when it can't be used. */
|
|
21
|
+
export function ensureWorkspace(dir, log) {
|
|
22
|
+
try {
|
|
23
|
+
if (existsSync(dir)) {
|
|
24
|
+
if (!statSync(dir).isDirectory())
|
|
25
|
+
throw new Error("it is a file, not a folder");
|
|
26
|
+
return dir;
|
|
27
|
+
}
|
|
28
|
+
mkdirSync(dir, { recursive: true });
|
|
29
|
+
log(`created the workspace folder ${dir}`);
|
|
30
|
+
return dir;
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
throw new Error(`Can't use the workspace ${dir}: ${e.message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The add-on folder CODESYS's own shortcuts start this install with
|
|
38
|
+
* (`--additionalfolder`). SP22 installs add-ons such as Scripting there:
|
|
39
|
+
* `<CODESYS>\AdditionalFolders\Default`, or the only folder in AdditionalFolders.
|
|
40
|
+
*/
|
|
41
|
+
export function findAdditionalFolder(base) {
|
|
42
|
+
const root = join(base, "AdditionalFolders");
|
|
43
|
+
if (!existsSync(root))
|
|
44
|
+
return undefined;
|
|
45
|
+
const dirs = readdirSync(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
46
|
+
const pick = dirs.find((d) => d.toLowerCase() === "default") ?? (dirs.length === 1 ? dirs[0] : undefined);
|
|
47
|
+
return pick ? join(root, pick) : undefined;
|
|
48
|
+
}
|
|
49
|
+
function profilesIn(dir) {
|
|
50
|
+
const profilesDir = join(dir, "Profiles");
|
|
51
|
+
if (!existsSync(profilesDir))
|
|
52
|
+
return [];
|
|
53
|
+
return readdirSync(profilesDir)
|
|
54
|
+
.filter((f) => f.toLowerCase().endsWith(".profile.xml"))
|
|
55
|
+
.map((f) => f.slice(0, -".profile.xml".length));
|
|
56
|
+
}
|
|
57
|
+
export function describeInstall(exe, additionalFolder) {
|
|
58
|
+
const base = installBase(exe);
|
|
59
|
+
const extra = additionalFolder ?? findAdditionalFolder(base);
|
|
60
|
+
const profiles = [...new Set([...profilesIn(base), ...(extra ? profilesIn(extra) : [])])].sort(compareProfiles);
|
|
61
|
+
const templatesDir = join(base, "Templates");
|
|
62
|
+
return {
|
|
63
|
+
exe,
|
|
64
|
+
profiles,
|
|
65
|
+
templatesDir: existsSync(templatesDir) ? templatesDir : undefined,
|
|
66
|
+
label: isCodesysExe(exe) ? (dirname(base).split(/[\\/]/).pop() ?? base) : dirname(exe).split(/[\\/]/).slice(-2).join(" "),
|
|
67
|
+
...(extra ? { additionalFolder: extra } : {}),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** Scan Program Files for CODESYS installs (Windows only). */
|
|
71
|
+
export function findInstalls() {
|
|
72
|
+
const roots = [process.env["ProgramFiles"], process.env["ProgramFiles(x86)"], process.env["ProgramW6432"]]
|
|
73
|
+
.filter((r) => !!r)
|
|
74
|
+
.filter((r, i, a) => a.indexOf(r) === i);
|
|
75
|
+
const found = [];
|
|
76
|
+
for (const root of roots) {
|
|
77
|
+
if (!existsSync(root))
|
|
78
|
+
continue;
|
|
79
|
+
for (const dir of readdirSync(root)) {
|
|
80
|
+
if (!/^codesys/i.test(dir))
|
|
81
|
+
continue;
|
|
82
|
+
const exe = join(root, dir, "CODESYS", "Common", "CODESYS.exe");
|
|
83
|
+
if (existsSync(exe))
|
|
84
|
+
found.push(describeInstall(exe));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Newest version first ("CODESYS 3.5.21.0" > "CODESYS 3.5.19.50").
|
|
88
|
+
return found.sort((a, b) => versionKey(b.label).localeCompare(versionKey(a.label)));
|
|
89
|
+
}
|
|
90
|
+
/** "CODESYS V3.5 SP22 Patch 1" -> a key that sorts numerically (SP9 < SP21 < SP22 < SP22 Patch 1). */
|
|
91
|
+
export function profileKey(name) {
|
|
92
|
+
const m = /V(\d+)\.(\d+)(?:\.(\d+))?(?:\s*SP\s*(\d+))?(?:\s*Patch\s*(\d+))?/i.exec(name);
|
|
93
|
+
if (!m)
|
|
94
|
+
return "";
|
|
95
|
+
return [m[1], m[2], m[3], m[4], m[5]].map((n) => (n ?? "0").padStart(4, "0")).join(".");
|
|
96
|
+
}
|
|
97
|
+
/** Newest profile first; names without a version last, alphabetically. */
|
|
98
|
+
export function compareProfiles(a, b) {
|
|
99
|
+
return profileKey(b).localeCompare(profileKey(a)) || a.localeCompare(b);
|
|
100
|
+
}
|
|
101
|
+
function versionKey(label) {
|
|
102
|
+
const m = label.match(/(\d+)\.(\d+)\.(\d+)\.(\d+)/);
|
|
103
|
+
return m ? m.slice(1).map((n) => n.padStart(4, "0")).join(".") : label;
|
|
104
|
+
}
|
|
105
|
+
/** Pick the CODESYS exe + profile from options, falling back to the newest install. */
|
|
106
|
+
export function resolveCodesys(exe, profile, additionalFolder) {
|
|
107
|
+
if (additionalFolder && !existsSync(additionalFolder))
|
|
108
|
+
throw new Error(`CODESYS additional folder not found: ${additionalFolder}`);
|
|
109
|
+
let install;
|
|
110
|
+
let reason;
|
|
111
|
+
if (exe) {
|
|
112
|
+
if (!existsSync(exe))
|
|
113
|
+
throw new Error(`CODESYS executable not found: ${exe}`);
|
|
114
|
+
install = describeInstall(exe, additionalFolder);
|
|
115
|
+
reason = "set with --codesys-path";
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
const all = findInstalls();
|
|
119
|
+
const withProfile = profile ? all.find((i) => i.profiles.includes(profile)) : undefined;
|
|
120
|
+
install = withProfile ?? all[0];
|
|
121
|
+
if (!install)
|
|
122
|
+
throw new Error("No CODESYS installation found. Pass --codesys-path <...\\CODESYS\\Common\\CODESYS.exe>.");
|
|
123
|
+
reason = withProfile ? `it has profile "${profile}"` : pickedNewest(all.map((i) => i.label));
|
|
124
|
+
if (additionalFolder)
|
|
125
|
+
install = { ...install, additionalFolder };
|
|
126
|
+
}
|
|
127
|
+
const prof = profile ?? install.profiles[0];
|
|
128
|
+
if (!prof)
|
|
129
|
+
throw new Error(`No CODESYS profile found next to ${install.exe}. Pass --codesys-profile "<name>".`);
|
|
130
|
+
let warning;
|
|
131
|
+
if (profile && !install.profiles.some((p) => p.toLowerCase() === profile.toLowerCase())) {
|
|
132
|
+
const known = install.profiles.join(", ");
|
|
133
|
+
// Only CODESYS.exe is known to name its profiles after these files; other products get the benefit of the doubt.
|
|
134
|
+
if (known && isCodesysExe(install.exe))
|
|
135
|
+
throw new Error(`Profile "${profile}" isn't installed next to ${install.exe}. Installed profiles: ${known}.`);
|
|
136
|
+
warning = known ? `profile "${profile}" isn't among those found next to ${install.exe} (${known}); trying it anyway` : `no Profiles folder found near ${install.exe}, so profile "${profile}" can't be checked`;
|
|
137
|
+
}
|
|
138
|
+
return { exe: install.exe, profile: prof, install, reason, ...(warning ? { warning } : {}) };
|
|
139
|
+
}
|
|
140
|
+
/** "newest of 2 installs found (CODESYS 3.5.22.40, CODESYS 3.5.21.0); --codesys-path picks another" */
|
|
141
|
+
export function pickedNewest(labels) {
|
|
142
|
+
return labels.length === 1 ? "the only install found" : `newest of ${labels.length} installs found (${labels.join(", ")}); --codesys-path picks another`;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/codesys/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface BrowseEntry {
|
|
2
|
+
name: string;
|
|
3
|
+
comment?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface InstalledLibrary {
|
|
6
|
+
title: string;
|
|
7
|
+
version: string;
|
|
8
|
+
company: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function parseBrowseCache(xml: string): BrowseEntry[];
|
|
11
|
+
/** The newest of several installed versions, compared as numbers (3.5.22 is newer than 3.5.9). */
|
|
12
|
+
export declare function newestLibrary<T extends {
|
|
13
|
+
version: string;
|
|
14
|
+
}>(libs: T[]): T | undefined;
|
|
15
|
+
/** The library's browse cache in one of the repositories CODESYS reports. */
|
|
16
|
+
export declare function browseCacheFile(repositories: string[], lib: InstalledLibrary): string | undefined;
|
|
17
|
+
export declare function readBrowseCache(file: string): BrowseEntry[];
|
|
18
|
+
/** A library's objects as text; `symbol` narrows it to names or descriptions containing that text. */
|
|
19
|
+
export declare function describeBrowse(head: string, entries: BrowseEntry[], symbol?: string, maxLines?: number): string;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What CODESYS's own library browser knows about an installed library. Each
|
|
3
|
+
* library in a repository has a `browsercache` XML file listing its objects
|
|
4
|
+
* and their descriptions. Scripting can't read a compiled library's contents,
|
|
5
|
+
* and this works for libraries the project doesn't reference yet.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
const decode = (s) => s
|
|
10
|
+
.replace(/</g, "<")
|
|
11
|
+
.replace(/>/g, ">")
|
|
12
|
+
.replace(/"/g, '"')
|
|
13
|
+
.replace(/'/g, "'")
|
|
14
|
+
.replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCodePoint(parseInt(h, 16)))
|
|
15
|
+
.replace(/&#(\d+);/g, (_, d) => String.fromCodePoint(parseInt(d, 10)))
|
|
16
|
+
.replace(/&/g, "&");
|
|
17
|
+
export function parseBrowseCache(xml) {
|
|
18
|
+
const out = [];
|
|
19
|
+
for (const node of xml.matchAll(/<Node\s+([^>]*?)\/?>/g)) {
|
|
20
|
+
const attrs = new Map([...node[1].matchAll(/(\w+)="([^"]*)"/g)].map((a) => [a[1], decode(a[2])]));
|
|
21
|
+
const name = attrs.get("Name");
|
|
22
|
+
if (!name)
|
|
23
|
+
continue;
|
|
24
|
+
const comment = attrs.get("Comment")?.trim().replace(/\s+/g, " ");
|
|
25
|
+
out.push({ name, ...(comment ? { comment } : {}) });
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
const versionParts = (v) => v.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
30
|
+
/** The newest of several installed versions, compared as numbers (3.5.22 is newer than 3.5.9). */
|
|
31
|
+
export function newestLibrary(libs) {
|
|
32
|
+
return [...libs].sort((a, b) => {
|
|
33
|
+
const [x, y] = [versionParts(a.version), versionParts(b.version)];
|
|
34
|
+
for (let i = 0; i < Math.max(x.length, y.length); i++)
|
|
35
|
+
if ((x[i] ?? 0) !== (y[i] ?? 0))
|
|
36
|
+
return (y[i] ?? 0) - (x[i] ?? 0);
|
|
37
|
+
return 0;
|
|
38
|
+
})[0];
|
|
39
|
+
}
|
|
40
|
+
/** The library's browse cache in one of the repositories CODESYS reports. */
|
|
41
|
+
export function browseCacheFile(repositories, lib) {
|
|
42
|
+
return repositories.map((root) => join(root, lib.company, lib.title, lib.version, "browsercache")).find((f) => existsSync(f));
|
|
43
|
+
}
|
|
44
|
+
export function readBrowseCache(file) {
|
|
45
|
+
return parseBrowseCache(readFileSync(file, "utf8"));
|
|
46
|
+
}
|
|
47
|
+
/** A library's objects as text; `symbol` narrows it to names or descriptions containing that text. */
|
|
48
|
+
export function describeBrowse(head, entries, symbol, maxLines = 300) {
|
|
49
|
+
const want = symbol?.toLowerCase();
|
|
50
|
+
const shown = want ? entries.filter((e) => e.name.toLowerCase().includes(want) || e.comment?.toLowerCase().includes(want)) : entries;
|
|
51
|
+
if (want && !shown.length)
|
|
52
|
+
return `${head} has nothing matching '${symbol}'.`;
|
|
53
|
+
const lines = shown.slice(0, maxLines).map((e) => `- ${e.name}${e.comment ? ` :: ${e.comment}` : ""}`);
|
|
54
|
+
const more = shown.length > maxLines ? [`... ${shown.length - maxLines} more. Pass symbol to narrow it.`] : [];
|
|
55
|
+
return [`${head}: ${want ? `${shown.length} of ` : ""}${entries.length} objects, from CODESYS's library browser. Parameters aren't listed here; test a call with compile.`, ...lines, ...more].join("\n");
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=library-browse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"library-browse.js","sourceRoot":"","sources":["../../src/codesys/library-browse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project outline for the model: what exists, how to call it, and what runs
|
|
3
|
+
* it, without reading every object. Built from the CODESYS export (all ST
|
|
4
|
+
* text in one call) and the task list.
|
|
5
|
+
*/
|
|
6
|
+
import type { TreeNode } from "./st.js";
|
|
7
|
+
export interface OutlineTask {
|
|
8
|
+
name: string;
|
|
9
|
+
interval?: string | null;
|
|
10
|
+
interval_unit?: string | null;
|
|
11
|
+
priority?: string | null;
|
|
12
|
+
kind?: string | null;
|
|
13
|
+
pous: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface OutlineOptions {
|
|
16
|
+
/** An object or folder path: details for an object, an overview of a folder. */
|
|
17
|
+
path?: string;
|
|
18
|
+
/** Max lines of output (default 400). */
|
|
19
|
+
maxLines?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function buildOutline(nodes: TreeNode[], tasks: OutlineTask[], o?: OutlineOptions): string;
|
|
22
|
+
export interface IoEntry {
|
|
23
|
+
owner: string;
|
|
24
|
+
name: string;
|
|
25
|
+
at: string;
|
|
26
|
+
type: string;
|
|
27
|
+
direction: "input" | "output" | "memory";
|
|
28
|
+
comment?: string;
|
|
29
|
+
placeholder: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** Variables with AT addresses (including the %I* and %Q* placeholders), from GVLs and POUs. */
|
|
32
|
+
export declare function ioList(nodes: TreeNode[]): IoEntry[];
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { parseDeclaration, signature } from "./decl.js";
|
|
2
|
+
const TOP_KINDS = new Set(["pou", "dut", "gvl", "interface"]);
|
|
3
|
+
const SECTION_LABEL = {
|
|
4
|
+
VAR_INPUT: "in",
|
|
5
|
+
VAR_OUTPUT: "out",
|
|
6
|
+
VAR_IN_OUT: "in_out",
|
|
7
|
+
VAR: "var",
|
|
8
|
+
VAR_TEMP: "temp",
|
|
9
|
+
VAR_STAT: "stat",
|
|
10
|
+
VAR_INST: "inst",
|
|
11
|
+
VAR_GLOBAL: "global",
|
|
12
|
+
VAR_EXTERNAL: "external",
|
|
13
|
+
VAR_CONFIG: "config",
|
|
14
|
+
};
|
|
15
|
+
function intervalText(t) {
|
|
16
|
+
if (!t.interval)
|
|
17
|
+
return t.kind ?? "";
|
|
18
|
+
return `${t.interval}${t.interval_unit && !/[a-z]/i.test(t.interval) ? ` ${t.interval_unit}` : ""}`;
|
|
19
|
+
}
|
|
20
|
+
const varText = (v, withComment = true) => `${v.name}${v.at ? ` AT ${v.at}` : ""} : ${v.type}${v.init !== undefined ? ` := ${v.init}` : ""}${withComment && v.comment ? ` // ${v.comment}` : ""}`;
|
|
21
|
+
export function buildOutline(nodes, tasks, o = {}) {
|
|
22
|
+
const byPath = new Map(nodes.map((n) => [n.path.toLowerCase(), n]));
|
|
23
|
+
const parentOf = (p) => (p.includes("/") ? p.slice(0, p.lastIndexOf("/")) : "");
|
|
24
|
+
const kids = new Map();
|
|
25
|
+
for (const n of nodes) {
|
|
26
|
+
const p = parentOf(n.path).toLowerCase();
|
|
27
|
+
if (!kids.has(p))
|
|
28
|
+
kids.set(p, []);
|
|
29
|
+
kids.get(p).push(n);
|
|
30
|
+
}
|
|
31
|
+
const childrenOf = (n, ...kinds) => (kids.get(n.path.toLowerCase()) ?? []).filter((c) => !kinds.length || kinds.includes(c.kind));
|
|
32
|
+
const isContainer = (p) => p === "" || byPath.get(p.toLowerCase())?.kind === "folder";
|
|
33
|
+
const tops = nodes.filter((n) => TOP_KINDS.has(n.kind) && isContainer(parentOf(n.path)));
|
|
34
|
+
const parsed = new Map();
|
|
35
|
+
const parse = (n) => {
|
|
36
|
+
let p = parsed.get(n.path);
|
|
37
|
+
if (!p) {
|
|
38
|
+
p = parseDeclaration(n.declaration);
|
|
39
|
+
parsed.set(n.path, p);
|
|
40
|
+
}
|
|
41
|
+
return p;
|
|
42
|
+
};
|
|
43
|
+
const runBy = new Map();
|
|
44
|
+
for (const t of tasks)
|
|
45
|
+
for (const pou of t.pous) {
|
|
46
|
+
const k = pou.split(".").pop().toLowerCase();
|
|
47
|
+
runBy.set(k, [...(runBy.get(k) ?? []), `${t.name} ${intervalText(t)}`.trim()]);
|
|
48
|
+
}
|
|
49
|
+
const isTest = (n) => parse(n).header.attributes.some((a) => a.toLowerCase() === "test");
|
|
50
|
+
// ---- one object, in detail
|
|
51
|
+
const detail = (n) => {
|
|
52
|
+
const p = parse(n);
|
|
53
|
+
const h = p.header;
|
|
54
|
+
const out = [];
|
|
55
|
+
const head = [n.path, kindText(n, p)];
|
|
56
|
+
const run = runBy.get(n.name.toLowerCase());
|
|
57
|
+
if (run)
|
|
58
|
+
head.push(`run by ${run.join(", ")}`);
|
|
59
|
+
out.push(head.join(" "));
|
|
60
|
+
if (n.st === false)
|
|
61
|
+
out.push(" (graphical body: not ST)");
|
|
62
|
+
if (h.attributes.length)
|
|
63
|
+
out.push(` attributes: ${h.attributes.join(", ")}`);
|
|
64
|
+
if (p.dutKind === "enum")
|
|
65
|
+
out.push(` members: ${p.enumMembers.map((m) => (m.value !== undefined ? `${m.name} := ${m.value}` : m.name)).join(", ")}${p.baseType ? ` (base ${p.baseType})` : ""}`);
|
|
66
|
+
if (p.dutKind === "alias")
|
|
67
|
+
out.push(` = ${p.baseType}`);
|
|
68
|
+
for (const f of p.fields ?? [])
|
|
69
|
+
out.push(` ${varText(f)}`);
|
|
70
|
+
let section = "";
|
|
71
|
+
for (const v of p.vars) {
|
|
72
|
+
if (v.section !== section) {
|
|
73
|
+
section = v.section;
|
|
74
|
+
out.push(` ${SECTION_LABEL[v.section] ?? v.section}${v.constant ? " constant" : ""}${v.retain ? " retain" : ""}${v.persistent ? " persistent" : ""}:`);
|
|
75
|
+
}
|
|
76
|
+
out.push(` ${varText(v)}`);
|
|
77
|
+
}
|
|
78
|
+
for (const m of childrenOf(n, "method", "interface_method")) {
|
|
79
|
+
const mp = parse(m);
|
|
80
|
+
const locals = mp.vars.filter((v) => !["VAR_INPUT", "VAR_OUTPUT", "VAR_IN_OUT"].includes(v.section)).length;
|
|
81
|
+
out.push(` method ${[...mp.header.modifiers, ""].join(" ")}${signature(mp, m.name)}${locals ? ` (${locals} local${locals > 1 ? "s" : ""})` : ""}`);
|
|
82
|
+
}
|
|
83
|
+
for (const pr of childrenOf(n, "property")) {
|
|
84
|
+
const pp = parse(pr);
|
|
85
|
+
const acc = childrenOf(pr, "accessor").map((a) => a.name.toLowerCase());
|
|
86
|
+
out.push(` property ${[...pp.header.modifiers, ""].join(" ")}${pr.name} : ${pp.header.returnType ?? "?"} (${acc.join(", ") || "no accessors"})`);
|
|
87
|
+
}
|
|
88
|
+
for (const a of childrenOf(n, "action"))
|
|
89
|
+
out.push(` action ${a.name}${a.st === false ? " [non-ST]" : ""}`);
|
|
90
|
+
const lines = (n.implementation ?? "").replace(/\s+$/, "").split("\n").filter((l) => l.trim()).length;
|
|
91
|
+
if (n.kind === "pou")
|
|
92
|
+
out.push(` implementation: ${lines} line(s)`);
|
|
93
|
+
return out;
|
|
94
|
+
};
|
|
95
|
+
// ---- overview line(s) for one object
|
|
96
|
+
const brief = (n) => {
|
|
97
|
+
const p = parse(n);
|
|
98
|
+
const run = runBy.get(n.name.toLowerCase());
|
|
99
|
+
const tags = [kindText(n, p), run ? `[${run.join(", ")}]` : "", n.st === false ? "[non-ST]" : ""].filter(Boolean).join(" ");
|
|
100
|
+
const out = [`${n.path} ${tags}`];
|
|
101
|
+
if (n.kind === "pou" && !isTest(n)) {
|
|
102
|
+
const ins = p.vars.filter((v) => v.section === "VAR_INPUT" || v.section === "VAR_IN_OUT");
|
|
103
|
+
const outs = p.vars.filter((v) => v.section === "VAR_OUTPUT");
|
|
104
|
+
if (ins.length)
|
|
105
|
+
out.push(` in: ${ins.map((v) => varText(v, false)).join("; ")}`);
|
|
106
|
+
if (outs.length)
|
|
107
|
+
out.push(` out: ${outs.map((v) => varText(v, false)).join("; ")}`);
|
|
108
|
+
const ms = childrenOf(n, "method").map((m) => signature(parse(m), m.name));
|
|
109
|
+
if (ms.length)
|
|
110
|
+
out.push(` methods: ${ms.join("; ")}`);
|
|
111
|
+
const ps = childrenOf(n, "property").map((pr) => {
|
|
112
|
+
const acc = childrenOf(pr, "accessor").map((a) => a.name.toLowerCase());
|
|
113
|
+
return `${pr.name} : ${parse(pr).header.returnType ?? "?"}${acc.length === 1 ? ` (${acc[0]} only)` : ""}`;
|
|
114
|
+
});
|
|
115
|
+
if (ps.length)
|
|
116
|
+
out.push(` properties: ${ps.join("; ")}`);
|
|
117
|
+
const as = childrenOf(n, "action").map((a) => a.name);
|
|
118
|
+
if (as.length)
|
|
119
|
+
out.push(` actions: ${as.join(", ")}`);
|
|
120
|
+
}
|
|
121
|
+
else if (n.kind === "interface") {
|
|
122
|
+
const ms = childrenOf(n, "interface_method", "method").map((m) => signature(parse(m), m.name));
|
|
123
|
+
if (ms.length)
|
|
124
|
+
out.push(` methods: ${ms.join("; ")}`);
|
|
125
|
+
}
|
|
126
|
+
else if (n.kind === "dut" && p.fields?.length) {
|
|
127
|
+
out.push(` fields: ${p.fields.slice(0, 12).map((f) => `${f.name} : ${f.type}`).join("; ")}${p.fields.length > 12 ? `; ... (${p.fields.length})` : ""}`);
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
};
|
|
131
|
+
const max = o.maxLines ?? 400;
|
|
132
|
+
const clip = (lines, hint) => (lines.length > max ? [...lines.slice(0, max), `... ${lines.length - max} more line(s). ${hint}`] : lines);
|
|
133
|
+
if (o.path) {
|
|
134
|
+
const target = byPath.get(o.path.replace(/^\/+/, "").toLowerCase());
|
|
135
|
+
if (!target) {
|
|
136
|
+
const close = nodes.filter((n) => n.name.toLowerCase() === o.path.split("/").pop().toLowerCase()).map((n) => n.path);
|
|
137
|
+
return `No object '${o.path}'.${close.length ? ` Did you mean: ${close.join(", ")}?` : ""}`;
|
|
138
|
+
}
|
|
139
|
+
if (target.kind === "folder") {
|
|
140
|
+
const inside = tops.filter((t) => t.path.toLowerCase().startsWith(target.path.toLowerCase() + "/"));
|
|
141
|
+
return clip([`${target.path}/ (${inside.length} object(s))`, ...inside.flatMap(brief)], "Ask for one object for details.").join("\n");
|
|
142
|
+
}
|
|
143
|
+
const top = TOP_KINDS.has(target.kind) ? target : undefined;
|
|
144
|
+
if (top)
|
|
145
|
+
return clip(detail(top), "").join("\n");
|
|
146
|
+
// a method, property or action: its own declaration
|
|
147
|
+
const p = parse(target);
|
|
148
|
+
return clip([`${target.path} ${target.kind}`, ...p.vars.map((v) => ` ${SECTION_LABEL[v.section] ?? v.section}: ${varText(v)}`), ...(p.header.returnType ? [` returns ${p.header.returnType}`] : [])], "").join("\n");
|
|
149
|
+
}
|
|
150
|
+
const count = (pred) => tops.filter(pred).length;
|
|
151
|
+
const tests = tops.filter((n) => n.kind === "pou" && isTest(n));
|
|
152
|
+
const header = [
|
|
153
|
+
`Outline: ${count((n) => n.kind === "pou" && !isTest(n))} POU(s), ${count((n) => n.kind === "gvl")} GVL(s), ${count((n) => n.kind === "dut")} DUT(s), ${count((n) => n.kind === "interface")} interface(s)${tests.length ? `, ${tests.length} test(s)` : ""}.`,
|
|
154
|
+
`Tasks: ${tasks.length ? tasks.map((t) => `${t.name} (${intervalText(t)}${t.priority ? `, prio ${t.priority}` : ""}) -> ${t.pous.join(", ") || "nothing"}`).join("; ") : "none"}`,
|
|
155
|
+
];
|
|
156
|
+
const order = (n) => (isTest(n) ? 9 : n.kind === "pou" ? (n.sub === "program" ? 0 : n.sub === "function_block" ? 1 : 2) : n.kind === "interface" ? 3 : n.kind === "dut" ? 4 : 5);
|
|
157
|
+
const sorted = [...tops].sort((a, b) => order(a) - order(b) || a.path.localeCompare(b.path));
|
|
158
|
+
const body = sorted.filter((n) => !isTest(n)).flatMap(brief);
|
|
159
|
+
if (tests.length)
|
|
160
|
+
body.push(`tests: ${tests.map((t) => t.path).join(", ")}`);
|
|
161
|
+
return clip([...header, ...body], "Pass path (an object or folder) to narrow it.").join("\n");
|
|
162
|
+
}
|
|
163
|
+
function kindText(n, p) {
|
|
164
|
+
const h = p.header;
|
|
165
|
+
const mods = h.modifiers.length ? `${h.modifiers.join(" ")} ` : "";
|
|
166
|
+
if (n.kind === "pou") {
|
|
167
|
+
if (h.attributes.some((a) => a.toLowerCase() === "test"))
|
|
168
|
+
return "TEST";
|
|
169
|
+
const kw = h.keyword || (n.sub ?? "").toUpperCase();
|
|
170
|
+
let s = `${mods}${kw}`;
|
|
171
|
+
if (h.returnType)
|
|
172
|
+
s += ` : ${h.returnType}`;
|
|
173
|
+
if (h.extends?.length)
|
|
174
|
+
s += ` EXTENDS ${h.extends.join(", ")}`;
|
|
175
|
+
if (h.implements?.length)
|
|
176
|
+
s += ` IMPLEMENTS ${h.implements.join(", ")}`;
|
|
177
|
+
return s;
|
|
178
|
+
}
|
|
179
|
+
if (n.kind === "interface") {
|
|
180
|
+
const bases = (h.extends ?? []).filter((b) => !/^__System\.IQueryInterface$/i.test(b));
|
|
181
|
+
return `INTERFACE${bases.length ? ` EXTENDS ${bases.join(", ")}` : ""}`;
|
|
182
|
+
}
|
|
183
|
+
if (n.kind === "gvl") {
|
|
184
|
+
const q = h.attributes.some((a) => a.toLowerCase() === "qualified_only");
|
|
185
|
+
return `GVL (${p.vars.length} var${p.vars.length === 1 ? "" : "s"}${q ? ", qualified_only" : ""})`;
|
|
186
|
+
}
|
|
187
|
+
if (n.kind === "dut") {
|
|
188
|
+
if (p.dutKind === "enum")
|
|
189
|
+
return `ENUM (${p.enumMembers.map((m) => (m.value !== undefined ? `${m.name}=${m.value}` : m.name)).join(", ")})`;
|
|
190
|
+
if (p.dutKind === "alias")
|
|
191
|
+
return `ALIAS = ${p.baseType}`;
|
|
192
|
+
return `${(p.dutKind ?? "dut").toUpperCase()}${p.fields ? ` (${p.fields.length} field${p.fields.length === 1 ? "" : "s"})` : ""}`;
|
|
193
|
+
}
|
|
194
|
+
return n.kind;
|
|
195
|
+
}
|
|
196
|
+
/** Variables with AT addresses (including the %I* and %Q* placeholders), from GVLs and POUs. */
|
|
197
|
+
export function ioList(nodes) {
|
|
198
|
+
const out = [];
|
|
199
|
+
for (const n of nodes) {
|
|
200
|
+
if (!["gvl", "pou"].includes(n.kind))
|
|
201
|
+
continue;
|
|
202
|
+
for (const v of parseDeclaration(n.declaration).vars) {
|
|
203
|
+
if (!v.at)
|
|
204
|
+
continue;
|
|
205
|
+
const area = /^%([IQM])/i.exec(v.at)?.[1]?.toUpperCase();
|
|
206
|
+
out.push({
|
|
207
|
+
owner: n.path,
|
|
208
|
+
name: v.name,
|
|
209
|
+
at: v.at,
|
|
210
|
+
type: v.type,
|
|
211
|
+
direction: area === "I" ? "input" : area === "Q" ? "output" : "memory",
|
|
212
|
+
...(v.comment ? { comment: v.comment } : {}),
|
|
213
|
+
placeholder: /\*$/.test(v.at),
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=outline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outline.js","sourceRoot":"","sources":["../../src/codesys/outline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { RegisterAction } from "../actions.js";
|
|
2
|
+
import type { AppContext } from "../tools.js";
|
|
3
|
+
interface Status {
|
|
4
|
+
running: boolean;
|
|
5
|
+
state?: string;
|
|
6
|
+
operation?: string;
|
|
7
|
+
forced?: string[];
|
|
8
|
+
cycles?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface CodesysSimState {
|
|
11
|
+
state: string;
|
|
12
|
+
forced: string[];
|
|
13
|
+
cycles: number;
|
|
14
|
+
/** The bridge's restart count when this was recorded: a restart ends the simulation. */
|
|
15
|
+
restarts?: number;
|
|
16
|
+
}
|
|
17
|
+
type ToolResult = {
|
|
18
|
+
content: {
|
|
19
|
+
type: "text";
|
|
20
|
+
text: string;
|
|
21
|
+
}[];
|
|
22
|
+
isError?: boolean;
|
|
23
|
+
};
|
|
24
|
+
export interface SimToolDeps {
|
|
25
|
+
reg: RegisterAction;
|
|
26
|
+
run: (fn: () => Promise<string>) => Promise<ToolResult>;
|
|
27
|
+
/** Compile; the build report when it has errors, otherwise undefined. */
|
|
28
|
+
compileErrors: () => Promise<string | undefined>;
|
|
29
|
+
}
|
|
30
|
+
/** The simulation the panel should show; undefined when none runs. */
|
|
31
|
+
export declare function currentCodesysSim(ctx: AppContext): CodesysSimState | undefined;
|
|
32
|
+
export declare function describeSim(s: Status): string;
|
|
33
|
+
export declare function registerCodesysSim(ctx: AppContext, deps: SimToolDeps): void;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CODESYS's own simulation: runs the active application inside CODESYS, with no
|
|
3
|
+
* controller and nothing extra to install. It uses the real CODESYS compiler
|
|
4
|
+
* and libraries. It can run, read, write, force and take single cycles. It has
|
|
5
|
+
* no breakpoints: CODESYS's scripting offers none, and its breakpoint commands
|
|
6
|
+
* work at an editor cursor, which a session without a window doesn't have.
|
|
7
|
+
*/
|
|
8
|
+
import * as z from "zod/v4";
|
|
9
|
+
/** The simulation the panel should show; undefined when none runs. */
|
|
10
|
+
export function currentCodesysSim(ctx) {
|
|
11
|
+
const s = ctx.codesysSim;
|
|
12
|
+
return s && s.restarts === ctx.bridge.health().restarts ? s : undefined;
|
|
13
|
+
}
|
|
14
|
+
export function describeSim(s) {
|
|
15
|
+
if (!s.running)
|
|
16
|
+
return "CODESYS simulation: not running.";
|
|
17
|
+
return `CODESYS simulation: application ${s.state}.${s.forced?.length ? ` Forced: ${s.forced.join(", ")}.` : ""}`;
|
|
18
|
+
}
|
|
19
|
+
const names = z.array(z.string()).min(1).describe("Variables as CODESYS expressions relative to the application, e.g. 'PLC_PRG.nCount' or 'GVL_IO.xValve'");
|
|
20
|
+
const values = z.record(z.string(), z.string()).describe("Variable to value, with values as Structured Text: { 'PLC_PRG.xStart': 'TRUE', 'PLC_PRG.nSpeed': '5' }");
|
|
21
|
+
export function registerCodesysSim(ctx, deps) {
|
|
22
|
+
const b = ctx.bridge;
|
|
23
|
+
const { reg, run } = deps;
|
|
24
|
+
const NOTE = "CODESYS's own simulation: no controller is involved.";
|
|
25
|
+
const status = async (op, args = {}, timeoutMs = 60_000) => {
|
|
26
|
+
const s = await b.call(op, args, { timeoutMs });
|
|
27
|
+
ctx.codesysSim = s.running ? { state: s.state ?? "unknown", forced: s.forced ?? [], cycles: s.cycles ?? 0, restarts: b.health().restarts } : undefined;
|
|
28
|
+
return describeSim(s);
|
|
29
|
+
};
|
|
30
|
+
reg("codesys_sim_start", {
|
|
31
|
+
description: `Compile, then load the active application into ${NOTE} Uses the real CODESYS compiler and libraries, and needs nothing installed beyond CODESYS. The application is loaded and stopped unless run is true. While the simulation runs the project is locked: compile, write_object and the other changing tools are refused until codesys_sim_stop. It has no breakpoints, and time runs at real speed.`,
|
|
32
|
+
inputSchema: z.object({ run: z.boolean().optional().describe("Start the application straight away (default false)") }),
|
|
33
|
+
}, async ({ run: start }) => run(async () => {
|
|
34
|
+
const errors = await deps.compileErrors();
|
|
35
|
+
if (errors)
|
|
36
|
+
throw new Error(`Fix the compile errors before simulating.\n${errors}`);
|
|
37
|
+
return `${await status("sim.start", { run: !!start }, 300_000)} The project is locked until codesys_sim_stop.`;
|
|
38
|
+
}));
|
|
39
|
+
reg("codesys_sim_stop", { description: "Stop CODESYS's own simulation, release all forced values and unlock the project.", inputSchema: z.object({}) }, async () => run(() => status("sim.stop", {}, 120_000)));
|
|
40
|
+
reg("codesys_sim_status", { description: "Whether CODESYS's own simulation is running, the application's state and the forced variables.", inputSchema: z.object({}) }, async () => run(() => status("sim.status")));
|
|
41
|
+
reg("codesys_sim_control", {
|
|
42
|
+
description: "Run, stop or reset the application in CODESYS's own simulation. A reset also releases forced values.",
|
|
43
|
+
inputSchema: z.object({ action: z.enum(["run", "stop", "reset_warm", "reset_cold"]) }),
|
|
44
|
+
}, async ({ action }) => run(() => status("sim.control", { action })));
|
|
45
|
+
reg("codesys_sim_cycle", {
|
|
46
|
+
description: "Run exactly this many task cycles in CODESYS's own simulation, then stay stopped. Stops the application first if it is running. Each cycle takes about 0.1 s. Timers follow the clock, not the cycle count: to let a TON elapse, wait, then run another cycle.",
|
|
47
|
+
inputSchema: z.object({ count: z.number().int().min(1).max(1000).optional().describe("Cycles to run (default 1)") }),
|
|
48
|
+
}, async ({ count }) => run(() => status("sim.cycle", { count: count ?? 1 }, Math.max(60_000, (count ?? 1) * 1_000))));
|
|
49
|
+
reg("codesys_sim_read", { description: "Read current values from CODESYS's own simulation. A read takes about 0.4 s, so read everything you need in one call.", inputSchema: z.object({ names }) }, async ({ names: list }) => run(async () => {
|
|
50
|
+
const r = await b.call("sim.read", { names: list });
|
|
51
|
+
return list.map((n) => (n in r.values ? `${n} = ${r.values[n]}` : `${n}: ${r.errors[n] ?? "no value"}`)).join("\n");
|
|
52
|
+
}));
|
|
53
|
+
reg("codesys_sim_write", { description: "Write values once in CODESYS's own simulation. If the program assigns the variable, the next cycle overwrites it; use codesys_sim_force to hold a value.", inputSchema: z.object({ values }) }, async ({ values: v }) => run(() => status("sim.write", { values: v })));
|
|
54
|
+
reg("codesys_sim_force", { description: "Force values in CODESYS's own simulation, until codesys_sim_unforce or codesys_sim_stop. Right for inputs. CODESYS re-applies a force between scans, so if the program assigns the variable, code before that assignment sees the forced value and code after it, in the same scan, sees the program's own value.", inputSchema: z.object({ values }) }, async ({ values: v }) => run(() => status("sim.force", { values: v })));
|
|
55
|
+
reg("codesys_sim_unforce", { description: "Release forced values in CODESYS's own simulation: the named ones, or all of them.", inputSchema: z.object({ names: names.optional() }) }, async ({ names: list }) => run(() => status("sim.unforce", list ? { names: list } : {})));
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=simulation.js.map
|