import { DeviceId } from '../device/device_id.js' import { ModeType } from '../shared/mode.js' import { ObjectId } from 'mongodb' export type CommandId = string export enum CommandType { SET_PARAMETER_VALUES = 'SET_PARAMETER_VALUES', GET_PARAMETER_VALUES = 'GET_PARAMETER_VALUES', GET_PARAMETER_NAMES = 'GET_PARAMETER_NAMES', SET_PARAMETER_ATTRIBUTES = 'SET_PARAMETER_ATTRIBUTES', GET_PARAMETER_ATTRIBUTES = 'GET_PARAMETER_ATTRIBUTES', DOWNLOAD = 'DOWNLOAD', UPLOAD = 'UPLOAD', REBOOT = 'REBOOT', FACTORY_RESET = 'FACTORY_RESET', SCHEDULE_INFORM = 'SCHEDULE_INFORM', REFRESH = 'REFRESH', } export enum CommandStatus { PENDING = 'PENDING', SENT = 'SENT', WAITING = 'WAITING', FAILED = 'FAILED', CANCELLED = 'CANCELLED', COMPLETED = 'COMPLETED', } export enum Priority { LOW = 0, NORMAL = 1, HIGH = 2, CRITICAL = 3, } export type CommandParameters = Record export interface CommandResult { success: boolean data?: any message?: string timestamp: Date } export interface CommandRecord { _id?: ObjectId deviceIdentifier: string mode: ModeType action: CommandType parameters: Record status: CommandStatus createdAt: Date updatedAt: Date executedAt?: Date completedAt?: Date priority: number } export class Command { private _status: CommandStatus private _executedAt: Date | null = null private _result: CommandResult | null = null private _error: string | null = null constructor( public readonly id: CommandId, public readonly deviceId: DeviceId, public readonly type: CommandType, public readonly parameters: CommandParameters = {}, public readonly priority: Priority = Priority.NORMAL, public readonly createdAt: Date = new Date(), status: CommandStatus = CommandStatus.PENDING ) { this._status = status } public get status(): CommandStatus { return this._status } public get executedAt(): Date | null { return this._executedAt } public get result(): CommandResult | null { return this._result } public get error(): string | null { return this._error } public markAsSent(): void { if (this._status !== CommandStatus.PENDING) { throw new Error(`Cannot mark command as sent. Current status: ${this._status}`) } this._status = CommandStatus.SENT this._executedAt = new Date() } public markAsWaiting(): void { if (this._status !== CommandStatus.SENT) { throw new Error(`Cannot mark command as waiting. Current status: ${this._status}`) } this._status = CommandStatus.WAITING } public complete(result: CommandResult): void { if (![CommandStatus.SENT, CommandStatus.WAITING].includes(this._status)) { throw new Error(`Cannot complete command. Current status: ${this._status}`) } this._status = CommandStatus.COMPLETED this._result = result this._executedAt = new Date() } public fail(error: string): void { if (this._status === CommandStatus.COMPLETED) { throw new Error('Cannot fail a successful command') } this._status = CommandStatus.FAILED this._error = error this._executedAt = new Date() } public cancel(): void { if ([CommandStatus.COMPLETED, CommandStatus.FAILED].includes(this._status)) { throw new Error(`Cannot cancel command with status: ${this._status}`) } this._status = CommandStatus.CANCELLED } public canExecute(): boolean { return this._status === CommandStatus.PENDING } public isCompleted(): boolean { return [CommandStatus.COMPLETED, CommandStatus.FAILED, CommandStatus.CANCELLED].includes(this._status) } public hasExpired(timeoutMinutes: number = 30): boolean { return (Date.now() - this.createdAt.getTime()) / (1000 * 60) > timeoutMinutes } }