webSocket

"@vueuse/core": "10.9.0"

webSocket.ts:
import { useWebSocket, WebSocketStatus } from '@vueuse/core'
import { getAccessToken } from '@/utils/auth'
import * as localConfig from '@/localConfig.js'

interface WebSocketOptions {
  autoReconnect?: boolean // 是否自动重连,可设置重试次数
  // 是否启用心跳机制,可设置间隔时间
  heartbeat?: {
    interval?: number
    pongTimeout?: number
  }
  onMessage?: (data: string) => void // 自定义消息处理回调函数
  onConnected?: () => void // 连接成功时的回调函数
  onDisconnected?: () => void // 断开连接时的回调函数
}

// 消息处理
interface MessageHandler {
  (content: any): void
}

class WebSocketUtil {
  private serverAddress: string
  private ws: ReturnType<typeof useWebSocket>
  private messageHandlers: Map<string, MessageHandler[]> = new Map() // 存储不同类型消息的处理器映射表
  private defaultOptions: WebSocketOptions = {
    autoReconnect: true,
    heartbeat: {
      interval: 10000,
      pongTimeout: 5000
    }
  }

  /**
   * 构造函数初始化 WebSocket 连接
   * @param url - 可选的 WebSocket 地址
   * @param options - 配置选项
   */
  constructor(url: string, options: WebSocketOptions) {
    const finalOptions = { ...this.defaultOptions, ...options }
    this.serverAddress = this.buildWebSocketUrl(url)
    const wsUrl = this.serverAddress + '?token=' + getAccessToken()
    this.ws = useWebSocket(wsUrl, {
      autoReconnect: finalOptions.autoReconnect,
      heartbeat: finalOptions.heartbeat,
      onConnected: finalOptions.onConnected,
      onDisconnected: finalOptions.onDisconnected
    })

    // 监听消息并分发到注册的处理器
    this.setupMessageListener(finalOptions.onMessage)
  }

  /**
   * 构建 WebSocket URL
   * @param url - 用户传入的 URL(如果有的话)
   * @returns 完整的 WebSocket 连接地址
   */
  private buildWebSocketUrl(url?: string): string {
    if (url && url.startsWith('ws')) {
      return url
    }
    return localConfig.default.socket_url
  }

  /**
   * 设置消息监听器
   * @param onMessage - 自定义消息处理函数
   */
  private setupMessageListener(onMessage?: (data: string) => void) {
    watchEffect(() => {
      if (!this.ws.data.value) return

      // 如果有自定义消息处理则先调用
      if (onMessage) {
        onMessage(this.ws.data.value as string)
        return
      }

      // 默认消息处理逻辑
      this.handleDefaultMessage(this.ws.data.value as string)
    })
  }

  /**
   * 默认消息处理逻辑
   * @param data - 接收到的消息数据
   */
  private handleDefaultMessage(data: string) {
    try {
      // 心跳消息
      if (data === 'pong') {
        return
      }
      // 解析消息
      const jsonMessage = JSON.parse(data)
      const type = jsonMessage.type
      // 找到对应类型的处理器
      const handlers = this.messageHandlers.get(type)
      if (handlers) {
        handlers.forEach((handler) => handler(jsonMessage.content))
      }
    } catch (error) {
      console.error('处理 WebSocket 消息异常:', error)
    }
  }

  /**
   * 注册特定类型的消息处理器
   * @param type - 消息类型
   * @param handler - 处理该类型消息的函数
   */
  onMessageType(type: string, handler: MessageHandler) {
    if (!this.messageHandlers.has(type)) {
      this.messageHandlers.set(type, [])
    }
    this.messageHandlers.get(type)!.push(handler)
  }

  /**
   * 发送消息
   * @param type - 消息类型
   * @param content - 消息内容
   */
  send(type: string, content: any) {
    const message = JSON.stringify({
      type,
      content: typeof content === 'string' ? content : JSON.stringify(content)
    })
    this.ws.send(message)
  }

  /**
   * 发送文本消息
   * @param text - 要发送的文本内容
   * @param toUserId - 目标用户的 ID(可选)
   */
  sendMessage(text: string, toUserId?: string) {
    const content = {
      text,
      toUserId
    }
    this.send('message-send', content)
  }

  /**
   * 获取连接状态
   * @returns 当前 WebSocket 连接状态
   */
  getStatus(): WebSocketStatus {
    return this.ws.status.value
  }

  /**
   * 判断是否已连接
   * @returns 如果当前处于 OPEN 状态返回 true,否则返回 false
   */
  isOpen(): boolean {
    return this.ws.status.value === 'OPEN'
  }

  /**
   * 打开连接
   */
  connect() {
    this.ws.open()
  }

  /**
   * 关闭连接
   */
  disconnect() {
    this.ws.close()
  }

  /**
   * 获取原始 WebSocket 对象(用于特殊需求)
   * @returns 原始的 WebSocket 对象
   */
  getRawWebSocket() {
    return this.ws
  }
}

// 添加全局实例映射表,用于存储已创建的 WebSocketUtil 实例
const webSocketInstanceMap = new Map<string, WebSocketUtil>()

/**
 * 创建 WebSocket 实例的工厂函数
 * @param url - WebSocket 地址
 * @param options - 配置选项
 * @returns 新创建的 WebSocketUtil 实例
 */
export function createWebSocketUtil(url: string, options: WebSocketOptions = {}) {
  // 构建 WebSocket URL 作为唯一标识符
  // const wsUrl = new WebSocketUtil(url, {})
  console.log('wsUrl-192-', url)

  // 检查是否已存在相同 URL 的实例
  if (webSocketInstanceMap.has(url)) {
    console.log('196-已存在相同 URL 的实例-')
    // 如果已存在,则直接返回已创建的实例
    return webSocketInstanceMap.get(url)!
  } else {
    console.log('200-不存在相同 URL 的实例-')
    // 如果不存在,则创建新实例并存储
    const newInstance = new WebSocketUtil(url, options)
    webSocketInstanceMap.set(url, newInstance)
    return newInstance
  }
}

export default WebSocketUtil


使用:
import { createWebSocketUtil } from '@/utils/webSocket'

const webSocketInstance = ref()
const connectWebSocket = () => {
  // WebSocket 工具实例
  webSocketInstance.value = createWebSocketUtil('')

  // 注册消息处理器
  webSocketInstance.value.onMessageType('Notice_Push', (data) => {
    console.log('收到消息', data)
    ElNotification({
      message: '收到消息:' + data,
      type: 'success',
      duration: 2000
    })
    // 处理消息
    getList()
  })
}

onMounted(() => {
  connectWebSocket()
})

 

websocket更新el-table表格数据

//--------------------------------------websocket
let didizhi = 'ws://10.10.160.191:8081/ws'
let webljFn = (didizhi) => {
  let socket = new WebSocket(didizhi)
  //监听连接成功事件
  socket.onopen = function(){
    console.log('1.客户端连接上了服务器');
    // socket.send('3.你好')
  }
  // 定义指标映射配置
  const indicatorConfig = {
    'FDFH': { index: 0, name: '机组发电负荷' },
    'JHFDFH': { index: 1, name: '计划发电负荷' },
    'FHL': { index: 2, name: '机组负荷率' },
    'GDMH': { index: 3, name: '供电煤耗' },
    'CYDL': { index: 4, name: '厂用电率' },
    'SO2PFND': { index: 5, name: 'SO2排放浓度' },
    'NOXPFND': { index: 6, name: 'NOX排放浓度' },
    'YCPFND': { index: 7, name: '烟尘排放浓度' },
    'JZYXZT': { index: 8, name: '机组运行状态' },
  }

  // 定义机组映射
  const unitConfig = {
    '1号机组': 'machine_1',
    '2号机组': 'machine_2'
  }
  //监听接收到的消息
  socket.onmessage = async (event) => {
    if(event.data){
      try {
        let item = JSON.parse(event.data)
        if(item.plantCode === 'CF') {
          // console.log(item)
          const machineKey = unitConfig[item.unitName]//'1号机组'
          const indicator = indicatorConfig[item.indicatorCode]//{ index: 0, name: '机组发电负荷(MW)' }
          
          if(machineKey && indicator) {
            // 更新数据
            let result = Number(item.indicatorValue).toFixed(2)
            if(result == 0){
              result = 0
            }
            if(indicator.index != 8){
              jizuData[indicator.index][machineKey] = result
            }else{
              //机组运行状态
              if(item.indicatorCode == 'JZYXZT'){
                if(machineKey == 'machine_1'){
                  //1号机组
                  if(item.indicatorValue == '停运中'){
                    jizuList.value[0].status = '冷态'
                  }else{
                    jizuList.value[0].status = '热态'
                  }
                }else if(machineKey == 'machine_2'){
                  //2号机组
                  if(item.indicatorValue == '停运中'){
                    jizuList.value[1].status = '冷态'
                  }else{
                    jizuList.value[1].status = '热态'
                  }
                }
              }
            }

            /* if(indicator.index == 0){
              //当前是发电负荷,发电负荷为负数或0,其他值都为0
              if(Number(item.indicatorValue) <= 0){
                jizuData[0][machineKey] = 0;       
                jizuData[1][machineKey] = 0;       
                jizuData[2][machineKey] = 0;       
                jizuData[3][machineKey] = 0;       
                jizuData[4][machineKey] = 0;       
                jizuData[5][machineKey] = 0;       
                jizuData[6][machineKey] = 0;       
                jizuData[7][machineKey] = 0;
              }else{
                //给发电负荷赋值
                jizuData[0][machineKey] = Number(item.indicatorValue).toFixed(2)
              }    
            }else{
              //当前不是发电负荷,先判断发电负荷是否为0,如果为0,全部值都为0,
              if(jizuData[0][machineKey] == 0){
                jizuData[0][machineKey] = 0;       
                jizuData[1][machineKey] = 0;       
                jizuData[2][machineKey] = 0;       
                jizuData[3][machineKey] = 0;       
                jizuData[4][machineKey] = 0;       
                jizuData[5][machineKey] = 0;       
                jizuData[6][machineKey] = 0;       
                jizuData[7][machineKey] = 0;
              }else{
                //发电负荷不为0再赋值
                jizuData[indicator.index][machineKey] = Number(item.indicatorValue).toFixed(2)
              }
            } */

  //机组负荷曲线----------------------------------------------------------
            /* let obj = {}
            if(item.indicatorCode == 'FDFH'){
              obj['发电负荷值'] = jizuData[0][machineKey]//本次发电负荷
              obj['时间'] = dayjs(item.time).format("YYYY-MM-DD HH:mm")//本次时间
            }else if(item.indicatorCode == 'JHFDFH'){
              obj['计划发电负荷'] = jizuData[1][machineKey]//本次计划发电负荷
              obj['时间'] = dayjs(item.time).format("YYYY-MM-DD HH:mm")//本次时间
            }
            // console.log(obj)
            //四个折线初始化数组1440,值为0
            //获取时间在一天数组中的索引位置,和值
            //用索引在对应折线数组中找位置,将0替换为值
            let jzjishuanFn = (jzVal) => {
              let timeMap = {};

              // 提取时分部分作为键
              const timeKey = jzVal['时间'].split(' ')[1];
              // console.log(timeKey)
              let str = jzVal['发电负荷值']
              // timeMap[timeKey] = parseFloat(str).toString();
              
              // return jizuxData.map(timeStr => timeMap[timeStr] || 0);
                      //索引,值
              return [jizuxData.indexOf(timeKey),parseFloat(str).toString()]

            }
            let jzjhjishuanFn = (jzVal) => {
              let timeMap = {};
              
                // 提取时分部分作为键
                const timeKey = jzVal['时间'].split(' ')[1];
                let str = jzVal['计划发电负荷']
                // timeMap[timeKey] = parseFloat(str).toString();
              
              // return jizuxData.map(timeStr => timeMap[timeStr] || 0);
              return [jizuxData.indexOf(timeKey),parseFloat(str).toString()]

            }
            
            if(machineKey == 'machine_1'){
              if(item.indicatorCode == 'FDFH'){//1机组
                console.log(obj)
                let resultJz1 = jzjishuanFn(obj)
                console.log(resultJz1)
                seriesJZFHQX[0].data[resultJz1[0]] = resultJz1[1]
              }else if(item.indicatorCode == 'JHFDFH'){//1机组计划
                let resultJzJh1 = jzjhjishuanFn(obj)
                seriesJZFHQX[1].data[resultJzJh1[0]] = resultJzJh1[1]
              }
            }else if(machineKey == 'machine_2'){
              if(item.indicatorCode == 'FDFH'){//2机组
                let resultJz2 = jzjishuanFn(obj)
                seriesJZFHQX[2].data[resultJz2[0]] = resultJz2[1]
              }else if(item.indicatorCode == 'JHFDFH'){//2机组计划
                let resultJzJh2 = jzjhjishuanFn(obj)
                seriesJZFHQX[3].data[resultJzJh2[0]] = resultJzJh2[1]
              }
            }
            let option = {
              xAxis: [{
                  data: jizuxData,
              }],
              series: seriesJZFHQX
            };
            // console.log(seriesJZFHQX) */

            // 触发响应式更新
            await nextTick()
            jizuTableData.value = [...jizuData]
            // 更新图表
            // loadChart.value!.update(option);
            // console.log(`更新 ${item.unitName} - ${indicator.name}:`, item.indicatorValue)
          }
        }
      } catch (error) {
        console.error('处理消息出错:', error)
      }
      
      
    }
  }
  //处理错误
  socket.onerror = function(error) {
    console.error('WebSocket error:', error);
  };
  //关闭事件
  socket.onclose = function(event) {
    console.log('Connection closed:', event.code, event.reason);
    setTimeout(function() {
      console.log('重试连接')
      webljFn(didizhi)
      // socket = new WebSocket(didizhi); // 重新创建连接
    }, 3000); // 3秒后重试连接
  };
}
webljFn(didizhi)
//--------------------------------------websocket

 

posted @ 2026-01-12 09:01  峪野  阅读(12)  评论(0)    收藏  举报