modbus-connect
Version:
Modbus RTU over Web Serial and Node.js SerialPort
882 lines (786 loc) • 28 kB
text/typescript
// src/polling-manager.ts
import { Mutex } from 'async-mutex';
import Logger from './logger.js';
import {
LogLevel,
LogContext,
LoggerInstance,
PollingManagerConfig,
PollingTaskOptions,
PollingTaskState,
PollingTaskStats,
PollingQueueInfo,
PollingSystemStats,
} from './types/modbus-types.js';
import {
ModbusFlushError,
ModbusTimeoutError,
PollingManagerError,
PollingTaskAlreadyExistsError,
PollingTaskNotFoundError,
PollingTaskValidationError,
ModbusCRCError,
ModbusParityError,
ModbusNoiseError,
ModbusFramingError,
ModbusOverrunError,
ModbusCollisionError,
ModbusConfigError,
ModbusBaudRateError,
ModbusSyncError,
ModbusFrameBoundaryError,
ModbusLRCError,
ModbusChecksumError,
ModbusDataConversionError,
ModbusBufferOverflowError,
ModbusBufferUnderrunError,
ModbusMemoryError,
ModbusStackOverflowError,
ModbusResponseError,
ModbusInvalidAddressError,
ModbusInvalidFunctionCodeError,
ModbusInvalidQuantityError,
ModbusIllegalDataAddressError,
ModbusIllegalDataValueError,
ModbusSlaveBusyError,
ModbusAcknowledgeError,
ModbusSlaveDeviceFailureError,
ModbusMalformedFrameError,
ModbusInvalidFrameLengthError,
ModbusInvalidTransactionIdError,
ModbusUnexpectedFunctionCodeError,
ModbusConnectionRefusedError,
ModbusConnectionTimeoutError,
ModbusNotConnectedError,
ModbusAlreadyConnectedError,
ModbusInsufficientDataError,
ModbusGatewayPathUnavailableError,
ModbusGatewayTargetDeviceError,
ModbusInvalidStartingAddressError,
ModbusMemoryParityError,
ModbusBroadcastError,
ModbusGatewayBusyError,
ModbusDataOverrunError,
ModbusTooManyEmptyReadsError,
ModbusInterFrameTimeoutError,
ModbusSilentIntervalError,
} from './errors.js';
/**
* TaskController управляет логикой одной задачи.
* Привязан к конкретному PollingManager.
*/
class TaskController {
public id: string;
public priority: number;
public name: string | null;
public fn: Array<() => Promise<unknown>>;
public interval: number;
public onData?: (data: unknown[]) => void;
public onError?: (error: Error, fnIndex: number, retryCount: number) => void;
public onStart?: () => void;
public onStop?: () => void;
public onFinish?: (success: boolean, results: unknown[]) => void;
public onBeforeEach?: () => void;
public onRetry?: (error: Error, fnIndex: number, retryCount: number) => void;
public shouldRun?: () => boolean;
public onSuccess?: (result: unknown) => void;
public onFailure?: (error: Error) => void;
public maxRetries: number;
public backoffDelay: number;
public taskTimeout: number;
public stopped: boolean;
public paused: boolean;
public executionInProgress: boolean;
public stats: PollingTaskStats;
public logger: LoggerInstance;
private manager: PollingManager;
private timerId: NodeJS.Timeout | null = null;
constructor(options: PollingTaskOptions, manager: PollingManager) {
const {
id,
priority = 0,
interval,
fn,
onData,
onError,
onStart,
onStop,
onFinish,
onBeforeEach,
onRetry,
shouldRun,
onSuccess,
onFailure,
name = null,
maxRetries = 3,
backoffDelay = 1000,
taskTimeout = 5000,
} = options;
this.id = id;
this.priority = priority;
this.name = name;
this.fn = Array.isArray(fn) ? fn : [fn];
this.interval = interval;
this.onData = onData;
this.onError = onError;
this.onStart = onStart;
this.onStop = onStop;
this.onFinish = onFinish;
this.onBeforeEach = onBeforeEach;
this.onRetry = onRetry;
this.shouldRun = shouldRun;
this.onSuccess = onSuccess;
this.onFailure = onFailure;
this.maxRetries = maxRetries;
this.backoffDelay = backoffDelay;
this.taskTimeout = taskTimeout;
this.stopped = true;
this.paused = false;
this.executionInProgress = false;
this.manager = manager;
this.stats = {
totalRuns: 0,
totalErrors: 0,
lastError: null,
lastResult: null,
lastRunTime: null,
retries: 0,
successes: 0,
failures: 0,
};
this.logger = manager.loggerInstance.createLogger(`Task:${id}`);
this.logger.setLevel('error');
this.logger.debug('TaskController created', {
id,
priority,
interval,
maxRetries,
backoffDelay,
taskTimeout,
} as LogContext);
}
start(): void {
if (!this.stopped) {
this.logger.debug('Task already running');
return;
}
this.stopped = false;
this.logger.info('Task started', { id: this.id } as LogContext);
this.onStart?.();
this._scheduleNextRun(true);
}
stop(): void {
if (this.stopped) {
this.logger.debug('Task already stopped', { id: this.id } as LogContext);
return;
}
this.stopped = true;
if (this.timerId) {
clearTimeout(this.timerId);
this.timerId = null;
}
this.manager.removeFromQueue(this.id);
this.logger.info('Task stopped', { id: this.id } as LogContext);
this.onStop?.();
}
pause(): void {
if (this.paused) {
this.logger.debug('Task already paused', { id: this.id } as LogContext);
return;
}
this.paused = true;
this.logger.info('Task paused', { id: this.id } as LogContext);
}
resume(): void {
if (!this.stopped && this.paused) {
this.paused = false;
this.logger.info('Task resumed', { id: this.id } as LogContext);
if (!this.timerId && !this.executionInProgress) {
this._scheduleNextRun(true);
}
} else {
this.logger.debug('Cannot resume task - not paused or stopped', {
id: this.id,
} as LogContext);
}
}
private _scheduleNextRun(immediate: boolean = false): void {
if (this.stopped) return;
if (this.timerId) {
clearTimeout(this.timerId);
this.timerId = null;
}
const delay = immediate ? 0 : this.interval;
this.timerId = setTimeout(() => {
this.timerId = null;
if (this.stopped) return;
this.manager.enqueueTask(this);
}, delay);
}
async execute(): Promise<void> {
if (this.stopped || this.paused) {
this.logger.debug('Cannot execute - task is stopped or paused', {
id: this.id,
} as LogContext);
this._scheduleNextRun();
return;
}
if (this.shouldRun && !this.shouldRun()) {
this.logger.debug('Task should not run according to shouldRun function', {
id: this.id,
} as LogContext);
this._scheduleNextRun();
return;
}
this.onBeforeEach?.();
this.executionInProgress = true;
this.stats.totalRuns++;
this.logger.debug('Executing task', { id: this.id } as LogContext);
try {
let overallSuccess = false;
const results: unknown[] = [];
for (let fnIndex = 0; fnIndex < this.fn.length; fnIndex++) {
if (this.stopped) break;
if (this.paused) break;
let retryCount = 0;
let result: unknown = null;
let fnSuccess = false;
while (!this.stopped && retryCount <= this.maxRetries) {
if (this.paused) break;
try {
const fnToExecute = this.fn[fnIndex];
if (typeof fnToExecute !== 'function') {
throw new PollingManagerError(
`Task ${this.id} fn at index ${fnIndex} is not a function`
);
}
const promiseResult = fnToExecute();
if (!(promiseResult instanceof Promise)) {
throw new PollingManagerError(
`Task ${this.id} fn ${fnIndex} did not return a Promise`
);
}
result = await this._withTimeout(promiseResult, this.taskTimeout);
fnSuccess = true;
this.stats.successes++;
this.stats.lastError = null;
break;
} catch (err: unknown) {
const error = err instanceof Error ? err : new PollingManagerError(String(err));
this._logSpecificError(error);
retryCount++;
this.stats.totalErrors++;
this.stats.retries++;
this.stats.lastError = error;
this.onRetry?.(error, fnIndex, retryCount);
if (retryCount > this.maxRetries) {
this.stats.failures++;
this.onFailure?.(error);
this.onError?.(error, fnIndex, retryCount);
this.logger.warn('Max retries exhausted for fn[' + fnIndex + ']', {
id: this.id,
fnIndex,
retryCount,
error: error.message,
} as LogContext);
} else {
const isFlushedError = error instanceof ModbusFlushError;
const baseDelay = isFlushedError
? 50
: this.backoffDelay * Math.pow(2, retryCount - 1);
const jitter = Math.random() * baseDelay * 0.5;
const delay = baseDelay + jitter;
this.logger.debug('Retrying fn[' + fnIndex + '] with delay', {
id: this.id,
delay,
retryCount,
} as LogContext);
await this._sleep(delay);
}
}
}
results.push(result);
overallSuccess = overallSuccess || fnSuccess;
}
this.stats.lastResult = results;
this.stats.lastRunTime = Date.now();
if (results.length > 0 && results.some(r => r !== null && r !== undefined)) {
this.onData?.(results);
}
if (overallSuccess) {
this.onSuccess?.(results);
}
this.onFinish?.(overallSuccess, results);
this.logger.info('Task execution completed', {
id: this.id,
success: overallSuccess,
resultsCount: results.length,
} as LogContext);
} catch (err: unknown) {
this.logger.error('Fatal error during task execution cycle', {
id: this.id,
error: err instanceof Error ? err.message : String(err),
} as LogContext);
} finally {
this.executionInProgress = false;
this._scheduleNextRun();
}
}
public isRunning(): boolean {
return !this.stopped;
}
public isPaused(): boolean {
return this.paused;
}
public setInterval(ms: number): void {
this.interval = ms;
this.logger.info('Interval updated', { id: this.id, interval: ms } as LogContext);
}
public getState(): PollingTaskState {
return {
stopped: this.stopped,
paused: this.paused,
running: !this.stopped,
inProgress: this.executionInProgress,
};
}
public getStats(): PollingTaskStats {
return { ...this.stats };
}
private _logSpecificError(error: Error): void {
const logContext = { id: this.id, error: error.message } as LogContext;
if (error instanceof ModbusTimeoutError) this.logger.error('Modbus timeout error', logContext);
else if (error instanceof ModbusCRCError) this.logger.error('Modbus CRC error', logContext);
else if (error instanceof ModbusParityError)
this.logger.error('Modbus parity error', logContext);
else if (error instanceof ModbusNoiseError) this.logger.error('Modbus noise error', logContext);
else if (error instanceof ModbusFramingError)
this.logger.error('Modbus framing error', logContext);
else if (error instanceof ModbusOverrunError)
this.logger.error('Modbus overrun error', logContext);
else if (error instanceof ModbusCollisionError)
this.logger.error('Modbus collision error', logContext);
else if (error instanceof ModbusConfigError)
this.logger.error('Modbus config error', logContext);
else if (error instanceof ModbusBaudRateError)
this.logger.error('Modbus baud rate error', logContext);
else if (error instanceof ModbusSyncError) this.logger.error('Modbus sync error', logContext);
else if (error instanceof ModbusFrameBoundaryError)
this.logger.error('Modbus frame boundary error', logContext);
else if (error instanceof ModbusLRCError) this.logger.error('Modbus LRC error', logContext);
else if (error instanceof ModbusChecksumError)
this.logger.error('Modbus checksum error', logContext);
else if (error instanceof ModbusDataConversionError)
this.logger.error('Modbus data conversion error', logContext);
else if (error instanceof ModbusBufferOverflowError)
this.logger.error('Modbus buffer overflow error', logContext);
else if (error instanceof ModbusBufferUnderrunError)
this.logger.error('Modbus buffer underrun error', logContext);
else if (error instanceof ModbusMemoryError)
this.logger.error('Modbus memory error', logContext);
else if (error instanceof ModbusStackOverflowError)
this.logger.error('Modbus stack overflow error', logContext);
else if (error instanceof ModbusResponseError)
this.logger.error('Modbus response error', logContext);
else if (error instanceof ModbusInvalidAddressError)
this.logger.error('Modbus invalid address error', logContext);
else if (error instanceof ModbusInvalidFunctionCodeError)
this.logger.error('Modbus invalid function code error', logContext);
else if (error instanceof ModbusInvalidQuantityError)
this.logger.error('Modbus invalid quantity error', logContext);
else if (error instanceof ModbusIllegalDataAddressError)
this.logger.error('Modbus illegal data address error', logContext);
else if (error instanceof ModbusIllegalDataValueError)
this.logger.error('Modbus illegal data value error', logContext);
else if (error instanceof ModbusSlaveBusyError)
this.logger.error('Modbus slave busy error', logContext);
else if (error instanceof ModbusAcknowledgeError)
this.logger.error('Modbus acknowledge error', logContext);
else if (error instanceof ModbusSlaveDeviceFailureError)
this.logger.error('Modbus slave device failure error', logContext);
else if (error instanceof ModbusMalformedFrameError)
this.logger.error('Modbus malformed frame error', logContext);
else if (error instanceof ModbusInvalidFrameLengthError)
this.logger.error('Modbus invalid frame length error', logContext);
else if (error instanceof ModbusInvalidTransactionIdError)
this.logger.error('Modbus invalid transaction ID error', logContext);
else if (error instanceof ModbusUnexpectedFunctionCodeError)
this.logger.error('Modbus unexpected function code error', logContext);
else if (error instanceof ModbusConnectionRefusedError)
this.logger.error('Modbus connection refused error', logContext);
else if (error instanceof ModbusConnectionTimeoutError)
this.logger.error('Modbus connection timeout error', logContext);
else if (error instanceof ModbusNotConnectedError)
this.logger.error('Modbus not connected error', logContext);
else if (error instanceof ModbusAlreadyConnectedError)
this.logger.error('Modbus already connected error', logContext);
else if (error instanceof ModbusInsufficientDataError)
this.logger.error('Modbus insufficient data error', logContext);
else if (error instanceof ModbusGatewayPathUnavailableError)
this.logger.error('Modbus gateway path unavailable error', logContext);
else if (error instanceof ModbusGatewayTargetDeviceError)
this.logger.error('Modbus gateway target device error', logContext);
else if (error instanceof ModbusInvalidStartingAddressError)
this.logger.error('Modbus invalid starting address error', logContext);
else if (error instanceof ModbusMemoryParityError)
this.logger.error('Modbus memory parity error', logContext);
else if (error instanceof ModbusBroadcastError)
this.logger.error('Modbus broadcast error', logContext);
else if (error instanceof ModbusGatewayBusyError)
this.logger.error('Modbus gateway busy error', logContext);
else if (error instanceof ModbusDataOverrunError)
this.logger.error('Modbus data overrun error', logContext);
else if (error instanceof ModbusTooManyEmptyReadsError)
this.logger.error('Modbus too many empty reads error', logContext);
else if (error instanceof ModbusInterFrameTimeoutError)
this.logger.error('Modbus inter-frame timeout error', logContext);
else if (error instanceof ModbusSilentIntervalError)
this.logger.error('Modbus silent interval error', logContext);
else this.logger.error('Polling error', logContext);
}
private _sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
private _withTimeout<T>(promise: Promise<T>, timeout: number): Promise<T> {
return new Promise<T>((resolve, reject) => {
const timer = setTimeout(() => reject(new ModbusTimeoutError('Task timed out')), timeout);
promise
.then(result => {
clearTimeout(timer);
resolve(result);
})
.catch(err => {
clearTimeout(timer);
reject(err);
});
});
}
}
/**
* PollingManager
*/
class PollingManager {
private config: Required<PollingManagerConfig>;
public tasks: Map<string, TaskController>;
private executionQueue: TaskController[];
private mutex: Mutex;
private isProcessing: boolean;
private paused: boolean;
public loggerInstance: Logger;
public logger: LoggerInstance;
constructor(config: PollingManagerConfig = {}, loggerInstance?: Logger) {
this.config = {
defaultMaxRetries: 3,
defaultBackoffDelay: 1000,
defaultTaskTimeout: 5000,
logLevel: 'trace',
...config,
} as Required<PollingManagerConfig>;
this.tasks = new Map();
this.executionQueue = [];
this.mutex = new Mutex();
this.isProcessing = false;
this.paused = false;
this.loggerInstance = loggerInstance || new Logger();
if (!loggerInstance) {
this.loggerInstance.setLogFormat(['timestamp', 'level', 'logger']);
this.loggerInstance.setCustomFormatter('logger', (value: unknown) => {
return value ? `[${value}]` : '';
});
}
this.logger = this.loggerInstance.createLogger('PollingManager');
this.logger.setLevel(this.config.logLevel as LogLevel);
this.logger.info('PollingManager initialized', {
config: JSON.stringify(this.config),
} as LogContext);
}
private _validateTaskOptions(options: PollingTaskOptions): void {
if (!options || typeof options !== 'object')
throw new PollingTaskValidationError('Task options must be an object');
if (!options.id) throw new PollingTaskValidationError('Task must have an "id"');
if (typeof options.interval !== 'number' || options.interval <= 0)
throw new PollingTaskValidationError('Interval must be a positive number');
if (!options.fn || (!Array.isArray(options.fn) && typeof options.fn !== 'function'))
throw new PollingTaskValidationError('fn must be a function or array of functions');
}
public addTask(options: PollingTaskOptions): void {
try {
this._validateTaskOptions(options);
if (this.tasks.has(options.id)) throw new PollingTaskAlreadyExistsError(options.id);
const task = new TaskController(
{
...options,
maxRetries: options.maxRetries ?? this.config.defaultMaxRetries,
backoffDelay: options.backoffDelay ?? this.config.defaultBackoffDelay,
taskTimeout: options.taskTimeout ?? this.config.defaultTaskTimeout,
},
this
);
this.tasks.set(options.id, task);
if (options.immediate !== false) {
task.start();
}
this.logger.info('Task added successfully', { id: options.id } as LogContext);
} catch (error: unknown) {
const err = error instanceof Error ? error : new PollingManagerError(String(error));
this.logger.error('Failed to add task', { error: err.message } as LogContext);
throw err;
}
}
public updateTask(id: string, newOptions: Partial<PollingTaskOptions>): void {
const oldTask = this.tasks.get(id);
if (!oldTask) throw new PollingTaskNotFoundError(id);
const oldOptions: PollingTaskOptions = {
id: oldTask.id,
priority: oldTask.priority,
interval: oldTask.interval,
fn: oldTask.fn,
onData: oldTask.onData,
onError: oldTask.onError,
onStart: oldTask.onStart,
onStop: oldTask.onStop,
onFinish: oldTask.onFinish,
onBeforeEach: oldTask.onBeforeEach,
onRetry: oldTask.onRetry,
shouldRun: oldTask.shouldRun,
onSuccess: oldTask.onSuccess,
onFailure: oldTask.onFailure,
name: oldTask.name ?? undefined,
maxRetries: oldTask.maxRetries,
backoffDelay: oldTask.backoffDelay,
taskTimeout: oldTask.taskTimeout,
};
const mergedOptions = { ...oldOptions, ...newOptions };
const wasRunning = oldTask.isRunning();
this.removeTask(id);
this.addTask(mergedOptions);
if (wasRunning) this.startTask(id);
}
public removeTask(id: string): void {
const task = this.tasks.get(id);
if (task) {
task.stop();
this.tasks.delete(id);
this.removeFromQueue(id);
this.logger.info('Task removed', { id } as LogContext);
} else {
this.logger.warn('Attempt to remove non-existent task', { id } as LogContext);
}
}
public restartTask(id: string): void {
const task = this.tasks.get(id);
if (task) {
task.stop();
setTimeout(() => {
const freshTask = this.tasks.get(id);
if (freshTask) freshTask.start();
}, 0);
}
}
public startTask(id: string): void {
const task = this.tasks.get(id);
if (task) task.start();
else throw new PollingTaskNotFoundError(id);
}
public stopTask(id: string): void {
const task = this.tasks.get(id);
if (task) task.stop();
}
public pauseTask(id: string): void {
const task = this.tasks.get(id);
if (task) task.pause();
}
public resumeTask(id: string): void {
const task = this.tasks.get(id);
if (task) task.resume();
}
public setTaskInterval(id: string, interval: number): void {
const task = this.tasks.get(id);
if (task) task.setInterval(interval);
}
public isTaskRunning(id: string): boolean {
const task = this.tasks.get(id);
return task ? task.isRunning() : false;
}
public isTaskPaused(id: string): boolean {
const task = this.tasks.get(id);
return task ? task.isPaused() : false;
}
public getTaskState(id: string): PollingTaskState | null {
const task = this.tasks.get(id);
return task ? task.getState() : null;
}
public getTaskStats(id: string): PollingTaskStats | null {
const task = this.tasks.get(id);
return task ? task.getStats() : null;
}
public hasTask(id: string): boolean {
return this.tasks.has(id);
}
public getTaskIds(): string[] {
return Array.from(this.tasks.keys());
}
public clearAll(): void {
this.logger.info('Clearing all tasks');
this.paused = true;
for (const task of this.tasks.values()) {
task.stop();
}
this.tasks.clear();
this.executionQueue = [];
this.logger.info('All tasks cleared');
}
public restartAllTasks(): void {
for (const id of this.tasks.keys()) {
this.restartTask(id);
}
}
public pauseAllTasks(): void {
this.paused = true;
for (const task of this.tasks.values()) {
task.pause();
}
}
public resumeAllTasks(): void {
this.paused = false;
for (const task of this.tasks.values()) {
task.resume();
}
this._processQueue();
}
public startAllTasks(): void {
this.paused = false;
for (const task of this.tasks.values()) {
task.start();
}
}
public stopAllTasks(): void {
this.paused = true;
for (const task of this.tasks.values()) {
task.stop();
}
this.executionQueue = [];
}
public getAllTaskStats(): Record<string, PollingTaskStats> {
const stats: Record<string, PollingTaskStats> = {};
for (const [id, task] of this.tasks.entries()) {
stats[id] = task.getStats();
}
return stats;
}
public getQueueInfo(): PollingQueueInfo {
return {
queueLength: this.executionQueue.length,
tasks: this.executionQueue.map(task => ({
id: task.id,
state: task.getState(),
})),
};
}
public getSystemStats(): PollingSystemStats {
return {
totalTasks: this.tasks.size,
totalQueues: 1,
queuedTasks: this.executionQueue.length,
tasks: this.getAllTaskStats(),
};
}
public enqueueTask(task: TaskController): void {
if (!this.executionQueue.includes(task)) {
this.executionQueue.push(task);
this.executionQueue.sort((a, b) => b.priority - a.priority);
this.logger.debug('Task enqueued', {
id: task.id,
queueLen: this.executionQueue.length,
} as LogContext);
this._processQueue();
}
}
public removeFromQueue(taskId: string): void {
this.executionQueue = this.executionQueue.filter(t => t.id !== taskId);
}
private _sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Основной цикл обработки очереди.
*/
private async _processQueue(): Promise<void> {
if (this.isProcessing || this.paused || this.executionQueue.length === 0) {
return;
}
this.isProcessing = true;
try {
while (this.executionQueue.length > 0 && !this.paused) {
const task = this.executionQueue[0];
if (task) {
this.executionQueue.shift();
this.logger.debug('Processing task from queue', { id: task.id } as LogContext);
await this._sleep(30);
await task.execute();
}
await this._sleep(10);
}
} catch (error: unknown) {
this.logger.error('Critical error in processQueue loop', {
error: error instanceof Error ? error.message : String(error),
} as LogContext);
} finally {
this.isProcessing = false;
if (this.executionQueue.length > 0 && !this.paused) {
setTimeout(() => this._processQueue(), 0);
}
}
}
/**
* Выполняет функцию с захватом мьютекса.
* Используется ModbusClient для обеспечения атомарности операций чтения/записи.
*/
public async executeImmediate<T>(fn: () => Promise<T>): Promise<T> {
const release = await this.mutex.acquire();
try {
return await fn();
} finally {
release();
// this._processQueue();
}
}
// === Логгеры ===
public enablePollingManagerLogger(level: LogLevel = 'info'): void {
this.logger.setLevel(level);
}
public disablePollingManagerLogger(): void {
this.logger.setLevel('error');
}
public enableTaskControllerLoggers(level: LogLevel = 'info'): void {
for (const task of this.tasks.values()) {
task.logger.setLevel(level);
}
}
public disableTaskControllerLoggers(): void {
for (const task of this.tasks.values()) {
task.logger.setLevel('error');
}
}
public enableTaskControllerLogger(taskId: string, level: LogLevel = 'info'): void {
this.tasks.get(taskId)?.logger.setLevel(level);
}
public disableTaskControllerLogger(taskId: string): void {
this.tasks.get(taskId)?.logger.setLevel('error');
}
public enableAllLoggers(level: LogLevel = 'info'): void {
this.enablePollingManagerLogger(level);
this.enableTaskControllerLoggers(level);
}
public disableAllLoggers(): void {
this.disablePollingManagerLogger();
this.disableTaskControllerLoggers();
}
public setLogLevelForAll(level: LogLevel): void {
this.logger.setLevel(level);
for (const task of this.tasks.values()) {
task.logger.setLevel(level);
}
}
}
export = PollingManager;