UNPKG

saxi

Version:

Drive the AxiDraw pen plotter

390 lines 17.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __asyncValues = (this && this.__asyncValues) || function (o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } }; var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i; function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; } function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.startServer = startServer; exports.waitForEbb = waitForEbb; exports.connectEBB = connectEBB; const cors_1 = __importDefault(require("cors")); require("web-streams-polyfill/polyfill"); const express_1 = __importDefault(require("express")); const node_http_1 = __importDefault(require("node:http")); const node_path_1 = __importDefault(require("node:path")); const wake_lock_1 = require("wake-lock"); const ws_1 = __importDefault(require("ws")); const serialport_serialport_1 = require("./serialport-serialport"); const planning_1 = require("./planning"); const util_1 = require("./util"); const bindings_cpp_1 = require("@serialport/bindings-cpp"); const _self = __importStar(require("./server")); // use self-import for test mocking const ebb_1 = require("./ebb"); const getDeviceInfo = (ebb, com) => { return { com: ebb ? com : null, hardware: ebb === null || ebb === void 0 ? void 0 : ebb.hardware }; }; function startServer(port_1) { return __awaiter(this, arguments, void 0, function* (port, hardware = 'v3', com = null, enableCors = false, maxPayloadSize = '200mb') { const app = (0, express_1.default)(); app.use('/', express_1.default.static(node_path_1.default.join(__dirname, '..', 'ui'))); app.use(express_1.default.json({ limit: maxPayloadSize })); if (enableCors) { app.use((0, cors_1.default)()); } const server = node_http_1.default.createServer(app); const wss = new ws_1.default.Server({ server }); let ebb; let clients = []; let cancelRequested = false; let unpaused = null; let signalUnpause = null; let motionIdx = null; let currentPlan = null; let plotting = false; wss.on("connection", (ws) => { clients.push(ws); ws.on("message", (message) => { const msg = JSON.parse(message.toString()); switch (msg.c) { case "ping": ws.send(JSON.stringify({ c: "pong" })); break; case "limp": if (ebb) { ebb.disableMotors(); } break; case "setPenHeight": if (ebb) { (() => __awaiter(this, void 0, void 0, function* () { if (yield ebb.supportsSR()) { yield ebb.setServoPowerTimeout(10000, true); } yield ebb.setPenHeight(msg.p.height, msg.p.rate); }))(); } break; } }); ws.send(JSON.stringify({ c: 'dev', p: getDeviceInfo(ebb, com) })); ws.send(JSON.stringify({ c: "pause", p: { paused: !!unpaused } })); if (motionIdx != null) { ws.send(JSON.stringify({ c: "progress", p: { motionIdx } })); } if (currentPlan != null) { ws.send(JSON.stringify({ c: "plan", p: { plan: currentPlan } })); } ws.on("close", () => { clients = clients.filter((w) => w !== ws); }); }); app.post("/plot", (req, res) => __awaiter(this, void 0, void 0, function* () { if (plotting) { console.log("Received plot request, but a plot is already in progress!"); return res.status(400).end('Plot in progress'); } plotting = true; try { const plan = planning_1.Plan.deserialize(req.body); currentPlan = req.body; console.log(`Received plan of estimated duration ${(0, util_1.formatDuration)(plan.duration())}`); console.log(ebb != null ? "Beginning plot..." : "Simulating plot..."); res.status(200).end(); const begin = Date.now(); let wakeLock; // The wake-lock module is macOS-only. if (process.platform === 'darwin') { try { wakeLock = new wake_lock_1.WakeLock("saxi plotting"); } catch (e) { console.warn("Couldn't acquire wake lock. Ensure your machine does not sleep during plotting"); } } try { yield doPlot(ebb != null ? realPlotter : simPlotter, plan); const end = Date.now(); console.log(`Plot took ${(0, util_1.formatDuration)((end - begin) / 1000)}`); } finally { if (wakeLock) { wakeLock.release(); } } } finally { plotting = false; } })); app.post("/cancel", (req, res) => { cancelRequested = true; if (unpaused) { signalUnpause(); } unpaused = signalUnpause = null; res.status(200).end(); }); app.post("/pause", (req, res) => { if (!unpaused) { unpaused = new Promise(resolve => { signalUnpause = resolve; }); broadcast({ c: "pause", p: { paused: true } }); } res.status(200).end(); }); app.post("/resume", (req, res) => { if (signalUnpause) { signalUnpause(); signalUnpause = unpaused = null; } res.status(200).end(); }); function broadcast(msg) { clients.forEach((ws) => { try { ws.send(JSON.stringify(msg)); } catch (e) { console.warn(e); } }); } const realPlotter = { prePlot(initialPenHeight) { return __awaiter(this, void 0, void 0, function* () { yield ebb.enableMotors(2); yield ebb.setPenHeight(initialPenHeight, 1000, 1000); }); }, executeMotion(motion, _progress) { return __awaiter(this, void 0, void 0, function* () { yield ebb.executeMotion(motion); }); }, postCancel() { return __awaiter(this, void 0, void 0, function* () { const device = (0, planning_1.Device)(ebb.hardware); // TODO: switch to pen up position yield ebb.setPenHeight(device.penPctToPos(50), 1000); }); }, postPlot() { return __awaiter(this, void 0, void 0, function* () { yield ebb.waitUntilMotorsIdle(); yield ebb.disableMotors(); }); }, }; const simPlotter = { // eslint-disable-next-line @typescript-eslint/no-empty-function prePlot(_initialPenHeight) { return __awaiter(this, void 0, void 0, function* () { }); }, executeMotion(motion, progress) { return __awaiter(this, void 0, void 0, function* () { console.log(`Motion ${progress[0] + 1}/${progress[1]}`); yield new Promise((resolve) => setTimeout(resolve, motion.duration() * 1000)); }); }, postCancel() { return __awaiter(this, void 0, void 0, function* () { console.log("Plot cancelled"); }); }, // eslint-disable-next-line @typescript-eslint/no-empty-function postPlot() { return __awaiter(this, void 0, void 0, function* () { }); }, }; function doPlot(plotter, plan) { return __awaiter(this, void 0, void 0, function* () { cancelRequested = false; unpaused = null; signalUnpause = null; motionIdx = 0; const firstPenMotion = plan.motions.find((x) => x instanceof planning_1.PenMotion); yield plotter.prePlot(firstPenMotion.initialPos); let penIsUp = true; for (const motion of plan.motions) { broadcast({ c: "progress", p: { motionIdx } }); yield plotter.executeMotion(motion, [motionIdx, plan.motions.length]); if (motion instanceof planning_1.PenMotion) { penIsUp = motion.initialPos < motion.finalPos; } if (unpaused && penIsUp) { yield unpaused; broadcast({ c: "pause", p: { paused: false } }); } if (cancelRequested) { break; } motionIdx += 1; } motionIdx = null; currentPlan = null; if (cancelRequested) { yield plotter.postCancel(); broadcast({ c: "cancelled" }); cancelRequested = false; } else { broadcast({ c: "finished" }); } yield plotter.postPlot(); }); } return new Promise((resolve) => { server.listen(port, () => { function connect() { return __awaiter(this, void 0, void 0, function* () { var _a, e_1, _b, _c; const devices = ebbs(com, hardware); try { for (var _d = true, devices_1 = __asyncValues(devices), devices_1_1; devices_1_1 = yield devices_1.next(), _a = devices_1_1.done, !_a; _d = true) { _c = devices_1_1.value; _d = false; const device = _c; ebb = device; broadcast({ c: 'dev', p: getDeviceInfo(ebb, com) }); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (!_d && !_a && (_b = devices_1.return)) yield _b.call(devices_1); } finally { if (e_1) throw e_1.error; } } }); } connect(); const { family, address, port } = server.address(); const addr = `${family === "IPv6" ? `[${address}]` : address}:${port}`; console.log(`Server listening on http://${addr}`); resolve(server); }); }); }); } function tryOpen(com) { return __awaiter(this, void 0, void 0, function* () { const port = new serialport_serialport_1.SerialPortSerialPort(com); yield port.open({ baudRate: 9600 }); return port; }); } function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } function isEBB(p) { return p.manufacturer === "SchmalzHaus" || p.manufacturer === "SchmalzHaus LLC" || (p.vendorId == "04D8" && p.productId == "FD92"); } function listEBBs() { return __awaiter(this, void 0, void 0, function* () { const Binding = (0, bindings_cpp_1.autoDetect)(); const ports = yield Binding.list(); return ports.filter(isEBB).map((p) => p.path); }); } function waitForEbb() { return __awaiter(this, void 0, void 0, function* () { // eslint-disable-next-line no-constant-condition while (true) { const ebbs = yield listEBBs(); if (ebbs.length) { return ebbs[0]; } yield sleep(5000); } }); } function ebbs(path_1) { return __asyncGenerator(this, arguments, function* ebbs_1(path, hardware = 'v3') { while (true) { try { const com = path || (yield __await(_self.waitForEbb())); // use self-import for test mocking console.log(`Found EBB at ${com}`); const port = yield __await(tryOpen(com)); const closed = new Promise((resolve) => { port.addEventListener('disconnect', resolve, { once: true }); }); yield yield __await(new ebb_1.EBB(port, hardware)); yield __await(closed); yield yield __await(null); console.error("Lost connection to EBB, reconnecting..."); } catch (e) { console.error(`Error connecting to EBB: ${e.message}`); console.error("Retrying in 5 seconds..."); yield __await(sleep(5000)); } } }); } function connectEBB() { return __awaiter(this, arguments, void 0, function* (hardware = 'v3', device) { if (!device) { const ebbs = yield listEBBs(); if (ebbs.length === 0) return null; device = ebbs[0]; } const port = yield tryOpen(device); return new ebb_1.EBB(port, hardware); }); } //# sourceMappingURL=server.js.map