Refactor Polling Mechanism: Implement Class-Based Poller with Start/Stop Control (#528)
* custom module updates to class based. * Finish poller logic. * Use composition for pollers * Rename poller * Handle recorder polling. * Fix quotes in bash command * Remove logs
This commit is contained in:
90
lib/poller/BashPoller.ts
Normal file
90
lib/poller/BashPoller.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { Variable as VariableType } from 'types/variable';
|
||||
import { Bind } from 'lib/types/variable';
|
||||
import { GenericFunction } from 'lib/types/customModules/generic';
|
||||
import { BarModule } from 'lib/types/options';
|
||||
import { Poller } from './Poller';
|
||||
|
||||
/**
|
||||
* A class that manages polling of a variable by executing a bash command at specified intervals.
|
||||
*/
|
||||
export class BashPoller<Value, Parameters extends unknown[]> {
|
||||
private poller: Poller;
|
||||
|
||||
private params: Parameters;
|
||||
|
||||
/**
|
||||
* Creates an instance of BashPoller.
|
||||
*
|
||||
* @param targetVariable - The target variable to poll.
|
||||
* @param trackers - An array of trackers to monitor.
|
||||
* @param pollingInterval - The interval at which polling occurs.
|
||||
* @param updateCommand - The command to update the target variable.
|
||||
* @param pollingFunction - The function to execute during each poll.
|
||||
* @param params - Additional parameters for the polling function.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* //##################### EXAMPLE ##########################
|
||||
* const updatesPoller = new BashPoller<string, []>(
|
||||
* pendingUpdates,
|
||||
* [padZero.bind('value'), postInputUpdater.bind('value')],
|
||||
* pollingInterval.bind('value'),
|
||||
* updateCommand.value,
|
||||
* processUpdateCount,
|
||||
* );
|
||||
* //#######################################################
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
constructor(
|
||||
private targetVariable: VariableType<Value>,
|
||||
private trackers: Bind[],
|
||||
private pollingInterval: Bind,
|
||||
private updateCommand: string,
|
||||
private pollingFunction: GenericFunction<Value, [string, ...Parameters]>,
|
||||
...params: Parameters
|
||||
) {
|
||||
this.params = params;
|
||||
|
||||
this.poller = new Poller(this.pollingInterval, this.trackers, this.execute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the bash command specified in the updateCommand property.
|
||||
*
|
||||
* The result of the command is processed by the pollingFunction and
|
||||
* assigned to the targetVariable.
|
||||
*/
|
||||
public execute = async (): Promise<void> => {
|
||||
try {
|
||||
const res = await Utils.execAsync(`bash -c "${this.updateCommand}"`);
|
||||
this.targetVariable.value = await this.pollingFunction(res, ...this.params);
|
||||
} catch (error) {
|
||||
console.error(`Error executing bash command "${this.updateCommand}":`, error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Starts the polling process.
|
||||
*/
|
||||
public start(): void {
|
||||
this.poller.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the polling process.
|
||||
*/
|
||||
public stop(): void {
|
||||
this.poller.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the poller with the specified module.
|
||||
*
|
||||
* @param moduleName - The name of the module to initialize.
|
||||
*/
|
||||
public initialize(moduleName?: BarModule): void {
|
||||
this.poller.initialize(moduleName);
|
||||
}
|
||||
}
|
||||
86
lib/poller/FunctionPoller.ts
Normal file
86
lib/poller/FunctionPoller.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Variable as VariableType } from 'types/variable';
|
||||
import { Bind } from 'lib/types/variable';
|
||||
import { GenericFunction } from 'lib/types/customModules/generic';
|
||||
import { BarModule } from 'lib/types/options';
|
||||
import { Poller } from './Poller';
|
||||
|
||||
/**
|
||||
* A class that manages polling of a variable by executing a generic function at specified intervals.
|
||||
*/
|
||||
export class FunctionPoller<Value, Parameters extends unknown[] = []> {
|
||||
private poller: Poller;
|
||||
|
||||
private params: Parameters;
|
||||
|
||||
/**
|
||||
* Creates an instance of FunctionPoller.
|
||||
*
|
||||
* @param targetVariable - The target variable to poll.
|
||||
* @param trackers - An array of trackers to monitor.
|
||||
* @param pollingInterval - The interval at which polling occurs.
|
||||
* @param pollingFunction - The function to execute during each poll.
|
||||
* @param params - Additional parameters for the polling function.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* //##################### EXAMPLE ##########################
|
||||
* const cpuPoller = new FunctionPoller<number, []>(
|
||||
* cpuUsage,
|
||||
* [round.bind('value')],
|
||||
* pollingInterval.bind('value'),
|
||||
* computeCPU,
|
||||
* );
|
||||
* //#######################################################
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
constructor(
|
||||
private targetVariable: VariableType<Value>,
|
||||
private trackers: Bind[],
|
||||
private pollingInterval: Bind,
|
||||
private pollingFunction: GenericFunction<Value, Parameters>,
|
||||
...params: Parameters
|
||||
) {
|
||||
this.params = params;
|
||||
|
||||
this.poller = new Poller(this.pollingInterval, this.trackers, this.execute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the polling function with the provided parameters.
|
||||
*
|
||||
* The result of the function is assigned to the target variable.
|
||||
*/
|
||||
private execute = async (): Promise<void> => {
|
||||
try {
|
||||
const result = await this.pollingFunction(...this.params);
|
||||
this.targetVariable.value = result;
|
||||
} catch (error) {
|
||||
console.error('Error executing polling function:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Starts the polling process.
|
||||
*/
|
||||
public start(): void {
|
||||
this.poller.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the polling process.
|
||||
*/
|
||||
public stop(): void {
|
||||
this.poller.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the poller with the specified module.
|
||||
*
|
||||
* @param moduleName - The name of the module to initialize.
|
||||
*/
|
||||
public initialize(moduleName?: BarModule): void {
|
||||
this.poller.initialize(moduleName);
|
||||
}
|
||||
}
|
||||
107
lib/poller/Poller.ts
Normal file
107
lib/poller/Poller.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import GLib from 'gi://GLib?version=2.0';
|
||||
import { Bind } from 'lib/types/variable';
|
||||
import { BarModule } from 'lib/types/options';
|
||||
import { getLayoutItems } from 'lib/utils';
|
||||
|
||||
const { layouts } = options.bar;
|
||||
|
||||
/**
|
||||
* A class that manages the polling lifecycle, including interval management and execution state.
|
||||
*/
|
||||
export class Poller {
|
||||
private intervalInstance: number | null = null;
|
||||
private isExecuting: boolean = false;
|
||||
private pollingFunction: () => Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates an instance of Poller.
|
||||
* @param pollingInterval - The interval at which polling occurs.
|
||||
* @param trackers - An array of trackers to monitor.
|
||||
* @param pollingFunction - The function to execute during each poll.
|
||||
*/
|
||||
constructor(
|
||||
private pollingInterval: Bind,
|
||||
private trackers: Bind[],
|
||||
pollingFunction: () => Promise<void>,
|
||||
) {
|
||||
this.pollingFunction = pollingFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the polling process by setting up the interval.
|
||||
*/
|
||||
public start(): void {
|
||||
Utils.merge([this.pollingInterval, ...this.trackers], (intervalMs: number) => {
|
||||
this.executePolling(intervalMs);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the polling process and cleans up resources.
|
||||
*/
|
||||
public stop(): void {
|
||||
if (this.intervalInstance !== null) {
|
||||
GLib.source_remove(this.intervalInstance);
|
||||
this.intervalInstance = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the polling based on module usage.
|
||||
*
|
||||
* If not module is provided then we can safely assume that we want
|
||||
* to always run the pollig interval.
|
||||
*
|
||||
* @param moduleName - The name of the module to initialize.
|
||||
*/
|
||||
public initialize(moduleName?: BarModule): void {
|
||||
if (moduleName === undefined) {
|
||||
return this.start();
|
||||
}
|
||||
|
||||
const initialModules = getLayoutItems();
|
||||
|
||||
if (initialModules.includes(moduleName)) {
|
||||
this.start();
|
||||
} else {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
layouts.connect('changed', () => {
|
||||
const usedModules = getLayoutItems();
|
||||
|
||||
if (usedModules.includes(moduleName)) {
|
||||
this.start();
|
||||
} else {
|
||||
this.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the polling function at the specified interval.
|
||||
*
|
||||
* @param intervalMs - The polling interval in milliseconds.
|
||||
*/
|
||||
private executePolling(intervalMs: number): void {
|
||||
if (this.intervalInstance !== null) {
|
||||
GLib.source_remove(this.intervalInstance);
|
||||
}
|
||||
|
||||
this.intervalInstance = Utils.interval(intervalMs, async () => {
|
||||
if (this.isExecuting) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isExecuting = true;
|
||||
|
||||
try {
|
||||
await this.pollingFunction();
|
||||
} catch (error) {
|
||||
console.error('Error during polling execution:', error);
|
||||
} finally {
|
||||
this.isExecuting = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
2
lib/types/customModules/generic.d.ts
vendored
2
lib/types/customModules/generic.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
export type GenericFunction<T, P extends unknown[] = unknown[]> = (...args: P) => T;
|
||||
export type GenericFunction<Value, Parameters extends unknown[]> = (...args: Parameters) => Promise<Value> | Value;
|
||||
|
||||
export type GenericResourceMetrics = {
|
||||
total: number;
|
||||
|
||||
32
lib/types/options.d.ts
vendored
32
lib/types/options.d.ts
vendored
@@ -15,8 +15,38 @@ export type RecursiveOptionsObject = {
|
||||
};
|
||||
|
||||
export type BarLocation = 'top' | 'bottom';
|
||||
export type BarModule =
|
||||
| 'battery'
|
||||
| 'dashboard'
|
||||
| 'workspaces'
|
||||
| 'windowtitle'
|
||||
| 'media'
|
||||
| 'notifications'
|
||||
| 'volume'
|
||||
| 'network'
|
||||
| 'bluetooth'
|
||||
| 'clock'
|
||||
| 'ram'
|
||||
| 'cpu'
|
||||
| 'cputemp'
|
||||
| 'storage'
|
||||
| 'netstat'
|
||||
| 'kbinput'
|
||||
| 'updates'
|
||||
| 'submap'
|
||||
| 'weather'
|
||||
| 'power'
|
||||
| 'systray'
|
||||
| 'hypridle'
|
||||
| 'hyprsunset';
|
||||
|
||||
export type BarLayout = {
|
||||
[key: string]: Layout;
|
||||
left: BarModule[];
|
||||
middle: BarModule[];
|
||||
right: BarModule[];
|
||||
};
|
||||
export type BarLayouts = {
|
||||
[key: string]: BarLayout;
|
||||
};
|
||||
|
||||
export type Unit = 'imperial' | 'metric';
|
||||
|
||||
25
lib/utils.ts
25
lib/utils.ts
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { type Application } from 'types/service/applications';
|
||||
import { NotificationAnchor } from './types/options';
|
||||
import { BarModule, NotificationAnchor } from './types/options';
|
||||
import { OSDAnchor } from 'lib/types/options';
|
||||
import icons, { substitutes } from './icons';
|
||||
import Gtk from 'gi://Gtk?version=3.0';
|
||||
@@ -18,6 +18,29 @@ import options from 'options';
|
||||
|
||||
export type Binding<T> = import('types/service').Binding<any, any, T>;
|
||||
|
||||
/**
|
||||
* Retrieves all unique layout items from the bar options.
|
||||
*
|
||||
* @returns An array of unique layout items.
|
||||
*/
|
||||
export const getLayoutItems = (): BarModule[] => {
|
||||
const { layouts } = options.bar;
|
||||
|
||||
const itemsInLayout: BarModule[] = [];
|
||||
|
||||
Object.keys(layouts.value).forEach((monitor) => {
|
||||
const leftItems = layouts.value[monitor].left;
|
||||
const rightItems = layouts.value[monitor].right;
|
||||
const middleItems = layouts.value[monitor].middle;
|
||||
|
||||
itemsInLayout.push(...leftItems);
|
||||
itemsInLayout.push(...middleItems);
|
||||
itemsInLayout.push(...rightItems);
|
||||
});
|
||||
|
||||
return [...new Set(itemsInLayout)];
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns substitute icon || name || fallback icon
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user