Add archived Siemens software

This commit is contained in:
2026-08-26 12:46:05 +03:00
parent aa7dbcf33d
commit 9990eed34c
125 changed files with 1227 additions and 19 deletions
+86 -16
View File
@@ -55,7 +55,10 @@ type Options = {
host: string;
importDirectory?: string;
limit?: number;
program?: string;
programs: Set<string>;
sshConfig?: string;
sshControl?: string;
sshPort?: number;
replace: boolean;
waitSeconds: number;
write: boolean;
@@ -86,13 +89,16 @@ const initialKeys = new Map([
"ENTER,TEXT=C:\\codex\\catalog-screenshots\\work\\brew-graphicpatch-creator\\brewrc.exe,ENTER",
],
["brew-apploader", "CLOSE-SMALL,CLOSE-SMALL"],
["cl50-dinghy-downloader", "ENTER"],
["efs-explorer-lite", "ENTER"],
["flash-imager", "ENTER"],
["flex-table-edit", "ENTER"],
["gagin", "CLOSE-SMALL,CLOSE-SMALL"],
["freia", "SELECT-TITLE=Engine"],
["initmap", "CLOSE-LARGEST"],
["mobilechat", "ALT+N"],
["s65-key-definer", "ENTER"],
["s80-user-download-tool", "ENTER"],
["siemens-screenshot", "ENTER"],
]);
@@ -102,8 +108,12 @@ function usage(): void {
Capture one screenshot for each Windows program.
--host HOST Windows SSH host (default: win)
--ssh-config PATH use an alternate OpenSSH configuration file
--ssh-control PATH
use an existing OpenSSH control socket
--ssh-port PORT connect to a specific SSH port
--import DIR import reviewed SLUG.png files without connecting to Windows
--program SLUG process one program directory
--program SLUG process a program directory; may be repeated
--limit COUNT stop after COUNT programs
--replace recapture only existing img/SLUG.png screenshots
--wait SECONDS wait for a program window (default: 8)
@@ -116,7 +126,10 @@ function parseArguments(args: string[]): Options {
let host = "win";
let importDirectory: string | undefined;
let limit: number | undefined;
let program: string | undefined;
const programs = new Set<string>();
let sshConfig: string | undefined;
let sshControl: string | undefined;
let sshPort: number | undefined;
let replace = false;
let waitSeconds = 8;
let write = false;
@@ -125,12 +138,18 @@ function parseArguments(args: string[]): Options {
const argument = args[index];
if (argument === "--host") {
host = args[++index] ?? "";
} else if (argument === "--ssh-config") {
sshConfig = path.resolve(args[++index] ?? "");
} else if (argument === "--ssh-control") {
sshControl = path.resolve(args[++index] ?? "");
} else if (argument === "--ssh-port") {
sshPort = Number(args[++index]);
} else if (argument === "--import") {
importDirectory = path.resolve(args[++index] ?? "");
} else if (argument === "--limit") {
limit = Number(args[++index]);
} else if (argument === "--program") {
program = args[++index];
programs.add(args[++index] ?? "");
} else if (argument === "--replace") {
replace = true;
} else if (argument === "--wait") {
@@ -159,19 +178,48 @@ function parseArguments(args: string[]): Options {
if (!Number.isInteger(waitSeconds) || waitSeconds < 1 || waitSeconds > 60) {
throw new Error("--wait must be an integer between 1 and 60");
}
if (programs.has("")) {
throw new Error("--program must not be empty");
}
if (sshPort !== undefined && (!Number.isInteger(sshPort) || sshPort < 1 || sshPort > 65535)) {
throw new Error("--ssh-port must be an integer between 1 and 65535");
}
return {
catalogDirectory: path.resolve(targets[0] ?? "catalog"),
host,
importDirectory,
limit,
program,
programs,
sshConfig,
sshControl,
sshPort,
replace,
waitSeconds,
write,
};
}
function sshArguments(options: Options, remoteCommand: string): string[] {
return [
...(options.sshConfig !== undefined ? ["-F", options.sshConfig] : []),
...(options.sshControl !== undefined ? ["-S", options.sshControl] : []),
...(options.sshPort !== undefined ? ["-p", String(options.sshPort)] : []),
options.host,
remoteCommand,
];
}
function scpArguments(options: Options, sources: string[], destination: string): string[] {
return [
...(options.sshConfig !== undefined ? ["-F", options.sshConfig] : []),
...(options.sshControl !== undefined ? ["-o", `ControlPath=${options.sshControl}`] : []),
...(options.sshPort !== undefined ? ["-P", String(options.sshPort)] : []),
...sources,
destination,
];
}
async function run(command: string, args: string[], allowFailure = false): Promise<string> {
return await new Promise((resolve, reject) => {
const child = spawn(command, args, { stdio: ["ignore", "pipe", "pipe"] });
@@ -239,7 +287,7 @@ async function collectPrograms(options: Options): Promise<{ programs: Program[];
if (programsWithoutStandaloneGui.has(slug)) {
continue;
}
if (options.program !== undefined && slug !== options.program) {
if (options.programs.size > 0 && !options.programs.has(slug)) {
continue;
}
if (typeof metadata.name !== "string" || metadata.name.length === 0) {
@@ -308,18 +356,30 @@ function failureReason(result: RemoteResult, program: Program): { reason: string
if (failedTitle !== undefined) {
return { reason: "Error window", details: failedTitle };
}
const setupTitle = program.slug === "java-midlet-installer" ? undefined : titles.find((title) => installerTitle.test(title));
const programsWithSetupInMainWindowTitle = new Set([
"ifwd-flashtool-e2",
"java-midlet-installer",
"kp500-flashing-tool",
]);
const setupTitle = programsWithSetupInMainWindowTitle.has(program.slug)
? undefined
: titles.find((title) => installerTitle.test(title));
if (setupTitle !== undefined) {
return { reason: "Installer opened", details: setupTitle };
}
return undefined;
}
async function addScreenshot(program: Program, screenshotPath: string, replace: boolean): Promise<void> {
async function addScreenshot(
options: Options,
program: Program,
screenshotPath: string,
replace: boolean,
): Promise<void> {
const imageDirectory = path.join(program.directory, "img");
const imagePath = path.join(imageDirectory, `${program.slug}.png`);
await mkdir(imageDirectory, { recursive: true });
await run("scp", [`${screenshotPath}`, imagePath]);
await run("scp", scpArguments(options, [screenshotPath], imagePath));
if (replace) {
return;
@@ -380,18 +440,23 @@ async function main(): Promise<void> {
}
let captured = 0;
const tools = ["screenshotApp.ps1", "runScreenshotTask.ps1", "screenshotArchive.ps1"];
await run("ssh", [
options.host,
await run("ssh", sshArguments(options,
`powershell -NoProfile -Command "New-Item -ItemType Directory -Force -Path '${remoteRoot}\\input','${remoteRoot}\\results' | Out-Null"`,
]);
await run("scp", tools.map((file) => path.resolve("tools", file)).concat(`${options.host}:${remoteScpRoot}/`));
));
await run(
"scp",
scpArguments(options, tools.map((file) => path.resolve("tools", file)), `${options.host}:${remoteScpRoot}/`),
);
console.log(`Programs to process: ${programs.length}`);
for (const [index, program] of programs.entries()) {
const prefix = `[${index + 1}/${programs.length}] ${program.name}`;
const remoteArchive = `${remoteRoot}\\input\\${program.slug}.zip`;
try {
await run("scp", [program.archive, `${options.host}:${remoteScpRoot}/input/${program.slug}.zip`]);
await run(
"scp",
scpArguments(options, [program.archive], `${options.host}:${remoteScpRoot}/input/${program.slug}.zip`),
);
const remoteCommand = [
"powershell",
"-NoProfile",
@@ -403,7 +468,7 @@ async function main(): Promise<void> {
`-WaitSeconds ${options.waitSeconds}`,
...(initialKeys.has(program.slug) ? [`-InitialKeys ${initialKeys.get(program.slug)}`] : []),
].join(" ");
const output = await run("ssh", [options.host, remoteCommand], true);
const output = await run("ssh", sshArguments(options, remoteCommand), true);
const result = parseRemoteResult(output);
const failure = failureReason(result, program);
if (failure !== undefined) {
@@ -417,7 +482,12 @@ async function main(): Promise<void> {
}
if (options.write) {
await addScreenshot(program, `${options.host}:${remoteScpRoot}/results/${program.slug}.png`, options.replace);
await addScreenshot(
options,
program,
`${options.host}:${remoteScpRoot}/results/${program.slug}.png`,
options.replace,
);
}
captured += 1;
console.log(`${prefix}: ${options.write ? "saved" : "captured"} (${result.title ?? result.selected ?? "window"})`);