489 lines
17 KiB
JavaScript
489 lines
17 KiB
JavaScript
let client;
|
|
var SequenceStatus;
|
|
(function (SequenceStatus) {
|
|
SequenceStatus[SequenceStatus["IDLE"] = 0] = "IDLE";
|
|
SequenceStatus[SequenceStatus["RUNNING"] = 1] = "RUNNING";
|
|
SequenceStatus[SequenceStatus["PAUSED"] = 2] = "PAUSED";
|
|
})(SequenceStatus || (SequenceStatus = {}));
|
|
class Display {
|
|
constructor() {
|
|
this.sequence = false;
|
|
this.focus = false;
|
|
this.framing = false;
|
|
this.offsetX = 0;
|
|
this.offsetY = 0;
|
|
this.width = 0;
|
|
this.height = 0;
|
|
this.canvasScale = 0;
|
|
this.canvasWidth = 0;
|
|
this.canvasHeight = 0;
|
|
this.canvasOffsetX = 0;
|
|
this.canvasOffsetY = 0;
|
|
this.screenWidth = 0;
|
|
this.screenHeight = 0;
|
|
this.screenOffsetX = 0;
|
|
this.screenOffsetY = 0;
|
|
this.displayWidth = 0;
|
|
this.displayHeight = 0;
|
|
this.displayOffsetX = 0;
|
|
this.displayOffsetY = 0;
|
|
this.parentElement = document.getElementById('display');
|
|
this.create();
|
|
window.onresize = this.onResize.bind(this);
|
|
}
|
|
create() {
|
|
this.canvas = this.parentElement.getElementsByTagName('canvas')[0];
|
|
this.ctx = this.canvas.getContext('2d');
|
|
this.screen = {
|
|
width: parseInt(document.getElementById('width').value),
|
|
height: parseInt(document.getElementById('height').value)
|
|
};
|
|
this.updateSize();
|
|
this.clear();
|
|
this.updateScreen();
|
|
this.updateDisplay();
|
|
}
|
|
updateSize() {
|
|
this.canvasScale = window.devicePixelRatio;
|
|
this.canvasWidth = this.parentElement.clientWidth - 12;
|
|
this.canvasHeight = this.parentElement.clientHeight - 12;
|
|
this.canvas.width = this.canvasWidth;
|
|
this.canvas.height = this.canvasHeight;
|
|
}
|
|
clear() {
|
|
this.ctx.fillStyle = 'rgb(0, 0, 0)';
|
|
this.ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
|
|
}
|
|
updateScreen() {
|
|
const clientRatio = this.canvasWidth / this.canvasHeight;
|
|
const screenRatio = this.screen.width / this.screen.height;
|
|
if (screenRatio > clientRatio) {
|
|
this.screenWidth = this.canvasWidth - 2;
|
|
this.screenHeight = Math.floor(this.canvasWidth / screenRatio);
|
|
this.screenOffsetX = 1;
|
|
this.screenOffsetY = Math.round((this.canvasHeight - this.screenHeight) / 2);
|
|
}
|
|
else {
|
|
this.screenWidth = Math.round(this.canvasHeight * screenRatio);
|
|
this.screenHeight = this.canvasHeight - 2;
|
|
this.screenOffsetY = 1;
|
|
this.screenOffsetX = Math.round((this.canvasWidth - this.screenWidth) / 2);
|
|
}
|
|
this.ctx.strokeStyle = 'rgb(0, 0, 255)';
|
|
this.ctx.rect(this.screenOffsetX, this.screenOffsetY, this.screenWidth, this.screenHeight);
|
|
this.ctx.stroke();
|
|
}
|
|
updateDisplay() {
|
|
if (!this.sequence && !this.focus && !this.framing) {
|
|
return;
|
|
}
|
|
const screenScaleX = this.screenWidth / this.screen.width;
|
|
const screenScaleY = this.screenHeight / this.screen.height;
|
|
this.displayWidth = Math.round(this.width * screenScaleX);
|
|
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(0, 0, 0)';
|
|
this.ctx.fillRect(this.displayOffsetX, this.displayOffsetY, this.displayWidth, this.displayHeight);
|
|
this.updateImage();
|
|
}
|
|
updateImage() {
|
|
this.img = new Image;
|
|
this.img.onload = function () {
|
|
this.ctx.drawImage(this.img, this.displayOffsetX, this.displayOffsetY, this.displayWidth, this.displayHeight);
|
|
}.bind(this);
|
|
this.img.src = `/${this.displayWidth}/${this.displayHeight}/image.jpg?cacheBreaker=${+new Date()}`;
|
|
}
|
|
update(msg) {
|
|
}
|
|
set(state) {
|
|
this.sequence = true;
|
|
this.focus = false;
|
|
this.offsetX = state.offset.x;
|
|
this.offsetY = state.offset.y;
|
|
this.width = state.display.width;
|
|
this.height = state.display.height;
|
|
this.updateDisplay();
|
|
}
|
|
setFocus() {
|
|
this.focus = true;
|
|
}
|
|
unsetFocus() {
|
|
this.focus = false;
|
|
}
|
|
setFraming() {
|
|
this.framing = true;
|
|
}
|
|
unsetFraming() {
|
|
this.framing = false;
|
|
}
|
|
onResize(event) {
|
|
this.updateSize();
|
|
this.clear();
|
|
this.updateScreen();
|
|
this.updateDisplay();
|
|
}
|
|
}
|
|
class Client {
|
|
constructor() {
|
|
this.connected = false;
|
|
this.frames = 0;
|
|
let uri = this.getWebsocketUri();
|
|
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);
|
|
this.resetForm('sequenceSelectForm');
|
|
this.resetForm('sequenceCtrlForm');
|
|
this.resetForm('manualCtrlForm');
|
|
this.resetForm('exposureCtrlForm');
|
|
this.resetForm('statisticsForm');
|
|
this.disableClass('sequenceCtrl');
|
|
this.disableClass('manualCtrl');
|
|
this.disableClass('exposureCtrl');
|
|
this.setProgress({ hash: null, progress: 0 });
|
|
}
|
|
getWebsocketUri() {
|
|
const host = (window.location.host + '').split(':')[0];
|
|
return `ws://${host}:${WEBSOCKET_PORT}`;
|
|
}
|
|
onMessage(event) {
|
|
const msg = JSON.parse(event.data);
|
|
if (typeof msg.cmd !== 'undefined' && msg.cmd !== null) {
|
|
this.cmd(msg);
|
|
}
|
|
}
|
|
onOpen(event) {
|
|
console.log('Connected');
|
|
this.connected = true;
|
|
this.active();
|
|
this.enableClass('manualCtrl');
|
|
}
|
|
onClose(event) {
|
|
console.log('Disconnected');
|
|
this.connected = false;
|
|
this.inactive();
|
|
}
|
|
setSequence(state) {
|
|
this.setProgress(state.sequence);
|
|
this.setFrame(state.sequence);
|
|
this.setStatus(state.sequence);
|
|
this.setExposure(state);
|
|
this.setDisplay(state);
|
|
this.set('sequence', state.sequence.hash);
|
|
this.removeClass('sequence', 'edited');
|
|
}
|
|
setUpdate(state) {
|
|
this.setProgress(state.sequence);
|
|
this.setFrame(state.sequence);
|
|
this.setStatus(state.sequence);
|
|
this.setExposure(state);
|
|
this.setStatistics(state.statistics);
|
|
this.display.updateImage();
|
|
}
|
|
setStatus(sequence) {
|
|
let status;
|
|
switch (sequence.status) {
|
|
case SequenceStatus.IDLE:
|
|
status = 'Idle';
|
|
break;
|
|
case SequenceStatus.RUNNING:
|
|
status = 'Running';
|
|
break;
|
|
case SequenceStatus.PAUSED:
|
|
status = 'Paused';
|
|
break;
|
|
default:
|
|
status = 'Unknown State';
|
|
}
|
|
this.frames = sequence.frames;
|
|
document.getElementById('sequenceStatus').innerText = status;
|
|
document.getElementById('sequenceName').innerText = sequence.name;
|
|
document.getElementById('sequenceLength').innerText = `Sequence Length: ${sequence.frames}`;
|
|
}
|
|
setProgress(sequence) {
|
|
const percent = sequence.progress * 100.0;
|
|
this.progress.value = sequence.progress;
|
|
this.progressText.innerText = `Progress: ${Math.floor(percent)}%`;
|
|
}
|
|
setFrame(sequence) {
|
|
if (typeof sequence.current !== 'undefined') {
|
|
this.set('frame', `${sequence.current}`.padStart(5, '0'));
|
|
this.removeClass('frame', 'edited');
|
|
}
|
|
}
|
|
setExposure(state) {
|
|
if (typeof state.exposure !== 'undefined') {
|
|
const el = document.getElementById('exposure');
|
|
this.enableClass('exposureCtrl');
|
|
this.set('exposure', `${state.exposure}`);
|
|
this.removeClass('exposure', 'edited');
|
|
}
|
|
}
|
|
setDisplay(state) {
|
|
if (typeof state.display !== 'undefined') {
|
|
this.set('displayWidth', state.display.width.toString());
|
|
this.set('displayHeight', state.display.height.toString());
|
|
this.set('offsetLeft', state.offset.x.toString());
|
|
this.set('offsetTop', state.offset.y.toString());
|
|
this.set('sourceWidth', state.source.width.toString());
|
|
this.set('sourceHeight', state.source.height.toString());
|
|
this.display.set(state);
|
|
}
|
|
}
|
|
edited(el) {
|
|
el.classList.add('edited');
|
|
}
|
|
setStatistics(stats) {
|
|
if (stats !== null) {
|
|
this.set('statsFrameTotalLast', this.roundDigits(stats.totalFrameLast, 0));
|
|
this.set('statsFrameTotalAvg', this.roundDigits(stats.totalFrameAvg, 2));
|
|
this.set('statsFrameTotalMargin', this.roundDigits(stats.totalFrameMargin, 1));
|
|
this.set('statsFPS', this.roundDigits(stats.fps, 2));
|
|
this.set('statsFrameLoadAvg', this.roundDigits(stats.loadAvg, 2));
|
|
this.set('statsFrameLoadMargin', this.roundDigits(stats.loadMargin, 1));
|
|
this.set('statsFrameOpenLast', this.roundDigits(stats.openLast, 0));
|
|
this.set('statsFrameOpenAvg', this.roundDigits(stats.openAvg, 2));
|
|
this.set('statsFrameOpenMargin', this.roundDigits(stats.openMargin, 1));
|
|
this.set('statsFrameCloseLast', this.roundDigits(stats.closeLast, 0));
|
|
this.set('statsFrameCloseAvg', this.roundDigits(stats.closeAvg, 2));
|
|
this.set('statsFrameCloseMargin', this.roundDigits(stats.closeMargin, 1));
|
|
this.set('statsExposureLast', this.roundDigits(stats.exposureLast, 0));
|
|
this.set('statsExposureAvg', this.roundDigits(stats.exposureAvg, 2));
|
|
this.set('statsExposureMargin', this.roundDigits(stats.exposureMargin, 1));
|
|
this.set('statsElapsed', this.roundDigits(stats.elapsed, 0));
|
|
this.set('statsEstimate', this.roundDigits(stats.estimate, 0));
|
|
this.set('statsElapsedHuman', this.shortenHumanize(Math.round(stats.elapsed)));
|
|
this.set('statsEstimateHuman', this.shortenHumanize(Math.round(stats.estimate)));
|
|
}
|
|
}
|
|
cmd(msg) {
|
|
switch (msg.cmd) {
|
|
case 'ping':
|
|
this.receivePing();
|
|
break;
|
|
case 'open':
|
|
this.receiveCameraOpen();
|
|
break;
|
|
case 'close':
|
|
this.receiveCameraClose();
|
|
break;
|
|
case 'select':
|
|
this.receiveSelect(msg);
|
|
break;
|
|
case 'update':
|
|
this.receiveUpdate(msg);
|
|
break;
|
|
case 'focus':
|
|
this.receiveFocus(msg);
|
|
break;
|
|
case 'unfocus':
|
|
this.receiveUnfocus(msg);
|
|
break;
|
|
case 'framing':
|
|
this.receiveFraming(msg);
|
|
break;
|
|
case 'unframing':
|
|
this.receiveUnframing(msg);
|
|
break;
|
|
case 'display':
|
|
this.receiveDisplay(msg);
|
|
break;
|
|
default:
|
|
console.warn(`No command "${msg.cmd}"`);
|
|
break;
|
|
}
|
|
}
|
|
disableClass(className) {
|
|
console.log(`Disabling class: ${className}`);
|
|
document.querySelectorAll(`.${className}`).forEach((el) => {
|
|
el.disabled = true;
|
|
});
|
|
}
|
|
enableClass(className) {
|
|
console.log(`Enabling class: ${className}`);
|
|
document.querySelectorAll(`.${className}`).forEach((el) => {
|
|
el.disabled = false;
|
|
});
|
|
}
|
|
sendCameraOpen() {
|
|
console.log('send camera open');
|
|
this.disableClass('manualCtrl');
|
|
this.client.send(JSON.stringify({ cmd: 'open' }));
|
|
}
|
|
receivePing() {
|
|
this.sendPong();
|
|
}
|
|
receiveCameraOpen() {
|
|
console.log('got camera open');
|
|
this.enableClass('manualCtrl');
|
|
}
|
|
sendCameraClose() {
|
|
console.log('send camera close');
|
|
this.disableClass('manualCtrl');
|
|
this.client.send(JSON.stringify({ cmd: 'close' }));
|
|
}
|
|
receiveCameraClose() {
|
|
console.log('got camera close');
|
|
this.enableClass('manualCtrl');
|
|
}
|
|
sendPong() {
|
|
this.client.send(JSON.stringify({ cmd: 'pong' }));
|
|
}
|
|
sendAdvance() {
|
|
this.client.send(JSON.stringify({ cmd: 'advance' }));
|
|
}
|
|
sendRewind() {
|
|
this.client.send(JSON.stringify({ cmd: 'rewind' }));
|
|
}
|
|
sendFrameSet() {
|
|
const frameStr = document.getElementById('frame').value;
|
|
let frameNum = null;
|
|
try {
|
|
frameNum = parseInt(frameStr);
|
|
}
|
|
catch (err) {
|
|
console.error(`Error parsing ${frameStr}`);
|
|
}
|
|
if (frameNum === null) {
|
|
frameNum = 0;
|
|
}
|
|
if (frameNum < 0) {
|
|
frameNum = 0;
|
|
}
|
|
if (frameNum > this.frames - 1) {
|
|
frameNum = this.frames - 1;
|
|
}
|
|
this.client.send(JSON.stringify({ cmd: 'set', state: { sequence: { current: frameNum } } }));
|
|
}
|
|
sendToEnd() {
|
|
this.client.send(JSON.stringify({ cmd: 'set', state: { sequence: { current: this.frames - 1 } } }));
|
|
}
|
|
sendToStart() {
|
|
this.client.send(JSON.stringify({ cmd: 'set', state: { sequence: { current: 0 } } }));
|
|
}
|
|
sendSelect() {
|
|
const hash = document.getElementById('sequence').value;
|
|
let msg;
|
|
if (hash === '- Select Image Sequence -') {
|
|
return;
|
|
}
|
|
msg = { cmd: 'select', state: { sequence: { hash } } };
|
|
console.log(`send select ${hash}`);
|
|
this.client.send(JSON.stringify(msg));
|
|
}
|
|
receiveSelect(msg) {
|
|
this.enableClass('sequenceCtrl');
|
|
this.setSequence(msg.state);
|
|
}
|
|
sendStart() {
|
|
this.client.send(JSON.stringify({ cmd: 'start' }));
|
|
}
|
|
sendStop() {
|
|
this.client.send(JSON.stringify({ cmd: 'stop' }));
|
|
}
|
|
sendExposure() {
|
|
const exposure = parseInt(this.get('exposure'));
|
|
this.client.send(JSON.stringify({ cmd: 'exposure', state: { exposure } }));
|
|
}
|
|
receiveUpdate(msg) {
|
|
this.setUpdate(msg.state);
|
|
}
|
|
receiveDisplay(msg) {
|
|
this.display.clear();
|
|
this.display.updateScreen();
|
|
this.setDisplay(msg.state);
|
|
}
|
|
sendFocus() {
|
|
console.log('send focus');
|
|
this.client.send(JSON.stringify({ cmd: 'focus' }));
|
|
}
|
|
receiveFocus(msg) {
|
|
this.display.setFocus();
|
|
this.display.updateImage();
|
|
}
|
|
receiveUnfocus(msg) {
|
|
this.display.unsetFocus();
|
|
this.display.updateImage();
|
|
}
|
|
sendFraming() {
|
|
console.log('send framing');
|
|
this.client.send(JSON.stringify({ cmd: 'framing' }));
|
|
}
|
|
receiveFraming(msg) {
|
|
this.display.setFraming();
|
|
this.display.updateImage();
|
|
}
|
|
receiveUnframing(msg) {
|
|
this.display.unsetFraming();
|
|
this.display.updateImage();
|
|
}
|
|
sendOffset(x, y) {
|
|
this.client.send(JSON.stringify({ cmd: 'offset', x, y }));
|
|
}
|
|
sendSize(width, height) {
|
|
this.client.send(JSON.stringify({ cmd: 'size', width, height }));
|
|
}
|
|
sendScale(scale) {
|
|
this.client.send(JSON.stringify({ cmd: 'scale', scale }));
|
|
}
|
|
fullscreen() {
|
|
if (!document.fullscreenElement) {
|
|
document.documentElement.requestFullscreen();
|
|
}
|
|
else {
|
|
this.exitFullscreen();
|
|
}
|
|
}
|
|
exitFullscreen() {
|
|
if (document.fullscreenElement) {
|
|
document.exitFullscreen();
|
|
}
|
|
}
|
|
active() {
|
|
this.addClass('overlay', 'active');
|
|
}
|
|
inactive() {
|
|
this.removeClass('overlay', 'active');
|
|
}
|
|
addClass(id, className) {
|
|
document.getElementById(id).classList.add(className);
|
|
}
|
|
removeClass(id, className) {
|
|
document.getElementById(id).classList.remove(className);
|
|
}
|
|
set(id, value) {
|
|
try {
|
|
document.getElementById(id).value = value;
|
|
}
|
|
catch (err) {
|
|
console.warn(`Element ${id} does not exist or cannot be set`);
|
|
}
|
|
}
|
|
get(id) {
|
|
return document.getElementById(id).value;
|
|
}
|
|
resetForm(id) {
|
|
document.getElementById(id).reset();
|
|
}
|
|
shortenHumanize(val) {
|
|
const str = humanizeDuration(val, { round: true });
|
|
return str.replace('years', 'y').replace('year', 'y')
|
|
.replace('months', 'mo').replace('month', 'mo')
|
|
.replace('weeks', 'w').replace('week', 'w')
|
|
.replace('days', 'd').replace('day', 'd')
|
|
.replace('hours', 'h').replace('hour', 'h')
|
|
.replace('minutes', 'm').replace('minute', 'm')
|
|
.replace('seconds', 's').replace('second', 's');
|
|
}
|
|
roundDigits(val, digits) {
|
|
const mult = Math.pow(10.0, digits);
|
|
const rounded = Math.round(val * mult) / mult;
|
|
return rounded.toString();
|
|
}
|
|
}
|
|
client = new Client();
|
|
//# sourceMappingURL=index.js.map
|