消毒机设备
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
6.5 KiB

2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
  1. import type { Ref } from 'vue'
  2. import { ref, watch } from 'vue'
  3. import { FtMessageBox } from './messageBox'
  4. // WebSocket客户端类
  5. export class WebSocketClient {
  6. public socket: WebSocket
  7. private url: string
  8. private isConnecting = false
  9. public readonly isConnected: Ref<boolean> = ref(false)
  10. public readonly connectionError: Ref<string | null> = ref(null)
  11. private responseHandlers = new Map<string, (response: Socket.WebSocketResponse) => void>()
  12. private eventListeners = new Map<string, ((response: Socket.WebSocketResponse) => void)[]>()
  13. private connectCount = 0
  14. private intervalVal: any = 0
  15. constructor(url: string) {
  16. this.url = url
  17. this.socket = this.createWebSocket()
  18. }
  19. // 创建WebSocket实例
  20. private createWebSocket() {
  21. const socket = new WebSocket(this.url)
  22. socket.onopen = () => {
  23. this.isConnecting = false
  24. this.isConnected.value = true
  25. this.connectionError.value = null
  26. this.connectCount = 0
  27. clearInterval(this.intervalVal)
  28. }
  29. socket.onclose = (event) => {
  30. this.isConnected.value = false
  31. this.connectionError.value = `连接关闭: ${event.code} ${event.reason}`
  32. // 非正常关闭时尝试重连
  33. if (event.code !== 1000) {
  34. this.scheduleReconnect()
  35. }
  36. }
  37. socket.onerror = (error) => {
  38. this.isConnected.value = false
  39. this.connectionError.value = `连接错误: ${(error as any).message || '未知错误'}`
  40. this.scheduleReconnect()
  41. }
  42. socket.onmessage = (event) => {
  43. try {
  44. const response: Socket.WebSocketResponse = JSON.parse(event.data)
  45. // 处理特定请求的响应
  46. const handler = this.responseHandlers.get(response.messageId)
  47. if (handler) {
  48. handler(response)
  49. this.responseHandlers.delete(response.messageId)
  50. return
  51. }
  52. // 处理事件订阅
  53. const listeners = this.eventListeners.get(response.fromClass) || []
  54. listeners.forEach(listener => listener(response))
  55. // 全局事件监听
  56. const globalListeners = this.eventListeners.get('*') || []
  57. globalListeners.forEach(listener => listener(response))
  58. }
  59. catch (parseError) {
  60. console.error('解析WebSocket消息失败', parseError)
  61. }
  62. }
  63. return socket
  64. }
  65. public async waitAndSend<T = Record<string, any>>(
  66. request: Socket.WebSocketRequest,
  67. ): Promise<Socket.WebSocketResponse & { rely: T }> {
  68. // 等待连接建立
  69. if (!this.isConnected.value) {
  70. await new Promise<void>((resolve) => {
  71. if (this.isConnected.value) {
  72. resolve()
  73. }
  74. else {
  75. const watcher = watch(this.isConnected, (connected) => {
  76. if (connected) {
  77. watcher() // 停止监听
  78. resolve()
  79. }
  80. })
  81. }
  82. })
  83. return this.sendRequest(request)
  84. }
  85. else { // 连接建立后发送请求
  86. return this.sendRequest(request)
  87. }
  88. }
  89. // 发送请求
  90. public sendRequest<T = Record<string, any>>(
  91. request: Socket.WebSocketRequest,
  92. ): Promise<Socket.WebSocketResponse & { rely: T }> {
  93. return new Promise((resolve, reject) => {
  94. if (this.isConnected.value) {
  95. this.socket.send(JSON.stringify(request))
  96. this.responseHandlers.set(request.messageId, (response) => {
  97. if (response.ackcode === 0) {
  98. resolve(response as Socket.WebSocketResponse & { rely: T })
  99. }
  100. else {
  101. // ElMessage.error(response.message)
  102. if (response.message) {
  103. FtMessageBox.error(response.message)
  104. }
  105. resolve(response as Socket.WebSocketResponse & { rely: T })
  106. }
  107. })
  108. // 设置超时
  109. setTimeout(() => {
  110. if (this.responseHandlers.has(request.messageId)) {
  111. this.responseHandlers.delete(request.messageId)
  112. reject(new Error(`请求超时: ${request.messageId}`))
  113. }
  114. }, 10000) // 10秒超时
  115. }
  116. else {
  117. if (!this.isConnecting) {
  118. this.reconnect()
  119. }
  120. }
  121. })
  122. }
  123. // 订阅事件
  124. public subscribe(messageType: string | '*', callback: (response: Socket.WebSocketResponse) => void) {
  125. if (!this.eventListeners.has(messageType)) {
  126. this.eventListeners.set(messageType, [])
  127. }
  128. this.eventListeners.get(messageType)?.push(callback)
  129. return () => {
  130. const listeners = this.eventListeners.get(messageType)
  131. if (listeners) {
  132. const index = listeners.indexOf(callback)
  133. if (index !== -1) {
  134. listeners.splice(index, 1)
  135. }
  136. }
  137. }
  138. }
  139. // 取消订阅方法
  140. public unsubscribe(messageType: string | '*', callback: (response: Socket.WebSocketResponse) => void) {
  141. const listeners = this.eventListeners.get(messageType)
  142. if (listeners) {
  143. // 过滤掉需要取消的回调函数
  144. const filteredListeners = listeners.filter(cb => cb !== callback)
  145. if (filteredListeners.length === 0) {
  146. this.eventListeners.delete(messageType)
  147. }
  148. else {
  149. // 否则更新监听器数组
  150. this.eventListeners.set(messageType, filteredListeners)
  151. }
  152. return true // 取消成功
  153. }
  154. return false // 未找到监听器
  155. }
  156. // 重连逻辑
  157. private reconnect() {
  158. // if (this.connectCount > this.maxConnectCount) {
  159. // clearInterval(this.intervalVal)
  160. // console.log('-----达到最大重连次数,停止重连------')
  161. // return
  162. // }
  163. this.isConnecting = true
  164. if (this.socket.readyState !== WebSocket.CLOSED && this.socket.readyState !== WebSocket.CLOSING) {
  165. this.socket.close()
  166. }
  167. this.socket = this.createWebSocket()
  168. }
  169. // 安排重连
  170. private scheduleReconnect() {
  171. if (this.isConnecting) {
  172. return
  173. }
  174. this.isConnecting = true
  175. this.intervalVal = setInterval(() => {
  176. this.isConnecting = false
  177. this.connectCount++
  178. console.log('重连------------', this.connectCount)
  179. this.reconnect()
  180. }, 3000) // 3秒后重连
  181. }
  182. // 手动关闭连接
  183. public close() {
  184. this.responseHandlers.clear()
  185. this.eventListeners.clear()
  186. this.socket.close()
  187. }
  188. }
  189. // 存储已创建的WebSocket实例
  190. const urlSocketMap = new Map<string, WebSocketClient>()
  191. // 创建WebSocket客户端的工厂函数
  192. export const createWebSocket = (url?: string): WebSocketClient => {
  193. url = url || import.meta.env.FT_WS_URL
  194. if (urlSocketMap.has(url)) {
  195. return urlSocketMap.get(url)!
  196. }
  197. else {
  198. const client = new WebSocketClient(url)
  199. urlSocketMap.set(url, client)
  200. return client
  201. }
  202. }