diff --git a/package.json b/package.json new file mode 100644 index 0000000..9746460 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "as-test-flight-sim", + "version": "0.1.0", + "private": true, + "description": "Offline Cesium aircraft maneuver planning prototype with a local simulation service.", + "scripts": { + "dev": "node tools/dev.mjs", + "service": "node service/src/server.js", + "test:service": "node service/src/simulator.test.js", + "web:dev": "npm --prefix web-test run dev", + "web:build": "npm --prefix web-test run build", + "copy:cesium": "node tools/copy-cesium-assets.mjs" + }, + "engines": { + "node": ">=20" + } +} diff --git a/tools/copy-cesium-assets.mjs b/tools/copy-cesium-assets.mjs new file mode 100644 index 0000000..1d2ef24 --- /dev/null +++ b/tools/copy-cesium-assets.mjs @@ -0,0 +1,26 @@ +import { cpSync, existsSync, mkdirSync, rmSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = dirname(dirname(fileURLToPath(import.meta.url))); +const candidateBuilds = [ + join(root, "node_modules", "cesium", "Build", "Cesium"), + join(root, "web-test", "node_modules", "cesium", "Build", "Cesium"), +]; +const cesiumBuild = candidateBuilds.find((value) => existsSync(value)); +const target = join(root, "web-test", "public", "cesium"); + +if (!cesiumBuild) { + throw new Error("Cesium package is not installed. Run npm.cmd install first."); +} + +mkdirSync(target, { recursive: true }); + +for (const name of ["Assets", "ThirdParty", "Workers", "Widgets"]) { + const source = join(cesiumBuild, name); + const destination = join(target, name); + rmSync(destination, { recursive: true, force: true }); + cpSync(source, destination, { recursive: true }); +} + +console.log(`Copied Cesium runtime assets to ${target}`); diff --git a/tools/dev.mjs b/tools/dev.mjs new file mode 100644 index 0000000..73c8115 --- /dev/null +++ b/tools/dev.mjs @@ -0,0 +1,78 @@ +import { spawn } from "node:child_process"; +import net from "node:net"; + +const commands = [ + { + name: "service", + command: "node", + args: ["service/src/server.js"], + port: 4317 + }, + { + name: "web", + command: "npm.cmd", + args: ["run", "web:dev"], + port: 5173 + } +]; + +const occupied = []; +for (const command of commands) { + if (await isPortInUse(command.port)) { + occupied.push(command); + } +} + +if (occupied.length > 0) { + for (const command of occupied) { + console.error(`[dev] port ${command.port} is already in use; not starting ${command.name}.`); + } + console.error("[dev] stop the old process first, then run npm.cmd run dev again."); + process.exit(1); +} + +const children = commands.map(({ name, command, args }) => { + const child = spawn(command, args, { + cwd: process.cwd(), + stdio: ["ignore", "pipe", "pipe"], + shell: false + }); + + child.stdout.on("data", (data) => writePrefixed(name, data)); + child.stderr.on("data", (data) => writePrefixed(name, data)); + child.on("exit", (code) => { + if (code !== 0 && code !== null) { + console.error(`[${name}] exited with code ${code}`); + } + }); + + return child; +}); + +for (const signal of ["SIGINT", "SIGTERM"]) { + process.on(signal, () => { + for (const child of children) { + child.kill(signal); + } + process.exit(0); + }); +} + +function writePrefixed(name, data) { + const lines = data.toString().split(/\r?\n/).filter(Boolean); + for (const line of lines) { + console.log(`[${name}] ${line}`); + } +} + +function isPortInUse(port) { + return new Promise((resolve) => { + const server = net.createServer(); + + server.once("error", () => resolve(true)); + server.once("listening", () => { + server.close(() => resolve(false)); + }); + server.listen(port, "127.0.0.1"); + }); +}