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.
This commit is contained in:
Matt McWilliams 2023-02-25 11:59:56 -05:00
parent e64277e438
commit 1290a8f324
15 changed files with 465 additions and 85 deletions

View File

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

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

@ -7,6 +7,7 @@ const ws_1 = require("ws");
const express_1 = __importDefault(require("express")); const express_1 = __importDefault(require("express"));
const promises_1 = require("fs/promises"); const promises_1 = require("fs/promises");
const mime_1 = __importDefault(require("mime")); const mime_1 = __importDefault(require("mime"));
const uuid_1 = require("uuid");
const Log = require("log"); const Log = require("log");
class Server { class Server {
constructor() { constructor() {
@ -25,6 +26,8 @@ class Server {
this.port = 9900; this.port = 9900;
this.wsPort = 9901; this.wsPort = 9901;
this.proxy = {}; this.proxy = {};
this.queue = {};
this.intervalPeriod = 10000; //10 sec
this.init(); this.init();
} }
async init() { async init() {
@ -57,18 +60,28 @@ class Server {
this.log.error(err); this.log.error(err);
return; return;
} }
this.wss.on('connection', (ws) => { this.wss.on('connection', async function (ws) {
this.log.info(`Client connected to WebSocketServer`); ws.on("message", function (data) {
console.dir(ws); let obj = JSON.parse(data);
ws.send('mcopy'); this.log.info(data);
}); if (obj.id && this.queue[obj.id]) {
this.queue[obj.id](obj);
delete this.queue[obj.id];
}
}.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} ]`); this.log.info(`WSS [ ws://localhost:${this.wsPort} ]`);
} }
async startHttp() { async startHttp() {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
this.httpd = this.http.listen(this.port, function () { this.httpd = this.http.listen(this.port, function () {
this.isActive = true; this.log.info(`HTTP server started!`);
this.log.info(`Server started!`);
this.log.info(`URL [ http://localhost:${this.port} ]`); this.log.info(`URL [ http://localhost:${this.port} ]`);
return resolve(true); return resolve(true);
}.bind(this)); }.bind(this));
@ -77,16 +90,24 @@ class Server {
async start() { async start() {
await this.startHttp(); await this.startHttp();
await this.startWss(); await this.startWss();
this.interval = setInterval(async function () {
await this.cmdAll('ping');
}.bind(this), this.intervalPeriod);
this.isActive = true;
} }
async stop() { async stopHttp() {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
return this.httpd.close(function () { return this.httpd.close(function () {
this.isActive = false; this.log.info(`HTTP server stopped :(`);
this.log.info(`Server stopped :(`);
return resolve(false); return resolve(false);
}.bind(this)); }.bind(this));
}.bind(this)); }.bind(this));
} }
async stop() {
await this.stopHttp();
clearInterval(this.interval);
this.isActive = false;
}
index(req, res, next) { index(req, res, next) {
const html = this.template('index', { PORT: `${this.port}` }); const html = this.template('index', { PORT: `${this.port}` });
this.log.info('GET /'); this.log.info('GET /');
@ -122,12 +143,43 @@ class Server {
}.bind(this)); }.bind(this));
} }
addProxy(key, filePath) { addProxy(key, filePath) {
//wipe every time
this.proxy = {};
this.proxy[key] = { this.proxy[key] = {
path: filePath, path: filePath,
mime: mime_1.default.getType(filePath) mime: mime_1.default.getType(filePath)
}; };
this.log.info(`Added proxy image [${key}]`); this.log.info(`Added proxy image [${key}]`);
} }
async cmdAll(action, options = {}) {
const cmds = [];
if (this.isActive && this.wss.clients.size > 0) {
this.wss.clients.forEach(function (ws) {
cmds.push(this.cmd(ws, action, options));
}.bind(this));
return await Promise.all(cmds);
}
return false;
}
/**
* 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 = function () { module.exports = function () {
return new Server(); return new Server();

File diff suppressed because one or more lines are too long

2
app/package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "mcopy-app", "name": "mcopy-app",
"version": "1.7.9", "version": "1.7.10",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {

View File

@ -1,6 +1,6 @@
{ {
"name": "mcopy-app", "name": "mcopy-app",
"version": "1.7.9", "version": "1.7.10",
"description": "GUI for the mcopy small gauge film optical printer platform", "description": "GUI for the mcopy small gauge film optical printer platform",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

View File

@ -3,6 +3,9 @@
<style> <style>
html, body{ background: #000; padding: 0; margin: 0; width: 100vw; height: 100vh; overflow: hidden; } html, body{ background: #000; padding: 0; margin: 0; width: 100vw; height: 100vh; overflow: hidden; }
body.meter { background: rgb(117, 117, 117); } body.meter { background: rgb(117, 117, 117); }
body.blank { background: #000; }
body.blank #img { display: none; }
body.blank #can { display: none; }
#nosleep { color: #fff; margin: 50px auto } #nosleep { color: #fff; margin: 50px auto }
#img { #img {
position: absolute; position: absolute;

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
{ {
"version": "1.7.9", "version": "1.7.10",
"ext_port": 1111, "ext_port": 1111,
"profiles": { "profiles": {
"mcopy": { "mcopy": {

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "mcopy", "name": "mcopy",
"version": "1.7.9", "version": "1.7.10",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mcopy", "name": "mcopy",
"version": "1.7.9", "version": "1.7.10",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"arduino": "file:app/lib/arduino", "arduino": "file:app/lib/arduino",

View File

@ -1,6 +1,6 @@
{ {
"name": "mcopy", "name": "mcopy",
"version": "1.7.9", "version": "1.7.10",
"description": "Small gauge film optical printer platform", "description": "Small gauge film optical printer platform",
"main": "build.js", "main": "build.js",
"directories": { "directories": {

View File

@ -1,5 +1,5 @@
{ {
"version": "1.7.9", "version": "1.7.10",
"ext_port": 1111, "ext_port": 1111,
"profiles": { "profiles": {
"mcopy": { "mcopy": {

View File

@ -2,7 +2,9 @@ import WebSocket, { WebSocketServer } from 'ws'
import express, { Express, Request, Response, Application } from 'express' import express, { Express, Request, Response, Application } from 'express'
import { readFile } from 'fs/promises' import { readFile } from 'fs/promises'
import mime from 'mime' import mime from 'mime'
import { v4 as uuidv4 } from 'uuid'
import Log = require('log') import Log = require('log')
import { delay } from 'delay';
interface ServerData { interface ServerData {
[key: string]: string; [key: string]: string;
@ -24,6 +26,10 @@ interface ServerProxyList {
[key: string]: ServerProxy; [key: string]: ServerProxy;
} }
interface ServerQueue {
[key: string]: Function;
}
class Server { class Server {
private id : string = 'server' private id : string = 'server'
public isActive : boolean = false public isActive : boolean = false
@ -44,10 +50,12 @@ class Server {
private port : number = 9900 private port : number = 9900
private wsPort : number = 9901 private wsPort : number = 9901
private proxy : ServerProxyList = {} private proxy : ServerProxyList = {}
private queue : ServerQueue = {}
private interval : ReturnType<typeof setInterval>
private intervalPeriod : number = 10000 //10 sec
constructor () { constructor () {
this.init() this.init()
} }
async init () { async init () {
@ -84,17 +92,32 @@ class Server {
this.log.error(err) this.log.error(err)
return return
} }
this.wss.on('connection', (ws) => { this.wss.on('connection', async function (ws : WebSocket) {
this.log.info(`Client connected to WebSocketServer`) ws.on("message", function (data : string ) {
ws.send(JSON.stringify({ action : 'mcopy' }); 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]
}
}.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} ]`) this.log.info(`WSS [ ws://localhost:${this.wsPort} ]`)
} }
async startHttp () { async startHttp () {
return new Promise(function (resolve : Function, reject : Function) { return new Promise(function (resolve : Function, reject : Function) {
this.httpd = this.http.listen(this.port, function () { this.httpd = this.http.listen(this.port, function () {
this.log.info(`Server started!`) this.log.info(`HTTP server started!`)
this.log.info(`URL [ http://localhost:${this.port} ]`) this.log.info(`URL [ http://localhost:${this.port} ]`)
return resolve(true) return resolve(true)
}.bind(this)) }.bind(this))
@ -104,19 +127,27 @@ class Server {
async start () { async start () {
await this.startHttp() await this.startHttp()
await this.startWss() await this.startWss()
this.interval = setInterval(async function () {
await this.cmdAll('ping');
}.bind(this), this.intervalPeriod)
this.isActive = true this.isActive = true
} }
async stop() { async stopHttp() {
return new Promise(function (resolve : Function, reject : Function) { return new Promise(function (resolve : Function, reject : Function) {
return this.httpd.close(function () { return this.httpd.close(function () {
this.isActive = false this.log.info(`HTTP server stopped :(`)
this.log.info(`Server stopped :(`)
return resolve(false) return resolve(false)
}.bind(this)) }.bind(this))
}.bind(this)) }.bind(this))
} }
async stop () {
await this.stopHttp()
clearInterval(this.interval)
this.isActive = false
}
index (req : Request, res : Response, next : Function) { index (req : Request, res : Response, next : Function) {
const html : string = this.template('index', { PORT : `${this.port}` }) const html : string = this.template('index', { PORT : `${this.port}` })
this.log.info('GET /') this.log.info('GET /')
@ -152,12 +183,48 @@ class Server {
} }
public addProxy (key : string, filePath : string) { public addProxy (key : string, filePath : string) {
//wipe every time
this.proxy = {}
this.proxy[key] = { this.proxy[key] = {
path : filePath, path : filePath,
mime : mime.getType(filePath) mime : mime.getType(filePath)
} }
this.log.info(`Added proxy image [${key}]`) this.log.info(`Added proxy image [${key}]`)
} }
public async cmdAll (action : string, options : any = {}) {
const cmds : any[] = []
if (this.isActive && this.wss.clients.size > 0) {
this.wss.clients.forEach(function (ws : WebSocket) {
cmds.push(this.cmd(ws, action, options))
}.bind(this))
return await Promise.all(cmds)
}
return false
}
/**
* 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 () { module.exports = function () {