/** * 协议工具类 - 提供状态转换、类型判断和请求引用管理功能 */ import { MessageStatus, Priority, ClientType, MessageType, AnyMessage, CommandMessage, ProgressUpdateMessage, ProgramMessage, RegisterMessage, HeartbeatMessage, ErrorMessage, CommandResponseMessage, ProgramResponseMessage, isCommandMessage as isCommandMsg, isProgressUpdateMessage as isProgressMsg, isProgramMessage as isProgramMsg, } from './index'; /** * 协议工具类 */ export class ProtocolUtils { /** * 将 MessageStatus 枚举转换为字符串 */ static statusToString(status: MessageStatus): string { return status.toString(); } /** * 将字符串转换为 MessageStatus 枚举 * @throws {Error} 如果字符串不是有效的状态值 */ static stringToStatus(status: string): MessageStatus { // 处理大小写和空格 const normalizedStatus = status.trim().toUpperCase(); // 检查是否是有效的枚举值 if (!Object.values(MessageStatus).includes(normalizedStatus as MessageStatus)) { throw new Error(`Invalid command status: ${status}. Valid values are: ${Object.values(MessageStatus).join(', ')}`); } return normalizedStatus as MessageStatus; } /** * 将字符串转换为 Priority 枚举 * @throws {Error} 如果字符串不是有效的优先级值 */ static priorityToEnum(priority: string): Priority { const normalizedPriority = priority.trim().toUpperCase(); if (!Object.values(Priority).includes(normalizedPriority as Priority)) { throw new Error(`Invalid priority: ${priority}. Valid values are: ${Object.values(Priority).join(', ')}`); } return normalizedPriority as Priority; } /** * 将 Priority 枚举转换为字符串 */ static enumToPriority(priority: Priority): string { return priority.toString(); } /** * 将字符串转换为 ClientType 枚举 * @throws {Error} 如果字符串不是有效的客户端类型 */ static clientTypeToEnum(clientType: string): ClientType { const normalizedType = clientType.trim().toUpperCase(); if (!Object.values(ClientType).includes(normalizedType as ClientType)) { throw new Error(`Invalid client type: ${clientType}. Valid values are: ${Object.values(ClientType).join(', ')}`); } return normalizedType as ClientType; } /** * 将 ClientType 枚举转换为字符串 */ static enumToClientType(clientType: ClientType): string { return clientType.toString(); } /** * 检查消息是否为命令消息 * 增强版本:不仅检查类型,还验证结构 */ static isCommandMessage(message: any): message is CommandMessage { if (!isCommandMsg(message)) { return false; } // 额外的结构验证 return !!( message.requestRef && message.targetClientId && message.command && message.priority && message.timeout !== undefined && message.callback ); } /** * 检查消息是否为进度更新消息 * 增强版本:包含额外的结构验证 */ static isProgressUpdate(message: any): message is ProgressUpdateMessage { if (!isProgressMsg(message)) { return false; } // 额外的结构验证 return !!( message.requestRef && message.status && message.phase && typeof message.progress === 'number' && message.sourceType && (message.sourceType === 'COMMAND' || message.sourceType === 'SYSTEM') ); } /** * 检查消息是否为程序消息 * 增强版本:包含额外的结构验证 */ static isProgramMessage(message: any): message is ProgramMessage { if (!isProgramMsg(message)) { return false; } // 额外的结构验证 return !!( message.requestRef && message.targetClientId && message.command && message.command.commandCode === 'UPLOAD_PROGRAM' && message.command.parameters && message.priority && message.timeout !== undefined && message.callback ); } /** * 从任意消息中提取请求引用 * @returns 请求引用,如果消息类型不包含请求引用则返回 undefined */ static extractRequestRef(message: AnyMessage): string | undefined { switch (message.type) { // 包含 requestRef 的消息类型 case MessageType.COMMAND: case MessageType.COMMAND_RESPONSE: case MessageType.PROGRAM: case MessageType.PROGRAM_RESPONSE: case MessageType.PROGRESS_UPDATE: return (message as any).requestRef; // ERROR 消息可能在 context 中包含 requestRef case MessageType.ERROR: const errorMsg = message as ErrorMessage; if (errorMsg.context && typeof errorMsg.context === 'object') { return errorMsg.context.requestRef; } return undefined; // 其他消息类型不包含 requestRef case MessageType.REGISTER: case MessageType.REGISTER_ACK: case MessageType.UNREGISTER: case MessageType.UNREGISTER_ACK: case MessageType.HEARTBEAT: case MessageType.HEARTBEAT_ACK: return undefined; default: return undefined; } } /** * 生成唯一的请求引用 ID * @param prefix 可选的前缀,用于标识请求来源 * @returns 格式为 "prefix-timestamp-random" 或 "timestamp-random" */ static generateRequestRef(prefix?: string): string { const timestamp = Date.now(); const random = Math.random().toString(36).substr(2, 9); if (prefix) { // 清理前缀,只保留字母数字和连字符 const cleanPrefix = prefix.replace(/[^a-zA-Z0-9-]/g, ''); return `${cleanPrefix}-${timestamp}-${random}`; } return `${timestamp}-${random}`; } /** * 生成带有特定格式的请求引用 * @param service 服务名称 * @param operation 操作名称 * @returns 格式为 "service:operation:timestamp:random" */ static generateStructuredRequestRef(service: string, operation: string): string { const timestamp = Date.now(); const random = Math.random().toString(36).substr(2, 6); const cleanService = service.replace(/[^a-zA-Z0-9]/g, '').toLowerCase(); const cleanOperation = operation.replace(/[^a-zA-Z0-9]/g, '').toLowerCase(); return `${cleanService}:${cleanOperation}:${timestamp}:${random}`; } /** * 安全的枚举转换(不抛出异常) * @returns 枚举值或 null */ static tryParseStatus(status: string): MessageStatus | null { try { return this.stringToStatus(status); } catch { return null; } } /** * 安全的优先级转换(不抛出异常) * @returns 枚举值或 null */ static tryParsePriority(priority: string): Priority | null { try { return this.priorityToEnum(priority); } catch { return null; } } /** * 安全的客户端类型转换(不抛出异常) * @returns 枚举值或 null */ static tryParseClientType(clientType: string): ClientType | null { try { return this.clientTypeToEnum(clientType); } catch { return null; } } /** * 从请求引用中解析信息 * @returns 解析后的组件,如果格式不匹配则返回 null */ static parseRequestRef(requestRef: string): { prefix?: string; timestamp?: number; random?: string; service?: string; operation?: string; } | null { // 尝试解析结构化格式 "service:operation:timestamp:random" const structuredMatch = requestRef.match(/^([a-z]+):([a-z]+):(\d+):([a-z0-9]+)$/); if (structuredMatch) { return { service: structuredMatch[1], operation: structuredMatch[2], timestamp: parseInt(structuredMatch[3], 10), random: structuredMatch[4] }; } // 尝试解析带前缀格式 "prefix-timestamp-random" const prefixMatch = requestRef.match(/^([a-zA-Z0-9-]+)-(\d+)-([a-z0-9]+)$/); if (prefixMatch) { return { prefix: prefixMatch[1], timestamp: parseInt(prefixMatch[2], 10), random: prefixMatch[3] }; } // 尝试解析简单格式 "timestamp-random" const simpleMatch = requestRef.match(/^(\d+)-([a-z0-9]+)$/); if (simpleMatch) { return { timestamp: parseInt(simpleMatch[1], 10), random: simpleMatch[2] }; } return null; } /** * 获取消息的类型名称(友好的字符串格式) */ static getMessageTypeName(message: AnyMessage): string { const typeNames: Record = { [MessageType.REGISTER]: 'Register', [MessageType.REGISTER_ACK]: 'Register Acknowledgment', [MessageType.UNREGISTER]: 'Unregister', [MessageType.UNREGISTER_ACK]: 'Unregister Acknowledgment', [MessageType.HEARTBEAT]: 'Heartbeat', [MessageType.HEARTBEAT_ACK]: 'Heartbeat Acknowledgment', [MessageType.COMMAND]: 'Command', [MessageType.COMMAND_RESPONSE]: 'Command Response', [MessageType.PROGRAM]: 'Program Upload', [MessageType.PROGRAM_RESPONSE]: 'Program Response', [MessageType.PROGRESS_UPDATE]: 'Progress Update', [MessageType.UPDATE_ROUTES]: 'Update Routes', [MessageType.UPDATE_ROUTES_ACK]: 'Update Routes Acknowledgment', [MessageType.REGISTER_PENDING]: 'Register Pending', [MessageType.AUTHORIZATION_GRANTED]: 'Authorization Granted', [MessageType.AUTHORIZATION_REJECTED]: 'Authorization Rejected', [MessageType.DEVICE_APPROVAL_REQUEST]: 'Device Approval Request', [MessageType.DEVICE_APPROVAL_RESPONSE]: 'Device Approval Response', [MessageType.ACL_INVALIDATED]: 'ACL Invalidated', [MessageType.ERROR]: 'Error' }; return typeNames[message.type] || 'Unknown'; } /** * 检查是否为请求消息(需要响应的消息) */ static isRequestMessage(message: AnyMessage): boolean { const requestTypes: MessageType[] = [ MessageType.REGISTER, MessageType.UNREGISTER, MessageType.HEARTBEAT, MessageType.COMMAND, MessageType.PROGRAM ]; return requestTypes.includes(message.type); } /** * 检查是否为响应消息 */ static isResponseMessage(message: AnyMessage): boolean { const responseTypes: MessageType[] = [ MessageType.REGISTER_ACK, MessageType.UNREGISTER_ACK, MessageType.HEARTBEAT_ACK, MessageType.COMMAND_RESPONSE, MessageType.PROGRAM_RESPONSE ]; return responseTypes.includes(message.type); } }