Added any number of untold features. Saving.
This commit is contained in:
parent
0b280ff623
commit
c33aaa2bec
|
@ -84,7 +84,7 @@ class Display {
|
|||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
private updateDisplay () {
|
||||
public updateDisplay () {
|
||||
if (!this.sequence) {
|
||||
return;
|
||||
}
|
||||
|
@ -96,13 +96,13 @@ class Display {
|
|||
this.displayOffsetX = this.screenOffsetX + Math.round(this.offsetX * screenScaleX);
|
||||
this.displayOffsetY = this.screenOffsetY + Math.round(this.offsetY * screenScaleY);
|
||||
|
||||
this.ctx.fillStyle = 'rgb(125, 125, 125)';
|
||||
this.ctx.fillStyle = 'rgb(0, 0, 0)';
|
||||
this.ctx.fillRect(this.displayOffsetX, this.displayOffsetY, this.displayWidth, this.displayHeight);
|
||||
console.log(`${this.displayOffsetX}, ${this.displayOffsetY}, ${this.displayWidth}, ${this.displayHeight}`);
|
||||
this.updateImage();
|
||||
}
|
||||
|
||||
private updateImage() {
|
||||
public updateImage() {
|
||||
const img : any = new Image;
|
||||
img.onload = function () {
|
||||
this.ctx.drawImage(img, this.displayOffsetX, this.displayOffsetY, this.displayWidth, this.displayHeight);
|
||||
|
@ -136,16 +136,18 @@ class Client {
|
|||
private client : WebSocket;
|
||||
private connected : boolean = false;
|
||||
private progress : HTMLProgressElement;
|
||||
private progressText : HTMLElement;
|
||||
|
||||
constructor () {
|
||||
let uri : string = 'ws://localhost:8082';
|
||||
this.progress = document.getElementById('progress') as HTMLProgressElement;
|
||||
this.progressText = document.getElementById('progressText');
|
||||
this.client = new WebSocket(uri);
|
||||
this.display = new Display();
|
||||
this.client.onopen = this.onOpen.bind(this);
|
||||
this.client.onclose = this.onClose.bind(this);
|
||||
this.client.onmessage = this.onMessage.bind(this);
|
||||
(document.getElementById('sequenceForm') as HTMLFormElement ).reset();
|
||||
(document.getElementById('sequenceSelectForm') as HTMLFormElement ).reset();
|
||||
(document.getElementById('sequenceCtrlForm') as HTMLFormElement ).reset();
|
||||
(document.getElementById('manualCtrlForm') as HTMLFormElement ).reset();
|
||||
this.disableClass('sequenceCtrl');
|
||||
|
@ -174,11 +176,19 @@ class Client {
|
|||
|
||||
private setSequence(state : State) {
|
||||
this.setProgress(state.sequence);
|
||||
this.setFrame(state.sequence);
|
||||
this.setStatus(state.sequence);
|
||||
this.setDisplay(state);
|
||||
(document.getElementById('sequence') as HTMLSelectElement ).value = state.sequence.hash;
|
||||
}
|
||||
|
||||
private setUpdate(state : State) {
|
||||
this.setProgress(state.sequence);
|
||||
this.setFrame(state.sequence);
|
||||
this.setStatus(state.sequence);
|
||||
this.display.updateImage();
|
||||
}
|
||||
|
||||
private setStatus (sequence : SequenceState) {
|
||||
let status : string;
|
||||
switch (sequence.status) {
|
||||
|
@ -202,10 +212,15 @@ class Client {
|
|||
private setProgress (sequence : SequenceState) {
|
||||
const percent : number = sequence.progress * 100.0;
|
||||
if (this.progress !== null) {
|
||||
this.progress.value = percent;
|
||||
this.progress.innerText = `${Math.floor(percent)}%`;
|
||||
this.progress.value = sequence.progress;
|
||||
this.progressText.innerText = `Progress: ${Math.floor(percent)}%`;
|
||||
}
|
||||
}
|
||||
|
||||
private setFrame (sequence : SequenceState) {
|
||||
if (typeof sequence.current !== 'undefined') {
|
||||
(document.getElementById('frame') as HTMLInputElement).value = `${sequence.current}`.padStart(5, '0');
|
||||
}
|
||||
document.getElementById('sequenceProgress').innerText = `Progress: ${Math.round(sequence.progress)}%`;
|
||||
}
|
||||
|
||||
private setDisplay (state : State) {
|
||||
|
@ -214,6 +229,7 @@ class Client {
|
|||
const srcWidthEl : HTMLInputElement = document.getElementById('sourceWidth') as HTMLInputElement;
|
||||
const srcHeightEl : HTMLInputElement = document.getElementById('sourceHeight') as HTMLInputElement;
|
||||
|
||||
if (typeof state.display !== 'undefined') {
|
||||
widthEl.value = state.display.width as any;
|
||||
heightEl.value = state.display.height as any;
|
||||
|
||||
|
@ -224,7 +240,7 @@ class Client {
|
|||
heightEl.readOnly = false;
|
||||
//console.dir(state);
|
||||
this.display.set(state);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private cmd (msg : Message) {
|
||||
|
@ -238,6 +254,9 @@ class Client {
|
|||
case 'select' :
|
||||
this.receiveSelect(msg);
|
||||
break;
|
||||
case 'update' :
|
||||
this.receiveUpdate(msg);
|
||||
break;
|
||||
default:
|
||||
console.warn(`No command "${msg.cmd}"`);
|
||||
break;
|
||||
|
@ -280,6 +299,14 @@ class Client {
|
|||
this.enableClass('manualCtrl');
|
||||
}
|
||||
|
||||
public sendAdvance () {
|
||||
this.client.send(JSON.stringify({ cmd : 'advance' }));
|
||||
}
|
||||
|
||||
public sendRewind () {
|
||||
this.client.send(JSON.stringify({ cmd : 'rewind' }));
|
||||
}
|
||||
|
||||
public sendSelect () {
|
||||
const hash : string = (document.getElementById('sequence') as HTMLSelectElement ).value;
|
||||
let msg : Message;
|
||||
|
@ -294,13 +321,21 @@ class Client {
|
|||
|
||||
private receiveSelect (msg : Message) {
|
||||
console.log('got select');
|
||||
console.dir(msg);
|
||||
//console.dir(msg);
|
||||
this.enableClass('sequenceCtrl');
|
||||
this.setSequence(msg.state);
|
||||
}
|
||||
|
||||
private receiveUpdate (msg : Message) {
|
||||
public sendStart () {
|
||||
this.client.send(JSON.stringify({ cmd : 'start' }));
|
||||
}
|
||||
|
||||
public sendStop () {
|
||||
this.client.send(JSON.stringify({ cmd : 'stop' }));
|
||||
}
|
||||
|
||||
private receiveUpdate (msg : Message) {
|
||||
this.setUpdate(msg.state);
|
||||
}
|
||||
|
||||
public fullscreen () {
|
||||
|
|
|
@ -47,7 +47,8 @@ export declare class FD {
|
|||
private socketAvailable;
|
||||
private socketConnected;
|
||||
private waiting;
|
||||
constructor(bin: string, width: number, height: number, host: string, port: number);
|
||||
private mock;
|
||||
constructor(bin: string, width: number, height: number, host: string, port: number, mock?: boolean);
|
||||
private startDisplay;
|
||||
private startClient;
|
||||
private logstd;
|
||||
|
|
|
@ -25,26 +25,33 @@ var Mode;
|
|||
Mode[Mode["INVERT_CHANNELS"] = 5] = "INVERT_CHANNELS";
|
||||
})(Mode || (Mode = {}));
|
||||
class FD {
|
||||
constructor(bin, width, height, host, port) {
|
||||
constructor(bin, width, height, host, port, mock = false) {
|
||||
this.socketAvailable = false;
|
||||
this.socketConnected = false;
|
||||
this.waiting = null;
|
||||
this.mock = false;
|
||||
this.bin = bin;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.mock = mock;
|
||||
this.log = (0, log_1.createLog)('fd');
|
||||
if (!this.mock)
|
||||
this.shell = new shell_1.Shell([this.bin, `${this.width}`, `${this.height}`, `${this.port}`], this.logstd.bind(this), this.logsterr.bind(this), null, true);
|
||||
this.startDisplay();
|
||||
this.startClient();
|
||||
this.test();
|
||||
//this.test();
|
||||
}
|
||||
async startDisplay() {
|
||||
this.log.info(`Launching fd binary ${this.bin}`);
|
||||
if (!this.mock)
|
||||
this.shell.execute();
|
||||
}
|
||||
async startClient() {
|
||||
if (this.mock) {
|
||||
return false;
|
||||
}
|
||||
this.client = new net_1.default.Socket();
|
||||
this.log.info(`Waiting for TCP socket server on ${this.host}:${this.port}...`);
|
||||
while (!this.socketAvailable) {
|
||||
|
@ -65,6 +72,7 @@ class FD {
|
|||
this.client.on('error', (err) => {
|
||||
this.log.error('Error in socket client', err);
|
||||
this.socketConnected = false;
|
||||
if (!this.mock)
|
||||
this.shell.kill();
|
||||
});
|
||||
}
|
||||
|
@ -90,6 +98,7 @@ class FD {
|
|||
send(msg) {
|
||||
const json = JSON.stringify(msg);
|
||||
this.log.info(json);
|
||||
if (!this.mock)
|
||||
this.client.write(json);
|
||||
}
|
||||
receive(json) {
|
||||
|
@ -112,6 +121,13 @@ class FD {
|
|||
}
|
||||
};
|
||||
const startTime = +new Date();
|
||||
if (this.mock) {
|
||||
return {
|
||||
action: Action.LOAD,
|
||||
image,
|
||||
time: (+new Date()) - startTime
|
||||
};
|
||||
}
|
||||
const promise = new Promise(function (resolve, reject) {
|
||||
this.waiting = function (msg) {
|
||||
if (msg.action == Action.LOAD && msg.success) {
|
||||
|
@ -137,6 +153,16 @@ class FD {
|
|||
exposure
|
||||
};
|
||||
const startTime = +new Date();
|
||||
if (this.mock) {
|
||||
for (let exp of exposure) {
|
||||
await (0, delay_1.delay)(exp);
|
||||
}
|
||||
return {
|
||||
action: Action.DISPLAY,
|
||||
image,
|
||||
time: (+new Date()) - startTime
|
||||
};
|
||||
}
|
||||
const promise = new Promise(function (resolve, reject) {
|
||||
this.waiting = function (msg) {
|
||||
if (msg.action == Action.DISPLAY && msg.success) {
|
||||
|
@ -161,6 +187,13 @@ class FD {
|
|||
image
|
||||
};
|
||||
const startTime = +new Date();
|
||||
if (this.mock) {
|
||||
return {
|
||||
action: Action.STOP,
|
||||
image,
|
||||
time: (+new Date()) - startTime
|
||||
};
|
||||
}
|
||||
const promise = new Promise(function (resolve, reject) {
|
||||
this.waiting = function (msg) {
|
||||
if (msg.action == Action.STOP && msg.success) {
|
||||
|
@ -194,6 +227,7 @@ class FD {
|
|||
await this.display(img);
|
||||
await (0, delay_1.delay)(2000);
|
||||
await this.stop(img);
|
||||
this.log.warn('QUITTING!!!!');
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -35,6 +35,7 @@ const Handlebars = __importStar(require("handlebars"));
|
|||
const ws_1 = require("ws");
|
||||
const log_1 = require("./log");
|
||||
const files_1 = require("./files");
|
||||
const fd_1 = require("./fd");
|
||||
const display_1 = require("./display");
|
||||
const ffmpeg_1 = require("./ffmpeg");
|
||||
const ffprobe_1 = require("./ffprobe");
|
||||
|
@ -175,7 +176,7 @@ function onWssConnection(ws, req) {
|
|||
ws.ip = ip;
|
||||
ws.session = (0, uuid_1.v4)();
|
||||
ws.on('message', function (data) { onClientMessage(data, ws); });
|
||||
sequence.updateClients();
|
||||
sequence.updateClientsOnLoad();
|
||||
}
|
||||
async function onClientMessage(data, ws) {
|
||||
let msg = null;
|
||||
|
@ -201,6 +202,18 @@ async function cmd(msg) {
|
|||
case 'select':
|
||||
await select(msg.state.sequence.hash);
|
||||
break;
|
||||
case 'start':
|
||||
start();
|
||||
break;
|
||||
case 'stop':
|
||||
stop();
|
||||
break;
|
||||
case 'advance':
|
||||
frameAdvance();
|
||||
break;
|
||||
case 'rewind':
|
||||
frameRewind();
|
||||
break;
|
||||
default:
|
||||
log.warn(`No matching command: ${msg.cmd}`);
|
||||
}
|
||||
|
@ -213,6 +226,12 @@ async function cameraClose() {
|
|||
await camera.close();
|
||||
send({ cmd: 'close' });
|
||||
}
|
||||
function frameAdvance() {
|
||||
sequence.frameAdvance();
|
||||
}
|
||||
function frameRewind() {
|
||||
sequence.frameRewind();
|
||||
}
|
||||
async function select(id) {
|
||||
const sequencesArr = await files_1.Files.enumerateSequences(sequences);
|
||||
const seq = sequencesArr.find(el => el.hash === id);
|
||||
|
@ -223,6 +242,12 @@ async function select(id) {
|
|||
await sequence.load(seq);
|
||||
return true;
|
||||
}
|
||||
function start() {
|
||||
sequence.start();
|
||||
}
|
||||
function stop() {
|
||||
sequence.stop();
|
||||
}
|
||||
async function send(msg) {
|
||||
const msgStr = JSON.stringify(msg);
|
||||
wss.clients.forEach((client) => {
|
||||
|
@ -271,7 +296,7 @@ async function main() {
|
|||
image = new image_1.Image();
|
||||
camera = new camera_1.Camera();
|
||||
display = new display_1.Display(width, height);
|
||||
//fd = new FD(process.env['FD'], width, height, process.env['FD_HOST'], parseInt(process.env['FD_PORT']));
|
||||
fd = new fd_1.FD(process.env['FD'], width, height, process.env['FD_HOST'], parseInt(process.env['FD_PORT']), true);
|
||||
app.listen(port, async () => {
|
||||
log.info(`filmout_manager HTTP server running on port ${port}`);
|
||||
});
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -28,14 +28,21 @@ export declare class Sequence {
|
|||
start(): void;
|
||||
stop(): void;
|
||||
isRunning(): boolean;
|
||||
private run;
|
||||
load(seq: SequenceObject): Promise<void>;
|
||||
updateClients(): void;
|
||||
updateClientsOnLoad(): void;
|
||||
updateClientsOnState(): void;
|
||||
private enumerate;
|
||||
unload(): void;
|
||||
getState(): State;
|
||||
getUpdateState(): State;
|
||||
getSequenceState(): SequenceState;
|
||||
setExposure(ms: number): void;
|
||||
getStatus(): SequenceStatus;
|
||||
getCurrent(): ImageObject;
|
||||
frameAdvance(frames?: number): void;
|
||||
frameRewind(frames?: number): void;
|
||||
frameSet(frame: number): void;
|
||||
private frameRecord;
|
||||
}
|
||||
export {};
|
||||
|
|
|
@ -28,26 +28,54 @@ class Sequence {
|
|||
this.send = send;
|
||||
}
|
||||
start() {
|
||||
if (this.current !== null) {
|
||||
this.running = true;
|
||||
this.log.info(`Started sequence: ${this.current.name}`);
|
||||
this.run();
|
||||
}
|
||||
}
|
||||
stop() {
|
||||
if (this.running && this.current !== null) {
|
||||
this.log.info(`Stopped sequence: ${this.current.name}`);
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
async run() {
|
||||
//update running
|
||||
for (let i = this.frame; i < this.images.length; i++) {
|
||||
if (!this.running) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
await this.frameRecord();
|
||||
}
|
||||
catch (err) {
|
||||
this.log.error(`Error recording frame`, err);
|
||||
}
|
||||
this.frameAdvance();
|
||||
}
|
||||
//complete running
|
||||
}
|
||||
async load(seq) {
|
||||
this.current = seq;
|
||||
this.frame = 0;
|
||||
this.progress = 0;
|
||||
await this.enumerate();
|
||||
this.updateClients();
|
||||
this.updateClientsOnLoad();
|
||||
}
|
||||
updateClients() {
|
||||
updateClientsOnLoad() {
|
||||
if (this.current !== null) {
|
||||
this.send({ cmd: 'select', state: this.getState() });
|
||||
}
|
||||
}
|
||||
updateClientsOnState() {
|
||||
if (this.current !== null) {
|
||||
this.send({ cmd: 'update', state: this.getState() });
|
||||
}
|
||||
}
|
||||
async enumerate() {
|
||||
let screen;
|
||||
if (this.current === null) {
|
||||
|
@ -105,6 +133,19 @@ class Sequence {
|
|||
exposure: this.exposure
|
||||
};
|
||||
}
|
||||
getUpdateState() {
|
||||
return {
|
||||
sequence: {
|
||||
hash: this.current.hash,
|
||||
name: this.current.name,
|
||||
progress: this.progress,
|
||||
current: this.frame,
|
||||
frames: this.frames,
|
||||
status: this.getStatus()
|
||||
},
|
||||
exposure: this.exposure
|
||||
};
|
||||
}
|
||||
getSequenceState() {
|
||||
if (this.current === null) {
|
||||
return null;
|
||||
|
@ -133,6 +174,45 @@ class Sequence {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
frameAdvance(frames = 1) {
|
||||
if (this.frame + frames >= this.images.length) {
|
||||
this.frame = this.images.length - 1;
|
||||
}
|
||||
else {
|
||||
this.frame += frames;
|
||||
}
|
||||
this.progress = this.frame / (this.images.length - 1);
|
||||
this.updateClientsOnState();
|
||||
}
|
||||
frameRewind(frames = 1) {
|
||||
if (this.frame + frames < 0) {
|
||||
this.frame = 0;
|
||||
}
|
||||
else {
|
||||
this.frame -= frames;
|
||||
}
|
||||
this.progress = this.frame > 0 ? this.frame / (this.images.length - 1) : 0;
|
||||
this.updateClientsOnState();
|
||||
}
|
||||
frameSet(frame) {
|
||||
if (frame < 0) {
|
||||
frame = 0;
|
||||
}
|
||||
else if (frame > this.images.length - 1) {
|
||||
frame = this.images.length - 1;
|
||||
}
|
||||
this.progress = this.frame > 0 ? this.frame / (this.images.length - 1) : 0;
|
||||
this.updateClientsOnState();
|
||||
}
|
||||
async frameRecord() {
|
||||
const img = this.images[this.frame];
|
||||
const dimensions = this.display.getDimensions();
|
||||
this.log.info(`Frame: ${this.frame} / ${this.images.length}`);
|
||||
await this.fd.load(img.path, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
|
||||
await this.camera.open();
|
||||
await this.fd.display(img.path, [this.exposure]);
|
||||
await this.camera.close();
|
||||
}
|
||||
}
|
||||
exports.Sequence = Sequence;
|
||||
//# sourceMappingURL=index.js.map
|
File diff suppressed because one or more lines are too long
|
@ -61,26 +61,31 @@ export class FD {
|
|||
private socketConnected : boolean = false;
|
||||
|
||||
private waiting : Function = null;
|
||||
private mock : boolean = false;
|
||||
|
||||
constructor (bin: string, width : number, height : number, host : string, port : number) {
|
||||
constructor (bin: string, width : number, height : number, host : string, port : number, mock : boolean = false) {
|
||||
this.bin = bin;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.mock = mock;
|
||||
this.log = createLog('fd');
|
||||
this.shell = new Shell([ this.bin, `${this.width}`, `${this.height}`, `${this.port}` ], this.logstd.bind(this), this.logsterr.bind(this), null, true);
|
||||
if (!this.mock) this.shell = new Shell([ this.bin, `${this.width}`, `${this.height}`, `${this.port}` ], this.logstd.bind(this), this.logsterr.bind(this), null, true);
|
||||
this.startDisplay();
|
||||
this.startClient();
|
||||
this.test();
|
||||
//this.test();
|
||||
}
|
||||
|
||||
private async startDisplay () {
|
||||
this.log.info(`Launching fd binary ${this.bin}`);
|
||||
this.shell.execute();
|
||||
if (!this.mock) this.shell.execute();
|
||||
}
|
||||
|
||||
private async startClient () {
|
||||
if (this.mock) {
|
||||
return false;
|
||||
}
|
||||
this.client = new net.Socket();
|
||||
this.log.info(`Waiting for TCP socket server on ${this.host}:${this.port}...`);
|
||||
while (!this.socketAvailable) {
|
||||
|
@ -104,7 +109,7 @@ export class FD {
|
|||
this.client.on('error', (err : Error) => {
|
||||
this.log.error('Error in socket client', err);
|
||||
this.socketConnected = false;
|
||||
this.shell.kill();
|
||||
if (!this.mock) this.shell.kill();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -131,7 +136,7 @@ export class FD {
|
|||
private send (msg : fdOutgoingMessage) {
|
||||
const json : string = JSON.stringify(msg);
|
||||
this.log.info(json);
|
||||
this.client.write(json);
|
||||
if (!this.mock) this.client.write(json);
|
||||
}
|
||||
|
||||
private receive (json : string) {
|
||||
|
@ -155,6 +160,13 @@ export class FD {
|
|||
}
|
||||
};
|
||||
const startTime : number = +new Date();
|
||||
if (this.mock) {
|
||||
return {
|
||||
action : Action.LOAD,
|
||||
image,
|
||||
time : (+new Date()) - startTime
|
||||
};
|
||||
}
|
||||
const promise : Promise<fdResult> = new Promise(function (resolve : Function, reject : Function) {
|
||||
this.waiting = function (msg : fdIncomingMessage) {
|
||||
if (msg.action == Action.LOAD && msg.success) {
|
||||
|
@ -180,6 +192,16 @@ export class FD {
|
|||
exposure
|
||||
};
|
||||
const startTime : number = +new Date();
|
||||
if (this.mock) {
|
||||
for (let exp of exposure) {
|
||||
await delay(exp);
|
||||
}
|
||||
return {
|
||||
action : Action.DISPLAY,
|
||||
image,
|
||||
time : (+new Date()) - startTime
|
||||
};
|
||||
}
|
||||
const promise : Promise<fdResult> = new Promise(function (resolve : Function, reject : Function) {
|
||||
this.waiting = function (msg : fdIncomingMessage) {
|
||||
if (msg.action == Action.DISPLAY && msg.success) {
|
||||
|
@ -204,6 +226,13 @@ export class FD {
|
|||
image
|
||||
};
|
||||
const startTime : number = +new Date();
|
||||
if (this.mock) {
|
||||
return {
|
||||
action : Action.STOP,
|
||||
image,
|
||||
time : (+new Date()) - startTime
|
||||
};
|
||||
}
|
||||
const promise : Promise<fdResult> = new Promise(function (resolve : Function, reject : Function) {
|
||||
this.waiting = function (msg : fdIncomingMessage) {
|
||||
if (msg.action == Action.STOP && msg.success) {
|
||||
|
@ -244,6 +273,7 @@ export class FD {
|
|||
await delay(2000);
|
||||
await this.stop(img);
|
||||
|
||||
this.log.warn('QUITTING!!!!');
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
|
|
33
src/index.ts
33
src/index.ts
|
@ -164,7 +164,7 @@ function onWssConnection (ws : WebSocketExtended, req : Request) {
|
|||
ws.ip = ip;
|
||||
ws.session = uuid();
|
||||
ws.on('message', function (data) { onClientMessage(data, ws) });
|
||||
sequence.updateClients();
|
||||
sequence.updateClientsOnLoad();
|
||||
}
|
||||
|
||||
async function onClientMessage (data : any, ws : WebSocket) {
|
||||
|
@ -191,6 +191,18 @@ async function cmd (msg : Message) {
|
|||
case 'select' :
|
||||
await select(msg.state.sequence.hash);
|
||||
break;
|
||||
case 'start' :
|
||||
start();
|
||||
break;
|
||||
case 'stop' :
|
||||
stop();
|
||||
break;
|
||||
case 'advance' :
|
||||
frameAdvance();
|
||||
break;
|
||||
case 'rewind' :
|
||||
frameRewind();
|
||||
break;
|
||||
default :
|
||||
log.warn(`No matching command: ${msg.cmd}`);
|
||||
}
|
||||
|
@ -206,6 +218,14 @@ async function cameraClose () {
|
|||
send({ cmd : 'close' });
|
||||
}
|
||||
|
||||
function frameAdvance () {
|
||||
sequence.frameAdvance();
|
||||
}
|
||||
|
||||
function frameRewind () {
|
||||
sequence.frameRewind();
|
||||
}
|
||||
|
||||
async function select (id : string) : Promise<boolean> {
|
||||
const sequencesArr : SequenceObject[] = await Files.enumerateSequences(sequences);
|
||||
const seq : SequenceObject = sequencesArr.find(el => el.hash === id);
|
||||
|
@ -217,6 +237,14 @@ async function select (id : string) : Promise<boolean> {
|
|||
return true;
|
||||
}
|
||||
|
||||
function start () {
|
||||
sequence.start();
|
||||
}
|
||||
|
||||
function stop () {
|
||||
sequence.stop();
|
||||
}
|
||||
|
||||
async function send (msg : Message) {
|
||||
const msgStr : string = JSON.stringify(msg);
|
||||
wss.clients.forEach((client : WebSocket ) => {
|
||||
|
@ -265,8 +293,7 @@ async function main () {
|
|||
image = new Image();
|
||||
camera = new Camera();
|
||||
display = new Display(width, height);
|
||||
//fd = new FD(process.env['FD'], width, height, process.env['FD_HOST'], parseInt(process.env['FD_PORT']));
|
||||
|
||||
fd = new FD(process.env['FD'], width, height, process.env['FD_HOST'], parseInt(process.env['FD_PORT']), true);
|
||||
|
||||
app.listen(port, async () => {
|
||||
log.info(`filmout_manager HTTP server running on port ${port}`);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Files } from '../files';
|
||||
import { createLog } from '../log'
|
||||
import { createLog } from '../log';
|
||||
import { delay } from '../delay';
|
||||
import type { Logger } from 'winston';
|
||||
import type { SequenceObject, ImageObject } from '../files';
|
||||
import type { FD, fdOutgoingPosition } from '../fd';
|
||||
|
@ -42,31 +43,60 @@ export class Sequence {
|
|||
}
|
||||
|
||||
public start () {
|
||||
if (this.current !== null) {
|
||||
this.running = true;
|
||||
this.log.info(`Started sequence: ${this.current.name}`);
|
||||
this.run();
|
||||
}
|
||||
}
|
||||
|
||||
public stop () {
|
||||
if (this.running && this.current !== null) {
|
||||
this.log.info(`Stopped sequence: ${this.current.name}`);
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
|
||||
public isRunning () {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
private async run () {
|
||||
//update running
|
||||
for (let i = this.frame; i < this.images.length; i++) {
|
||||
if (!this.running) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
await this.frameRecord();
|
||||
} catch (err) {
|
||||
this.log.error(`Error recording frame`, err);
|
||||
}
|
||||
this.frameAdvance();
|
||||
}
|
||||
//complete running
|
||||
}
|
||||
|
||||
public async load (seq : SequenceObject) {
|
||||
this.current = seq;
|
||||
this.frame = 0;
|
||||
this.progress = 0;
|
||||
await this.enumerate();
|
||||
this.updateClients();
|
||||
this.updateClientsOnLoad();
|
||||
}
|
||||
|
||||
public updateClients () {
|
||||
public updateClientsOnLoad () {
|
||||
if (this.current !== null) {
|
||||
this.send({ cmd : 'select', state : this.getState() });
|
||||
}
|
||||
}
|
||||
|
||||
public updateClientsOnState () {
|
||||
if (this.current !== null) {
|
||||
this.send({ cmd : 'update', state : this.getState() });
|
||||
}
|
||||
}
|
||||
|
||||
private async enumerate () {
|
||||
let screen : Dimensions;
|
||||
|
||||
|
@ -131,6 +161,20 @@ export class Sequence {
|
|||
}
|
||||
}
|
||||
|
||||
public getUpdateState () : State {
|
||||
return {
|
||||
sequence : {
|
||||
hash : this.current.hash,
|
||||
name : this.current.name,
|
||||
progress : this.progress,
|
||||
current : this.frame,
|
||||
frames : this.frames,
|
||||
status : this.getStatus() as SequenceStatus
|
||||
},
|
||||
exposure : this.exposure
|
||||
}
|
||||
}
|
||||
|
||||
public getSequenceState () : SequenceState {
|
||||
if (this.current === null) {
|
||||
return null;
|
||||
|
@ -161,4 +205,46 @@ export class Sequence {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public frameAdvance (frames : number = 1) {
|
||||
if (this.frame + frames >= this.images.length) {
|
||||
this.frame = this.images.length - 1;
|
||||
} else {
|
||||
this.frame += frames;
|
||||
}
|
||||
|
||||
this.progress = this.frame / (this.images.length - 1);
|
||||
this.updateClientsOnState();
|
||||
}
|
||||
|
||||
public frameRewind (frames : number = 1) {
|
||||
if (this.frame + frames < 0) {
|
||||
this.frame = 0;
|
||||
} else {
|
||||
this.frame -= frames;
|
||||
}
|
||||
|
||||
this.progress = this.frame > 0 ? this.frame / (this.images.length - 1) : 0;
|
||||
this.updateClientsOnState();
|
||||
}
|
||||
|
||||
public frameSet (frame : number) {
|
||||
if (frame < 0) {
|
||||
frame = 0;
|
||||
} else if (frame > this.images.length - 1) {
|
||||
frame = this.images.length - 1;
|
||||
}
|
||||
this.progress = this.frame > 0 ? this.frame / (this.images.length - 1) : 0;
|
||||
this.updateClientsOnState();
|
||||
}
|
||||
|
||||
private async frameRecord () {
|
||||
const img : ImageObject = this.images[this.frame];
|
||||
const dimensions : fdOutgoingPosition = this.display.getDimensions();
|
||||
this.log.info(`Frame: ${this.frame} / ${this.images.length}`);
|
||||
await this.fd.load(img.path, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
|
||||
await this.camera.open();
|
||||
await this.fd.display(img.path, [ this.exposure ] );
|
||||
await this.camera.close();
|
||||
}
|
||||
}
|
|
@ -64,3 +64,10 @@ fieldset.inline .field-row {
|
|||
fieldset.inline .field-row input {
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
progress {
|
||||
width: 97vw;
|
||||
position: absolute;
|
||||
bottom: 28px;
|
||||
left: 0.5vw;
|
||||
}
|
|
@ -31,8 +31,8 @@ declare class Display {
|
|||
private updateSize;
|
||||
private clear;
|
||||
private updateScreen;
|
||||
private updateDisplay;
|
||||
private updateImage;
|
||||
updateDisplay(): void;
|
||||
updateImage(): void;
|
||||
update(msg: Message): void;
|
||||
set(state: State): void;
|
||||
private onResize;
|
||||
|
@ -42,13 +42,16 @@ declare class Client {
|
|||
private client;
|
||||
private connected;
|
||||
private progress;
|
||||
private progressText;
|
||||
constructor();
|
||||
private onMessage;
|
||||
private onOpen;
|
||||
private onClose;
|
||||
private setSequence;
|
||||
private setUpdate;
|
||||
private setStatus;
|
||||
private setProgress;
|
||||
private setFrame;
|
||||
private setDisplay;
|
||||
private cmd;
|
||||
disableClass(className: string): void;
|
||||
|
@ -57,8 +60,12 @@ declare class Client {
|
|||
private receiveCameraOpen;
|
||||
sendCameraClose(): void;
|
||||
private receiveCameraClose;
|
||||
sendAdvance(): void;
|
||||
sendRewind(): void;
|
||||
sendSelect(): void;
|
||||
private receiveSelect;
|
||||
sendStart(): void;
|
||||
sendStop(): void;
|
||||
private receiveUpdate;
|
||||
fullscreen(): void;
|
||||
exitFullscreen(): void;
|
||||
|
|
|
@ -82,7 +82,7 @@ class Display {
|
|||
this.displayHeight = Math.round(this.height * screenScaleY);
|
||||
this.displayOffsetX = this.screenOffsetX + Math.round(this.offsetX * screenScaleX);
|
||||
this.displayOffsetY = this.screenOffsetY + Math.round(this.offsetY * screenScaleY);
|
||||
this.ctx.fillStyle = 'rgb(125, 125, 125)';
|
||||
this.ctx.fillStyle = 'rgb(0, 0, 0)';
|
||||
this.ctx.fillRect(this.displayOffsetX, this.displayOffsetY, this.displayWidth, this.displayHeight);
|
||||
console.log(`${this.displayOffsetX}, ${this.displayOffsetY}, ${this.displayWidth}, ${this.displayHeight}`);
|
||||
this.updateImage();
|
||||
|
@ -116,12 +116,13 @@ class Client {
|
|||
this.connected = false;
|
||||
let uri = 'ws://localhost:8082';
|
||||
this.progress = document.getElementById('progress');
|
||||
this.progressText = document.getElementById('progressText');
|
||||
this.client = new WebSocket(uri);
|
||||
this.display = new Display();
|
||||
this.client.onopen = this.onOpen.bind(this);
|
||||
this.client.onclose = this.onClose.bind(this);
|
||||
this.client.onmessage = this.onMessage.bind(this);
|
||||
document.getElementById('sequenceForm').reset();
|
||||
document.getElementById('sequenceSelectForm').reset();
|
||||
document.getElementById('sequenceCtrlForm').reset();
|
||||
document.getElementById('manualCtrlForm').reset();
|
||||
this.disableClass('sequenceCtrl');
|
||||
|
@ -146,10 +147,17 @@ class Client {
|
|||
}
|
||||
setSequence(state) {
|
||||
this.setProgress(state.sequence);
|
||||
this.setFrame(state.sequence);
|
||||
this.setStatus(state.sequence);
|
||||
this.setDisplay(state);
|
||||
document.getElementById('sequence').value = state.sequence.hash;
|
||||
}
|
||||
setUpdate(state) {
|
||||
this.setProgress(state.sequence);
|
||||
this.setFrame(state.sequence);
|
||||
this.setStatus(state.sequence);
|
||||
this.display.updateImage();
|
||||
}
|
||||
setStatus(sequence) {
|
||||
let status;
|
||||
switch (sequence.status) {
|
||||
|
@ -172,16 +180,21 @@ class Client {
|
|||
setProgress(sequence) {
|
||||
const percent = sequence.progress * 100.0;
|
||||
if (this.progress !== null) {
|
||||
this.progress.value = percent;
|
||||
this.progress.innerText = `${Math.floor(percent)}%`;
|
||||
this.progress.value = sequence.progress;
|
||||
this.progressText.innerText = `Progress: ${Math.floor(percent)}%`;
|
||||
}
|
||||
}
|
||||
setFrame(sequence) {
|
||||
if (typeof sequence.current !== 'undefined') {
|
||||
document.getElementById('frame').value = `${sequence.current}`.padStart(5, '0');
|
||||
}
|
||||
document.getElementById('sequenceProgress').innerText = `Progress: ${Math.round(sequence.progress)}%`;
|
||||
}
|
||||
setDisplay(state) {
|
||||
const widthEl = document.getElementById('displayWidth');
|
||||
const heightEl = document.getElementById('displayHeight');
|
||||
const srcWidthEl = document.getElementById('sourceWidth');
|
||||
const srcHeightEl = document.getElementById('sourceHeight');
|
||||
if (typeof state.display !== 'undefined') {
|
||||
widthEl.value = state.display.width;
|
||||
heightEl.value = state.display.height;
|
||||
srcWidthEl.value = state.source.width;
|
||||
|
@ -190,6 +203,7 @@ class Client {
|
|||
heightEl.readOnly = false;
|
||||
this.display.set(state);
|
||||
}
|
||||
}
|
||||
cmd(msg) {
|
||||
switch (msg.cmd) {
|
||||
case 'open':
|
||||
|
@ -201,6 +215,9 @@ class Client {
|
|||
case 'select':
|
||||
this.receiveSelect(msg);
|
||||
break;
|
||||
case 'update':
|
||||
this.receiveUpdate(msg);
|
||||
break;
|
||||
default:
|
||||
console.warn(`No command "${msg.cmd}"`);
|
||||
break;
|
||||
|
@ -236,6 +253,12 @@ class Client {
|
|||
console.log('got camera close');
|
||||
this.enableClass('manualCtrl');
|
||||
}
|
||||
sendAdvance() {
|
||||
this.client.send(JSON.stringify({ cmd: 'advance' }));
|
||||
}
|
||||
sendRewind() {
|
||||
this.client.send(JSON.stringify({ cmd: 'rewind' }));
|
||||
}
|
||||
sendSelect() {
|
||||
const hash = document.getElementById('sequence').value;
|
||||
let msg;
|
||||
|
@ -249,11 +272,17 @@ class Client {
|
|||
}
|
||||
receiveSelect(msg) {
|
||||
console.log('got select');
|
||||
console.dir(msg);
|
||||
this.enableClass('sequenceCtrl');
|
||||
this.setSequence(msg.state);
|
||||
}
|
||||
sendStart() {
|
||||
this.client.send(JSON.stringify({ cmd: 'start' }));
|
||||
}
|
||||
sendStop() {
|
||||
this.client.send(JSON.stringify({ cmd: 'stop' }));
|
||||
}
|
||||
receiveUpdate(msg) {
|
||||
this.setUpdate(msg.state);
|
||||
}
|
||||
fullscreen() {
|
||||
if (!document.fullscreenElement) {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -64,10 +64,10 @@
|
|||
{{/each}}
|
||||
</select>
|
||||
-->
|
||||
<div>
|
||||
<fieldset id="sequenceSelect">
|
||||
<div class="flex">
|
||||
<fieldset id="sequenceSelect" class="inline half">
|
||||
<legend>Sequence</legend>
|
||||
<form id="sequenceForm" onsubmit="return false;">
|
||||
<form id="sequenceSelectForm" onsubmit="return false;">
|
||||
<select name="sequence" id="sequence">
|
||||
<option> - Select Image Sequence - </option>
|
||||
{{#each sequences}}
|
||||
|
@ -77,17 +77,26 @@
|
|||
<button id="select" onclick="client.sendSelect();">Select</button>
|
||||
</form>
|
||||
</fieldset>
|
||||
<fieldset id="displayAdjust" class="inline half">
|
||||
<legend>Display Adjust</legend>
|
||||
<form id="displayAdjustForm" onsubmit="return false;">
|
||||
<button id="offsetXPlus">X +</button>
|
||||
<button id="offsetXMinus">X -</button>
|
||||
<button id="offsetYPlus">Y +</button>
|
||||
<button id="offsetYMinus">Y -</button>
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div>
|
||||
<fieldset id="sequenceCtrl">
|
||||
<legend>Sequence Controls</legend>
|
||||
<form id="sequenceCtrlForm" onsubmit="return false;">
|
||||
<button id="start" class="sequenceCtrl" disabled>Start</button>
|
||||
<button id="stop" class="sequenceCtrl" disabled>Stop</button>
|
||||
<button id="start" class="sequenceCtrl" onclick="client.sendStart();" disabled>Start</button>
|
||||
<button id="stop" class="sequenceCtrl" onclick="client.sendStop();" disabled>Stop</button>
|
||||
<button id="pause" class="sequenceCtrl" disabled>Pause</button>
|
||||
<button id="rewind" class="sequenceCtrl" disabled><<</button>
|
||||
<button id="rewind" class="sequenceCtrl" onclick="client.sendRewind();" disabled><<</button>
|
||||
<input id="frame" value="00000" class="sequenceCtrl" disabled />
|
||||
<button id="forward" class="sequenceCtrl" disabled>>></button>
|
||||
<button id="advance" class="sequenceCtrl" onclick="client.sendAdvance()" disabled>>></button>
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
@ -108,7 +117,7 @@
|
|||
<div class="status-bar" id="status">
|
||||
<p class="status-bar-field" id="sequenceStatus">Idle</p>
|
||||
<p class="status-bar-field" id="sequenceName">Not Set</p>
|
||||
<p class="status-bar-field" id="sequenceProgress">Progress: 0%</p>
|
||||
<p class="status-bar-field" id="progressText">Progress: 0%</p>
|
||||
<p class="status-bar-field" id="sequenceLength">Sequence Length: 0</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue