index.vue 8.23 KB
<script lang="ts" setup>
  import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
  import { option } from './config';
  import { Slider, Spin } from 'ant-design-vue';
  import { computed, ref, unref } from 'vue';
  import { useComponentScale } from '../../../hook/useComponentScale';
  import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  import { useSendCommand } from '../../../hook/useSendCommand';
  import { useReceiveValue } from '../../../hook/useReceiveValue';
  import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  import { useDataFetch } from '../../../hook/socket/useSocket';
  import { TaskTypeEnum } from '/@/views/task/center/config';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { SingleToHex } from '/@/views/device/list/cpns/tabs/ObjectModelCommandDeliveryModal/config';
  import { genModbusCommand } from '/@/api/task';

  const props = defineProps<{
    config: ComponentPropsConfigType<typeof option>;
  }>();

  const sliderValue = ref<number>(33);
  const oldSliderValue = ref<number>(33);
  const noSendValue = ref<number>(0);
  const sliderEl = ref<Nullable<InstanceType<typeof Slider>>>(null);

  const { loading, sendCommand } = useSendCommand();

  const getDesign = computed(() => {
    const { option, persetOption } = props.config;
    const {
      componentInfo,
      attribute,
      attributeRename,
      attributeName,
      commandType,
      extensionDesc,
      codeType,
      deviceCode,
      customCommand,
    } = option;
    const {
      controlBarColor: persetControlBarColor,
      fonColor: persetFontColor,
      minNumber: persetMinNumber,
      maxNumber: persetMaxNumber,
    } = persetOption || {};
    const { controlBarColor, fontColor, minNumber, maxNumber } = componentInfo || {};
    return {
      attribute: attributeRename || attributeName || attribute,
      controlBarColor: controlBarColor ?? persetControlBarColor,
      fontColor: fontColor ?? persetFontColor,
      minNumber: minNumber ?? persetMinNumber,
      maxNumber: maxNumber ?? persetMaxNumber,
      extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {},
      commandType,
      codeType,
      deviceCode,
      customCommand,
    };
  });

  const index = ref<number>(0);
  const afterValue = ref<number>(0); //点击Slider获取的值

  const handleChange = async (e) => {
    index.value++;
    afterValue.value = e;
    if (index.value == 1) {
      oldSliderValue.value = unref(sliderValue);
      return; //这个是因为设置了最大值时,当sliderValue的值大于最大值时只会显示设置的最大值,会执行一次change
    }
    sliderValue.value = e;
  };

  const handleAfterChange = async () => {
    unref(sliderEl)?.blur();
    if (unref(sliderValue) == unref(afterValue)) return;
    sliderValue.value = afterValue.value; //这一步是因为当我们是点击不是拖动时候,会让值更新不了,所以需要赋值一次
  };

  const { createMessage } = useMessage();

  // 获取小数
  const getFloatPart = (number: string | number) => {
    const isLessZero = Number(number) < 0;
    number = number.toString();
    const floatPartStartIndex = number.indexOf('.');
    const value = ~floatPartStartIndex
      ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}`
      : '0';
    return Number(value);
  };

  const getArray = (values) => {
    const str = values.replace(/\s+/g, '');
    const array: any = [];

    for (let i = 0; i < str.length; i += 4) {
      const chunk = parseInt(str.substring(i, i + 4), 16);
      array.push(chunk);
    }
    return array;
  };

  const getSendValue = async (value: number) => {
    const { extensionDesc, codeType, deviceCode, customCommand } = unref(getDesign) || {};
    const { transportType } = customCommand || {};
    const { registerAddress, actionType, zoomFactor } = extensionDesc || {};
    const newZoomValue = zoomFactor ? Number(zoomFactor) : 1;
    if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) {
      if (!deviceCode) {
        createMessage.warning('当前设备没有设置地址码');
        return;
      }
      const modbusForm = ref({
        crc: 'CRC_16_LOWER',
        deviceCode: deviceCode,
        method: actionType == '16' ? '10' : actionType,
        registerAddress,
        registerNumber: 1,
        registerValues: [value],
      }) as any;
      if (!actionType) {
        createMessage.warning('当前物模型扩展描述没有填写');
        return;
      }
      if (actionType == '06') {
        const newValue = Math.trunc(value) * newZoomValue + getFloatPart(value) * newZoomValue;
        if (newValue % 1 != 0) {
          createMessage.warning(`值必须是整数,缩放因子为${unref(newZoomValue)}`);
          return;
        }

        if (newValue > 65535) {
          createMessage.warning(`值不能超过65535,缩放因子是${unref(newZoomValue)}`);
          return;
        }
        modbusForm.value.registerValues = [newValue];
      }
      if (actionType == '05') {
        if (Number(value) != 0 || Number(value) != 1) {
          createMessage.warning(`当前物模型仅支持值为0和1`);
          return;
        }
      }
      if (actionType == '16' || actionType == '10') {
        const regex = /^-?\d+(\.\d{0,2})?$/;
        const values =
          Math.trunc(value) * unref(newZoomValue) + getFloatPart(value) * unref(newZoomValue);
        if (!regex.test(values as any)) {
          createMessage.warning(`值精确到两位小数,缩放因子是${unref(newZoomValue)}`);
          return;
        }
        const newValue = values == 0 ? [0, 0] : getArray(SingleToHex(values));
        modbusForm.value.registerValues = newValue;
        modbusForm.value.registerNumber = 2;
        modbusForm.value.method = '10';
      }
      const sendValue = await genModbusCommand(unref(modbusForm));
      return sendValue;
    }
  };

  const handleBlur = async () => {
    if (unref(oldSliderValue) !== unref(sliderValue)) {
      const { codeType, customCommand } = unref(getDesign) || {};
      const { transportType } = customCommand || {};
      const sendValue = ref<any>(unref(sliderValue));
      if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) {
        sendValue.value = await getSendValue(unref(sliderValue));
      }
      const flag = await sendCommand(props.config.option, unref(sendValue), true);
      flag
        ? ((sliderValue.value = unref(sliderValue)),
          (oldSliderValue.value = sliderValue.value),
          (index.value = 0))
        : (sliderValue.value = unref(oldSliderValue));
    }
  };

  const { getNumberValue } = useReceiveValue();
  const updateFn: DataFetchUpdateFn = (message, attribute) => {
    const { data = {} } = message;
    const [latest] = data[attribute] || [];
    const [_, value] = latest;
    noSendValue.value = getNumberValue(value);
    sliderValue.value = getNumberValue(value);
  };

  useDataFetch(props, updateFn);
  const { getScale } = useComponentScale(props);
</script>

<template>
  <main class="w-full h-full flex flex-col justify-center">
    <DeviceName :config="config" class="text-center" />
    <main :style="getScale">
      <Spin :spinning="loading" class="w-full h-full">
        <div class="flex flex-col" style="width: 80%">
          <span
            :style="{ color: getDesign.fontColor }"
            class="font-bold text-xl mt-3 truncate text-center"
            >{{ sliderValue }}</span
          >
          <Slider
            ref="sliderEl"
            :style="{ '--slider-color': getDesign.controlBarColor }"
            class="no-drag"
            :value="sliderValue"
            :min="getDesign.minNumber"
            :max="getDesign.maxNumber"
            @change="handleChange"
            @afterChange="handleAfterChange"
            @blur="handleBlur"
          />

          <span
            :style="{ color: getDesign.fontColor }"
            class="mt-3 truncate font-bold text-xs text-center"
          >
            {{ getDesign.attribute || '属性' }}
          </span>
        </div>
      </Spin>
    </main>
  </main>
</template>
<style lang="less" scoped>
  :deep(.ant-slider-track) {
    background: var(--slider-color) !important;
  }

  :deep(.ant-spin-container) {
    display: flex !important;
    justify-content: center !important;
  }
</style>