|
|
// src/websocket/socket.ts
// 基础消息接口
interface BaseMessage { messageType: 'Report' // 消息类型
dataType: string // 数据类型
timestamp: number // 时间戳
}
// 耗材状态消息
interface OptScanModuleStateMessage extends BaseMessage { type: 'OptScanModuleState' messageType: 'Report' dataType: 'OptScanModuleState' data: { state: 'EMPTY' | 'OCCUPIED' // 状态
isErrorPlate: boolean // 是否为错误板
bloodType: 'WHOLE_BLOOD' | 'SERUM_OR_PLASMA' // 血液类型
sampleBarcode: string // 样本条码
userid: string // 用户ID
projInfo: ProjectInfo // 项目信息
sampleId: string // 样本ID
projId: number // 项目ID
} timestamp: number } // 提示信息接口
interface PromptInfo { type: 'Warn' | 'Error' info: string detailInfos: string[] stackInfo: null } interface EventType { typeName: string timestamp: number prompt?: PromptInfo[] actionStep?: string actionStepName?: string } // 应用事件消息接口
interface AppEventMessage extends BaseMessage { type: 'AppEvent' messageType: 'Report' dataType: 'AppEvent' data: EventType timestamp: number } // 设备工作状态消息
interface DeviceWorkStateMessage extends BaseMessage { type: 'DeviceWorkState' messageType: 'Report' dataType: 'DeviceWorkState' data: { workState: 'IDLE' | 'RUNNING' | 'ERROR' | 'PAUSE' | 'STOP' // 设备工作状态
fatalErrorFlag: boolean // 致命错误标志
ecodeList: string[] // 错误代码列表
pending: boolean // 待处理状态
} timestamp: number // 时间戳
}
// 急诊位状态消息
interface EmergencyPosStateMessage extends BaseMessage { type: 'EmergencyPosState' messageType: 'Report' dataType: 'EmergencyPosState' data: { tube: { sampleId: string | null // 样本ID
pos: number // 位置
isHighTube: boolean // 是否为高试管
isEmergency: boolean // 是否为急诊
bloodType: 'WHOLE_BLOOD' | 'SERUM_OR_PLASMA' // 血液类型
sampleBarcode: string // 样本条码
userid: string // 用户ID
projInfo: ProjectInfo[] // 项目信息列表
projIds: number[] // 项目ID列表
state: string // 状态
errors: string[] // 错误信息列表
} } timestamp: number }
// 试管信息接口
// interface Tube {
// sampleId: string | null // 样本ID
// pos: number // 位置
// isHighTube: boolean // 是否为高试管
// isEmergency: boolean // 是否为急诊
// bloodType: 'WHOLE_BLOOD' | 'SERUM_OR_PLASMA' // 血液类型
// sampleBarcode: string // 样本条码
// userid: string // 用户ID
// projInfo: ProjectInfo[] // 项目信息列表
// projIds: number[] // 项目ID列表
// state: 'EMPTY' | 'OCCUPIED' // 状态
// errors: string[] // 错误信息列表
// }
// 试管架状态消息
interface TubeHolderStateMessage extends BaseMessage { type: 'TubeHolderState' messageType: 'Report' dataType: 'TubeHolderState' data: { tubeHolderType: string tubes: Array<{ sampleId: string | null pos: number isHighTube: boolean isEmergency: boolean bloodType: string sampleBarcode: string userid: string projInfo: any[] projIds: number[] state: string errors: string[] }> state: string } timestamp: number }
// 传感器状态消息
interface SensorStateMessage extends BaseMessage { type: 'SensorState' messageType: 'Report' dataType: 'SensorState' data: { pboxTemperature: number // P盒温度
incubateBoxTemperature: number // 孵育盒温度
wasteBinFullFlag: boolean // 废物箱满标志
} timestamp: number }
// 项目信息接口
interface ProjectInfo { projId: number projName: string projShortName: string color: string }
// 子槽位信息接口
interface Subtank { pos: string // 位置编号 SPACE01-SPACE20
state: 'EMPTY' | 'OCCUPIED' // 槽位状态
bloodType: 'WHOLE_BLOOD' | 'SERUM_OR_PLASMA' // 血液类型
sampleBarcode: string // 样本条码
userid: string // 用户ID
projInfo: ProjectInfo // 项目信息
sampleId: string // 样本ID
projId: number // 项目ID
startIncubatedTime: number // 开始孵育时间
incubatedTimeSec: number // 孵育时间(秒)
errors: string[] // 错误信息列表
isPlaceholder?: boolean // 是否为占位符
}
// 孵育板状态消息
interface IncubationPlateStateMessage extends BaseMessage { type: 'IncubationPlateState' messageType: 'Report' dataType: 'IncubationPlateState' data: { subtanks: Subtank[] // 20个子槽位信息
} timestamp: number }
// ��材组信息基础接口
interface ConsumableGroupBase { projId: number | null projName: string | null projShortName: string | null lotId: string color: string num: number }
// 小缓冲液接口
interface LittleBottleGroup extends ConsumableGroupBase { type: string | null }
// 大缓冲液接口
interface LargeBottleGroup extends ConsumableGroupBase { isUse: boolean }
// Tips信息接口
interface TipInfo { tipNum: number }
// 耗材状态消息接口
interface ConsumablesStateMessage extends BaseMessage { type: 'ConsumablesState' messageType: 'Report' dataType: 'ConsumablesState' data: { scanDate: number tips: TipInfo[] reactionPlateGroup: ConsumableGroupBase[] littBottleGroup: LittleBottleGroup[] larBottleGroup: LargeBottleGroup[] } timestamp: number }
// 消息类型联合
type WebSocketMessage = | OptScanModuleStateMessage | DeviceWorkStateMessage | EmergencyPosStateMessage | TubeHolderStateMessage | SensorStateMessage | IncubationPlateStateMessage | AppEventMessage | ConsumablesStateMessage
// 消息处理器类型
type MessageHandler<T extends WebSocketMessage> = (data: T['data']) => void
class WebSocketClient { private ws: WebSocket | null = null private url: string private reconnectAttempts: number = 0 private maxReconnectAttempts: number = 5 private reconnectInterval: number = 3000
// 使用类型安全的消息处理器映射
private messageHandlers: Map< WebSocketMessage['dataType'], Set<MessageHandler<any>> > = new Map()
constructor(url: string) { this.url = url }
// 类型安全的订阅方法
subscribe<T extends WebSocketMessage>( messageType: T['dataType'], handler: MessageHandler<T>, ): void { console.log('messageType==', messageType) if (!this.messageHandlers.has(messageType)) { console.log( '🚀 ~ WebSocketClient ~ subscribe ~ messageType:', messageType, ) this.messageHandlers.set(messageType, new Set()) } this.messageHandlers.get(messageType)?.add(handler) }
// 类型安全的取消订阅方法
unsubscribe<T extends WebSocketMessage>( messageType: T['dataType'], handler: MessageHandler<T>, ): void { this.messageHandlers.get(messageType)?.delete(handler) }
private handleMessage(message: WebSocketMessage): void { const handlers = this.messageHandlers.get(message.dataType) if (!handlers) { if (message.dataType == 'AppEvent') { console.log( '🚀 ~ WebSocketClient ~ handleMessage ~ handlers is undefined for message type:', message.dataType, ) }
return } handlers.forEach((handler) => { try { handler(message.data) } catch (error) { console.error(`处理 ${message.dataType} 消息时出错:`, error) } }) }
// 连接 WebSocket
connect(): void { try { this.ws = new WebSocket(this.url) this.bindEvents() } catch (error) { console.error('WebSocket 连接失败:', error) this.reconnect() } }
// 绑定事件
private bindEvents(): void { if (!this.ws) return
// 连接建立时的处理
this.ws.onopen = () => { console.log('WebSocket 连接已建立') this.reconnectAttempts = 0 // 重置重连次数
}
// 接收消息的处理
this.ws.onmessage = (event: MessageEvent) => { try { const data = JSON.parse(event.data) // console.log('🚀 ~ WebSocketClient ~ bindEvents ~ data:', data)
this.handleMessage(data) } catch (error) { console.error('消息解析错误:', error) } }
// 连接关闭时的处理
this.ws.onclose = () => { console.log('WebSocket 连接已关闭') this.reconnect() }
// 错误处理
this.ws.onerror = (error) => { console.error('WebSocket 错误:', error) } }
// 重连机制
private reconnect(): void { if (this.reconnectAttempts >= this.maxReconnectAttempts) { console.log('达到最大重连次数,停止重连') return }
setTimeout(() => { console.log(`尝试第 ${this.reconnectAttempts + 1} 次重连...`) this.reconnectAttempts++ this.connect() }, this.reconnectInterval) }
// 关闭连接
disconnect(): void { if (this.ws) { this.ws.close() this.ws = null } } }
// 创建单例
// let wsInstance: WebSocketClient | null = null
// 导出消息类型
export type { WebSocketMessage, OptScanModuleStateMessage, DeviceWorkStateMessage, EmergencyPosStateMessage, TubeHolderStateMessage, SensorStateMessage, IncubationPlateStateMessage, ProjectInfo, Subtank, AppEventMessage, ConsumablesStateMessage, ConsumableGroupBase, LittleBottleGroup, LargeBottleGroup, TipInfo, }
// 导出 WebSocket 客户端
export const createWebSocket = (url: string): WebSocketClient => { // if (!wsInstance) {
// wsInstance = new WebSocketClient(url)
// }
return new WebSocketClient(url) }
// 使用示例:
/* import { createWebSocket } from './websocket/socket';
// 创建 WebSocket 连接
const ws = createWebSocket('ws://your-websocket-server-url'); ws.connect();
// 在组件销毁时断开连接
onUnmounted(() => { ws.disconnect(); }); */
|