UNPKG

@opalkelly/frontpanel-platform-api

Version:

TypeScript definitions for Opal Kelly FrontPanel Platform API

190 lines (150 loc) 6.04 kB
/** * Copyright (c) 2024 Opal Kelly Incorporated * * This source code is licensed under the FrontPanel license. * See the LICENSE file found in the root directory of this project. */ import IFrontPanelEventSource from "./IFrontPanelEventSource"; import IFrontPanel from "./IFrontPanel"; import { IFrontPanelEvent } from "./IFrontPanelEvent"; import FrontPanelEvent from "./FrontPanelEvent"; /** * Class representing a timer that periodically updates WireOuts and TriggerOuts and dispatches * events to notify subscribers of changes. */ class FrontPanelPeriodicUpdateTimer implements IFrontPanelEventSource { /** * Event that is dispatched when WireOut values change. */ private readonly _Device: IFrontPanel; /** * Event that notifies subscribers when WireOut values change. */ private readonly _WireOutValuesChangedEvent = new FrontPanelEvent(); /** * Event that notifies subscribers when TriggerOut values change. */ private readonly _TriggerOutValuesChangedEvent = new FrontPanelEvent(); /** * Reference to the update timer loop used to identify when the timer * loop has started and when it has exited. */ private _UpdateTimer: Promise<void> | null = null; /** * The period in milliseconds between updates. */ private _UpdatePeriodMilliseconds: number; /** * Flag indicating whether the timer loop is currently running. */ private _IsRunning: boolean = false; /** * Flag indicating that the timer loop should exit on the next iteration. */ private _IsStopPending: boolean = false; /** * Function that cancels the timeout used to wait for the next update period. */ private _CancelTimeout?: () => void; /** * Event that notifies subscribers when WireOut values change. */ public get wireOutValuesChangedEvent(): IFrontPanelEvent { return this._WireOutValuesChangedEvent; } /** * Event that notifies subscribers when TriggerOut values change. */ public get triggerOutValuesChangedEvent(): IFrontPanelEvent { return this._TriggerOutValuesChangedEvent; } /** * Creates a new instance of the FrontPanelPeriodicUpdateTimer. * @param device - The interface to the FrontPanel device that is the source of WireOuts and TriggerOuts. * @param periodMilliseconds - The period in milliseconds between updates. */ constructor(device: IFrontPanel, periodMilliseconds: number) { this._Device = device; this._UpdatePeriodMilliseconds = periodMilliseconds; } /** * Starts the update timer loop if it is not already running. * @returns true if the timer loop was successfully started; false if the timer loop was already started. */ public async start(): Promise<boolean> { let retval: boolean; // Wait for the timer to stop if stop is pending if (this._IsStopPending) { await this._UpdateTimer; } // Start the timer loop if it is currently stopped if (!this._IsRunning) { this._UpdateTimer = this.updateTimerLoop(); retval = this._IsRunning; } else { retval = false; //ERROR: Timer already started } return retval; } /** * Stops the update timer loop and returns a promise that resolves when the loop has exited. * @returns A promise that resolves when the timer loop has exited. */ public async stop(): Promise<void> { if (!this._IsStopPending) { this._IsStopPending = true; if (this._CancelTimeout) { this._CancelTimeout(); } if (this._UpdateTimer !== null) { await this._UpdateTimer; this._UpdateTimer = null; } this._IsStopPending = false; } } /** * The main loop for the update timer. It periodically updates WireOuts and TriggerOuts, and * dispatches the events. * @returns A promise that resolves when the timer loop has exited. */ private async updateTimerLoop(): Promise<void> { this._IsRunning = true; while (!this._IsStopPending) { const start: number = performance.now(); // Update WireOuts and TriggerOuts and dispatch events await this.updateWireOuts(); await this.updateTriggerOuts(); const elapsed: number = performance.now() - start; // NOTE: Stop pending can change while waiting for the update promises to // resolve so we check it again before waiting for the next update period. if (!this._IsStopPending) { // Wait until the update period has elapsed const delay: number = this._UpdatePeriodMilliseconds - elapsed; await new Promise<void>((resolve) => { const timeoutId = setTimeout(resolve, delay); this._CancelTimeout = () => { clearTimeout(timeoutId); resolve(); }; }); } } this._IsRunning = false; } /** * Updates the WireOuts and dispatches the WireOutValuesChangedEvent. */ private async updateWireOuts(): Promise<void> { await this._Device.updateWireOuts(); this._WireOutValuesChangedEvent.dispatch(this._Device); } /** * Updates the TriggerOuts and dispatches the TriggerOutValuesChangedEvent. */ private async updateTriggerOuts(): Promise<void> { await this._Device.updateTriggerOuts(); this._TriggerOutValuesChangedEvent.dispatch(this._Device); } } export default FrontPanelPeriodicUpdateTimer;