socketStore.ts 13.6 KB
import { defineStore } from "pinia";
import { KeyBoundComponentList, SocketComponentRecord, SocketConnectionPoolType, SocketReceiveMessageType, SocketSendMessageItemType, SocketSendMessageType, SocketStoreType, UnsubscribePoolType } from '@/store/external/modules/socketStore.d'
import { CreateComponentType } from "@/packages/index.d";
import { RequestContentTypeEnum } from "@/enums/external/httpEnum";
import { useChartEditStore } from "@/store/modules/chartEditStore/chartEditStore";
import { pinia } from '@/store'
import { cloneDeep } from "lodash";
import { useFilterFn } from "@/hooks/external/useFilterFn";


// const KEYS_SEPARATOR = ','
// const chartEditStore = useChartEditStore(pinia) //放在这里总是报错
export const useSocketStore = defineStore({
  id: 'useSocketStore',
  state: (): SocketStoreType => ({
    connectionPool: {},
    subscribePool: [],
    cacheMessage: {},
    currentSubscribeId: 0,
    unsubscribePool: [],
    componentUpdateFnPool: {}
  }),
  getters: {
    /**
     * @description 获取所有socket连接的组件
     * @returns 
     */
    getSocketComponentsRecord(): SocketComponentRecord[] {
      const chartEditStore = useChartEditStore(pinia)
      const socketComponents = chartEditStore.getComponentList.filter(item => (item.request.requestContentType as RequestContentTypeEnum) === RequestContentTypeEnum.WEB_SOCKET)
      return socketComponents.map(item => {
        const { request, id } = item
        const { requestParams } = request
        const { Params } = requestParams
        const { keys } = Params
        return {
          componentId: id,
          keys: keys as unknown as string[]
        }
      })
    },
  },
  actions: {
    getSubscribeId() {
      return this.currentSubscribeId++
    },

    setUnsubscribePool(message: UnsubscribePoolType) {
      this.unsubscribePool.push(message)
    },

    removeUnsubscribePool(message: SocketReceiveMessageType) {
      const index = this.unsubscribePool.findIndex(item => item.subscribeId === message.subscriptionId)
      this.unsubscribePool.splice(index, 1)
    },

    /**
     * @description 更新连接池
     */
    updateConnectionPool(entityId: string, keys: string[], componentId: string) {
      const isExist = Reflect.has(this.connectionPool, entityId)
      if (isExist) {
        const temp = Reflect.get(this.connectionPool, entityId) as { [key: string]: KeyBoundComponentList[] }
        keys.forEach(key => {
          const isExistKey = Reflect.has(temp, key)
          if (!isExistKey) {
            const keyBindEntityIdList = Reflect.get(temp, key) || []
            Reflect.set(temp, key, [...keyBindEntityIdList, { componentId }] as KeyBoundComponentList[])
          } else {
            temp[key].push({ componentId })
          }
        })
      } else {
        const keysRecord: Record<string, KeyBoundComponentList[]> = {}
        /**这里修改自定义tab切换传的单个属性问题
         * ft
         * 源代码 keys.forEach
         * 修改代码 const overrideKeys = typeof keys==="string"?[keys]: keys  overrideKeys.forEach
         */
        const overrideKeys = typeof keys === "string" ? [keys] : keys

        overrideKeys.forEach(key => {
          Reflect.set(keysRecord, key, [{ componentId }])
        })
        //ft
        Reflect.set(this.connectionPool, entityId, keysRecord)
      }
      return this.refreshSubscribedMessage(entityId)
    },

    /**
     * @description 获取重新刷新的消息
     * @param entityId 
     * @param needUnsubscribe
     * @returns 
     */
    refreshSubscribedMessage(entityId: string) {
      const isExist = this.subscribePool.findIndex(item => item.entityId === entityId)
      const needUnsubscribe = !!~isExist

      const newSubscribeId = this.getSubscribeId()
      let oldSubscribeId: number
      // 订阅设备不存在时
      if (!~isExist) {

        this.subscribePool.push({ subscribeId: newSubscribeId, entityId })
      } else {
        oldSubscribeId = this.subscribePool.findIndex(item => item.entityId === entityId)
        this.subscribePool.splice(oldSubscribeId, 1)
        this.subscribePool.push({ subscribeId: newSubscribeId, entityId })
      }

      const unSubscribeMessage = needUnsubscribe ? this.createUnSubscribeMessage(oldSubscribeId!, entityId) : null
      const subscribeMessage = this.createMessage(newSubscribeId, entityId)

      return {
        unSubscribeMessage,
        subscribeMessage
      }
    },

    /**
     * @description 创建消息
     * @param subscribeId 
     * @param entityId 
     * @returns 
     */
    createMessage(subscribeId: number, entityId: string): SocketSendMessageType {
      const keys = Object.keys(Reflect.get(this.connectionPool, entityId)).join(',')
      const messageInfo = { entityType: 'DEVICE', entityId: entityId, scope: "LATEST_TELEMETRY", cmdId: subscribeId, keys }
      return {
        tsSubCmds: [
          messageInfo
        ]
      }
    },

    /**
     * @description 创建取消订阅的消息
     * @param subscribeId 
     * @param entityId 
     */
    createUnSubscribeMessage(subscribeId: number, entityId: string): SocketSendMessageType {
      const keys = Object.keys(Reflect.get(this.connectionPool, entityId)).join(',')
      const messageInfo = { entityType: 'DEVICE', entityId: entityId, scope: "LATEST_TELEMETRY", cmdId: subscribeId, keys, unsubscribe: true } as SocketSendMessageItemType
      const message = {
        tsSubCmds: [messageInfo]
      }
      this.setUnsubscribePool({ subscribeId, message })
      return message
    },

    /**
     * @description 订阅
     * @param targetComponent 
     */
    subscribe(targetComponent: CreateComponentType, updateCallback?: Fn) {
      const { id: componentId, request } = targetComponent
      const { requestContentType, requestParams } = request
      if ((requestContentType as RequestContentTypeEnum) === RequestContentTypeEnum.WEB_SOCKET) {
        const { Params } = requestParams
        const { entityId = '', keys = [] } = Params
        updateCallback && (this.componentUpdateFnPool[componentId] = updateCallback)
        return this.updateConnectionPool(entityId, keys as string[], componentId)
      }
    },

    /**
     * @description 缓存消息
     * @param message 
     */
    setCacheMessage(message: SocketReceiveMessageType) {
      const { subscriptionId } = message
      const existedIndex = this.subscribePool.findIndex(item => item.subscribeId === subscriptionId)

      if (~existedIndex) {
        const isExistMessage = Reflect.get(this.cacheMessage, subscriptionId)
        if (!isExistMessage) Reflect.set(this.cacheMessage, subscriptionId, [])
        Reflect.set(this.cacheMessage, subscriptionId, [...Reflect.get(this.cacheMessage, subscriptionId), message])
      }
    },

    /**
     * @description 获取需要更新的组件通过订阅id
     * @param subscribeId 
     * @param keys 
     * @returns 
     */
    getNeedUpdateComponentsIdBySubscribeId(subscribeId: number, keys: string[]) {
      const entityId = this.subscribePool.find(item => item.subscribeId === subscribeId)?.entityId
      /**这里修改自定义tab切换传的单个属性问题
        * ft
        * 源代码 keys.map(key => keysRecord[key])
        * 修改代码 const overrideKeys = typeof keys==="string"?[keys]: keys   overrideKeys.map(key => keysRecord[key])
        */
      const overrideKeys = typeof keys === "string" ? [keys] : keys

      if (entityId) {
        const keysRecord = Reflect.get(this.connectionPool, entityId)
        const needUpdateComponents = overrideKeys.map(key => keysRecord[key])
        const ids: string[] = needUpdateComponents
          .reduce((prev, next) => [...prev, ...next], [])
          .map((item: KeyBoundComponentList) => item.componentId)
        return [...new Set(ids)]
      }
      //ft
    },

    /**
     * @description 获取当前组件绑定的keys数据
     * @param targetComponent 
     * @param value 
     * @returns 
     */
    getComponentValueByKeys(targetComponent: CreateComponentType, value: SocketReceiveMessageType) {
      const { request: { requestParams } } = targetComponent
      const { Params } = requestParams
      const { keys = [] } = Params
      /**这里修改自定义tab切换传的单个属性问题
        * ft
        * 源代码 keys as unknown as string[]
        * 修改代码 const overrideKeys = typeof keys==="string"?[keys]: keys   overrideKeys as unknown as string[]
      */
      const overrideKeys = typeof keys === "string" ? [keys] : keys

      const targetComponentBindKeys = overrideKeys as unknown as string[]
      //ft

      const _value = cloneDeep(value) || { data: {}, latestValues: {} }
      _value.data = targetComponentBindKeys.reduce((prev, next) => {
        return { ...prev, [next]: _value.data[next] }
      }, {})
      _value.latestValues = targetComponentBindKeys.reduce((prev, next) => {
        return { ...prev, [next]: _value.latestValues[next] }
      }, {})

      return _value
    },

    /**
     * @description 更新组件数据通过组件id
     * @param id 
     * @param value 
     */
    updateComponentById(id: string, value: SocketReceiveMessageType) {
      /**
       * 原来的代码
      // const targetComponent = this.getSocketComponentsRecord.find(item => item.componentId === id)
      // const targetComponentIndex = chartEditStore.fetchTargetIndex(targetComponent?.componentId)
      // const target = chartEditStore.componentList[targetComponentIndex] as CreateComponentType
      // const _target = cloneDeep(target)
      // const { filter } = _target
      // const _value = this.getComponentValueByKeys(target, value)
      // const { value: filterValue, reason, flag } = useFilterFn(filter, _value)
      // _target.option.dataset = flag ? filterValue : reason
      // // TODO 存在重复更新未变化的值
      // // console.log({ _target })
      // chartEditStore.updateComponentList(targetComponentIndex, _target)
       */

      /**
       * 修改后的代码
       * 修改ws绑定单个文本组件,然后有多个,并且进行分组,显示的信息为原始信息,而非过滤函数返回的信息
       */
      const chartEditStore = useChartEditStore(pinia)
      chartEditStore.getComponentList.forEach(targetItem => {
        if (targetItem.isGroup) {
          /**
           * 如果是分组,并且request里是ws,则分组组件这个整体是接受ws绑定的,之前分组是不能绑定ws的
           */
          if (targetItem.request.requestUrl?.includes('ws')) {
            const _value = this.getComponentValueByKeys(targetItem, value)
            const { filter } = targetItem
            const { value: filterValue, reason, flag } = useFilterFn(filter, _value)
            targetItem.option.dataset = flag ? filterValue : reason
            try {
              const dumpSaveHistoryInput = JSON.parse((!targetItem.saveHistoryInput ? null : targetItem.saveHistoryInput) as string) || JSON.parse(window.localStorage.getItem('CACHE_HISTORY_INPUT_VALUE') as string)
              if (!dumpSaveHistoryInput) return
              if (!Array.isArray(dumpSaveHistoryInput)) return
              targetItem.groupList?.forEach((item: CreateComponentType) => {
                dumpSaveHistoryInput?.forEach((inputItem: { id: string, inputValue: string }) => {
                  if (item.id === inputItem.id) {
                    item.option.dataset = Function('res', `return ${inputItem?.inputValue}`)(targetItem.option?.dataset)
                  }
                })
              })
            } catch (e) {
              console.log(e)
            }
          }
          /**
           * 这也是分组,但这个是分组这个整体不绑定ws,而是下面的子组件都绑定了ws
           */
          targetItem.groupList?.forEach(groupItem => {
            if (groupItem.id === id) {
              const _value = this.getComponentValueByKeys(groupItem, value)
              const { filter } = groupItem
              const { value: filterValue, reason, flag } = useFilterFn(filter, _value)
              groupItem.option.dataset = flag ? filterValue : reason
            }
          })
        } else {
          //单个
          if (targetItem.id === id) {
            const _value = this.getComponentValueByKeys(targetItem, value)
            const { filter } = targetItem
            const { value: filterValue, reason, flag } = useFilterFn(filter, _value)
            targetItem.option.dataset = flag ? filterValue : reason
          }
        }
        //
      })
    },

    /**
     * @description 更新组件数据源
     * @param value 
     */
    updateComponentDataset(value: SocketReceiveMessageType) {
      const { subscriptionId, data } = value
      const keys = Object.keys(data)
      const componentIds = this.getNeedUpdateComponentsIdBySubscribeId(subscriptionId, keys)
      componentIds?.forEach((targetComponentId) => {
        const fn = this.componentUpdateFnPool[targetComponentId]
        try {
          fn?.(value)
        } catch (error) {
          throw `componentIds: ${componentIds} call update function happend error!`
        }
        this.updateComponentById(targetComponentId as string, value)
      })
    },

    /**
     * @description socket接受到消息后,从需要取消的订阅池中取消订阅消息
     */
    unsubscribe(message: SocketReceiveMessageType, unsubscribeFn: Fn) {
      const { subscriptionId } = message
      if (subscriptionId === undefined) return
      const index = this.unsubscribePool.findIndex(item => item.subscribeId === subscriptionId)
      if (!~index) return
      const sendMessage = this.unsubscribePool[index].message
      unsubscribeFn(JSON.stringify(sendMessage))
      this.removeUnsubscribePool(message)
    }
  }
})