Can use fi in filmout_manager sequences. Can set in the UI and set the exposure times in the UI

This commit is contained in:
mattmcw 2026-03-28 18:55:26 -04:00
parent fc8fc1b92b
commit 8547e905da
16 changed files with 532 additions and 37 deletions

View File

@ -51,7 +51,9 @@ interface State {
screen? : StateDimensions,
sequence? : SequenceState,
statistics? : SequenceStatistics,
exposure? : number
mode? : any,
exposure? : number,
exposures? : number[]
}
interface Message {
@ -62,6 +64,7 @@ interface Message {
width? : number;
height? : number;
scale? : number;
mode? : string;
}
interface Channels {

View File

@ -207,6 +207,7 @@ class Client {
this.resetForm('manualCtrlForm');
this.resetForm('exposureCtrlForm');
this.resetForm('statisticsForm');
this.resetForm('displayAdjustForm');
this.disableClass('sequenceCtrl');
this.disableClass('manualCtrl');
@ -246,6 +247,8 @@ class Client {
this.setFrame(state.sequence);
this.setStatus(state.sequence);
this.setExposure(state);
this.setExposures(state);
this.setImageMode(state);
this.setDisplay(state);
this.set('sequence', state.sequence.hash);
this.removeClass('sequence', 'edited');
@ -256,6 +259,8 @@ class Client {
this.setFrame(state.sequence);
this.setStatus(state.sequence);
this.setExposure(state);
this.setExposures(state);
this.setImageMode(state);
this.setStatistics(state.statistics);
this.display.updateImage();
}
@ -303,6 +308,41 @@ class Client {
}
}
private setExposures (state : State) {
if (typeof state.exposures !== 'undefined') {
const r : HTMLInputElement = document.getElementById('exposureR') as HTMLInputElement;
const g : HTMLInputElement = document.getElementById('exposureG') as HTMLInputElement;
const b : HTMLInputElement = document.getElementById('exposureB') as HTMLInputElement;
this.enableClass('exposureCtrl');
this.set('exposureR', `${state.exposures[0]}`);
this.set('exposureG', `${state.exposures[1]}`);
this.set('exposureB', `${state.exposures[2]}`);
this.removeClass('exposureR', 'edited');
this.removeClass('exposureG', 'edited');
this.removeClass('exposureB', 'edited');
}
}
private setImageMode (state : State) {
if (typeof state.mode !== 'undefined') {
(document.getElementById(`imageMode-${state.mode}`) as HTMLInputElement).checked = true;
const exposureCtrl : HTMLElement = document.getElementById('exposureCtrl');
if (state.mode === 'rgb' || state.mode === 'irgb') {
if (exposureCtrl.classList.contains('single')) {
exposureCtrl.classList.replace('single', 'multiple');
}
} else {
if (exposureCtrl.classList.contains('multiple')) {
exposureCtrl.classList.replace('multiple', 'single');
}
}
}
}
private setDisplay (state : State) {
if (typeof state.display !== 'undefined') {
this.set('displayWidth', state.display.width.toString());
@ -497,8 +537,21 @@ class Client {
}
public sendExposure () {
const exposure : number = parseInt(this.get('exposure'));
this.client.send(JSON.stringify({ cmd : 'exposure', state : { exposure }}));
let exposure : number;
let r : number;
let g : number;
let b : number;
if (document.getElementById('exposureCtrl').classList.contains('single')) {
exposure = parseInt(this.get('exposure'));
this.client.send(JSON.stringify({ cmd : 'exposure', state : { exposure }}));
} else {
r = parseInt(this.get('exposureR'));
g = parseInt(this.get('exposureG'));
b = parseInt(this.get('exposureB'));
this.client.send(JSON.stringify({ cmd : 'exposures', state : { exposures : [r, g, b] }}));
}
}
private receiveUpdate (msg : Message) {
@ -567,10 +620,22 @@ class Client {
this.client.send(JSON.stringify({ cmd : 'blank' }));
}
public sendImageMode () {
const radioName : string = `imageMode`;
const checked : HTMLInputElement = document.querySelector(`input[name="${radioName}"]:checked`);
const mode : string = checked.value;
console.log(`sent mode: ${mode}`);
this.send({ cmd : 'mode', mode });
}
/**
* HELPERS
**/
private send (msg : any) {
this.client.send(JSON.stringify(msg));
}
public fullscreen () {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();

19
dist/index.js vendored
View File

@ -249,6 +249,9 @@ async function cmd(msg) {
case 'exposure':
exposureSet(msg.state.exposure);
break;
case 'exposures':
exposuresSet(msg.state.exposures);
break;
case 'focus':
await focus();
break;
@ -267,6 +270,9 @@ async function cmd(msg) {
case 'scale':
scale(msg);
break;
case 'mode':
modeSet(msg);
break;
default:
log.warn(`No matching command: ${msg.cmd}`);
}
@ -297,6 +303,15 @@ function exposureSet(exposure) {
}
sequence.setExposure(exposure);
}
function exposuresSet(exposures) {
const r = exposures[0] > 0 ? exposures[0] : 1;
const g = exposures[1] > 0 ? exposures[1] : 1;
const b = exposures[2] > 0 ? exposures[2] : 1;
sequence.setExposures(r, g, b);
}
function modeSet(msg) {
sequence.setMode(msg.mode);
}
async function select(id) {
const sequencesArr = await files_1.Files.enumerateSequences();
const seq = sequencesArr.find(el => el.hash === id);
@ -367,7 +382,7 @@ async function focus() {
};
}
focusImage = await testimage_1.TestImage.Focus(pos.w, pos.h);
fd.setMode(fd_1.Mode.RGB);
fd.setMode(fd_1.Mode.RGB); // this never changes
await fd.load(focusImage, pos.x, pos.y, pos.w, pos.h);
await fd.display(focusImage);
send({ cmd: 'focus' });
@ -512,7 +527,7 @@ async function main() {
log.info(`filmout_manager WebSocket server running on port ${wsPort}`);
//ffmpeg.listFormats();
//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, image, send);
setInterval(keepAlive, 30000);
}
main();

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -3,6 +3,7 @@ import type { FD } from '../fd';
import type { Camera } from '../camera';
import type { Display } from '../display';
import type { FFPROBE } from '../ffprobe';
import type { Image } from '../image';
declare enum SequenceStatus {
IDLE = 0,
RUNNING = 1,
@ -16,6 +17,7 @@ export declare class Sequence {
private camera;
private display;
private ffprobe;
private image;
private fd;
private send;
private stats;
@ -24,8 +26,10 @@ export declare class Sequence {
private progress;
private frame;
private frames;
private mode;
private exposure;
constructor(camera: Camera, fd: FD, display: Display, ffprobe: FFPROBE, send: Function);
private exposures;
constructor(camera: Camera, fd: FD, display: Display, ffprobe: FFPROBE, image: Image, send: Function);
start(): void;
stop(): void;
isRunning(): boolean;
@ -41,6 +45,8 @@ export declare class Sequence {
getUpdateState(): State;
getSequenceState(): SequenceState;
setExposure(ms: number): void;
setExposures(r: number, g: number, b: number): void;
setMode(mode: string): void;
getStatus(): SequenceStatus;
getCurrent(): ImageObject;
frameAdvance(frames?: number): void;
@ -50,5 +56,6 @@ export declare class Sequence {
updateSize(width: number, height: number): void;
updateScale(scale: number): void;
private frameRecord;
private frameRecordRGB;
}
export {};

157
dist/sequence/index.js vendored
View File

@ -3,12 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.Sequence = void 0;
const files_1 = require("../files");
const log_1 = require("../log");
const promises_1 = require("fs/promises");
var SequenceStatus;
(function (SequenceStatus) {
SequenceStatus[SequenceStatus["IDLE"] = 0] = "IDLE";
SequenceStatus[SequenceStatus["RUNNING"] = 1] = "RUNNING";
SequenceStatus[SequenceStatus["PAUSED"] = 2] = "PAUSED";
})(SequenceStatus || (SequenceStatus = {}));
var ImageMode;
(function (ImageMode) {
ImageMode["Default"] = "8bit";
ImageMode["Inverted"] = "inverted";
ImageMode["RGB"] = "rgb";
ImageMode["IRGB"] = "irgb";
})(ImageMode || (ImageMode = {}));
class Statistics {
constructor(exposure, frames) {
this.exposure = exposure;
@ -76,7 +84,7 @@ class Statistics {
}
}
class Sequence {
constructor(camera, fd, display, ffprobe, send) {
constructor(camera, fd, display, ffprobe, image, send) {
this.current = null;
this.info = null;
this.images = [];
@ -86,12 +94,15 @@ class Sequence {
this.progress = 0;
this.frame = 0;
this.frames = 0;
this.mode = ImageMode.Default;
this.exposure = 1000;
this.exposures = [1000, 1000, 1000];
this.log = (0, log_1.createLog)('seq');
this.camera = camera;
this.fd = fd;
this.display = display;
this.ffprobe = ffprobe;
this.image = image;
this.send = send;
}
start() {
@ -120,11 +131,22 @@ class Sequence {
if (!this.running) {
break;
}
try {
await this.frameRecord();
console.dir(this.mode);
if (this.mode === ImageMode.Default || this.mode === ImageMode.Inverted) {
try {
await this.frameRecord(this.mode === ImageMode.Inverted);
}
catch (err) {
this.log.error(`Error recording frame`, err);
}
}
catch (err) {
this.log.error(`Error recording frame`, err);
else if (this.mode === ImageMode.RGB || this.mode === ImageMode.IRGB) {
try {
await this.frameRecordRGB(this.mode === ImageMode.IRGB);
}
catch (err) {
this.log.error(`Error recording frame`, err);
}
}
this.frameAdvance();
}
@ -218,7 +240,9 @@ class Sequence {
frames: this.frames,
status: this.getStatus()
},
mode: this.mode,
exposure: this.exposure,
exposures: this.exposures,
statistics: this.stats !== null ? this.stats.calculate(this.frame) : null
};
}
@ -250,6 +274,18 @@ class Sequence {
this.log.info(`Updated exposure: ${ms}ms`);
this.updateClientsOnState();
}
setExposures(r, g, b) {
this.exposures = [r, g, b];
this.log.info(`Updated exposures: ${r}ms, ${g}ms, ${b}ms`);
this.updateClientsOnState();
}
setMode(mode) {
if (this.mode !== mode) {
this.log.info(`Update mode: ${mode}`);
}
this.mode = mode;
this.updateClientsOnState();
}
getStatus() {
if (this.running && this.paused) {
return SequenceStatus.PAUSED;
@ -381,7 +417,7 @@ class Sequence {
this.display.setOffsetY(newOffsetY);
this.updateClientsOnDisplay();
}
async frameRecord() {
async frameRecord(invert = false) {
const start = Date.now();
let load;
let open;
@ -390,11 +426,21 @@ class Sequence {
let close;
let total;
let result;
let inverted;
const img = this.images[this.frame];
const dimensions = this.display.getOutgoingPosition();
this.log.info(`Frame: ${this.frame} / ${this.images.length}`);
if (invert) {
try {
inverted = await this.image.invert(img.path);
this.log.info(`Inverted ${img.path}`);
}
catch (err) {
this.log.error(`Error inverting image`, err);
}
}
try {
await this.fd.load(img.path, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
await this.fd.load(invert ? inverted : img.path, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
}
catch (err) {
this.log.error(`Error loading image ${img.path}`, err);
@ -421,6 +467,103 @@ class Sequence {
catch (err) {
this.log.error(`Error closing camera`, err);
}
if (invert) {
await (0, promises_1.unlink)(inverted);
}
close = Date.now() - start - load - open - exposureElapsed;
total = Date.now() - start;
this.stats.add(load, open, exposureElapsed, exposureReported, close, total);
}
async frameRecordRGB(invert = false) {
const start = Date.now();
let load;
let open;
let exposureElapsed = 0;
let exposureReported = 0;
let close;
let total;
let result;
let channels;
const img = this.images[this.frame];
const dimensions = this.display.getOutgoingPosition();
this.log.info(`RGB: ${img.name} ${this.frame} / ${this.images.length}`);
try {
channels = await this.image.rgb(img.path, invert);
}
catch (err) {
this.log.error(`Error getting temp files for channels`, err);
}
try {
await this.camera.open();
}
catch (err) {
this.log.error(`Error opening camera`, err);
}
open = Date.now() - start - load;
try {
await this.fd.load(channels.red, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
}
catch (err) {
this.log.error(`Error loading red channel for display`, err);
}
load = Date.now() - start;
try {
await this.camera.open();
}
catch (err) {
this.log.error(`Error opening camera`, err);
}
this.log.info(`Displaying RED channel for ${img.name}`);
try {
result = await this.fd.display(channels.red, [this.exposures[0]]);
}
catch (err) {
this.log.error(`Error displaying red channel`, err);
}
exposureReported += result.reported;
try {
await this.fd.load(channels.green, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
}
catch (err) {
this.log.error(`Error loading green channel`, err);
}
this.log.info(`Displaying GREEN channel for ${img.name}`);
try {
result = await this.fd.display(channels.green, [this.exposures[1]]);
}
catch (err) {
this.log.error(`Error displaying green channel`, err);
}
exposureReported += result.reported;
try {
await this.fd.load(channels.blue, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
}
catch (err) {
this.log.error(`Error loading blue channel`, err);
}
this.log.info(`Displaying BLUE channel for ${img.name}`);
try {
result = await this.fd.display(channels.blue, [this.exposures[2]]);
}
catch (err) {
this.log.error(`Error displaying blue channel`, err);
}
exposureReported += result.reported;
try {
await (0, promises_1.unlink)(channels.red);
await (0, promises_1.unlink)(channels.green);
await (0, promises_1.unlink)(channels.blue);
}
catch (err) {
this.log.error(`Error cleaning up temp channel files`, err);
}
exposureElapsed = Date.now() - start - load - open;
try {
await this.camera.close();
}
catch (err) {
this.log.error(`Error closing camera`, err);
}
close = Date.now() - start - load - open - exposureElapsed;
total = Date.now() - start;
this.stats.add(load, open, exposureElapsed, exposureReported, close, total);

File diff suppressed because one or more lines are too long

View File

@ -96,6 +96,19 @@ html, body{
padding-right: 3px;
}
#exposureCtrl{
&.single{
.multiple{
display: none;
}
}
&.multiple{
.single{
display: none;
}
}
}
.half{
box-sizing: border-box;
flex: 0 0 50%;

5
src/globals.d.ts vendored
View File

@ -51,7 +51,9 @@ interface State {
screen? : StateDimensions,
sequence? : SequenceState,
statistics? : SequenceStatistics,
exposure? : number
mode? : any,
exposure? : number,
exposures? : number[]
}
interface Message {
@ -62,6 +64,7 @@ interface Message {
width? : number;
height? : number;
scale? : number;
mode? : string;
}
interface Channels {

View File

@ -236,6 +236,9 @@ async function cmd (msg : Message) {
case 'exposure' :
exposureSet(msg.state.exposure);
break;
case 'exposures' :
exposuresSet(msg.state.exposures);
break;
case 'focus' :
await focus();
break;
@ -254,6 +257,9 @@ async function cmd (msg : Message) {
case 'scale' :
scale(msg);
break;
case 'mode' :
modeSet(msg);
break;
default :
log.warn(`No matching command: ${msg.cmd}`);
}
@ -291,6 +297,17 @@ function exposureSet (exposure : number) {
sequence.setExposure(exposure);
}
function exposuresSet (exposures : number[]) {
const r : number = exposures[0] > 0 ? exposures[0] : 1;
const g : number = exposures[1] > 0 ? exposures[1] : 1;
const b : number = exposures[2] > 0 ? exposures[2] : 1;
sequence.setExposures(r, g, b);
}
function modeSet (msg : Message) {
sequence.setMode(msg.mode);
}
async function select (id : string) : Promise<boolean> {
const sequencesArr : SequenceObject[] = await Files.enumerateSequences();
const seq : SequenceObject = sequencesArr.find(el => el.hash === id);
@ -374,7 +391,7 @@ async function focus () {
focusImage = await TestImage.Focus(pos.w, pos.h);
fd.setMode(Mode.RGB);
fd.setMode(Mode.RGB); // this never changes
await fd.load (focusImage, pos.x, pos.y, pos.w, pos.h);
await fd.display(focusImage);
send({ cmd : 'focus' });
@ -525,7 +542,7 @@ async function main () {
//ffmpeg.listFormats();
//log.info(await TestImage.Focus(640, 480));
sequence = new Sequence(camera, fd, display, ffprobe, send);
sequence = new Sequence(camera, fd, display, ffprobe, image, send);
setInterval(keepAlive, 30000);
}

View File

@ -1,12 +1,14 @@
import { Files } from '../files';
import { createLog } from '../log';
import { delay } from '../delay';
import { unlink } from 'fs/promises';
import type { Logger } from 'winston';
import type { SequenceObject, ImageObject } from '../files';
import type { FD, fdOutgoingPosition, fdResult } from '../fd';
import type { Camera } from '../camera';
import type { Display, Dimensions, Offset } from '../display';
import type { FFPROBE, VideoInfo } from '../ffprobe';
import type { Image } from '../image';
enum SequenceStatus {
IDLE,
@ -14,6 +16,13 @@ enum SequenceStatus {
PAUSED
}
enum ImageMode {
Default = '8bit',
Inverted = 'inverted',
RGB = 'rgb',
IRGB = 'irgb'
}
class Statistics {
private frameLoad : number[];
private frameOpen : number[];
@ -113,6 +122,7 @@ export class Sequence {
private camera : Camera;
private display : Display;
private ffprobe : FFPROBE;
private image : Image;
private fd : FD;
private send : Function;
private stats : Statistics = null;
@ -123,14 +133,17 @@ export class Sequence {
private frame : number = 0;
private frames : number = 0;
private mode : ImageMode = ImageMode.Default;
private exposure : number = 1000;
private exposures : number[] = [ 1000, 1000, 1000 ];
constructor (camera : Camera, fd : FD, display: Display, ffprobe : FFPROBE, send : Function) {
constructor (camera : Camera, fd : FD, display: Display, ffprobe : FFPROBE, image : Image, send : Function) {
this.log = createLog('seq');
this.camera = camera;
this.fd = fd;
this.display = display;
this.ffprobe = ffprobe;
this.image = image;
this.send = send;
}
@ -164,11 +177,19 @@ export class Sequence {
if (!this.running) {
break;
}
try {
await this.frameRecord();
} catch (err) {
this.log.error(`Error recording frame`, err);
console.dir(this.mode)
if (this.mode === ImageMode.Default || this.mode === ImageMode.Inverted) {
try {
await this.frameRecord(this.mode === ImageMode.Inverted);
} catch (err) {
this.log.error(`Error recording frame`, err);
}
} else if (this.mode === ImageMode.RGB || this.mode === ImageMode.IRGB) {
try {
await this.frameRecordRGB(this.mode === ImageMode.IRGB);
} catch (err) {
this.log.error(`Error recording frame`, err);
}
}
this.frameAdvance();
@ -274,7 +295,9 @@ export class Sequence {
frames : this.frames,
status : this.getStatus() as SequenceStatus
},
mode : this.mode,
exposure : this.exposure,
exposures : this.exposures,
statistics : this.stats !== null ? this.stats.calculate(this.frame) : null
}
}
@ -310,6 +333,20 @@ export class Sequence {
this.updateClientsOnState();
}
public setExposures(r : number, g: number, b: number) {
this.exposures = [r, g, b];
this.log.info(`Updated exposures: ${r}ms, ${g}ms, ${b}ms`);
this.updateClientsOnState();
}
public setMode (mode : string) {
if (this.mode !== mode) {
this.log.info(`Update mode: ${mode}`);
}
this.mode = mode as ImageMode;
this.updateClientsOnState();
}
public getStatus () : SequenceStatus {
if (this.running && this.paused) {
return SequenceStatus.PAUSED;
@ -447,7 +484,7 @@ export class Sequence {
this.updateClientsOnDisplay();
}
private async frameRecord () {
private async frameRecord (invert : boolean = false) {
const start : number = Date.now();
let load : number;
let open : number;
@ -456,13 +493,23 @@ export class Sequence {
let close : number;
let total : number;
let result : fdResult;
let inverted : string;
const img : ImageObject = this.images[this.frame];
const dimensions : fdOutgoingPosition = this.display.getOutgoingPosition();
this.log.info(`Frame: ${this.frame} / ${this.images.length}`);
if (invert) {
try {
inverted = await this.image.invert(img.path);
this.log.info(`Inverted ${img.path}`);
} catch (err) {
this.log.error(`Error inverting image`, err);
}
}
try {
await this.fd.load(img.path, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
await this.fd.load(invert ? inverted : img.path, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
} catch (err) {
this.log.error(`Error loading image ${img.path}`, err);
}
@ -492,6 +539,114 @@ export class Sequence {
this.log.error(`Error closing camera`, err);
}
if (invert) {
await unlink(inverted);
}
close = Date.now() - start - load - open - exposureElapsed;
total = Date.now() - start;
this.stats.add(load, open, exposureElapsed, exposureReported, close, total);
}
private async frameRecordRGB (invert : boolean = false) {
const start : number = Date.now();
let load : number;
let open : number;
let exposureElapsed : number = 0;
let exposureReported : number = 0;
let close : number;
let total : number;
let result : fdResult;
let channels : Channels
const img : ImageObject = this.images[this.frame];
const dimensions : fdOutgoingPosition = this.display.getOutgoingPosition();
this.log.info(`RGB: ${img.name} ${this.frame} / ${this.images.length}`);
try {
channels = await this.image.rgb(img.path, invert);
} catch (err) {
this.log.error(`Error getting temp files for channels`, err);
}
try {
await this.camera.open();
} catch (err) {
this.log.error(`Error opening camera`, err);
}
open = Date.now() - start - load;
try {
await this.fd.load(channels.red, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
} catch (err) {
this.log.error(`Error loading red channel for display`, err);
}
load = Date.now() - start;
try {
await this.camera.open();
} catch (err) {
this.log.error(`Error opening camera`, err);
}
this.log.info(`Displaying RED channel for ${img.name}`);
try {
result = await this.fd.display(channels.red, [ this.exposures[0] ] );
} catch (err) {
this.log.error(`Error displaying red channel`, err);
}
exposureReported += result.reported;
try {
await this.fd.load(channels.green, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
} catch (err) {
this.log.error(`Error loading green channel`, err);
}
this.log.info(`Displaying GREEN channel for ${img.name}`);
try {
result = await this.fd.display(channels.green, [ this.exposures[1] ] );
} catch (err) {
this.log.error(`Error displaying green channel`, err);
}
exposureReported += result.reported;
try {
await this.fd.load(channels.blue, dimensions.x, dimensions.y, dimensions.w, dimensions.h);
} catch (err) {
this.log.error(`Error loading blue channel`, err);
}
this.log.info(`Displaying BLUE channel for ${img.name}`);
try {
result = await this.fd.display(channels.blue, [ this.exposures[2] ] );
} catch (err) {
this.log.error(`Error displaying blue channel`, err);
}
exposureReported += result.reported;
try {
await unlink(channels.red);
await unlink(channels.green);
await unlink(channels.blue);
} catch (err) {
this.log.error(`Error cleaning up temp channel files`, err);
}
exposureElapsed = Date.now() - start - load - open;
try {
await this.camera.close();
} catch (err) {
this.log.error(`Error closing camera`, err);
}
close = Date.now() - start - load - open - exposureElapsed;
total = Date.now() - start;
this.stats.add(load, open, exposureElapsed, exposureReported, close, total);

View File

@ -85,6 +85,12 @@ body {
display: inline-block;
padding-right: 3px;
}
#exposureCtrl.single .multiple {
display: none;
}
#exposureCtrl.multiple .single {
display: none;
}
.half {
box-sizing: border-box;
flex: 0 0 50%;

View File

@ -65,6 +65,8 @@ declare class Client {
private setProgress;
private setFrame;
private setExposure;
private setExposures;
private setImageMode;
private setDisplay;
edited(el: HTMLElement): void;
private setStatistics;
@ -100,6 +102,8 @@ declare class Client {
sendSize(width: number, height: number): void;
sendScale(scale: number): void;
sendBlank(): void;
sendImageMode(): void;
private send;
fullscreen(): void;
exitFullscreen(): void;
private active;

View File

@ -164,6 +164,7 @@ class Client {
this.resetForm('manualCtrlForm');
this.resetForm('exposureCtrlForm');
this.resetForm('statisticsForm');
this.resetForm('displayAdjustForm');
this.disableClass('sequenceCtrl');
this.disableClass('manualCtrl');
this.disableClass('exposureCtrl');
@ -195,6 +196,8 @@ class Client {
this.setFrame(state.sequence);
this.setStatus(state.sequence);
this.setExposure(state);
this.setExposures(state);
this.setImageMode(state);
this.setDisplay(state);
this.set('sequence', state.sequence.hash);
this.removeClass('sequence', 'edited');
@ -204,6 +207,8 @@ class Client {
this.setFrame(state.sequence);
this.setStatus(state.sequence);
this.setExposure(state);
this.setExposures(state);
this.setImageMode(state);
this.setStatistics(state.statistics);
this.display.updateImage();
}
@ -246,6 +251,36 @@ class Client {
this.removeClass('exposure', 'edited');
}
}
setExposures(state) {
if (typeof state.exposures !== 'undefined') {
const r = document.getElementById('exposureR');
const g = document.getElementById('exposureG');
const b = document.getElementById('exposureB');
this.enableClass('exposureCtrl');
this.set('exposureR', `${state.exposures[0]}`);
this.set('exposureG', `${state.exposures[1]}`);
this.set('exposureB', `${state.exposures[2]}`);
this.removeClass('exposureR', 'edited');
this.removeClass('exposureG', 'edited');
this.removeClass('exposureB', 'edited');
}
}
setImageMode(state) {
if (typeof state.mode !== 'undefined') {
document.getElementById(`imageMode-${state.mode}`).checked = true;
const exposureCtrl = document.getElementById('exposureCtrl');
if (state.mode === 'rgb' || state.mode === 'irgb') {
if (exposureCtrl.classList.contains('single')) {
exposureCtrl.classList.replace('single', 'multiple');
}
}
else {
if (exposureCtrl.classList.contains('multiple')) {
exposureCtrl.classList.replace('multiple', 'single');
}
}
}
}
setDisplay(state) {
if (typeof state.display !== 'undefined') {
this.set('displayWidth', state.display.width.toString());
@ -409,8 +444,20 @@ class Client {
this.client.send(JSON.stringify({ cmd: 'stop' }));
}
sendExposure() {
const exposure = parseInt(this.get('exposure'));
this.client.send(JSON.stringify({ cmd: 'exposure', state: { exposure } }));
let exposure;
let r;
let g;
let b;
if (document.getElementById('exposureCtrl').classList.contains('single')) {
exposure = parseInt(this.get('exposure'));
this.client.send(JSON.stringify({ cmd: 'exposure', state: { exposure } }));
}
else {
r = parseInt(this.get('exposureR'));
g = parseInt(this.get('exposureG'));
b = parseInt(this.get('exposureB'));
this.client.send(JSON.stringify({ cmd: 'exposures', state: { exposures: [r, g, b] } }));
}
}
receiveUpdate(msg) {
this.setUpdate(msg.state);
@ -463,6 +510,16 @@ class Client {
sendBlank() {
this.client.send(JSON.stringify({ cmd: 'blank' }));
}
sendImageMode() {
const radioName = `imageMode`;
const checked = document.querySelector(`input[name="${radioName}"]:checked`);
const mode = checked.value;
console.log(`sent mode: ${mode}`);
this.send({ cmd: 'mode', mode });
}
send(msg) {
this.client.send(JSON.stringify(msg));
}
fullscreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();

File diff suppressed because one or more lines are too long

View File

@ -88,11 +88,18 @@
<button id="select" onclick="client.sendSelect();">Select</button>
</form>
</fieldset>
<fieldset id="exposureCtrl" class="inline half">
<fieldset id="exposureCtrl" class="inline half single">
<legend>Exposure</legend>
<form id="exposureCtrlForm" onsubmit="return false;">
<input id="exposure" class="large exposureCtrl" type="number" value="0" oninput="client.edited(this);" disabled />
<span>ms </span>
<input id="exposure" class="large exposureCtrl single" type="number" value="0" oninput="client.edited(this);" disabled />
<span class="single">ms </span>
<input id="exposureR" class="large exposureCtrl multiple" type="number" value="0" oninput="client.edited(this);" disabled />
<span class="multiple">ms </span>
<input id="exposureG" class="large exposureCtrl multiple" type="number" value="0" oninput="client.edited(this);" disabled />
<span class="multiple">ms </span>
<input id="exposureB" class="large exposureCtrl multiple" type="number" value="0" oninput="client.edited(this);" disabled />
<span class="multiple">ms </span>
<button id="exposureUpdate" class="exposureCtrl" onclick="client.sendExposure();">Update</button>
</form>
</fieldset>
@ -193,21 +200,21 @@
<button class="small manualCtrl" id="scaleMinus" onclick="client.sendScale(-2);" disabled>S -</button>
</div>
</span>
<span>
<span style="margin-left: 15px;">
<div class="check">
<input type="radio" name="imageMode" id="imageMode-8bit" value="8bit" checked />
<input type="radio" name="imageMode" id="imageMode-8bit" value="8bit" checked onclick="client.sendImageMode();" />
<label for="imageMode-8bit">Positive 8bit</label>
</div>
<div class="check">
<input type="radio" name="imageMode" id="imageMode-inverted" value="inverted" />
<input type="radio" name="imageMode" id="imageMode-inverted" value="inverted" onclick="client.sendImageMode();" />
<label for="imageMode-inverted">Negative 8bit</label>
</div>
<div class="check">
<input type="radio" name="imageMode" id="imageMode-rgb" value="rgb" />
<input type="radio" name="imageMode" id="imageMode-rgb" value="rgb" onclick="client.sendImageMode();" />
<label for="imageMode-rgb">Positive RGB</label>
</div>
<div class="check">
<input type="radio" name="imageMode" id="imageMode-irgb" value="irgb" />
<input type="radio" name="imageMode" id="imageMode-irgb" value="irgb" onclick="client.sendImageMode();" />
<label for="imageMode-irgb">Negative RGB</label>
</div>
</span>