This commit is contained in:
2026-01-09 07:14:39 +00:00
parent 95da37590c
commit 05e1f94c79
22 changed files with 6549 additions and 10 deletions

View File

@@ -0,0 +1,202 @@
import * as plugins from '../plugins.js';
import type {
IDeviceInfo,
TDeviceType,
TDeviceStatus,
TConnectionState,
IRetryOptions,
} from '../interfaces/index.js';
import { withRetry } from '../helpers/helpers.retry.js';
/**
* Abstract base class for all devices (scanners, printers)
*/
export abstract class Device extends plugins.events.EventEmitter {
public readonly id: string;
public readonly name: string;
public readonly type: TDeviceType;
public readonly address: string;
public readonly port: number;
protected _status: TDeviceStatus = 'unknown';
protected _connectionState: TConnectionState = 'disconnected';
protected _lastError: Error | null = null;
public manufacturer?: string;
public model?: string;
public serialNumber?: string;
public firmwareVersion?: string;
protected retryOptions: IRetryOptions;
constructor(info: IDeviceInfo, retryOptions?: IRetryOptions) {
super();
this.id = info.id;
this.name = info.name;
this.type = info.type;
this.address = info.address;
this.port = info.port;
this._status = info.status;
this.manufacturer = info.manufacturer;
this.model = info.model;
this.serialNumber = info.serialNumber;
this.firmwareVersion = info.firmwareVersion;
this.retryOptions = retryOptions ?? {
maxRetries: 5,
baseDelay: 1000,
maxDelay: 16000,
multiplier: 2,
jitter: true,
};
}
/**
* Get current device status
*/
public get status(): TDeviceStatus {
return this._status;
}
/**
* Get current connection state
*/
public get connectionState(): TConnectionState {
return this._connectionState;
}
/**
* Get last error if any
*/
public get lastError(): Error | null {
return this._lastError;
}
/**
* Check if device is connected
*/
public get isConnected(): boolean {
return this._connectionState === 'connected';
}
/**
* Update device status
*/
protected setStatus(status: TDeviceStatus): void {
if (this._status !== status) {
const oldStatus = this._status;
this._status = status;
this.emit('status:changed', { oldStatus, newStatus: status });
}
}
/**
* Update connection state
*/
protected setConnectionState(state: TConnectionState): void {
if (this._connectionState !== state) {
const oldState = this._connectionState;
this._connectionState = state;
this.emit('connection:changed', { oldState, newState: state });
}
}
/**
* Set error state
*/
protected setError(error: Error): void {
this._lastError = error;
this.setStatus('error');
this.emit('error', error);
}
/**
* Clear error state
*/
protected clearError(): void {
this._lastError = null;
if (this._status === 'error') {
this.setStatus('online');
}
}
/**
* Execute an operation with retry logic
*/
protected async withRetry<T>(fn: () => Promise<T>): Promise<T> {
return withRetry(fn, this.retryOptions);
}
/**
* Connect to the device
*/
public async connect(): Promise<void> {
if (this.isConnected) {
return;
}
this.setConnectionState('connecting');
this.clearError();
try {
await this.withRetry(() => this.doConnect());
this.setConnectionState('connected');
this.setStatus('online');
} catch (error) {
this.setConnectionState('error');
this.setError(error instanceof Error ? error : new Error(String(error)));
throw error;
}
}
/**
* Disconnect from the device
*/
public async disconnect(): Promise<void> {
if (this._connectionState === 'disconnected') {
return;
}
try {
await this.doDisconnect();
} finally {
this.setConnectionState('disconnected');
}
}
/**
* Get device info as plain object
*/
public getInfo(): IDeviceInfo {
return {
id: this.id,
name: this.name,
type: this.type,
address: this.address,
port: this.port,
status: this._status,
manufacturer: this.manufacturer,
model: this.model,
serialNumber: this.serialNumber,
firmwareVersion: this.firmwareVersion,
};
}
/**
* Implementation-specific connect logic
* Override in subclasses
*/
protected abstract doConnect(): Promise<void>;
/**
* Implementation-specific disconnect logic
* Override in subclasses
*/
protected abstract doDisconnect(): Promise<void>;
/**
* Refresh device status
* Override in subclasses
*/
public abstract refreshStatus(): Promise<void>;
}