Compare commits

..

13 Commits

Author SHA1 Message Date
Matt McWilliams a57519adce Merge pull request 'Merge all work on server with filmout features' (#77) from server into main
Reviewed-on: #77
2023-02-25 21:16:28 -05:00
Matt McWilliams 02639466ee By doing a check for active clients during the sequencer start and stop actions, the local display is no longer used. Resolves first part of #20. 2023-02-25 17:00:11 -05:00
Matt McWilliams 6e2795d380 Remove ACK messages for ping, rely on 2023-02-25 16:33:55 -05:00
Matt McWilliams 8e35596088 Fixed the iOS issue. Also allow for fullscreen on all browsers (with no sleep). Can preview properly but normal mode does not work 2023-02-25 16:32:08 -05:00
Matt McWilliams 0162d012c5 Server can now display images and will completely preempt local display when done 2023-02-25 13:22:20 -05:00
Matt McWilliams 47fb673b78 Combined script and html into single request (problems with iOS safari).
Can get script loaded but still cannot connect to websocket server without SSL.

ALSO: Now can pre-empt opening new display if an active client is connected to server.
Otherwise will open a local link.
2023-02-25 13:11:40 -05:00
Matt McWilliams f296488bc2 Further progress on #20. TODO: Image display logic on the server side. Need to add image to proxy list and give it a properly-extensioned name and cmdAll.
ALSO: Server is not working on iOS.
2023-02-25 12:40:35 -05:00
Matt McWilliams f5392aea9f Resolves #74 2023-02-25 12:33:18 -05:00
Matt McWilliams 24b1301f9f Adding contextIsolation: false to the display module fixes the initial issue in #74, cannot use "require" but the escape button does not work due to "Cannot read properties of undefined (reading 'getCurrentWindow')" 2023-02-25 12:16:47 -05:00
Matt McWilliams 1290a8f324 Major progress on #20. Promised-based websocket command structure is completed. All functionality needs to be placed into individual classes and any new functions get added to the server.
Will create entire thin client using this method.
2023-02-25 11:59:56 -05:00
Matt McWilliams e64277e438 More work on #20. Fix typos in display code. Should re-work script into transpiled ts file. 2023-02-25 02:08:56 -05:00
Matt McWilliams c9bcb74a9f Progress on #20. Can start up a server and serve client.js 2023-02-25 02:08:05 -05:00
Matt McWilliams 42db1f81b8 Server work 2023-02-25 00:24:07 -05:00
33 changed files with 2451 additions and 164 deletions

View File

@ -1,5 +1,5 @@
{
"version": "1.7.7",
"version": "1.7.18",
"ext_port": 1111,
"profiles": {
"mcopy": {

View File

@ -13,6 +13,16 @@
body.meter {
background: rgb(117, 117, 117);
}
body.meter #img,
body.meter #can {
display: none;
}
body.image #can{
display: none;
}
body.image #img {
display: block;
}
#img {
position: absolute;
/*background-image: url(".../img/background.jpg");*/
@ -46,16 +56,16 @@
</canvas>
<script>
'use strict';
const { remote, ipcRenderer } = require('electron')
const { ipcRenderer } = require('electron')
const remote = require('@electron/remote')
let imgTmp;
async function setImage (src) {
return new Promise(async (resolve, reject) => {
imgTmp = new Image()
let img = document.getElementById('img')
let body = document.querySelector('body')
if (body.classList.contains('meter')) {
body.classList.remove('meter')
}
body.className = ''
body.classList.add('image')
imgTmp.onload = function () {
img.style.backgroundImage = `url('${src}')`;
return resolve(src);
@ -66,15 +76,16 @@
async function onMeter () {
console.log('meter')
const body = document.querySelector('body')
if (!body.classList.contains('meter')) {
body.className = ''
body.classList.add('meter')
}
}
async function onFocus () {
console.log('focus')
const can = document.getElementById('can')
const dpr = window.devicePixelRatio || 1
const body = document.querySelector('body')
let ctx;
body.className = ''
if (!can.classList.contains('show')) {
can.classList.add('show')
}
@ -122,7 +133,9 @@
const can = document.getElementById('can')
const dpr = window.devicePixelRatio || 1
const screen = window.outerWidth / window.outerHeight
const body = document.querySelector('body')
let ctx;
body.className = ''
if (!can.classList.contains('show')) {
can.classList.add('show')
}
@ -145,7 +158,7 @@
can.style.width = `${window.innerHeight * arg.ratio}px`
can.style.height = `${window.innerHeight}px`
} else {
can.style.width = `${window.inneWidth}px`
can.style.width = `${window.innerWidth}px`
can.style.height = `${window.innerWidth / arg.ratio}px`
}
} else {
@ -217,9 +230,11 @@
}
return event.returnValue = true
}
async function onEscape (evt) {
let isEscape = false
let win
evt = evt || window.event
if ('key' in evt) {
@ -227,6 +242,7 @@
} else {
isEscape = (evt.keyCode == 27)
}
if (isEscape) {
win = remote.getCurrentWindow()
win.close()

287
app/lib/client/index.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
/**
* Delay in an async/await function
*
* @param {integer} ms Milliseconds to delay for
*
* @returns {Promise} Promise to resolve after timeout
**/
declare function delay(ms: number): Promise<unknown>;

View File

@ -17,7 +17,8 @@ class WebView {
webPreferences: {
nodeIntegration: true,
allowRunningInsecureContent: false,
enableRemoteModule: true
enableRemoteModule: true,
contextIsolation: false
},
width: 800,
height: 600,
@ -35,6 +36,7 @@ class WebView {
prefs.y = display.y + 50;
}
this.digitalWindow = new BrowserWindow(prefs);
require('@electron/remote/main').enable(this.digitalWindow.webContents);
this.digitalWindow.loadURL(pageUrl);
if (process.argv.indexOf('-d') !== -1 || process.argv.indexOf('--dev') !== -1) {
this.digitalWindow.webContents.openDevTools();

File diff suppressed because one or more lines are too long

View File

@ -23,7 +23,7 @@ class FilmOut {
* @param {object} ui Electron ui object
* @param {object} light Light device object
**/
constructor(display, ffmpeg, ffprobe, ui, light) {
constructor(display, server, ffmpeg, ffprobe, ui, light) {
this.id = 'filmout';
this.videoExtensions = ['.mpg', '.mpeg', '.mov', '.mkv', '.avi', '.mp4'];
this.stillExtensions = ['.tif', '.tiff', '.png', '.jpg', '.jpeg', '.bmp'];
@ -42,6 +42,7 @@ class FilmOut {
files: []
};
this.display = display;
this.server = server;
this.ffmpeg = ffmpeg;
this.ffprobe = ffprobe;
this.ui = ui;
@ -123,6 +124,10 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true);
throw err;
}
if (this.server.displayImage(path)) {
await delay_1.delay(20);
return;
}
await this.display.show(path);
await delay_1.delay(20);
}
@ -411,6 +416,9 @@ class FilmOut {
throw err;
}
try {
if (await this.server.displayImage(path)) {
return;
}
await this.display.open();
await this.display.show(path);
}
@ -424,6 +432,9 @@ class FilmOut {
async focus(evt, arg) {
this.log.info(`Showing focus screen`);
try {
if (await this.server.cmdAll('focus')) {
return;
}
await this.display.open();
await this.display.focus();
}
@ -438,6 +449,9 @@ class FilmOut {
const ratio = arg.ratio;
this.log.info(`Showing field guide screen`);
try {
if (await this.server.cmdAll('field', { ratio })) {
return;
}
await this.display.open();
await this.display.field(ratio);
}
@ -451,6 +465,9 @@ class FilmOut {
async meter(evt, arg) {
this.log.info(`Showing meter screen`);
try {
if (await this.server.cmdAll('meter')) {
return;
}
await this.display.open();
await this.display.meter();
}
@ -463,6 +480,9 @@ class FilmOut {
**/
async close(evt, arg) {
try {
if (await this.server.cmdAll('blank')) {
return;
}
await this.display.hide();
await this.display.close();
}
@ -478,7 +498,7 @@ class FilmOut {
this.log.info(`Changing the display to ${arg.display}`);
}
}
module.exports = (display, ffmpeg, ffprobe, ui, light) => {
return new FilmOut(display, ffmpeg, ffprobe, ui, light);
module.exports = (display, server, ffmpeg, ffprobe, ui, light) => {
return new FilmOut(display, server, ffmpeg, ffprobe, ui, light);
};
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@ -50,7 +50,7 @@ async function logFile() {
*
* @returns {object} Logger transport
**/
module.exports = async function (arg) {
module.exports = async function Log(arg) {
let consoleFormat = {
colorize: true
};

View File

@ -1 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/log/index.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/log/index.ts"],"names":[],"mappings":"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"}

File diff suppressed because one or more lines are too long

View File

@ -146,7 +146,7 @@ class Sequencer {
//start sequence
this.log.info(`Starting sequence...`);
this.ui.send(this.id, { start: true });
if (this.cmd.proj.filmout.state.enabled === true) {
if (this.cmd.proj.filmout.state.enabled === true && !this.cmd.proj.filmout.server.useServer()) {
await this.cmd.proj.filmout.display.open();
}
for (let x = 0; x < this.loops; x++) {
@ -179,7 +179,7 @@ class Sequencer {
this.log.info(`Ended loop ${x + 1}`);
this.ui.send(this.id, { loop: x, stop: true });
}
if (this.cmd.proj.filmout.state.enabled === true) {
if (this.cmd.proj.filmout.state.enabled === true && !this.cmd.proj.filmout.server.useServer()) {
await this.cmd.proj.filmout.display.close();
}
electron_1.powerSaveBlocker.stop(psbId);

File diff suppressed because one or more lines are too long

1
app/lib/server/index.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export {};

View File

@ -1,90 +1,197 @@
'use script'
let os
let restify
let app
let cam
let proj
let light
const PACKAGE = require('../../package.json')
class Server {
constructor (camera, projector, light) {
restify = require('restify')
os = require('os')
app = restify.createServer({
name: 'mcopy-server',
version: PACKAGE.version
})
this.ip = this.getIp()
/*app.get('/', function (req, res) {
mcopy.mobile.log('Device connected');
res.send(fs.readFileSync('tmpl/mcopy_index.html', 'utf8'));
})
app.get('/js/mcopy_mobile.js', function (req, res) {
res.send(fs.readFileSync('js/mcopy_mobile.js', 'utf8'));
});
app.get('/js/jquery.js', function (req, res) {
res.send(fs.readFileSync('js/jquery.js', 'utf8'));
});
app.get('/cmd/:cmd', function (req, res) {
var cmd,
success = function (res) {
var obj = {
success: true,
cmd : cmd,
cam: {
dir : cam.dir,
pos : cam.pos
},
proj: {
dir : proj.dir,
pos : proj.pos
}
}
res.json(obj);
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
if (typeof req.params.cmd !== 'undefined') {
mcopy.log('Receiving command from mobile: ' + req.params.cmd);
cmd = req.params.cmd;
if (cmd === 'CF'){
mcopy.cmd.camera_forward(success);
} else if (cmd === 'CB') {
mcopy.cmd.camera_backward(success);
} else if (cmd === 'PF') {
mcopy.cmd.projector_forward(success);
} else if (cmd === 'PB') {
mcopy.cmd.projector_backward(success);
} else {
mcopy.mobile.fail(res, 'Command ' + cmd + ' not found');
}
} else {
mcopy.mobile.fail(res, 'No command provided');
}
});
app.get('/state', function (req, res) {
res.json({
cam: {
dir : cam.dir,
pos : cam.pos
Object.defineProperty(exports, "__esModule", { value: true });
const ws_1 = require("ws");
const express_1 = __importDefault(require("express"));
const promises_1 = require("fs/promises");
const path_1 = require("path");
const uuid_1 = require("uuid");
const Log = require("log");
class Server {
constructor() {
this.id = 'server';
this.isActive = false;
this.templates = [
{
name: 'index',
path: 'server.html'
},
proj: {
dir : proj.dir,
pos : proj.pos
{
name: 'script',
path: 'lib/client/index.js'
}
];
this.port = 9900;
this.wsPort = 9901;
this.proxy = {};
this.queue = {};
this.intervalPeriod = 10000; //10 sec
this.init();
}
async init() {
this.log = await Log({ label: this.id });
await this.load();
await this.start();
}
async load() {
this.http = express_1.default();
for (let tmpl of this.templates) {
tmpl.data = await promises_1.readFile(tmpl.path, 'utf8');
}
this.http.get('/', this.index.bind(this));
this.http.get('/image/:key', this.image.bind(this));
this.log.info("Server assets loaded");
}
template(name, data) {
let html = this.templates.find(el => el.name === name).data;
for (let key of Object.keys(data)) {
html = html.replace(`{{${key}}}`, data[key]);
}
return html;
}
async startWss() {
try {
this.wss = new ws_1.WebSocketServer({ port: this.wsPort });
}
catch (err) {
this.log.error(err);
return;
}
this.wss.on('connection', async function (ws) {
ws.on("message", function (data) {
let obj = JSON.parse(data);
//this.log.info(data)
if (obj.id && this.queue[obj.id]) {
this.queue[obj.id](obj);
delete this.queue[obj.id];
if (obj.action !== 'ping') {
this.log.info(`${obj.action} ACK`);
}
}
}.bind(this));
ws.on('close', function () {
this.log.info('Client disconnected');
}.bind(this));
await this.cmd(ws, 'mcopy');
this.log.info('Client connected');
}.bind(this));
this.log.info(`Websocket server started!`);
this.log.info(`WSS [ ws://localhost:${this.wsPort} ]`);
}
async startHttp() {
return new Promise(function (resolve, reject) {
this.httpd = this.http.listen(this.port, function () {
this.log.info(`HTTP server started!`);
this.log.info(`URL [ http://localhost:${this.port} ]`);
return resolve(true);
}.bind(this));
}.bind(this));
}
async start() {
await this.startHttp();
await this.startWss();
this.interval = setInterval(async function () {
await this.cmdAll('ping');
}.bind(this), this.intervalPeriod);
this.isActive = true;
}
async stopHttp() {
return new Promise(function (resolve, reject) {
return this.httpd.close(function () {
this.log.info(`HTTP server stopped :(`);
return resolve(false);
}.bind(this));
}.bind(this));
}
async stop() {
await this.stopHttp();
clearInterval(this.interval);
this.isActive = false;
}
index(req, res, next) {
const SCRIPT = this.template('script', { PORT: `${this.wsPort}` });
const html = this.template('index', { SCRIPT });
this.log.info('GET /');
return res.send(html);
}
async image(req, res, next) {
let filePath;
if (req.params && req.params.key) {
if (this.proxy[req.params.key]) {
filePath = this.proxy[req.params.key].path;
}
else {
return false;
}
}
else {
return false;
}
return new Promise(function (resolve, reject) {
return res.sendFile(filePath, function (err) {
if (err) {
res.status(err.status).end();
return reject(err);
}
return resolve(true);
});
});*/
}.bind(this));
}
end () {
app.close()
app = null
addProxy(key, filePath) {
//wipe every time
this.proxy = {};
this.proxy[key] = {
path: filePath
};
this.log.info(`Added proxy image [${key}]`);
}
async cmdAll(action, options = {}) {
const cmds = [];
if (this.useServer()) {
this.wss.clients.forEach(function (ws) {
cmds.push(this.cmd(ws, action, options));
}.bind(this));
await Promise.all(cmds);
return true;
}
return false;
}
async displayImage(src) {
let key;
if (this.useServer()) {
key = path_1.basename(src);
this.addProxy(key, src);
await this.cmdAll('image', { image: `/image/${key}` });
return true;
}
return false;
}
useServer() {
return this.isActive && this.wss.clients.size > 0;
}
/**
* WSS
**/
async cmd(ws, action, options = {}) {
const id = uuid_1.v4();
let obj = {
id, action
};
let str;
obj = Object.assign(obj, options);
str = JSON.stringify(obj);
ws.send(str);
return new Promise(function (resolve, reject) {
this.queue[id] = function (obj) {
return resolve(obj);
};
//setTimeout() ?
}.bind(this));
}
}
module.exports = Server
module.exports = function () {
return new Server();
};
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@ -39,6 +39,7 @@ let cmd;
let seq;
let capper;
let alert;
let server;
const cfg = require('./data/cfg.json')
@ -116,8 +117,9 @@ var init = async function () {
log.error('Error enumerating connected devices', err)
}
server = require('server')()
light = require('light')(arduino, cfg, mainWindow.webContents)
filmout = require('filmout')(display, ffmpeg, ffprobe, mainWindow.webContents, light)
filmout = require('filmout')(display, server, ffmpeg, ffprobe, mainWindow.webContents, light)
cam = require('cam')(arduino, cfg, mainWindow.webContents, filmout)
proj = require('proj')(arduino, cfg, mainWindow.webContents, filmout)
alert = require('alert')(mainWindow.webContents)
@ -135,7 +137,6 @@ var init = async function () {
cmd = require('cmd')(cfg, proj, cam, light, alert, cam2, proj2, capper)
seq = require('sequencer')(cfg, cmd, mainWindow.webContents)
}
app.on('ready', init)

1167
app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "mcopy-app",
"version": "1.7.7",
"version": "1.7.18",
"description": "GUI for the mcopy small gauge film optical printer platform",
"main": "main.js",
"scripts": {
@ -64,6 +64,7 @@
"display": "file:lib/display",
"exec": "file:lib/exec",
"exit": "file:lib/exit",
"express": "^4.18.2",
"ffmpeg": "file:lib/ffmpeg",
"ffmpeg-static": "^5.0.0",
"ffprobe": "file:lib/ffprobe",
@ -89,7 +90,8 @@
"spawn": "file:lib/spawn",
"system": "file:lib/system",
"uuid": "^8.3.2",
"winston": "^3.7.2"
"winston": "^3.7.2",
"ws": "^8.12.1"
},
"optionalDependencies": {
"electron-installer-debian": "^3.1.0"

39
app/server.html Normal file
View File

@ -0,0 +1,39 @@
<html>
<head>
<style>
html, body{ background: #000; padding: 0; margin: 0; width: 100vw; height: 100vh; overflow: hidden; }
body.meter { background: rgb(117, 117, 117); }
body.blank { background: #000; }
body.blank #img, body.blank #can, body.meter #img, body.meter #can, body.image #can { display: none; }
body.image #img, #can.show { display: block; }
#nosleep { color: #fff; margin: 50px auto; text-align: center; }
#img {
position: absolute;
background-repeat:no-repeat;
background-size: contain;
background-position: center;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
#can{
margin: 0;
background: #fff;
display: none;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
</style>
<div id="nosleep">Click to full screen and prevent sleep</div>
<div id="img">
</div>
<canvas id="can">
</canvas>
<script>
{{SCRIPT}}
</script>
</body>
</html>

271
app/src/lib/client/index.ts Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
{
"version": "1.7.7",
"version": "1.7.18",
"ext_port": 1111,
"profiles": {
"mcopy": {

164
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "mcopy",
"version": "1.7.7",
"version": "1.7.18",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "mcopy",
"version": "1.7.7",
"version": "1.7.18",
"license": "MIT",
"dependencies": {
"arduino": "file:app/lib/arduino",
@ -28,12 +28,14 @@
},
"devDependencies": {
"@types/electron": "^1.6.10",
"@types/express": "^4.17.17",
"@types/fs-extra": "^9.0.7",
"@types/jimp": "^0.2.28",
"@types/node": "^14.14.31",
"@types/request": "^2.48.5",
"@types/sharp": "^0.27.1",
"@types/uuid": "^8.3.0",
"@types/ws": "^8.5.4",
"jsdoc-to-markdown": "^6.0.1",
"typescript": "^4.1.5"
}
@ -596,12 +598,31 @@
"node": ">=6"
}
},
"node_modules/@types/body-parser": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
"integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
"dev": true,
"dependencies": {
"@types/connect": "*",
"@types/node": "*"
}
},
"node_modules/@types/caseless": {
"version": "0.12.2",
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz",
"integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==",
"dev": true
},
"node_modules/@types/connect": {
"version": "3.4.35",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
"integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/electron": {
"version": "1.6.10",
"resolved": "https://registry.npmjs.org/@types/electron/-/electron-1.6.10.tgz",
@ -611,6 +632,29 @@
"electron": "*"
}
},
"node_modules/@types/express": {
"version": "4.17.17",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
"integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
"dev": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"node_modules/@types/express-serve-static-core": {
"version": "4.17.33",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz",
"integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==",
"dev": true,
"dependencies": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*"
}
},
"node_modules/@types/fs-extra": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.7.tgz",
@ -629,12 +673,30 @@
"jimp": "*"
}
},
"node_modules/@types/mime": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
"integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==",
"dev": true
},
"node_modules/@types/node": {
"version": "14.14.31",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.31.tgz",
"integrity": "sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==",
"dev": true
},
"node_modules/@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
"integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
"dev": true
},
"node_modules/@types/range-parser": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
"dev": true
},
"node_modules/@types/request": {
"version": "2.48.5",
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz",
@ -647,6 +709,16 @@
"form-data": "^2.5.0"
}
},
"node_modules/@types/serve-static": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz",
"integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==",
"dev": true,
"dependencies": {
"@types/mime": "*",
"@types/node": "*"
}
},
"node_modules/@types/sharp": {
"version": "0.27.1",
"resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.27.1.tgz",
@ -668,6 +740,15 @@
"integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==",
"dev": true
},
"node_modules/@types/ws": {
"version": "8.5.4",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
"integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/ansi-escape-sequences": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz",
@ -3284,12 +3365,31 @@
"defer-to-connect": "^1.0.1"
}
},
"@types/body-parser": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
"integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
"dev": true,
"requires": {
"@types/connect": "*",
"@types/node": "*"
}
},
"@types/caseless": {
"version": "0.12.2",
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz",
"integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==",
"dev": true
},
"@types/connect": {
"version": "3.4.35",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
"integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/electron": {
"version": "1.6.10",
"resolved": "https://registry.npmjs.org/@types/electron/-/electron-1.6.10.tgz",
@ -3299,6 +3399,29 @@
"electron": "*"
}
},
"@types/express": {
"version": "4.17.17",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
"integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
"dev": true,
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"@types/express-serve-static-core": {
"version": "4.17.33",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz",
"integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==",
"dev": true,
"requires": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*"
}
},
"@types/fs-extra": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.7.tgz",
@ -3317,12 +3440,30 @@
"jimp": "*"
}
},
"@types/mime": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
"integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==",
"dev": true
},
"@types/node": {
"version": "14.14.31",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.31.tgz",
"integrity": "sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==",
"dev": true
},
"@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
"integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
"dev": true
},
"@types/range-parser": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
"dev": true
},
"@types/request": {
"version": "2.48.5",
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz",
@ -3335,6 +3476,16 @@
"form-data": "^2.5.0"
}
},
"@types/serve-static": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz",
"integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==",
"dev": true,
"requires": {
"@types/mime": "*",
"@types/node": "*"
}
},
"@types/sharp": {
"version": "0.27.1",
"resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.27.1.tgz",
@ -3356,6 +3507,15 @@
"integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==",
"dev": true
},
"@types/ws": {
"version": "8.5.4",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
"integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"ansi-escape-sequences": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "mcopy",
"version": "1.7.7",
"version": "1.7.18",
"description": "Small gauge film optical printer platform",
"main": "build.js",
"directories": {
@ -26,12 +26,14 @@
"homepage": "https://github.com/sixteenmillimeter/mcopy#readme",
"devDependencies": {
"@types/electron": "^1.6.10",
"@types/express": "^4.17.17",
"@types/fs-extra": "^9.0.7",
"@types/jimp": "^0.2.28",
"@types/node": "^14.14.31",
"@types/request": "^2.48.5",
"@types/sharp": "^0.27.1",
"@types/uuid": "^8.3.0",
"@types/ws": "^8.5.4",
"jsdoc-to-markdown": "^6.0.1",
"typescript": "^4.1.5"
},
@ -47,8 +49,8 @@
"light": "file:app/lib/light",
"log": "file:app/lib/log",
"mscript": "file:app/lib/mscript",
"proj": "file:app/lib/proj",
"processing": "file:app/lib/processing",
"proj": "file:app/lib/proj",
"sequencer": "file:app/lib/sequencer",
"settings": "file:app/lib/settings",
"system": "file:app/lib/system"

View File

@ -1,5 +1,5 @@
{
"version": "1.7.7",
"version": "1.7.18",
"ext_port": 1111,
"profiles": {
"mcopy": {

View File

@ -8,7 +8,7 @@ echo "VERSION: $version"
git add ./package.json
declare -a fileArr=("./package-lock.json" "./data/cfg.json" "./app/package.json" "./app/package-lock.json" "./processing/mcopy/cfg.json")
declare -a fileArr=("./package-lock.json" "./data/cfg.json" "./app/package.json" "./app/package-lock.json" "./app/data/cfg.json" "./processing/mcopy/cfg.json")
for i in "${fileArr[@]}"
do

View File

@ -25,7 +25,8 @@ class WebView {
webPreferences: {
nodeIntegration: true,
allowRunningInsecureContent: false,
enableRemoteModule: true
enableRemoteModule: true,
contextIsolation: false
},
width: 800,
height: 600,
@ -43,6 +44,7 @@ class WebView {
prefs.y = display.y + 50;
}
this.digitalWindow = new BrowserWindow(prefs);
require('@electron/remote/main').enable(this.digitalWindow.webContents)
this.digitalWindow.loadURL(pageUrl);
if (process.argv.indexOf('-d') !== -1 || process.argv.indexOf('--dev') !== -1) {
this.digitalWindow.webContents.openDevTools();

View File

@ -37,6 +37,7 @@ class FilmOut {
private ipc : any;
private ui : any;
private log : any;
private server : any;
/**
* @constructor
* Builds FilmOut class with display, ffmpeg, ffprobe, ui and light as internal properties.
@ -47,8 +48,9 @@ class FilmOut {
* @param {object} ui Electron ui object
* @param {object} light Light device object
**/
constructor (display : any, ffmpeg : any, ffprobe : any, ui : any, light : any) {
constructor (display : any, server : any, ffmpeg : any, ffprobe : any, ui : any, light : any) {
this.display = display;
this.server = server;
this.ffmpeg = ffmpeg;
this.ffprobe = ffprobe;
this.ui = ui;
@ -133,6 +135,11 @@ class FilmOut {
throw err;
}
if (this.server.displayImage(path)) {
await delay(20)
return
}
await this.display.show(path);
await delay(20);
}
@ -430,6 +437,9 @@ class FilmOut {
}
try {
if (await this.server.displayImage(path)) {
return
}
await this.display.open();
await this.display.show(path);
} catch (err) {
@ -442,6 +452,9 @@ class FilmOut {
async focus (evt : any, arg : any) {
this.log.info(`Showing focus screen`);
try {
if (await this.server.cmdAll('focus')) {
return
}
await this.display.open();
await this.display.focus();
} catch (err) {
@ -455,8 +468,12 @@ class FilmOut {
const ratio : number = arg.ratio;
this.log.info(`Showing field guide screen`);
try {
if (await this.server.cmdAll('field', { ratio })) {
return
}
await this.display.open();
await this.display.field(ratio);
} catch (err) {
this.log.error(err, 'FILMOUT', true, true);
}
@ -467,6 +484,9 @@ class FilmOut {
async meter (evt : any, arg : any) {
this.log.info(`Showing meter screen`);
try {
if (await this.server.cmdAll('meter')) {
return
}
await this.display.open();
await this.display.meter();
} catch (err) {
@ -478,8 +498,12 @@ class FilmOut {
**/
async close (evt : any, arg : any) {
try {
if (await this.server.cmdAll('blank')) {
return
}
await this.display.hide();
await this.display.close();
} catch (err) {
this.log.error(err, 'FILMOUT', true, true);
}
@ -493,6 +517,6 @@ class FilmOut {
}
}
module.exports = (display : any, ffmpeg : any, ffprobe : any, ui : any, light : any) => {
return new FilmOut(display, ffmpeg, ffprobe, ui, light);
module.exports = (display : any, server : any, ffmpeg : any, ffprobe : any, ui : any, light : any) => {
return new FilmOut(display, server, ffmpeg, ffprobe, ui, light);
}

View File

@ -53,7 +53,7 @@ async function logFile () {
*
* @returns {object} Logger transport
**/
module.exports = async function (arg : any) {
module.exports = async function Log (arg : any) {
let consoleFormat : any = {
colorize : true
}

View File

@ -155,7 +155,7 @@ class Projector {
}
message += ' 1 frame'
} else if (cmd === this.cfg.arduino.cmd.projectors) {
message += 'Projectors both MOVED 1 frame each';
message += 'Projectors both MOVED 1 frame each'
}
message += ` ${ms}ms`
this.log.info(message, 'PROJECTOR')
@ -164,5 +164,5 @@ class Projector {
}
module.exports = function (arduino : Arduino, cfg : any, ui : any, filmout : any, second : boolean) {
return new Projector(arduino, cfg, ui, filmout, second);
return new Projector(arduino, cfg, ui, filmout, second)
}

View File

@ -168,7 +168,7 @@ class Sequencer {
this.log.info(`Starting sequence...`);
this.ui.send(this.id, { start : true });
if (this.cmd.proj.filmout.state.enabled === true) {
if (this.cmd.proj.filmout.state.enabled === true && !this.cmd.proj.filmout.server.useServer()) {
await this.cmd.proj.filmout.display.open();
}
@ -209,7 +209,7 @@ class Sequencer {
this.ui.send(this.id, { loop : x, stop : true });
}
if (this.cmd.proj.filmout.state.enabled === true) {
if (this.cmd.proj.filmout.state.enabled === true && !this.cmd.proj.filmout.server.useServer()) {
await this.cmd.proj.filmout.display.close();
}

244
src/server/index.ts Normal file
View File

@ -0,0 +1,244 @@
import WebSocket, { WebSocketServer } from 'ws'
import express, { Express, Request, Response, Application } from 'express'
import { readFile } from 'fs/promises'
import { basename } from 'path'
import { v4 as uuidv4 } from 'uuid'
import Log = require('log')
import { delay } from 'delay'
interface ServerData {
[key: string]: string;
PORT? : string;
SCRIPT? : string;
}
interface ServerTemplate {
name : string;
path : string;
data? : string;
}
interface ServerProxy {
path : string;
}
interface ServerProxyList {
[key: string]: ServerProxy;
}
interface ServerQueue {
[key: string]: Function;
}
class Server {
private id : string = 'server'
public isActive : boolean = false
private log : any
private templates : ServerTemplate[] = [
{
name :'index',
path : 'server.html'
},
{
name : 'script',
path : 'lib/client/index.js'
}
]
private http : Application
private httpd : Server
private wss : WebSocketServer
private port : number = 9900
private wsPort : number = 9901
private proxy : ServerProxyList = {}
private queue : ServerQueue = {}
private interval : ReturnType<typeof setInterval>
private intervalPeriod : number = 10000 //10 sec
constructor () {
this.init()
}
async init () {
this.log = await Log({ label : this.id });
await this.load()
await this.start()
}
async load () {
this.http = express()
for (let tmpl of this.templates) {
tmpl.data = await readFile(tmpl.path, 'utf8')
}
this.http.get('/', this.index.bind(this))
this.http.get('/image/:key', this.image.bind(this))
this.log.info("Server assets loaded")
}
template (name: string, data : ServerData) {
let html : string = this.templates.find(el => el.name === name).data
for (let key of Object.keys(data)) {
html = html.replace(`{{${key}}}`, data[key])
}
return html
}
async startWss () {
try {
this.wss = new WebSocketServer({ port: this.wsPort })
} catch (err) {
this.log.error(err)
return
}
this.wss.on('connection', async function (ws : WebSocket) {
ws.on("message", function (data : string ) {
let obj : any = JSON.parse(data)
//this.log.info(data)
if (obj.id && this.queue[obj.id]) {
this.queue[obj.id](obj)
delete this.queue[obj.id]
if (obj.action !== 'ping') {
this.log.info(`${obj.action} ACK`)
}
}
}.bind(this))
ws.on('close', function () {
this.log.info('Client disconnected')
}.bind(this))
await this.cmd(ws, 'mcopy')
this.log.info('Client connected')
}.bind(this))
this.log.info(`Websocket server started!`)
this.log.info(`WSS [ ws://localhost:${this.wsPort} ]`)
}
async startHttp () {
return new Promise(function (resolve : Function, reject : Function) {
this.httpd = this.http.listen(this.port, function () {
this.log.info(`HTTP server started!`)
this.log.info(`URL [ http://localhost:${this.port} ]`)
return resolve(true)
}.bind(this))
}.bind(this))
}
async start () {
await this.startHttp()
await this.startWss()
this.interval = setInterval(async function () {
await this.cmdAll('ping');
}.bind(this), this.intervalPeriod)
this.isActive = true
}
async stopHttp() {
return new Promise(function (resolve : Function, reject : Function) {
return this.httpd.close(function () {
this.log.info(`HTTP server stopped :(`)
return resolve(false)
}.bind(this))
}.bind(this))
}
async stop () {
await this.stopHttp()
clearInterval(this.interval)
this.isActive = false
}
index (req : Request, res : Response, next : Function) {
const SCRIPT = this.template('script', { PORT : `${this.wsPort}` })
const html : string = this.template('index', { SCRIPT })
this.log.info('GET /')
return res.send(html)
}
async image (req : Request, res : Response, next : Function) {
let filePath : string
if (req.params && req.params.key) {
if (this.proxy[req.params.key]) {
filePath = this.proxy[req.params.key].path
} else {
return false
}
} else {
return false
}
return new Promise(function (resolve : Function, reject: Function) {
return res.sendFile(filePath, function (err : any) {
if (err) {
res.status(err.status).end()
return reject(err)
}
return resolve(true)
})
}.bind(this))
}
public addProxy (key : string, filePath : string) {
//wipe every time
this.proxy = {}
this.proxy[key] = {
path : filePath
}
this.log.info(`Added proxy image [${key}]`)
}
public async cmdAll (action : string, options : any = {}) {
const cmds : any[] = []
if (this.useServer()) {
this.wss.clients.forEach(function (ws : WebSocket) {
cmds.push(this.cmd(ws, action, options))
}.bind(this))
await Promise.all(cmds)
return true
}
return false
}
public async displayImage (src : string) {
let key
if (this.useServer()) {
key = basename(src)
this.addProxy(key, src)
await this.cmdAll('image', { image : `/image/${key}` })
return true
}
return false
}
public useServer () : boolean {
return this.isActive && this.wss.clients.size > 0
}
/**
* WSS
**/
public async cmd (ws : WebSocket, action : string, options : any = {}) {
const id : string = uuidv4()
let obj = {
id, action
}
let str : string
obj = Object.assign(obj, options)
str = JSON.stringify(obj)
ws.send(str)
return new Promise(function (resolve : Function, reject: Function) {
this.queue[id] = function (obj : any) {
return resolve(obj)
}
//setTimeout() ?
}.bind(this))
}
}
module.exports = function () {
return new Server()
}