"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Sequence = void 0; const files_1 = require("../files"); const log_1 = require("../log"); var SequenceStatus; (function (SequenceStatus) { SequenceStatus[SequenceStatus["IDLE"] = 0] = "IDLE"; SequenceStatus[SequenceStatus["RUNNING"] = 1] = "RUNNING"; SequenceStatus[SequenceStatus["PAUSED"] = 2] = "PAUSED"; })(SequenceStatus || (SequenceStatus = {})); class Statistics { constructor(exposure, frames) { this.exposure = exposure; this.frames = frames; this.frameLoad = []; this.frameOpen = []; this.frameExposureElapsed = []; this.frameExposureReported = []; this.frameClose = []; this.frameTotal = []; } add(load, open, exposureElapsed, exposureReported, close, total) { this.frameLoad.push(load); this.frameOpen.push(open); this.frameExposureElapsed.push(exposureElapsed); this.frameExposureReported.push(exposureReported); this.frameClose.push(close); this.frameTotal.push(total); } average(arr) { return arr.reduce((a, b) => a + b) / arr.length; } elapsed() { return this.frameTotal.reduce((a, b) => a + b); } estimate(frame, avg) { const frames = this.frames - frame; return frames * avg; } margin(target, arr) { const min = Math.min(...arr); const max = Math.max(...arr); const diff = Math.abs(max - min); return (diff / target) / 2; } calculate(frame) { if (this.frameTotal.length === 0) { return null; } const totalFrameAvg = this.average(this.frameTotal); const openAvg = this.average(this.frameOpen); const closeAvg = this.average(this.frameClose); const exposureAvg = this.average(this.frameExposureReported); const loadAvg = this.average(this.frameLoad); return { totalFrameLast: this.frameTotal[this.frameTotal.length - 1], totalFrameAvg, totalFrameMargin: this.margin(totalFrameAvg, this.frameTotal) * 100.0, fps: 1000.0 / totalFrameAvg, loadLast: this.frameLoad[this.frameLoad.length - 1], loadAvg, loadMargin: this.margin(loadAvg, this.frameLoad), openLast: this.frameOpen[this.frameOpen.length - 1], openAvg, openMargin: this.margin(openAvg, this.frameOpen) * 100.0, closeLast: this.frameClose[this.frameClose.length - 1], closeAvg, closeMargin: this.margin(closeAvg, this.frameClose) * 100.0, exposureLast: this.frameExposureReported[this.frameExposureReported.length - 1], exposureAvg, exposureMargin: this.margin(exposureAvg, this.frameExposureReported) * 100.0, elapsed: this.elapsed(), estimate: this.estimate(frame, totalFrameAvg) }; } } class Sequence { constructor(camera, fd, display, ffprobe, send) { this.current = null; this.info = null; this.images = []; this.stats = null; this.running = false; this.paused = false; this.progress = 0; this.frame = 0; this.frames = 0; this.exposure = 1000; this.log = (0, log_1.createLog)('seq'); this.camera = camera; this.fd = fd; this.display = display; this.ffprobe = ffprobe; this.send = send; } start() { if (this.current !== null) { this.running = true; this.log.info(`Started sequence: ${this.current.name}`); this.stats = new Statistics(this.exposure, this.frames); this.run(); } } stop() { if (this.running && this.current !== null) { this.log.info(`Stopped sequence: ${this.current.name}`); this.running = false; } } isRunning() { return this.running; } isLoaded() { return this.current !== null; } 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 this.updateClientsOnState(); this.stats = null; } async load(seq) { this.current = seq; this.frame = 0; this.progress = 0; await this.enumerate(); this.updateClientsOnLoad(); } updateClientsOnLoad() { if (this.current !== null) { this.send({ cmd: 'select', state: this.getState() }); } } updateClientsOnState() { if (this.current !== null) { this.send({ cmd: 'update', state: this.getState() }); } } updateClientsOnDisplay() { if (this.current !== null) { this.send({ cmd: 'display', state: this.getState() }); } } async enumerate() { let screen; if (this.current === null) { this.log.error('Cannot enumerate sequence because it is not set'); return; } try { this.images = await files_1.Files.enumerateSequence(this.current.path); } catch (err) { this.log.error(`Error enumerating images in sequence: ${this.current.name}`, err); return; } this.frames = this.images.length; this.log.info(`Sequence ${this.current.name} contains ${this.images.length} image${this.images.length === 1 ? '' : 's'}`); if (this.frames > 0) { this.info = await this.ffprobe.info(this.images[0].path); } if (this.info !== null) { screen = this.display.getScreen(); this.display.setSource(this.info.width, this.info.height); this.log.info(`Screen : ${screen.width},${screen.height}`); this.log.info(`Sequence : ${this.info.width},${this.info.height}`); this.log.info(`Display : ${JSON.stringify(this.display.getOutgoingPosition())}`); } } unload() { this.current = null; this.info = null; this.images = []; } getState() { const dimensions = this.display.getOutgoingPosition(); const source = this.display.getSource(); const screen = this.display.getScreen(); return { display: { width: dimensions.w, height: dimensions.h }, offset: { x: dimensions.x, y: dimensions.y }, screen, source, sequence: { hash: this.current.hash, name: this.current.name, progress: this.progress, current: this.frame, frames: this.frames, status: this.getStatus() }, exposure: this.exposure, statistics: this.stats !== null ? this.stats.calculate(this.frame) : null }; } 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; } return { hash: this.current.hash, name: this.current.name, progress: this.progress }; } setExposure(ms) { this.exposure = ms; this.log.info(`Updated exposure: ${ms}ms`); this.updateClientsOnState(); } getStatus() { if (this.running && this.paused) { return SequenceStatus.PAUSED; } else if (this.running && !this.paused) { return SequenceStatus.RUNNING; } return SequenceStatus.IDLE; } getCurrent() { if (this.current !== null && this.images.length > 0 && typeof this.images[this.frame] !== 'undefined') { return this.images[this.frame]; } 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.frame = frame; this.progress = this.frame > 0 ? this.frame / (this.images.length - 1) : 0; this.updateClientsOnState(); } updateOffset(x, y) { const current = this.display.getOutgoingPosition(); const screen = this.display.getScreen(); let totalX; let totalY; let updated = false; if (x !== 0) { if (current.x + x < 0) { return; } totalX = current.x + current.w + x; if (totalX > screen.width) { return; } this.display.setOffsetX(current.x + x); updated = true; } if (y !== 0) { if (current.y + y < 0) { return; } totalY = current.y + current.h + y; if (totalY > screen.height) { return; } this.display.setOffsetY(current.y + y); updated = true; } if (updated) { this.updateClientsOnDisplay(); } } updateSize(width, height) { const current = this.display.getOutgoingPosition(); const screen = this.display.getScreen(); let totalX; let totalY; let updated = false; if (width !== 0) { if (current.w + width < 1) { return; } totalX = current.x + current.w + width; if (totalX > screen.width) { return; } this.display.setWidth(current.w + width); updated = true; } if (height !== 0) { if (current.h + height < 1) { return; } totalY = current.y + current.h + height; if (totalY > screen.height) { return; } this.display.setHeight(current.h + height); updated = true; } if (updated) { this.updateClientsOnDisplay(); } } updateScale(scale) { const current = this.display.getDimensions(); const offset = this.display.getOffset(); const screen = this.display.getScreen(); console.dir(scale); } async frameRecord() { const start = Date.now(); let load; let open; let exposureElapsed; let exposureReported; let close; let total; let result; const img = this.images[this.frame]; const dimensions = this.display.getOutgoingPosition(); this.log.info(`Frame: ${this.frame} / ${this.images.length}`); await this.fd.load(img.path, dimensions.x, dimensions.y, dimensions.w, dimensions.h); load = Date.now() - start; await this.camera.open(); open = Date.now() - start - load; result = await this.fd.display(img.path, [this.exposure]); exposureReported = result.reported; exposureElapsed = Date.now() - start - load - open; await this.camera.close(); close = Date.now() - start - load - open - exposureElapsed; total = Date.now() - start; this.stats.add(load, open, exposureElapsed, exposureReported, close, total); } } exports.Sequence = Sequence; //# sourceMappingURL=index.js.map