203 lines
4.5 KiB
TypeScript
203 lines
4.5 KiB
TypeScript
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>;
|
|
}
|