UI updates and improved client connection logic to allow for multiple machines.

This commit is contained in:
mmcwilliams 2024-08-24 16:26:49 -04:00
parent c33aaa2bec
commit 3c68496f5e
13 changed files with 146 additions and 34 deletions

View File

@ -139,7 +139,7 @@ class Client {
private progressText : HTMLElement; private progressText : HTMLElement;
constructor () { constructor () {
let uri : string = 'ws://localhost:8082'; let uri : string = this.getWebsocketUri();
this.progress = document.getElementById('progress') as HTMLProgressElement; this.progress = document.getElementById('progress') as HTMLProgressElement;
this.progressText = document.getElementById('progressText'); this.progressText = document.getElementById('progressText');
this.client = new WebSocket(uri); this.client = new WebSocket(uri);
@ -152,6 +152,14 @@ class Client {
(document.getElementById('manualCtrlForm') as HTMLFormElement ).reset(); (document.getElementById('manualCtrlForm') as HTMLFormElement ).reset();
this.disableClass('sequenceCtrl'); this.disableClass('sequenceCtrl');
this.disableClass('manualCtrl'); this.disableClass('manualCtrl');
this.setProgress({ hash: null, progress: 0 });
}
private getWebsocketUri () : string {
const host : string = (window.location.host + '').split(':')[0];
//WEBSOCKET_PORT defined on page via template
//@ts-ignore
return `ws://${host}:${WEBSOCKET_PORT}`
} }
private onMessage (event : any) { private onMessage (event : any) {
@ -245,6 +253,9 @@ class Client {
private cmd (msg : Message) { private cmd (msg : Message) {
switch (msg.cmd) { switch (msg.cmd) {
case 'ping' :
this.receivePing();
break;
case 'open' : case 'open' :
this.receiveCameraOpen(); this.receiveCameraOpen();
break; break;
@ -283,6 +294,10 @@ class Client {
this.client.send(JSON.stringify({ cmd : 'open' })); this.client.send(JSON.stringify({ cmd : 'open' }));
} }
private receivePing() {
this.sendPong();
}
private receiveCameraOpen () { private receiveCameraOpen () {
console.log('got camera open'); console.log('got camera open');
this.enableClass('manualCtrl'); this.enableClass('manualCtrl');
@ -299,6 +314,10 @@ class Client {
this.enableClass('manualCtrl'); this.enableClass('manualCtrl');
} }
private sendPong () {
this.client.send(JSON.stringify({ cmd : 'pong' }));
}
public sendAdvance () { public sendAdvance () {
this.client.send(JSON.stringify({ cmd : 'advance' })); this.client.send(JSON.stringify({ cmd : 'advance' }));
} }

View File

@ -8,7 +8,8 @@ export declare class Camera {
private next; private next;
private port; private port;
private prefix; private prefix;
constructor(); private mock;
constructor(mock: boolean);
private begin; private begin;
private filter; private filter;
private enumerate; private enumerate;

13
dist/camera/index.js vendored
View File

@ -53,7 +53,7 @@ class CameraSerialPortMock extends serialport_1.SerialPortMock {
} }
} }
class Camera { class Camera {
constructor() { constructor(mock) {
this.ready = false; this.ready = false;
this.connected = false; this.connected = false;
this.serial = null; this.serial = null;
@ -61,6 +61,8 @@ class Camera {
this.next = null; this.next = null;
this.port = null; this.port = null;
this.prefix = ''; this.prefix = '';
this.mock = false;
this.mock = mock;
this.log = (0, log_1.createLog)('camera'); this.log = (0, log_1.createLog)('camera');
this.parser = new parser_readline_1.ReadlineParser({ delimiter: '\r\n' }); this.parser = new parser_readline_1.ReadlineParser({ delimiter: '\r\n' });
this.begin(); this.begin();
@ -74,7 +76,7 @@ class Camera {
catch (err) { catch (err) {
this.log.error(this.prefix + 'Error calling enumerate()', err); this.log.error(this.prefix + 'Error calling enumerate()', err);
} }
if (ports.length > 0) { if (!this.mock && ports.length > 0) {
for (let port of ports) { for (let port of ports) {
this.log.info(this.prefix + `Found USB serial device: ${port} ${selected ? '*' : ''}`); this.log.info(this.prefix + `Found USB serial device: ${port} ${selected ? '*' : ''}`);
selected = false; selected = false;
@ -87,7 +89,12 @@ class Camera {
} }
} }
else { else {
this.log.warn(this.prefix + `No USB serial devices found, connecting to MOCK...`); if (this.mock) {
this.log.info(`Starting camera in MOCK mode due to system setting`);
}
else {
this.log.warn(this.prefix + `No USB serial devices found, connecting to MOCK...`);
}
try { try {
await this.connectMock(); await this.connectMock();
} }

File diff suppressed because one or more lines are too long

23
dist/index.js vendored
View File

@ -42,6 +42,7 @@ const ffprobe_1 = require("./ffprobe");
const camera_1 = require("./camera"); const camera_1 = require("./camera");
const sequence_1 = require("./sequence"); const sequence_1 = require("./sequence");
const image_1 = require("./image"); const image_1 = require("./image");
let mock = false;
const log = (0, log_1.createLog)('fm'); const log = (0, log_1.createLog)('fm');
const app = (0, express_1.default)(); const app = (0, express_1.default)();
let wss; let wss;
@ -167,6 +168,15 @@ async function settings() {
} }
log.info(`VIDEOS=${videos}`); log.info(`VIDEOS=${videos}`);
} }
if (typeof process.env['MOCK'] !== 'undefined') {
if (process.env['MOCK'].trim().toLowerCase() === "true" || process.env['MOCK'].trim() === '1') {
mock = true;
log.info(`MOCK=true`);
}
else {
mock = false;
}
}
} }
function onWssConnection(ws, req) { function onWssConnection(ws, req) {
let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
@ -193,6 +203,9 @@ async function onClientMessage(data, ws) {
async function cmd(msg) { async function cmd(msg) {
let success = false; let success = false;
switch (msg.cmd) { switch (msg.cmd) {
case 'pong':
//received keepalive
break;
case 'open': case 'open':
await cameraOpen(); await cameraOpen();
break; break;
@ -254,10 +267,13 @@ async function send(msg) {
client.send(msgStr); client.send(msgStr);
}); });
} }
async function keepAlive() {
await send({ cmd: 'ping' });
}
app.get('/', async (req, res, next) => { app.get('/', async (req, res, next) => {
const sequencesArr = await files_1.Files.enumerateSequences(sequences); const sequencesArr = await files_1.Files.enumerateSequences(sequences);
//const videosArr : VideoObject[] = await Files.enumerateVideos(videos); //const videosArr : VideoObject[] = await Files.enumerateVideos(videos);
const html = index({ sequences: sequencesArr, width, height }); const html = index({ sequences: sequencesArr, width, height, wsPort });
res.send(html); res.send(html);
}); });
app.get('/:width/:height/image.jpg', async (req, res, next) => { app.get('/:width/:height/image.jpg', async (req, res, next) => {
@ -294,9 +310,9 @@ async function main() {
ffmpeg = new ffmpeg_1.FFMPEG(process.env['FFMPEG']); ffmpeg = new ffmpeg_1.FFMPEG(process.env['FFMPEG']);
ffprobe = new ffprobe_1.FFPROBE(); ffprobe = new ffprobe_1.FFPROBE();
image = new image_1.Image(); image = new image_1.Image();
camera = new camera_1.Camera(); camera = new camera_1.Camera(mock);
display = new display_1.Display(width, height); display = new display_1.Display(width, height);
fd = new fd_1.FD(process.env['FD'], width, height, process.env['FD_HOST'], parseInt(process.env['FD_PORT']), true); fd = new fd_1.FD(process.env['FD'], width, height, process.env['FD_HOST'], parseInt(process.env['FD_PORT']), mock);
app.listen(port, async () => { app.listen(port, async () => {
log.info(`filmout_manager HTTP server running on port ${port}`); log.info(`filmout_manager HTTP server running on port ${port}`);
}); });
@ -306,6 +322,7 @@ async function main() {
//ffmpeg.listFormats(); //ffmpeg.listFormats();
//log.info(await TestImage.Focus(640, 480)); //log.info(await TestImage.Focus(640, 480));
sequence = new sequence_1.Sequence(camera, fd, display, ffprobe, send); sequence = new sequence_1.Sequence(camera, fd, display, ffprobe, send);
setInterval(keepAlive, 30000);
} }
main(); main();
//# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -78,8 +78,10 @@ export class Camera {
private next : Function = null; private next : Function = null;
private port : string = null; private port : string = null;
private prefix : string = ''; private prefix : string = '';
private mock : boolean = false;
constructor () { constructor (mock : boolean) {
this.mock = mock;
this.log = createLog('camera'); this.log = createLog('camera');
this.parser = new ReadlineParser({ delimiter: '\r\n' }); this.parser = new ReadlineParser({ delimiter: '\r\n' });
this.begin(); this.begin();
@ -93,7 +95,7 @@ export class Camera {
} catch (err) { } catch (err) {
this.log.error(this.prefix + 'Error calling enumerate()', err); this.log.error(this.prefix + 'Error calling enumerate()', err);
} }
if (ports.length > 0) { if (!this.mock && ports.length > 0) {
for (let port of ports) { for (let port of ports) {
this.log.info(this.prefix + `Found USB serial device: ${port} ${selected ? '*' : ''}`); this.log.info(this.prefix + `Found USB serial device: ${port} ${selected ? '*' : ''}`);
selected = false; selected = false;
@ -104,7 +106,11 @@ export class Camera {
this.log.error(this.prefix + `Error connecting to ${ports[0]}`, err); this.log.error(this.prefix + `Error connecting to ${ports[0]}`, err);
} }
} else { } else {
this.log.warn(this.prefix + `No USB serial devices found, connecting to MOCK...`) if (this.mock) {
this.log.info(`Starting camera in MOCK mode due to system setting`);
} else {
this.log.warn(this.prefix + `No USB serial devices found, connecting to MOCK...`)
}
try { try {
await this.connectMock(); await this.connectMock();
} catch (err) { } catch (err) {

View File

@ -29,6 +29,7 @@ import { Camera } from './camera';
import { Sequence } from './sequence'; import { Sequence } from './sequence';
import { Image } from './image'; import { Image } from './image';
let mock : boolean = false;
const log : Logger = createLog('fm'); const log : Logger = createLog('fm');
const app : Express = express(); const app : Express = express();
let wss : Server; let wss : Server;
@ -155,6 +156,14 @@ async function settings () {
} }
log.info(`VIDEOS=${videos}`); log.info(`VIDEOS=${videos}`);
} }
if (typeof process.env['MOCK'] !== 'undefined') {
if (process.env['MOCK'].trim().toLowerCase() === "true" || process.env['MOCK'].trim() === '1') {
mock = true;
log.info(`MOCK=true`);
} else {
mock = false;
}
}
} }
function onWssConnection (ws : WebSocketExtended, req : Request) { function onWssConnection (ws : WebSocketExtended, req : Request) {
@ -182,6 +191,9 @@ async function onClientMessage (data : any, ws : WebSocket) {
async function cmd (msg : Message) { async function cmd (msg : Message) {
let success : boolean = false let success : boolean = false
switch(msg.cmd) { switch(msg.cmd) {
case 'pong' :
//received keepalive
break;
case 'open' : case 'open' :
await cameraOpen(); await cameraOpen();
break; break;
@ -252,10 +264,14 @@ async function send (msg : Message) {
}); });
} }
async function keepAlive () {
await send({ cmd : 'ping' });
}
app.get('/', async (req : Request, res : Response, next : NextFunction) => { app.get('/', async (req : Request, res : Response, next : NextFunction) => {
const sequencesArr : SequenceObject[] = await Files.enumerateSequences(sequences); const sequencesArr : SequenceObject[] = await Files.enumerateSequences(sequences);
//const videosArr : VideoObject[] = await Files.enumerateVideos(videos); //const videosArr : VideoObject[] = await Files.enumerateVideos(videos);
const html : string = index({ sequences : sequencesArr, width, height }); const html : string = index({ sequences : sequencesArr, width, height, wsPort });
res.send(html); res.send(html);
}); });
@ -291,9 +307,9 @@ async function main () {
ffmpeg = new FFMPEG(process.env['FFMPEG']); ffmpeg = new FFMPEG(process.env['FFMPEG']);
ffprobe = new FFPROBE(); ffprobe = new FFPROBE();
image = new Image(); image = new Image();
camera = new Camera(); camera = new Camera(mock);
display = new Display(width, height); display = new Display(width, height);
fd = new FD(process.env['FD'], width, height, process.env['FD_HOST'], parseInt(process.env['FD_PORT']), true); fd = new FD(process.env['FD'], width, height, process.env['FD_HOST'], parseInt(process.env['FD_PORT']), mock);
app.listen(port, async () => { app.listen(port, async () => {
log.info(`filmout_manager HTTP server running on port ${port}`); log.info(`filmout_manager HTTP server running on port ${port}`);
@ -306,6 +322,7 @@ async function main () {
//ffmpeg.listFormats(); //ffmpeg.listFormats();
//log.info(await TestImage.Focus(640, 480)); //log.info(await TestImage.Focus(640, 480));
sequence = new Sequence(camera, fd, display, ffprobe, send); sequence = new Sequence(camera, fd, display, ffprobe, send);
setInterval(keepAlive, 30000);
} }

View File

@ -33,8 +33,10 @@ html, body{
} }
#frame{ #frame{
text-align: center; text-align: center !important;
font-family: monospace; font-family: monospace;
padding: 0;
text-indent: 5px;
} }
#display{ #display{
@ -61,13 +63,31 @@ fieldset.inline .field-row {
display: inline-block; display: inline-block;
} }
fieldset.inline .field-row input { fieldset.inline .field-row input{
max-width: 50px; max-width: 50px;
} }
fieldset.inline input.medium {
max-width: 70px;
}
progress { progress {
width: 97vw; width: 97vw;
position: absolute; position: absolute;
bottom: 28px; bottom: 28px;
left: 0.5vw; left: 0.5vw;
}
button.small{
width: 35px;
max-width: 35px;
min-width: 35px;
padding: 0 6px;
}
button.medium{
width: 50px;
max-width: 50px;
min-width: 50px;
padding: 0 6px;
} }

View File

@ -44,6 +44,7 @@ declare class Client {
private progress; private progress;
private progressText; private progressText;
constructor(); constructor();
private getWebsocketUri;
private onMessage; private onMessage;
private onOpen; private onOpen;
private onClose; private onClose;
@ -57,9 +58,11 @@ declare class Client {
disableClass(className: string): void; disableClass(className: string): void;
enableClass(className: string): void; enableClass(className: string): void;
sendCameraOpen(): void; sendCameraOpen(): void;
private receivePing;
private receiveCameraOpen; private receiveCameraOpen;
sendCameraClose(): void; sendCameraClose(): void;
private receiveCameraClose; private receiveCameraClose;
private sendPong;
sendAdvance(): void; sendAdvance(): void;
sendRewind(): void; sendRewind(): void;
sendSelect(): void; sendSelect(): void;

View File

@ -114,7 +114,7 @@ class Display {
class Client { class Client {
constructor() { constructor() {
this.connected = false; this.connected = false;
let uri = 'ws://localhost:8082'; let uri = this.getWebsocketUri();
this.progress = document.getElementById('progress'); this.progress = document.getElementById('progress');
this.progressText = document.getElementById('progressText'); this.progressText = document.getElementById('progressText');
this.client = new WebSocket(uri); this.client = new WebSocket(uri);
@ -127,6 +127,11 @@ class Client {
document.getElementById('manualCtrlForm').reset(); document.getElementById('manualCtrlForm').reset();
this.disableClass('sequenceCtrl'); this.disableClass('sequenceCtrl');
this.disableClass('manualCtrl'); this.disableClass('manualCtrl');
this.setProgress({ hash: null, progress: 0 });
}
getWebsocketUri() {
const host = (window.location.host + '').split(':')[0];
return `ws://${host}:${WEBSOCKET_PORT}`;
} }
onMessage(event) { onMessage(event) {
const msg = JSON.parse(event.data); const msg = JSON.parse(event.data);
@ -206,6 +211,9 @@ class Client {
} }
cmd(msg) { cmd(msg) {
switch (msg.cmd) { switch (msg.cmd) {
case 'ping':
this.receivePing();
break;
case 'open': case 'open':
this.receiveCameraOpen(); this.receiveCameraOpen();
break; break;
@ -240,6 +248,9 @@ class Client {
this.disableClass('manualCtrl'); this.disableClass('manualCtrl');
this.client.send(JSON.stringify({ cmd: 'open' })); this.client.send(JSON.stringify({ cmd: 'open' }));
} }
receivePing() {
this.sendPong();
}
receiveCameraOpen() { receiveCameraOpen() {
console.log('got camera open'); console.log('got camera open');
this.enableClass('manualCtrl'); this.enableClass('manualCtrl');
@ -253,6 +264,9 @@ class Client {
console.log('got camera close'); console.log('got camera close');
this.enableClass('manualCtrl'); this.enableClass('manualCtrl');
} }
sendPong() {
this.client.send(JSON.stringify({ cmd: 'pong' }));
}
sendAdvance() { sendAdvance() {
this.client.send(JSON.stringify({ cmd: 'advance' })); this.client.send(JSON.stringify({ cmd: 'advance' }));
} }

File diff suppressed because one or more lines are too long

View File

@ -80,23 +80,28 @@
<fieldset id="displayAdjust" class="inline half"> <fieldset id="displayAdjust" class="inline half">
<legend>Display Adjust</legend> <legend>Display Adjust</legend>
<form id="displayAdjustForm" onsubmit="return false;"> <form id="displayAdjustForm" onsubmit="return false;">
<button id="offsetXPlus">X +</button> <button class="small sequenceCtrl" id="offsetXPlus">X +</button>
<button id="offsetXMinus">X -</button> <button class="small sequenceCtrl" id="offsetXMinus">X -</button>
<button id="offsetYPlus">Y +</button> <button class="small sequenceCtrl" id="offsetYPlus">Y +</button>
<button id="offsetYMinus">Y -</button> <button class="small sequenceCtrl" id="offsetYMinus">Y -</button>
<button class="small sequenceCtrl" id="widthPlus">W +</button>
<button class="small sequenceCtrl" id="widthMinus">W -</button>
<button class="small sequenceCtrl" id="heightPlus">H +</button>
<button class="small sequenceCtrl" id="heightMinus">H -</button>
</form> </form>
</fieldset> </fieldset>
</div> </div>
<div> <div class="flex">
<fieldset id="sequenceCtrl"> <fieldset class="inline half" id="sequenceCtrl">
<legend>Sequence Controls</legend> <legend>Sequence Controls</legend>
<form id="sequenceCtrlForm" onsubmit="return false;"> <form id="sequenceCtrlForm" onsubmit="return false;">
<button id="start" class="sequenceCtrl" onclick="client.sendStart();" disabled>Start</button> <button id="start" class="medium sequenceCtrl" onclick="client.sendStart();" disabled>Start</button>
<button id="stop" class="sequenceCtrl" onclick="client.sendStop();" disabled>Stop</button> <button id="stop" class="medium sequenceCtrl" onclick="client.sendStop();" disabled>Stop</button>
<button id="pause" class="sequenceCtrl" disabled>Pause</button> <button id="rewind" class="small sequenceCtrl" onclick="client.sendToStart();" disabled><<</button>
<button id="rewind" class="sequenceCtrl" onclick="client.sendRewind();" disabled><<</button> <button id="rewind" class="small sequenceCtrl" onclick="client.sendRewind();" disabled><</button>
<input id="frame" value="00000" class="sequenceCtrl" disabled /> <input id="frame" type="number" value="00000" class="sequenceCtrl medium" disabled />
<button id="advance" class="sequenceCtrl" onclick="client.sendAdvance()" disabled>>></button> <button id="advance" class="small sequenceCtrl" onclick="client.sendAdvance()" disabled>></button>
<button id="advance" class="small sequenceCtrl" onclick="client.sendToEnd()" disabled>>></button>
</form> </form>
</fieldset> </fieldset>
</div> </div>
@ -104,8 +109,8 @@
<fieldset id="manualCtrl"> <fieldset id="manualCtrl">
<legend>Manual Controls</legend> <legend>Manual Controls</legend>
<form id="manualCtrlForm" onsubmit="return false;" > <form id="manualCtrlForm" onsubmit="return false;" >
<button id="open" class="manualCtrl" onclick="client.sendCameraOpen()">Open Gate</button> <button id="open" class="manualCtrl" onclick="client.sendCameraOpen()">Open</button>
<button id="close" class="manualCtrl" onclick="client.sendCameraClose()">Close Gate</button> <button id="close" class="manualCtrl" onclick="client.sendCameraClose()">Close</button>
<button id="focus" class="manualCtrl" onclick="">Focus</button> <button id="focus" class="manualCtrl" onclick="">Focus</button>
<button id="framing" class="manualCtrl" onclick="">Framing</button> <button id="framing" class="manualCtrl" onclick="">Framing</button>
</form> </form>
@ -123,6 +128,9 @@
</div> </div>
</div> </div>
<div id="overlay"></div> <div id="overlay"></div>
<script>
const WEBSOCKET_PORT = {{wsPort}};
</script>
<script src="/static/js/index.js"></script> <script src="/static/js/index.js"></script>
</body> </body>
</html> </html>