initial
This commit is contained in:
202
ts/abstract/device.abstract.ts
Normal file
202
ts/abstract/device.abstract.ts
Normal 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>;
|
||||
}
|
||||
Reference in New Issue
Block a user