useCoverModbusCommand.ts 4.9 KB
import { useParseOriginalDataType } from './useParseOriginalDataType';
import { getDeviceHistoryInfo } from '/@/api/alarm/position';
import { getDeviceDetail } from '/@/api/device/deviceManager';
import { ExtensionDesc, Specs, Tsl } from '/@/api/device/model/modelOfMatterModel';
import { genModbusCommand } from '/@/api/task';
import { GenModbusCommandType } from '/@/api/task/model';
import { ModbusCRCEnum, OriginalDataTypeEnum } from '/@/enums/objectModelEnum';
import { useBaseConversion } from '/@/hooks/business/useBaseConversion';
import { useMessage } from '/@/hooks/web/useMessage';
import { isNullOrUnDef } from '/@/utils/is';
import {
  isFloatType,
  isNumberType,
  useParseOperationType,
} from '/@/views/device/profiles/components/ObjectModelForm/ExtendDesc/useParseOperationType';

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);
};

function getValueFromValueRange(value: number, valueRange?: Record<'min' | 'max', number>) {
  const { min, max } = valueRange || {};
  if (!isNullOrUnDef(min) && value < min) return min;
  if (!isNullOrUnDef(max) && value > max) return max;
  return value;
}

async function getCurrentBitCommand(entityId: string, objectModel: Tsl, value: number) {
  const deviceDetail = await getDeviceDetail(entityId);
  const thingsModels = deviceDetail.deviceProfile.profileData.thingsModel;

  const { registerAddress } = objectModel.extensionDesc || {};

  const bitsModel = thingsModels?.filter(
    (item) =>
      item.extensionDesc?.originalDataType === OriginalDataTypeEnum.BITS &&
      item.extensionDesc.registerAddress === registerAddress
  );

  const valuePositionMap =
    bitsModel?.reduce((prev, next) => {
      return { ...prev, [next.identifier]: next.extensionDesc?.bitMask };
    }, {} as Record<string, number>) || {};

  const attrKeys = Object.keys(valuePositionMap);

  const latestBitsValues = await getDeviceHistoryInfo({ entityId, keys: attrKeys.join(',') });

  const binaryArr = Array.from({ length: 16 }, () => 0);

  for (const key of attrKeys) {
    const index = valuePositionMap[key];

    if (!isNullOrUnDef(index)) {
      const [latest] = latestBitsValues[key];
      const { value } = latest;
      binaryArr[index] = Number(value);
    }
  }

  if (objectModel.extensionDesc?.bitMask) {
    binaryArr[objectModel.extensionDesc.bitMask] = value;
  }

  return [parseInt(binaryArr.reverse().join(''), 2)];
}

export function useCoverModbusCommand() {
  const { createMessage } = useMessage();

  const doCoverCommand = async (
    value: number,
    objectModel: Tsl,
    deviceAddressCode?: string,
    entityId?: string
  ) => {
    if (!deviceAddressCode) {
      const message = '当前设备未绑定设备地址码';
      createMessage.warning(message);
      throw new Error(message);
    }

    const {
      registerAddress,
      operationType,
      scaling,
      originalDataType,
      bitMask,
      registerCount: registerNumber,
    } = objectModel.extensionDesc as Required<ExtensionDesc>;

    const { writeRegisterAddress } = useParseOperationType(operationType);
    const { unsigned, exchangeSortFlag, registerCount } =
      useParseOriginalDataType(originalDataType);

    const params: GenModbusCommandType = {
      crc: ModbusCRCEnum.CRC_16_LOWER,
      registerNumber: registerCount || registerNumber,
      deviceCode: deviceAddressCode,
      registerAddress: parseInt(registerAddress, 16),
      method: writeRegisterAddress!,
      registerValues: [value],
    };

    if (exchangeSortFlag) params.hexByteOrderEnum = exchangeSortFlag;

    const { getRegisterValueByOriginalDataType } = useBaseConversion();

    if (isNumberType(originalDataType)) {
      let newValue = Math.trunc(value) * scaling + getFloatPart(value) * scaling;

      newValue = unsigned ? newValue : Math.abs(newValue);

      newValue = getValueFromValueRange(
        newValue,
        (objectModel.specs?.dataType.specs as Specs).valueRange
      );

      if (!isFloatType(originalDataType) && newValue % 1 !== 0) {
        const message = `属性下发类型必须是整数,缩放因子为${scaling}`;
        createMessage.warning(message);
        throw Error(message);
      }

      value = newValue;
    }

    params.registerValues =
      originalDataType === OriginalDataTypeEnum.BITS
        ? await getCurrentBitCommand(entityId!, objectModel, value)
        : getRegisterValueByOriginalDataType(value, originalDataType, {
            bitMask,
            registerNumber,
          });

    if (!params.method) {
      const message = '物模型操作类型无法进行写入';
      createMessage.warning(message);
      throw Error(message);
    }

    return await genModbusCommand(params);
  };

  return {
    doCoverCommand,
  };
}