Commit a4392a5dfa923f3fe17ae3205bc1ebb5bd91188c
Committed by
xp.Huang
1 parent
36e69e2d
feat:支持标准modbus_rtu解析及命令下发
Showing
39 changed files
with
1305 additions
and
763 deletions
| 1 | 1 | import type { DeviceActiveType, DeviceAttributeItemType, DeviceItemType, DeviceProfileItemType, OrganizationItemType, ProductsDetailWithThingsModelType, RpcCommandType, SendValue, ThingsModel, ThingsModelItemType } from './model' |
| 2 | 2 | import { CommandWayEnum } from '@/enums/commandEnum' |
| 3 | -import type { DeviceTypeEnum } from '@/enums/datasource' | |
| 4 | -import { FunctionTypeEnum } from '@/enums/datasource' | |
| 3 | +import type { DeviceTypeEnum } from '@/enums/deviceEnum' | |
| 4 | +import { FunctionTypeEnum } from '@/enums/objectModelEnum' | |
| 5 | 5 | import { isShareMode } from '@/utils/env' |
| 6 | 6 | import { defHttp } from '@/utils/http' |
| 7 | 7 | |
| ... | ... | @@ -24,6 +24,7 @@ enum Api { |
| 24 | 24 | GET_LIST_BY_CONFIGURATION_ID = '/configuration/center/getListByConfigurationId', |
| 25 | 25 | |
| 26 | 26 | GET_PRODUCTS_DETAIL_WITH_THINGS_MODEL = '/things_model/batch/get_tsl', |
| 27 | + | |
| 27 | 28 | } |
| 28 | 29 | |
| 29 | 30 | export interface GenModbusCommandType { |
| ... | ... | @@ -33,6 +34,7 @@ export interface GenModbusCommandType { |
| 33 | 34 | registerAddress: number |
| 34 | 35 | registerNumber?: number |
| 35 | 36 | registerValues?: number[] |
| 37 | + hexByteOrderEnum?: string | |
| 36 | 38 | } |
| 37 | 39 | |
| 38 | 40 | export const getDeviceProfile = (deviceType?: DeviceTypeEnum) => { |
| ... | ... | @@ -128,6 +130,17 @@ export const getDeviceActiveTime = (entityId: string) => { |
| 128 | 130 | ) |
| 129 | 131 | } |
| 130 | 132 | |
| 133 | +export const getDeviceTelemetryValue = (params: { entityId: string; keys: string }) => { | |
| 134 | + return defHttp.get({ | |
| 135 | + url: `${Api.GET_DEVICE_ACTIVE}${params.entityId}/values/timeseries`, | |
| 136 | + params: { | |
| 137 | + keys: params.keys, | |
| 138 | + }, | |
| 139 | + }, { | |
| 140 | + joinPrefix: false, | |
| 141 | + }) | |
| 142 | +} | |
| 143 | + | |
| 131 | 144 | // 获取设备详情 |
| 132 | 145 | export const getDeviceInfo = (deviceId: string) => { |
| 133 | 146 | return defHttp.get<DeviceItemType>( | ... | ... |
| 1 | -import type { DataTypeEnum, FunctionTypeEnum, TransportTypeEnum } from '@/enums/datasource' | |
| 1 | +import type { CommandCallWayEnum } from '@/enums/commandEnum' | |
| 2 | +import type { TCPProtocolTypeEnum, TransportTypeEnum } from '@/enums/deviceEnum' | |
| 3 | +import type { DataTypeEnum, ExtendDescOperationTypeEnum, FunctionTypeEnum, OriginalDataTypeEnum } from '@/enums/objectModelEnum' | |
| 2 | 4 | |
| 3 | 5 | export interface DeviceProfileItemType { |
| 4 | 6 | id: string |
| ... | ... | @@ -41,6 +43,7 @@ export interface Configuration { |
| 41 | 43 | |
| 42 | 44 | export interface TransportConfiguration { |
| 43 | 45 | type: string |
| 46 | + protocol: TCPProtocolTypeEnum | |
| 44 | 47 | } |
| 45 | 48 | |
| 46 | 49 | export interface ProvisionConfiguration { |
| ... | ... | @@ -116,6 +119,10 @@ export interface DeviceItemType { |
| 116 | 119 | deviceType: string |
| 117 | 120 | alarmStatus: number |
| 118 | 121 | enable: boolean |
| 122 | + deviceProfile: { | |
| 123 | + transportType: TransportTypeEnum | |
| 124 | + profileData: ProfileData | |
| 125 | + } | |
| 119 | 126 | |
| 120 | 127 | transportType: TransportTypeEnum |
| 121 | 128 | } |
| ... | ... | @@ -135,10 +142,14 @@ export interface ThingsModelItemType { |
| 135 | 142 | } |
| 136 | 143 | |
| 137 | 144 | export interface ExtensionDesc { |
| 138 | - zoomFactor?: number | |
| 139 | - actionType?: string | |
| 140 | - dataType: string | |
| 141 | - registerAddress: number | |
| 145 | + writeOnly?: boolean | |
| 146 | + bitMask?: number | |
| 147 | + operationType: ExtendDescOperationTypeEnum | |
| 148 | + originalDataType: OriginalDataTypeEnum | |
| 149 | + registerAddress: string | |
| 150 | + scaling?: number | |
| 151 | + valueRange?: Record<'min' | 'max', number> | |
| 152 | + registerCount?: number | |
| 142 | 153 | } |
| 143 | 154 | |
| 144 | 155 | export interface Detail { |
| ... | ... | @@ -146,7 +157,7 @@ export interface Detail { |
| 146 | 157 | } |
| 147 | 158 | |
| 148 | 159 | export interface DataType { |
| 149 | - type: string | |
| 160 | + type: DataTypeEnum | |
| 150 | 161 | specs: Specs | StructJSON[] |
| 151 | 162 | specsList: Specs[] |
| 152 | 163 | } |
| ... | ... | @@ -163,6 +174,7 @@ export interface Specs { |
| 163 | 174 | name?: string |
| 164 | 175 | value?: any |
| 165 | 176 | dataType?: DataTypeEnum |
| 177 | + step?: number | |
| 166 | 178 | } |
| 167 | 179 | |
| 168 | 180 | export interface ValueRange { |
| ... | ... | @@ -207,6 +219,8 @@ export interface Tsl { |
| 207 | 219 | functionName: string |
| 208 | 220 | identifier: string |
| 209 | 221 | accessMode: string |
| 222 | + functionType: FunctionTypeEnum | |
| 223 | + callType: CommandCallWayEnum | |
| 210 | 224 | specs: { |
| 211 | 225 | dataType: DataType |
| 212 | 226 | } | ... | ... |
| 1 | 1 | import type { ThingsModelItemType } from '@/api/device/model' |
| 2 | 2 | import type { ImageSelectorDataType } from '@/core/Library/components/ImageSelector' |
| 3 | -import type { CommandWayEnum } from '@/enums/commandEnum' | |
| 4 | -import type { ActRangListItemTypeEnum, ActTypeEnum, AggregateTypeEnum, CommandDeliveryWayEnum, DeviceTypeEnum, EventActionTypeEnum, EventTypeEnum, SocketSubscriberEnum } from '@/enums/datasource' | |
| 3 | +import type { CommandDeliveryWayEnum, CommandWayEnum } from '@/enums/commandEnum' | |
| 4 | +import type { ActRangListItemTypeEnum, ActTypeEnum, AggregateTypeEnum, EventActionTypeEnum, EventTypeEnum, SocketSubscriberEnum } from '@/enums/datasource' | |
| 5 | +import type { DeviceTypeEnum } from '@/enums/deviceEnum' | |
| 5 | 6 | |
| 6 | 7 | export enum DeleteNodeDataTypeEnum { |
| 7 | 8 | DATASOURCE = 'DATASOURCE', | ... | ... |
| ... | ... | @@ -2,7 +2,9 @@ import type { FormSchema } from '@/components/Form' |
| 2 | 2 | import { ComponentEnum } from '@/components/Form/src/enum' |
| 3 | 3 | import type { BasicColumn } from '@/components/Table' |
| 4 | 4 | import type { CommandWayEnum } from '@/enums/commandEnum' |
| 5 | -import { ActTypeEnum, type CodeTypeEnum, type ContentDataFieldsEnum, type DeviceTypeEnum } from '@/enums/datasource' | |
| 5 | +import { ActTypeEnum, type ContentDataFieldsEnum } from '@/enums/datasource' | |
| 6 | +import type { DeviceTypeEnum, TCPProtocolTypeEnum } from '@/enums/deviceEnum' | |
| 7 | + | |
| 6 | 8 | export enum TableColumnFieldEnum { |
| 7 | 9 | DEVICE_ID = 'deviceId', |
| 8 | 10 | WAY = 'way', |
| ... | ... | @@ -15,7 +17,7 @@ export interface TableRecordItemType { |
| 15 | 17 | [TableColumnFieldEnum.WAY]?: Nullable<CommandWayEnum> |
| 16 | 18 | [TableColumnFieldEnum.COMMAND]?: Nullable<string | Recordable> |
| 17 | 19 | [ContentDataFieldsEnum.DEVICE_TYPE]?: Nullable<DeviceTypeEnum> |
| 18 | - [ContentDataFieldsEnum.CODE_TYPE]?: Nullable<CodeTypeEnum> | |
| 20 | + [ContentDataFieldsEnum.CODE_TYPE]?: Nullable<TCPProtocolTypeEnum> | |
| 19 | 21 | } |
| 20 | 22 | |
| 21 | 23 | export const getFormSchemas = (): FormSchema[] => { | ... | ... |
| ... | ... | @@ -2,10 +2,10 @@ import type { DeviceItemType, Specs, StructJSON, Tsl } from '@/api/device/model' |
| 2 | 2 | import type { NodeDataDataSourceJsonType, OperationPasswordDataType, SingleClickEventDataType } from '@/api/node/model' |
| 3 | 3 | import { type FormSchema, useComponentRegister } from '@/components/Form' |
| 4 | 4 | import { ComponentEnum } from '@/components/Form/src/enum' |
| 5 | -import { ThingsModelForm, validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm' | |
| 5 | +import { ThingsModelForm } from '@/core/Library/components/ThingsModelForm' | |
| 6 | 6 | import { getFormSchemas } from '@/core/Library/components/ThingsModelForm/config' |
| 7 | -import { CodeTypeEnum, DataTypeEnum, TransportTypeEnum } from '@/enums/datasource' | |
| 8 | -import { TCPObjectModelActionTypeEnum } from '@/enums/objectModelEnum' | |
| 7 | +import { TCPProtocolTypeEnum, TransportTypeEnum } from '@/enums/deviceEnum' | |
| 8 | +import { DataTypeEnum, OriginalDataTypeEnum } from '@/enums/objectModelEnum' | |
| 9 | 9 | |
| 10 | 10 | useComponentRegister(ComponentEnum.THINGS_MODEL_FORM, ThingsModelForm) |
| 11 | 11 | |
| ... | ... | @@ -35,59 +35,57 @@ function getStructJsonFromDataSourceJson(objectModelTsl: Tsl): StructJSON { |
| 35 | 35 | } |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | -function getTCPModbusSchemas({ objectModelTsl, required }: { objectModelTsl: Tsl; required?: boolean }): FormSchema { | |
| 39 | - const { specs: tslSpecs, extensionDesc } = objectModelTsl | |
| 40 | - const { dataType } = tslSpecs | |
| 41 | - const { specs, type } = dataType || {} | |
| 42 | - const { valueRange, length = 10240 } = specs! as Specs | |
| 43 | - const { max = Number.MAX_SAFE_INTEGER, min = Number.MIN_SAFE_INTEGER } = valueRange || {} | |
| 44 | - const { actionType } = extensionDesc || {} | |
| 38 | +const validateDouble = (value: number, min?: number | string, max?: number | string) => { | |
| 39 | + min = Number(min) ?? Number.MIN_SAFE_INTEGER | |
| 40 | + max = Number(max) ?? Number.MAX_SAFE_INTEGER | |
| 45 | 41 | |
| 46 | - function createInputNumber({ | |
| 47 | - identifier, | |
| 48 | - functionName, | |
| 49 | - }: Tsl): FormSchema { | |
| 50 | - return { | |
| 51 | - field: identifier, | |
| 52 | - label: functionName, | |
| 53 | - component: ComponentEnum.INPUT_NUMBER, | |
| 54 | - required, | |
| 55 | - componentProps: { | |
| 56 | - max: actionType === TCPObjectModelActionTypeEnum.BOOL ? 1 : max, | |
| 57 | - min: actionType === TCPObjectModelActionTypeEnum.BOOL ? 0 : min, | |
| 58 | - precision: actionType === TCPObjectModelActionTypeEnum.BOOL ? 0 : 2, | |
| 59 | - }, | |
| 60 | - } | |
| 42 | + return { | |
| 43 | + flag: value < min || value > max, | |
| 44 | + message: `取值范围在${min}~${max}之间`, | |
| 61 | 45 | } |
| 46 | +} | |
| 62 | 47 | |
| 63 | - const createInput = ({ identifier, functionName }: Tsl): FormSchema => { | |
| 64 | - return { | |
| 65 | - field: identifier, | |
| 66 | - label: functionName, | |
| 67 | - component: ComponentEnum.INPUT, | |
| 68 | - rules: [ | |
| 69 | - { | |
| 70 | - required, | |
| 71 | - message: `${functionName}是必填项`, | |
| 72 | - }, | |
| 73 | - { | |
| 74 | - validator: validateTCPCustomCommand, | |
| 75 | - }, | |
| 76 | - ], | |
| 77 | - componentProps: { | |
| 78 | - maxLength: length, | |
| 79 | - }, | |
| 80 | - } as FormSchema | |
| 81 | - } | |
| 48 | +export const createModbusValueInput = (objectModel: Tsl): FormSchema => { | |
| 49 | + const { identifier, functionName, extensionDesc } = objectModel | |
| 50 | + | |
| 51 | + const { dataType } = objectModel.specs || {} | |
| 52 | + const { specs } = dataType || {} | |
| 53 | + const { valueRange } = specs as Specs | |
| 54 | + const { max, min } = valueRange || {} | |
| 82 | 55 | |
| 83 | - return type === DataTypeEnum.STRING ? createInput(objectModelTsl) : createInputNumber(objectModelTsl) | |
| 56 | + const isStringType = extensionDesc?.originalDataType === OriginalDataTypeEnum.STRING | |
| 57 | + return { | |
| 58 | + field: identifier, | |
| 59 | + label: functionName, | |
| 60 | + component: isStringType ? ComponentEnum.INPUT : ComponentEnum.INPUT_NUMBER, | |
| 61 | + rules: isStringType | |
| 62 | + ? [] | |
| 63 | + : [ | |
| 64 | + { | |
| 65 | + type: 'number', | |
| 66 | + validator: (_rule, value) => { | |
| 67 | + const { flag, message } = validateDouble(value, min, max) | |
| 68 | + if (flag) | |
| 69 | + return Promise.reject(Error(`${functionName}${message}`)) | |
| 70 | + | |
| 71 | + return Promise.resolve(value) | |
| 72 | + }, | |
| 73 | + }, | |
| 74 | + ], | |
| 75 | + componentProps: { | |
| 76 | + // max: max ?? Number.MAX_SAFE_INTEGER, | |
| 77 | + // min: min ?? Number.MIN_SAFE_INTEGER, | |
| 78 | + placeholder: `请输入${functionName}`, | |
| 79 | + precision: 0, | |
| 80 | + }, | |
| 81 | + } | |
| 84 | 82 | } |
| 85 | 83 | |
| 86 | 84 | export const createFormSchemas = ({ operationPasswordInfo, objectModelTsl, deviceInfo }: CreateFormSchemasParamsType): FormSchema[] => { |
| 87 | 85 | const schemas: FormSchema[] = [] |
| 88 | 86 | |
| 89 | - if (deviceInfo?.transportType === TransportTypeEnum.TCP && deviceInfo.codeType === CodeTypeEnum.MODBUS_RTU && deviceInfo.code) { | |
| 90 | - schemas.push(getTCPModbusSchemas({ required: true, objectModelTsl })) | |
| 87 | + if (deviceInfo?.transportType === TransportTypeEnum.TCP && deviceInfo.deviceProfile?.profileData?.transportConfiguration?.protocol === TCPProtocolTypeEnum.MODBUS_RTU) { | |
| 88 | + schemas.push(createModbusValueInput(objectModelTsl)) | |
| 91 | 89 | } |
| 92 | 90 | else { |
| 93 | 91 | const isStructType = objectModelTsl.specs.dataType.type === DataTypeEnum.STRUCT | ... | ... |
| 1 | 1 | <script setup lang="ts"> |
| 2 | 2 | import { Modal } from 'ant-design-vue' |
| 3 | 3 | import { nextTick, ref, toRaw, unref } from 'vue' |
| 4 | -import type { AttributeDeliverModalOpenParamsType } from './AttributeDeliverModal.config' | |
| 5 | -import { createFormSchemas } from './AttributeDeliverModal.config' | |
| 6 | -import { useCommandDeliver } from './useCommadDeliver' | |
| 4 | +import { type AttributeDeliverModalOpenParamsType, createFormSchemas } from './AttributeDeliverModal.config' | |
| 7 | 5 | import { BasicForm, useForm } from '@/components/Form' |
| 8 | 6 | import { FormLayoutEnum } from '@/components/Form/src/enum' |
| 9 | -import { DataTypeEnum } from '@/enums/datasource' | |
| 7 | +import type { DeviceItemType, Tsl } from '@/api/device/model' | |
| 8 | +import { useProductsStore } from '@/store/modules/products' | |
| 9 | +import { getDeviceInfo } from '@/api/device' | |
| 10 | +import type { CommandWayEnum } from '@/enums/commandEnum' | |
| 11 | +import { useCommandDelivery } from '@/hooks/business/useCommandDelivery' | |
| 12 | +import { DataTypeEnum } from '@/enums/objectModelEnum' | |
| 13 | +import { useMessage } from '@/hooks/web/useMessage' | |
| 10 | 14 | |
| 11 | 15 | const visible = ref(false) |
| 12 | 16 | |
| ... | ... | @@ -15,25 +19,41 @@ const [register, { getFieldsValue, resetFields, validate, setProps, clearValidat |
| 15 | 19 | showActionButtonGroup: false, |
| 16 | 20 | }) |
| 17 | 21 | |
| 18 | -const { setup, getDeviceInfo, getObjectModelTsl, doCommandDeliver } = useCommandDeliver() | |
| 22 | +const deviceInfo = ref<DeviceItemType>() | |
| 23 | +const objectModelTsl = ref<Tsl>() | |
| 24 | +const productsStore = useProductsStore() | |
| 25 | +const commandWay = ref<CommandWayEnum>() | |
| 19 | 26 | |
| 20 | 27 | const open = async ({ operationPasswordInfo, dataSourceJson, eventBindData }: AttributeDeliverModalOpenParamsType) => { |
| 21 | 28 | visible.value = true |
| 22 | 29 | const { deviceId, deviceProfileId, attr } = dataSourceJson |
| 23 | 30 | const { way } = eventBindData |
| 24 | - await setup({ deviceId, deviceProfileId, attr, callType: way }) | |
| 31 | + commandWay.value = way | |
| 32 | + objectModelTsl.value = productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, attr as string)! | |
| 33 | + deviceInfo.value = await getDeviceInfo(deviceId) | |
| 25 | 34 | |
| 26 | 35 | nextTick(() => { |
| 27 | - const schemas = createFormSchemas({ operationPasswordInfo, deviceInfo: toRaw(unref(getDeviceInfo)!), objectModelTsl: toRaw(unref(getObjectModelTsl)!) }) | |
| 36 | + const schemas = createFormSchemas({ operationPasswordInfo, deviceInfo: toRaw(unref(deviceInfo)!), objectModelTsl: toRaw(unref(objectModelTsl)!) }) | |
| 28 | 37 | setProps({ schemas }) |
| 29 | 38 | }) |
| 30 | 39 | } |
| 31 | 40 | |
| 32 | 41 | async function getResult() { |
| 33 | - const result = getFieldsValue() | |
| 34 | - const isStructJSON = unref(getObjectModelTsl)?.specs.dataType.type === DataTypeEnum.STRUCT | |
| 35 | - const identifier = unref(getObjectModelTsl)!.identifier | |
| 36 | - await doCommandDeliver(isStructJSON ? result : result[identifier]) | |
| 42 | + let value = getFieldsValue() | |
| 43 | + const { doCommandDelivery } = useCommandDelivery() | |
| 44 | + | |
| 45 | + const isStructJSON = unref(objectModelTsl)?.specs.dataType.type === DataTypeEnum.STRUCT | |
| 46 | + const identifier = unref(objectModelTsl)!.identifier | |
| 47 | + value = isStructJSON ? value : value[identifier] | |
| 48 | + | |
| 49 | + await doCommandDelivery({ | |
| 50 | + objectModel: unref(objectModelTsl), | |
| 51 | + deviceDetail: unref(deviceInfo), | |
| 52 | + value, | |
| 53 | + }) | |
| 54 | + | |
| 55 | + const { createMessage } = useMessage() | |
| 56 | + createMessage.success('命令下发成功') | |
| 37 | 57 | } |
| 38 | 58 | |
| 39 | 59 | const handleOk = async () => { |
| ... | ... | @@ -53,7 +73,7 @@ defineExpose({ open }) |
| 53 | 73 | |
| 54 | 74 | <template> |
| 55 | 75 | <Modal |
| 56 | - v-model:open="visible" :title="`属性下发 / ${getDeviceInfo?.alias || getDeviceInfo?.name}`" ok-text="确认" :width="400" cancel-text="取消" @cancel="handleCancel" | |
| 76 | + v-model:open="visible" :title="`属性下发 / ${deviceInfo?.alias || deviceInfo?.name}`" ok-text="确认" :width="400" cancel-text="取消" @cancel="handleCancel" | |
| 57 | 77 | @ok="handleOk" |
| 58 | 78 | > |
| 59 | 79 | <BasicForm @register="register" /> | ... | ... |
| ... | ... | @@ -2,10 +2,16 @@ |
| 2 | 2 | import { Modal } from 'ant-design-vue' |
| 3 | 3 | import { computed, nextTick, ref, unref } from 'vue' |
| 4 | 4 | import { Icon } from '@iconify/vue' |
| 5 | -import { useCommandDeliver } from './useCommadDeliver' | |
| 6 | 5 | import { BasicForm, type FormSchema, useForm } from '@/components/Form' |
| 7 | 6 | import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum' |
| 8 | 7 | import type { NodeDataDataSourceJsonType, OperationPasswordDataType, SingleClickEventDataType } from '@/api/node/model' |
| 8 | +import type { DoCommandDeliverParamsType } from '@/hooks/business/useCommandDelivery' | |
| 9 | +import { useCommandDelivery } from '@/hooks/business/useCommandDelivery' | |
| 10 | +import { CommandDeliveryWayEnum } from '@/enums/commandEnum' | |
| 11 | +import { useProductsStore } from '@/store/modules/products' | |
| 12 | +import { getDeviceInfo } from '@/api/device' | |
| 13 | +import { TransportTypeEnum } from '@/enums/deviceEnum' | |
| 14 | +import { useMessage } from '@/hooks/web/useMessage' | |
| 9 | 15 | |
| 10 | 16 | interface SwitchCommandDeliveryModalParamsType { |
| 11 | 17 | operationPasswordInfo?: OperationPasswordDataType |
| ... | ... | @@ -18,8 +24,6 @@ const visible = ref(false) |
| 18 | 24 | |
| 19 | 25 | const currentOperationPasswordInfo = ref<OperationPasswordDataType>() |
| 20 | 26 | |
| 21 | -const currendSendValue = ref() | |
| 22 | - | |
| 23 | 27 | const [register, { resetFields, validate, setProps }] = useForm({ |
| 24 | 28 | showActionButtonGroup: false, |
| 25 | 29 | layout: FormLayoutEnum.VERTICAL, |
| ... | ... | @@ -45,17 +49,17 @@ const createFormSchemas = (password: string): FormSchema[] => { |
| 45 | 49 | ] |
| 46 | 50 | } |
| 47 | 51 | |
| 48 | -const getHasEnableOperationPassword = computed(() => !!(unref(currentOperationPasswordInfo)?.checked && unref(currentOperationPasswordInfo)?.value)) | |
| 52 | +const productsStore = useProductsStore() | |
| 49 | 53 | |
| 50 | -const { setup, doCommandDeliver, getDeviceInfo } = useCommandDeliver() | |
| 54 | +const getHasEnableOperationPassword = computed(() => !!(unref(currentOperationPasswordInfo)?.checked && unref(currentOperationPasswordInfo)?.value)) | |
| 51 | 55 | |
| 52 | -const open = async ({ operationPasswordInfo, dataSourceJson, eventBindData, sendValue }: SwitchCommandDeliveryModalParamsType) => { | |
| 56 | +const currentParams = ref<SwitchCommandDeliveryModalParamsType>() | |
| 57 | +const open = async (params: SwitchCommandDeliveryModalParamsType) => { | |
| 58 | + const { operationPasswordInfo } = params | |
| 53 | 59 | visible.value = true |
| 60 | + currentParams.value = params | |
| 54 | 61 | currentOperationPasswordInfo.value = operationPasswordInfo |
| 55 | - currendSendValue.value = sendValue | |
| 56 | - const { deviceId, deviceProfileId, attr } = unref(dataSourceJson) | |
| 57 | - const { way } = eventBindData | |
| 58 | - await setup({ deviceId, deviceProfileId, attr, callType: way }) | |
| 62 | + | |
| 59 | 63 | nextTick(() => { |
| 60 | 64 | unref(getHasEnableOperationPassword) && setProps({ schemas: createFormSchemas(operationPasswordInfo!.value!) }) |
| 61 | 65 | }) |
| ... | ... | @@ -64,7 +68,36 @@ const open = async ({ operationPasswordInfo, dataSourceJson, eventBindData, send |
| 64 | 68 | const handlerOk = async () => { |
| 65 | 69 | if (unref(getHasEnableOperationPassword)) |
| 66 | 70 | await validate() |
| 67 | - await doCommandDeliver(unref(currendSendValue)) | |
| 71 | + const { doCommandDelivery } = useCommandDelivery() | |
| 72 | + | |
| 73 | + const { sendValue, eventBindData, dataSourceJson } = unref(currentParams)! | |
| 74 | + const { way, commandWay } = eventBindData | |
| 75 | + const { deviceProfileId, attr, deviceId } = dataSourceJson | |
| 76 | + | |
| 77 | + const deviceDetail = await getDeviceInfo(deviceId) | |
| 78 | + | |
| 79 | + const doCommandParams: DoCommandDeliverParamsType = { | |
| 80 | + value: sendValue, | |
| 81 | + identifier: attr as string, | |
| 82 | + deviceDetail, | |
| 83 | + deviceProfileId, | |
| 84 | + way, | |
| 85 | + } | |
| 86 | + | |
| 87 | + if (commandWay === CommandDeliveryWayEnum.CUSTOM) { | |
| 88 | + doCommandParams.penetration = true | |
| 89 | + } | |
| 90 | + else if (commandWay === CommandDeliveryWayEnum.SERVICE) { | |
| 91 | + const { service } = eventBindData | |
| 92 | + doCommandParams.objectModel = productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, service!)! | |
| 93 | + doCommandParams.value = deviceDetail?.transportType === TransportTypeEnum.TCP ? doCommandParams.value : { [doCommandParams.objectModel.identifier]: doCommandParams.value } | |
| 94 | + } | |
| 95 | + | |
| 96 | + await doCommandDelivery(doCommandParams) | |
| 97 | + | |
| 98 | + const { createMessage } = useMessage() | |
| 99 | + createMessage.success('命令下发成功') | |
| 100 | + | |
| 68 | 101 | if (unref(getHasEnableOperationPassword)) resetFields() |
| 69 | 102 | visible.value = false |
| 70 | 103 | } |
| ... | ... | @@ -73,22 +106,7 @@ defineExpose({ open }) |
| 73 | 106 | </script> |
| 74 | 107 | |
| 75 | 108 | <template> |
| 76 | - <Modal | |
| 77 | - v-model:open="visible" | |
| 78 | - :title="`属性下发 / ${getDeviceInfo?.alias || getDeviceInfo?.name}`" | |
| 79 | - :width="300" | |
| 80 | - ok-text="确认" | |
| 81 | - cancel-text="取消" | |
| 82 | - destroy-on-close | |
| 83 | - @ok="handlerOk" | |
| 84 | - > | |
| 85 | - <!-- <template #title> | |
| 86 | - <div v-if="getHasEnableOperationPassword" /> | |
| 87 | - <div v-else class="flex"> | |
| 88 | - <span><Icon icon="ant-design:exclamation-circle-filled" class="text-yellow-300 text-xl" /></span> | |
| 89 | - <span class="font-bold ml-2">属性下发 / 提示</span> | |
| 90 | - </div> | |
| 91 | - </template> --> | |
| 109 | + <Modal v-model:open="visible" :width="300" ok-text="确认" cancel-text="取消" destroy-on-close @ok="handlerOk"> | |
| 92 | 110 | <BasicForm v-if="getHasEnableOperationPassword" @register="register" /> |
| 93 | 111 | <div v-else class="flex items-center"> |
| 94 | 112 | <Icon icon="ant-design:exclamation-circle-filled" class="text-yellow-300 text-xl" /> | ... | ... |
| 1 | 1 | import { ComponentEnum } from '@/components/Form/src/enum' |
| 2 | 2 | import type { FormSchema } from '@/components/Form' |
| 3 | -import type { Tsl } from '@/api/device/model' | |
| 4 | 3 | import type { OperationPasswordDataType } from '@/api/node/model' |
| 5 | 4 | import { validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm' |
| 6 | 5 | import { JSONEditorValidator } from '@/components/CodeEditor/src/JSONEditor' |
| 7 | -import { DataTypeEnum } from '@/enums/datasource' | |
| 8 | 6 | |
| 9 | 7 | export enum FormFieldsEnum { |
| 10 | 8 | CUSTOM_COMMAND = 'customCommand', |
| ... | ... | @@ -22,75 +20,6 @@ export const formSchemas: FormSchema[] = [ |
| 22 | 20 | }, |
| 23 | 21 | ] |
| 24 | 22 | |
| 25 | -export const getModbusSchemas = (objectModelTsl: Tsl): FormSchema[] => { | |
| 26 | - const { functionName, identifier, specs } = objectModelTsl | |
| 27 | - const { type } = specs.dataType! | |
| 28 | - const HEX_MAX_VALUE = Number(0xffff) | |
| 29 | - | |
| 30 | - const _formSchema: FormSchema = { | |
| 31 | - field: identifier, | |
| 32 | - label: functionName, | |
| 33 | - component: ComponentEnum.INPUT_NUMBER, | |
| 34 | - } | |
| 35 | - | |
| 36 | - const getIntSchema = () => { | |
| 37 | - return Object.assign(_formSchema, { | |
| 38 | - required: true, | |
| 39 | - componentProps: { | |
| 40 | - min: 0, | |
| 41 | - max: HEX_MAX_VALUE, | |
| 42 | - precision: 2, | |
| 43 | - placeholder: '请输入正数', | |
| 44 | - }, | |
| 45 | - } as Partial<FormSchema>) | |
| 46 | - } | |
| 47 | - | |
| 48 | - const getBoolSchema = () => { | |
| 49 | - return Object.assign(_formSchema, { | |
| 50 | - required: true, | |
| 51 | - componentProps: { | |
| 52 | - min: 0, | |
| 53 | - max: 1, | |
| 54 | - precision: 0, | |
| 55 | - placeholder: '请输入0或1', | |
| 56 | - }, | |
| 57 | - } as Partial<FormSchema>) | |
| 58 | - } | |
| 59 | - | |
| 60 | - const getDoubleSchema = () => { | |
| 61 | - return Object.assign(_formSchema, { | |
| 62 | - required: true, | |
| 63 | - componentProps: { | |
| 64 | - placeholder: '请输入数字', | |
| 65 | - precision: 2, | |
| 66 | - max: HEX_MAX_VALUE, | |
| 67 | - }, | |
| 68 | - } as Partial<FormSchema>) | |
| 69 | - } | |
| 70 | - | |
| 71 | - const getStringSchema = () => { | |
| 72 | - return Object.assign(_formSchema, { | |
| 73 | - component: ComponentEnum.INPUT, | |
| 74 | - required: true, | |
| 75 | - rules: [{ validator: validateTCPCustomCommand }], | |
| 76 | - componentProps: { | |
| 77 | - placeholder: '请输入自定义命令', | |
| 78 | - }, | |
| 79 | - } as Partial<FormSchema>) | |
| 80 | - } | |
| 81 | - | |
| 82 | - const schemasMap: Partial<Record<DataTypeEnum, Fn<any, FormSchema>>> = { | |
| 83 | - [DataTypeEnum.NUMBER_INT]: getIntSchema, | |
| 84 | - [DataTypeEnum.BOOL]: getBoolSchema, | |
| 85 | - [DataTypeEnum.NUMBER_DOUBLE]: getDoubleSchema, | |
| 86 | - [DataTypeEnum.STRING]: getStringSchema, | |
| 87 | - } | |
| 88 | - | |
| 89 | - const schemas = schemasMap[type as DataTypeEnum]?.() | |
| 90 | - | |
| 91 | - return [schemas!] | |
| 92 | -} | |
| 93 | - | |
| 94 | 23 | export const getOperationPasswordSchemas = (operationPasswordInfo: OperationPasswordDataType): FormSchema[] => { |
| 95 | 24 | return [ |
| 96 | 25 | { | ... | ... |
| 1 | 1 | <script setup lang="ts"> |
| 2 | 2 | import { nextTick, reactive, ref, toRaw, unref } from 'vue' |
| 3 | 3 | import { Button } from 'ant-design-vue' |
| 4 | -import { FormFieldsEnum, formSchemas, getJSONCommandSchemas, getModbusSchemas, getOperationPasswordSchemas, getTcpCustomCommandSchemas } from './config.ts' | |
| 5 | -import { useCommandDeliver } from './useCommadDeliver' | |
| 4 | +import { FormFieldsEnum, formSchemas, getJSONCommandSchemas, getOperationPasswordSchemas, getTcpCustomCommandSchemas } from './config.ts' | |
| 5 | +import { createModbusValueInput } from './AttributeDeliverModal.config' | |
| 6 | 6 | import { BasicModal, useModalInner } from '@/components/Modal' |
| 7 | 7 | import type { NodeDataDataSourceJsonType, OperationPasswordDataType, SingleClickEventDataType } from '@/api/node/model' |
| 8 | 8 | import { BasicForm, useForm } from '@/components/Form' |
| 9 | 9 | import { FormLayoutEnum } from '@/components/Form/src/enum' |
| 10 | -import { CommandDeliveryWayEnum, TransportTypeEnum } from '@/enums/datasource' | |
| 11 | 10 | import { ThingsModelForm } from '@/core/Library/components/ThingsModelForm' |
| 12 | 11 | import type { DeviceItemType, Tsl } from '@/api/device/model' |
| 13 | -import { useProductsStoreWithOut } from '@/store/modules/products' | |
| 12 | +import { useProductsStore, useProductsStoreWithOut } from '@/store/modules/products' | |
| 14 | 13 | import { useJsonParse } from '@/hooks/business/useJSONParse' |
| 15 | -import { CommandCallWayEnum, CommandWayEnum } from '@/enums/commandEnum' | |
| 14 | +import { CommandDeliveryWayEnum } from '@/enums/commandEnum' | |
| 15 | +import { TransportTypeEnum } from '@/enums/deviceEnum' | |
| 16 | +import { getDeviceInfo } from '@/api/device' | |
| 17 | +import type { DoCommandDeliverParamsType } from '@/hooks/business/useCommandDelivery' | |
| 18 | +import { useCommandDelivery } from '@/hooks/business/useCommandDelivery' | |
| 19 | +import { useMessage } from '@/hooks/web/useMessage' | |
| 16 | 20 | |
| 17 | 21 | interface OpenParamsType { |
| 18 | 22 | dataSource: NodeDataDataSourceJsonType |
| ... | ... | @@ -82,66 +86,60 @@ function handleOpenServiceCommandModal(deviceInfo: DeviceItemType, eventBindData |
| 82 | 86 | |
| 83 | 87 | function handleOpenModbusCommandModal(objectModelTsl: Tsl) { |
| 84 | 88 | const { setProps } = customCommandFormAction |
| 85 | - setProps({ schemas: getModbusSchemas(objectModelTsl) }) | |
| 89 | + setProps({ schemas: [createModbusValueInput(objectModelTsl)] }) | |
| 86 | 90 | } |
| 87 | 91 | |
| 88 | -const { setup, getDeviceInfo, getObjectModelTsl, doCommandDeliver } = useCommandDeliver() | |
| 92 | +const deviceInfo = ref<DeviceItemType>() | |
| 93 | +const objectModelTsl = ref<Tsl>() | |
| 94 | +const currentParams = ref<OpenParamsType>() | |
| 95 | +const productsStore = useProductsStore() | |
| 89 | 96 | |
| 90 | -const open = async ({ eventBindData, operationPasswordInfo, dataSource }: OpenParamsType) => { | |
| 97 | +const open = async (params: OpenParamsType) => { | |
| 98 | + const { eventBindData, operationPasswordInfo, dataSource } = params | |
| 91 | 99 | const { deviceId, deviceProfileId, attr } = dataSource |
| 92 | - const { commandWay, callType, way, service } = eventBindData | |
| 100 | + const { commandWay } = eventBindData | |
| 93 | 101 | const { checked, value: operationPassword } = operationPasswordInfo || {} |
| 94 | 102 | configuration.commandDeliveryWay = commandWay! |
| 95 | - | |
| 103 | + currentParams.value = params | |
| 104 | + deviceInfo.value = await getDeviceInfo(deviceId) | |
| 105 | + objectModelTsl.value = productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, attr as string)! | |
| 96 | 106 | setModalProps({ |
| 97 | 107 | open: true, |
| 98 | 108 | loading: true, |
| 99 | 109 | }) |
| 100 | 110 | |
| 101 | - await setup({ | |
| 102 | - deviceId, | |
| 103 | - deviceProfileId, | |
| 104 | - attr, | |
| 105 | - commandDeliverWay: commandWay, | |
| 106 | - callType: configuration.commandDeliveryWay === CommandDeliveryWayEnum.SERVICE | |
| 107 | - ? callType === CommandCallWayEnum.SYNC | |
| 108 | - ? CommandWayEnum.TWO_WAY | |
| 109 | - : CommandWayEnum.ONE_WAY | |
| 110 | - : way, | |
| 111 | - serviceIdentifier: service, | |
| 112 | - }) | |
| 113 | - | |
| 114 | 111 | if (checked && operationPassword) |
| 115 | 112 | handleHasOperationPassword(operationPasswordInfo!) |
| 116 | 113 | |
| 114 | + await nextTick() | |
| 115 | + | |
| 117 | 116 | if (commandWay === CommandDeliveryWayEnum.CUSTOM) |
| 118 | - handleOpenCustomCommandModal(toRaw(unref(getDeviceInfo)!), eventBindData) | |
| 117 | + handleOpenCustomCommandModal(toRaw(unref(deviceInfo)!), eventBindData) | |
| 119 | 118 | else if (commandWay === CommandDeliveryWayEnum.SERVICE) |
| 120 | - handleOpenServiceCommandModal(toRaw(unref(getDeviceInfo)!), eventBindData) | |
| 119 | + handleOpenServiceCommandModal(toRaw(unref(deviceInfo)!), eventBindData) | |
| 121 | 120 | else if (commandWay === CommandDeliveryWayEnum.MODBUS) |
| 122 | - handleOpenModbusCommandModal(toRaw(unref(getObjectModelTsl)!)) | |
| 121 | + handleOpenModbusCommandModal(toRaw(unref(objectModelTsl)!)) | |
| 123 | 122 | |
| 124 | 123 | setModalProps({ |
| 125 | 124 | loading: false, |
| 126 | - title: `参数设置 / ${unref(getDeviceInfo)?.alias || unref(getDeviceInfo)?.name}`, | |
| 125 | + title: `参数设置 / ${unref(deviceInfo)?.alias || unref(deviceInfo)?.name}`, | |
| 127 | 126 | }) |
| 128 | 127 | } |
| 129 | 128 | |
| 130 | 129 | function handleGetCustomCommand() { |
| 131 | 130 | const res = customCommandFormAction.getFieldsValue() |
| 132 | 131 | const command = res[FormFieldsEnum.CUSTOM_COMMAND] |
| 133 | - return unref(getDeviceInfo)?.transportType === TransportTypeEnum.TCP ? command : useJsonParse(command).value | |
| 132 | + return unref(deviceInfo)?.transportType === TransportTypeEnum.TCP ? command : useJsonParse(command).value | |
| 134 | 133 | } |
| 135 | 134 | |
| 136 | 135 | function handleGetServiceCommand() { |
| 137 | 136 | const res = unref(thingsModelElRef)!.getFieldsValue() |
| 138 | - | |
| 139 | - return unref(getDeviceInfo)?.transportType === TransportTypeEnum.TCP ? res[FormFieldsEnum.SERVICE_COMMAND] : res | |
| 137 | + return unref(deviceInfo)?.transportType === TransportTypeEnum.TCP ? res[FormFieldsEnum.SERVICE_COMMAND] : res | |
| 140 | 138 | } |
| 141 | 139 | |
| 142 | 140 | function handleGetModbusCommand() { |
| 143 | 141 | const res = customCommandFormAction.getFieldsValue() |
| 144 | - const identifier = unref(getObjectModelTsl)!.identifier | |
| 142 | + const identifier = unref(objectModelTsl)!.identifier | |
| 145 | 143 | return res[identifier] |
| 146 | 144 | } |
| 147 | 145 | |
| ... | ... | @@ -156,7 +154,31 @@ const handleSubmit = async () => { |
| 156 | 154 | ? handleGetModbusCommand() |
| 157 | 155 | : handleGetServiceCommand() |
| 158 | 156 | |
| 159 | - await doCommandDeliver(res) | |
| 157 | + const { doCommandDelivery } = useCommandDelivery() | |
| 158 | + | |
| 159 | + const { eventBindData, dataSource } = unref(currentParams)! | |
| 160 | + const { deviceProfileId } = dataSource | |
| 161 | + const { commandWay, way } = eventBindData | |
| 162 | + | |
| 163 | + const params: DoCommandDeliverParamsType = { | |
| 164 | + value: res, | |
| 165 | + deviceDetail: unref(deviceInfo), | |
| 166 | + objectModel: unref(objectModelTsl), | |
| 167 | + way, | |
| 168 | + } | |
| 169 | + | |
| 170 | + if (commandWay === CommandDeliveryWayEnum.CUSTOM) { | |
| 171 | + params.penetration = true | |
| 172 | + } | |
| 173 | + else if (commandWay === CommandDeliveryWayEnum.SERVICE) { | |
| 174 | + const { service } = eventBindData | |
| 175 | + params.objectModel = productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, service!)! | |
| 176 | + params.value = unref(deviceInfo)?.transportType === TransportTypeEnum.TCP ? params.value : { [params.objectModel.identifier]: params.value } | |
| 177 | + } | |
| 178 | + | |
| 179 | + await doCommandDelivery(params) | |
| 180 | + const { createMessage } = useMessage() | |
| 181 | + createMessage.success('命令下发成功') | |
| 160 | 182 | closeModal() |
| 161 | 183 | } |
| 162 | 184 | ... | ... |
src/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal/useCommadDeliver.ts
deleted
100644 → 0
| 1 | -import { computed, ref, unref } from 'vue' | |
| 2 | -import { useGetModbusCommand } from './useGetModbusCommand' | |
| 3 | -import { CodeTypeEnum, CommandDeliveryWayEnum, CommandTypeEnum, DataTypeEnum, TransportTypeEnum } from '@/enums/datasource' | |
| 4 | -import type { DeviceItemType, RpcCommandType, Tsl } from '@/api/device/model' | |
| 5 | -import { getDeviceActive, getDeviceInfo, sendRpcOneway } from '@/api/device' | |
| 6 | -import { useProductsStoreWithOut } from '@/store/modules/products' | |
| 7 | -import { CommandMethodEnum, CommandWayEnum } from '@/enums/commandEnum' | |
| 8 | -import { useMessage } from '@/hooks/web/useMessage' | |
| 9 | - | |
| 10 | -interface UseCommandDeliverParamsType { | |
| 11 | - deviceProfileId: string | |
| 12 | - deviceId: string | |
| 13 | - attr: string | |
| 14 | - callType?: CommandWayEnum | |
| 15 | - commandDeliverWay?: CommandDeliveryWayEnum | |
| 16 | - serviceIdentifier?: string | |
| 17 | -} | |
| 18 | - | |
| 19 | -/** | |
| 20 | - * @description 命令下发流程 | |
| 21 | - * - TCP设备(下发命令为String): | |
| 22 | - * > - Modbus类型(INT,DOUBLE,BOOL,TEXT): | |
| 23 | - * >> 1. 验证设备是否绑定地址码与物模型是否绑定寄存器地址 | |
| 24 | - * >> 2. 物模型类型为INT,DOUBLE,BOOL: 调用接口生成Modbus命令 | |
| 25 | - * >>> 1. 数据类型为INT,DOUBLE时需要进行乘以缩放因子后验证值是否为整型与浮点型 | |
| 26 | - * >>> 2. 数据类型为INT,DOUBLE缩放处理后进行HEX转码 | |
| 27 | - * >>> 3. DOUBLE类型寄存器个数为2 | |
| 28 | - * >>> 4. BOOL类型值为0与1 | |
| 29 | - * >> 3. 物模型类型为TEXT: 输入值为自定义Modbus命令 | |
| 30 | - * >> 4. 物模型类型为服务: 服务命令为下发值 | |
| 31 | - * > - 自定义类型: 输入值为Modubs命令 | |
| 32 | - * - 非TCP设备(下发命令为(Object), {[identifier]: value}): | |
| 33 | - * > - 自定义命令下发: JSON格式命令下发 | |
| 34 | - * > - 属性下发与服务下发: | |
| 35 | - * >> - | |
| 36 | - * | |
| 37 | - * @returns | |
| 38 | - */ | |
| 39 | -export function useCommandDeliver(params?: UseCommandDeliverParamsType) { | |
| 40 | - const deviceInfo = ref<DeviceItemType>() | |
| 41 | - const objectModelTsl = ref<Nullable<Tsl>>(null) | |
| 42 | - const { createMessage } = useMessage() | |
| 43 | - | |
| 44 | - const configuration: Partial<UseCommandDeliverParamsType> = { | |
| 45 | - deviceId: undefined, | |
| 46 | - deviceProfileId: undefined, | |
| 47 | - attr: undefined, | |
| 48 | - } | |
| 49 | - | |
| 50 | - const getDeviceInfoById = async () => { | |
| 51 | - const res = await getDeviceInfo(configuration.deviceId!) | |
| 52 | - deviceInfo.value = res | |
| 53 | - } | |
| 54 | - | |
| 55 | - const getObjectModelTslByIdWithIdentifier = () => { | |
| 56 | - const productStore = useProductsStoreWithOut() | |
| 57 | - const { deviceProfileId, attr } = configuration | |
| 58 | - objectModelTsl.value = productStore.getObjectModelByIdWithIdentifier(deviceProfileId!, attr!) | |
| 59 | - } | |
| 60 | - | |
| 61 | - const setup = async (params?: UseCommandDeliverParamsType) => { | |
| 62 | - Object.assign(configuration, params || {}) | |
| 63 | - const { deviceId, deviceProfileId, attr } = configuration | |
| 64 | - if (!deviceId || !deviceProfileId || !attr) return | |
| 65 | - getObjectModelTslByIdWithIdentifier() | |
| 66 | - await getDeviceInfoById() | |
| 67 | - } | |
| 68 | - | |
| 69 | - if (params && params.deviceId && params.deviceProfileId && params.attr) setup() | |
| 70 | - | |
| 71 | - const getCommand = async (command: any) => { | |
| 72 | - const { commandDeliverWay, serviceIdentifier } = configuration | |
| 73 | - | |
| 74 | - const isTcpDevice = unref(deviceInfo)?.transportType === TransportTypeEnum.TCP | |
| 75 | - | |
| 76 | - // 指定下发命令方式 | |
| 77 | - if (commandDeliverWay) { | |
| 78 | - if (commandDeliverWay === CommandDeliveryWayEnum.CUSTOM) return command | |
| 79 | - | |
| 80 | - if (commandDeliverWay === CommandDeliveryWayEnum.SERVICE && serviceIdentifier) { | |
| 81 | - return isTcpDevice | |
| 82 | - ? command | |
| 83 | - : { | |
| 84 | - [serviceIdentifier]: command, | |
| 85 | - } | |
| 86 | - } | |
| 87 | - } | |
| 88 | - | |
| 89 | - const isTCPModbusDevice = isTcpDevice && unref(deviceInfo)?.codeType === CodeTypeEnum.MODBUS_RTU | |
| 90 | - const identifier = unref(objectModelTsl)!.identifier | |
| 91 | - | |
| 92 | - if (!isTCPModbusDevice) { | |
| 93 | - return { | |
| 94 | - [identifier]: command, | |
| 95 | - } | |
| 96 | - } | |
| 97 | - else { | |
| 98 | - const isString = objectModelTsl.value?.specs.dataType.type === DataTypeEnum.STRING | |
| 99 | - | |
| 100 | - if (isString) return command | |
| 101 | - | |
| 102 | - const { extensionDesc } = unref(objectModelTsl)! | |
| 103 | - const { code } = unref(deviceInfo)! | |
| 104 | - | |
| 105 | - const { getModbusCommand, validateCanGetCommand } = useGetModbusCommand() | |
| 106 | - if (!validateCanGetCommand(extensionDesc, code).flag) return | |
| 107 | - | |
| 108 | - const res = await getModbusCommand(command as number, extensionDesc!, code) | |
| 109 | - return res | |
| 110 | - } | |
| 111 | - } | |
| 112 | - | |
| 113 | - async function doCommandDeliver(command: any) { | |
| 114 | - const { callType = CommandWayEnum.ONE_WAY, commandDeliverWay } = configuration | |
| 115 | - | |
| 116 | - if (callType === CommandWayEnum.TWO_WAY) { | |
| 117 | - const res = await getDeviceActive(unref(deviceInfo)!.tbDeviceId!) | |
| 118 | - const [firstItem] = res | |
| 119 | - const { value } = firstItem | |
| 120 | - | |
| 121 | - if (!value) { | |
| 122 | - createMessage.warning('设备不在线') | |
| 123 | - return Promise.reject(Error('设备不在线')) | |
| 124 | - } | |
| 125 | - } | |
| 126 | - | |
| 127 | - const res = await getCommand(command) | |
| 128 | - | |
| 129 | - const rpcCommandParams: RpcCommandType = { | |
| 130 | - additionalInfo: { | |
| 131 | - cmdType: | |
| 132 | - unref(commandDeliverWay) === CommandDeliveryWayEnum.SERVICE | |
| 133 | - ? CommandTypeEnum.SERVICE | |
| 134 | - : CommandTypeEnum.API, | |
| 135 | - }, | |
| 136 | - persistent: true, | |
| 137 | - method: CommandMethodEnum.THINGSKIT, | |
| 138 | - params: res, | |
| 139 | - } | |
| 140 | - | |
| 141 | - await sendRpcOneway(rpcCommandParams, unref(deviceInfo)?.tbDeviceId, callType) | |
| 142 | - createMessage.success('命令下发成功') | |
| 143 | - } | |
| 144 | - | |
| 145 | - return { | |
| 146 | - getDeviceInfo: computed(() => unref(deviceInfo)), | |
| 147 | - getObjectModelTsl: computed(() => unref(objectModelTsl)), | |
| 148 | - setup, | |
| 149 | - getCommand, | |
| 150 | - doCommandDeliver, | |
| 151 | - } | |
| 152 | -} |
| 1 | +import { unref } from 'vue' | |
| 2 | +import { DataTypeEnum, OriginalDataTypeEnum } from '@/enums/objectModelEnum' | |
| 3 | +import type { FormSchema } from '@/components/Form' | |
| 4 | +import type { DataType, DeviceItemType, Specs, StructJSON, Tsl } from '@/api/device/model' | |
| 5 | +import { ComponentEnum } from '@/components/Form/src/enum' | |
| 6 | +import { TCPProtocolTypeEnum } from '@/enums/deviceEnum' | |
| 7 | + | |
| 8 | +export interface BasicCreateFormParams { | |
| 9 | + identifier: string | |
| 10 | + functionName: string | |
| 11 | + dataType: DataType | |
| 12 | +} | |
| 13 | + | |
| 14 | +const validateDouble = (value: number, min?: number | string, max?: number | string) => { | |
| 15 | + min = Number(min) ?? Number.MIN_SAFE_INTEGER | |
| 16 | + max = Number(max) ?? Number.MAX_SAFE_INTEGER | |
| 17 | + | |
| 18 | + return { | |
| 19 | + flag: value < min || value > max, | |
| 20 | + message: `取值范围在${min}~${max}之间`, | |
| 21 | + } | |
| 22 | +} | |
| 23 | + | |
| 24 | +export const useGenerateFormSchemasByObjectModel = () => { | |
| 25 | + const createInputNumber = ({ | |
| 26 | + identifier, | |
| 27 | + functionName, | |
| 28 | + dataType, | |
| 29 | + }: BasicCreateFormParams): FormSchema => { | |
| 30 | + const { specs, type } = dataType | |
| 31 | + const { valueRange, step } = specs! as Partial<Specs> | |
| 32 | + const { max, min } = valueRange || {} | |
| 33 | + return { | |
| 34 | + field: identifier, | |
| 35 | + label: functionName, | |
| 36 | + component: 'InputNumber', | |
| 37 | + rules: [ | |
| 38 | + { | |
| 39 | + type: 'number', | |
| 40 | + trigger: 'change', | |
| 41 | + validator: (_rule, value) => { | |
| 42 | + const { flag, message } = validateDouble(value, min, max) | |
| 43 | + if (flag) | |
| 44 | + return Promise.reject(Error(`${functionName}${message}`)) | |
| 45 | + | |
| 46 | + return Promise.resolve(value) | |
| 47 | + }, | |
| 48 | + }, | |
| 49 | + ], | |
| 50 | + componentProps: { | |
| 51 | + max: max ?? Number.MAX_SAFE_INTEGER, | |
| 52 | + min: min ?? Number.MIN_SAFE_INTEGER, | |
| 53 | + step, | |
| 54 | + placeholder: `请输入${functionName}`, | |
| 55 | + precision: type === DataTypeEnum.NUMBER_INT ? 0 : 2, | |
| 56 | + }, | |
| 57 | + } as FormSchema | |
| 58 | + } | |
| 59 | + | |
| 60 | + const createInput = ({ | |
| 61 | + identifier, | |
| 62 | + functionName, | |
| 63 | + dataType, | |
| 64 | + }: BasicCreateFormParams): FormSchema => { | |
| 65 | + const { specs } = dataType | |
| 66 | + const { length = 10240 } = specs! as Partial<Specs> | |
| 67 | + return { | |
| 68 | + field: identifier, | |
| 69 | + label: functionName, | |
| 70 | + component: 'Input', | |
| 71 | + rules: [ | |
| 72 | + { | |
| 73 | + type: 'string', | |
| 74 | + trigger: 'change', | |
| 75 | + validator: (_rule, value) => { | |
| 76 | + if (value?.length > length) | |
| 77 | + return Promise.reject(Error(`${functionName}数据长度应该小于${length}`)) | |
| 78 | + | |
| 79 | + return Promise.resolve(value) | |
| 80 | + }, | |
| 81 | + }, | |
| 82 | + ], | |
| 83 | + componentProps: { | |
| 84 | + maxLength: length, | |
| 85 | + placeholder: `请输入${functionName}`, | |
| 86 | + }, | |
| 87 | + } as FormSchema | |
| 88 | + } | |
| 89 | + | |
| 90 | + const createSelect = ({ | |
| 91 | + identifier, | |
| 92 | + functionName, | |
| 93 | + dataType, | |
| 94 | + }: BasicCreateFormParams): FormSchema => { | |
| 95 | + const { specs } = dataType | |
| 96 | + const { boolClose, boolOpen } = specs! as Partial<Specs> | |
| 97 | + return { | |
| 98 | + field: identifier, | |
| 99 | + label: functionName, | |
| 100 | + component: ComponentEnum.SELECT, | |
| 101 | + componentProps: { | |
| 102 | + options: [ | |
| 103 | + { label: `${boolClose}-0`, value: 0 }, | |
| 104 | + { label: `${boolOpen}-1`, value: 1 }, | |
| 105 | + ], | |
| 106 | + placeholder: `请选择${functionName}`, | |
| 107 | + getPopupContainer: () => document.body, | |
| 108 | + }, | |
| 109 | + } | |
| 110 | + } | |
| 111 | + | |
| 112 | + const createEnumSelect = ({ | |
| 113 | + identifier, | |
| 114 | + functionName, | |
| 115 | + dataType, | |
| 116 | + }: BasicCreateFormParams): FormSchema => { | |
| 117 | + const { specsList } = dataType | |
| 118 | + return { | |
| 119 | + field: identifier, | |
| 120 | + label: functionName, | |
| 121 | + component: ComponentEnum.SELECT, | |
| 122 | + componentProps: { | |
| 123 | + options: specsList?.map(item => ({ label: item.name, value: item.value })), | |
| 124 | + placeholder: `请选择${functionName}`, | |
| 125 | + getPopupContainer: () => document.body, | |
| 126 | + }, | |
| 127 | + } | |
| 128 | + } | |
| 129 | + | |
| 130 | + const createModbusValueInput = (objectModel: Tsl): FormSchema => { | |
| 131 | + const { identifier, functionName, extensionDesc } = objectModel | |
| 132 | + | |
| 133 | + const { dataType } = objectModel.specs || {} | |
| 134 | + const { specs } = dataType || {} | |
| 135 | + const { valueRange } = specs as Specs | |
| 136 | + const { max, min } = valueRange || {} | |
| 137 | + | |
| 138 | + const isStringType = extensionDesc?.originalDataType === OriginalDataTypeEnum.STRING | |
| 139 | + return { | |
| 140 | + field: identifier, | |
| 141 | + label: functionName, | |
| 142 | + component: isStringType ? ComponentEnum.INPUT : ComponentEnum.INPUT_NUMBER, | |
| 143 | + rules: isStringType | |
| 144 | + ? [] | |
| 145 | + : [ | |
| 146 | + { | |
| 147 | + type: 'number', | |
| 148 | + validator: (_rule, value) => { | |
| 149 | + const { flag, message } = validateDouble(value, min, max) | |
| 150 | + if (flag) | |
| 151 | + return Promise.reject(Error(`${functionName}${message}`)) | |
| 152 | + | |
| 153 | + return Promise.resolve(value) | |
| 154 | + }, | |
| 155 | + }, | |
| 156 | + ], | |
| 157 | + componentProps: { | |
| 158 | + max: max ?? Number.MAX_SAFE_INTEGER, | |
| 159 | + min: min ?? Number.MIN_SAFE_INTEGER, | |
| 160 | + placeholder: `请输入${functionName}`, | |
| 161 | + precision: 0, | |
| 162 | + }, | |
| 163 | + } | |
| 164 | + } | |
| 165 | + | |
| 166 | + const schemaMethod: Partial<Record<DataTypeEnum, (...args: any[]) => FormSchema>> = { | |
| 167 | + [DataTypeEnum.BOOL]: createSelect, | |
| 168 | + [DataTypeEnum.NUMBER_DOUBLE]: createInputNumber, | |
| 169 | + [DataTypeEnum.NUMBER_INT]: createInputNumber, | |
| 170 | + [DataTypeEnum.STRING]: createInput, | |
| 171 | + [DataTypeEnum.ENUM]: createEnumSelect, | |
| 172 | + } | |
| 173 | + | |
| 174 | + const getFormByObjectModel = ( | |
| 175 | + objectModel: Tsl, | |
| 176 | + deviceDetail: DeviceItemType, | |
| 177 | + ): FormSchema[] => { | |
| 178 | + const { functionName, identifier, specs } = objectModel | |
| 179 | + | |
| 180 | + const isTCPModbusProduct | |
| 181 | + = unref(deviceDetail).deviceProfile?.profileData?.transportConfiguration?.protocol | |
| 182 | + === TCPProtocolTypeEnum.MODBUS_RTU | |
| 183 | + | |
| 184 | + const { dataType } = specs | |
| 185 | + const { type } = dataType || {} | |
| 186 | + | |
| 187 | + if (isTCPModbusProduct) | |
| 188 | + return [createModbusValueInput(objectModel)] | |
| 189 | + | |
| 190 | + if (type === DataTypeEnum.STRUCT) { | |
| 191 | + return (dataType?.specs as StructJSON[]).map((item) => { | |
| 192 | + const { functionName, identifier, dataType } = item | |
| 193 | + const { type } = dataType || {} | |
| 194 | + | |
| 195 | + return schemaMethod[type!]?.({ identifier, functionName, dataType }) | |
| 196 | + }) as FormSchema[] | |
| 197 | + } | |
| 198 | + | |
| 199 | + const result = schemaMethod[type!]?.({ identifier, functionName, dataType: dataType! }) | |
| 200 | + return result ? [result] : [] | |
| 201 | + } | |
| 202 | + | |
| 203 | + return { getFormByObjectModel } | |
| 204 | +} | ... | ... |
src/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal/useGetModbusCommand.ts
deleted
100644 → 0
| 1 | -import { type GenModbusCommandType, genModbusCommand } from '@/api/device' | |
| 2 | -import type { ExtensionDesc } from '@/api/device/model' | |
| 3 | -import { ModbusCRCEnum } from '@/enums/commandEnum' | |
| 4 | -import { TCPObjectModelActionTypeEnum } from '@/enums/objectModelEnum' | |
| 5 | -import { useMessage } from '@/hooks/web/useMessage' | |
| 6 | - | |
| 7 | -export const InsertString = (t: any, c: any, n: any) => { | |
| 8 | - const r: string | number[] = [] | |
| 9 | - | |
| 10 | - for (let i = 0; i * 2 < t.length; i++) | |
| 11 | - r.push(t.substr(i * 2, n)) | |
| 12 | - | |
| 13 | - return r.join(c) | |
| 14 | -} | |
| 15 | - | |
| 16 | -export const FillString = (t: any, c: any, n: any, b: any) => { | |
| 17 | - if (t === '' || c.length !== 1 || n <= t.length) | |
| 18 | - return t | |
| 19 | - | |
| 20 | - const l = t.length | |
| 21 | - | |
| 22 | - for (let i = 0; i < n - l; i++) { | |
| 23 | - if (b === true) | |
| 24 | - t = c + t | |
| 25 | - | |
| 26 | - else | |
| 27 | - t += c | |
| 28 | - } | |
| 29 | - return t | |
| 30 | -} | |
| 31 | - | |
| 32 | -export const SingleToHex = (t: any) => { | |
| 33 | - if (t === '') | |
| 34 | - return '' | |
| 35 | - | |
| 36 | - t = parseFloat(t) | |
| 37 | - | |
| 38 | - if (isNaN(t) === true) | |
| 39 | - return 'Error' | |
| 40 | - | |
| 41 | - if (t === 0) | |
| 42 | - return '00000000' | |
| 43 | - | |
| 44 | - let s, e, m | |
| 45 | - | |
| 46 | - if (t > 0) { | |
| 47 | - s = 0 | |
| 48 | - } | |
| 49 | - else { | |
| 50 | - s = 1 | |
| 51 | - | |
| 52 | - t = 0 - t | |
| 53 | - } | |
| 54 | - m = t.toString(2) | |
| 55 | - | |
| 56 | - if (m >= 1) { | |
| 57 | - if (m.indexOf('.') === -1) | |
| 58 | - m = `${m}.0` | |
| 59 | - | |
| 60 | - e = m.indexOf('.') - 1 | |
| 61 | - } | |
| 62 | - else { | |
| 63 | - e = 1 - m.indexOf('1') | |
| 64 | - } | |
| 65 | - if (e >= 0) | |
| 66 | - m = m.replace('.', '') | |
| 67 | - | |
| 68 | - else | |
| 69 | - m = m.substring(m.indexOf('1')) | |
| 70 | - | |
| 71 | - if (m.length > 24) | |
| 72 | - m = m.substr(0, 24) | |
| 73 | - | |
| 74 | - else | |
| 75 | - m = FillString(m, '0', 24, false) | |
| 76 | - | |
| 77 | - m = m.substring(1) | |
| 78 | - | |
| 79 | - e = (e + 127).toString(2) | |
| 80 | - | |
| 81 | - e = FillString(e, '0', 8, true) | |
| 82 | - | |
| 83 | - let r = parseInt(s + e + m, 2).toString(16) | |
| 84 | - | |
| 85 | - r = FillString(r, '0', 8, true) | |
| 86 | - | |
| 87 | - return InsertString(r, ' ', 2).toUpperCase() | |
| 88 | -} | |
| 89 | - | |
| 90 | -export const FormatHex = (t: any, n: any, ie: any) => { | |
| 91 | - const r: string[] = [] | |
| 92 | - | |
| 93 | - let s = '' | |
| 94 | - | |
| 95 | - let c = 0 | |
| 96 | - | |
| 97 | - for (let i = 0; i < t.length; i++) { | |
| 98 | - if (t.charAt(i) !== ' ') { | |
| 99 | - s += t.charAt(i) | |
| 100 | - | |
| 101 | - c += 1 | |
| 102 | - | |
| 103 | - if (c === n) { | |
| 104 | - r.push(s) | |
| 105 | - | |
| 106 | - s = '' | |
| 107 | - | |
| 108 | - c = 0 | |
| 109 | - } | |
| 110 | - } | |
| 111 | - if (ie === false) { | |
| 112 | - if (i === t.length - 1 && s !== '') | |
| 113 | - r.push(s) | |
| 114 | - } | |
| 115 | - } | |
| 116 | - return r.join('\n') | |
| 117 | -} | |
| 118 | - | |
| 119 | -export const FormatHexBatch = (t: any, n: any, ie: any) => { | |
| 120 | - const a = t.split('\n') | |
| 121 | - | |
| 122 | - const r: string[] = [] | |
| 123 | - | |
| 124 | - for (let i = 0; i < a.length; i++) | |
| 125 | - r[i] = FormatHex(a[i], n, ie) | |
| 126 | - | |
| 127 | - return r.join('\n') | |
| 128 | -} | |
| 129 | - | |
| 130 | -export const SingleToHexBatch = (t: any) => { | |
| 131 | - const a = t.split('\n') | |
| 132 | - | |
| 133 | - const r: string[] = [] | |
| 134 | - | |
| 135 | - for (let i = 0; i < a.length; i++) | |
| 136 | - r[i] = SingleToHex(a[i]) | |
| 137 | - | |
| 138 | - return r.join('\r\n') | |
| 139 | -} | |
| 140 | - | |
| 141 | -const getArray = (values: any) => { | |
| 142 | - const str = values.replace(/\s+/g, '') | |
| 143 | - const array: any = [] | |
| 144 | - | |
| 145 | - for (let i = 0; i < str.length; i += 4) { | |
| 146 | - const chunk = parseInt(str.substring(i, i + 4), 16) | |
| 147 | - array.push(chunk) | |
| 148 | - } | |
| 149 | - return array | |
| 150 | -} | |
| 151 | - | |
| 152 | -const getFloatPart = (number: string | number) => { | |
| 153 | - const isLessZero = Number(number) < 0 | |
| 154 | - number = number.toString() | |
| 155 | - const floatPartStartIndex = number.indexOf('.') | |
| 156 | - const value = ~floatPartStartIndex | |
| 157 | - ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}` | |
| 158 | - : '0' | |
| 159 | - return Number(value) | |
| 160 | -} | |
| 161 | - | |
| 162 | -const REGISTER_MAX_VALUE = Number(0xffff) | |
| 163 | - | |
| 164 | -export function useGetModbusCommand() { | |
| 165 | - const { createMessage } = useMessage() | |
| 166 | - | |
| 167 | - const getModbusCommand = async (value: number, extensionDesc: ExtensionDesc, deviceAddressCode: string) => { | |
| 168 | - const { registerAddress, actionType, zoomFactor } = extensionDesc as Required<ExtensionDesc> | |
| 169 | - | |
| 170 | - const params: GenModbusCommandType = { | |
| 171 | - crc: ModbusCRCEnum.CRC_16_LOWER, | |
| 172 | - registerNumber: 1, | |
| 173 | - deviceCode: deviceAddressCode, | |
| 174 | - registerAddress, | |
| 175 | - method: actionType, | |
| 176 | - registerValues: [value], | |
| 177 | - } | |
| 178 | - | |
| 179 | - if (actionType === TCPObjectModelActionTypeEnum.INT) { | |
| 180 | - const newValue | |
| 181 | - = Math.trunc(value) * zoomFactor | |
| 182 | - + getFloatPart(value) * zoomFactor | |
| 183 | - | |
| 184 | - if (newValue % 1 !== 0) { | |
| 185 | - createMessage.warning(`属性下发类型必须是整数,缩放因子为${zoomFactor}`) | |
| 186 | - return | |
| 187 | - } | |
| 188 | - | |
| 189 | - if (value * zoomFactor > REGISTER_MAX_VALUE) { | |
| 190 | - createMessage.warning(`属性下发值不能超过${REGISTER_MAX_VALUE},缩放因子为${zoomFactor}`) | |
| 191 | - return | |
| 192 | - } | |
| 193 | - | |
| 194 | - params.registerValues = [newValue] | |
| 195 | - } | |
| 196 | - else if (actionType === TCPObjectModelActionTypeEnum.DOUBLE) { | |
| 197 | - const regex = /^-?\d+(\.\d{0,2})?$/ | |
| 198 | - const values | |
| 199 | - = Math.trunc(value) * zoomFactor | |
| 200 | - + getFloatPart(value) * zoomFactor | |
| 201 | - | |
| 202 | - if (!regex.test(values.toString())) { | |
| 203 | - createMessage.warning(`属性下发值精确到两位小数,缩放因子为${zoomFactor}`) | |
| 204 | - return | |
| 205 | - } | |
| 206 | - | |
| 207 | - const newValue | |
| 208 | - = values === 0 ? [0, 0] : getArray(SingleToHex(values)) | |
| 209 | - params.registerValues = newValue | |
| 210 | - params.registerNumber = 2 | |
| 211 | - params.method = '10' | |
| 212 | - } | |
| 213 | - | |
| 214 | - return await genModbusCommand(params) | |
| 215 | - } | |
| 216 | - | |
| 217 | - /** | |
| 218 | - * | |
| 219 | - * @param extensionDesc 物模型拓展描述符 | |
| 220 | - * @param deviceAddressCode 设备地址码 | |
| 221 | - */ | |
| 222 | - const validateCanGetCommand = (extensionDesc?: ExtensionDesc, deviceAddressCode?: string, createValidateMessage = true) => { | |
| 223 | - const result = { flag: true, message: '' } | |
| 224 | - if (!extensionDesc) { | |
| 225 | - result.flag = false | |
| 226 | - result.message = '当前物模型扩展描述没有填写' | |
| 227 | - } | |
| 228 | - | |
| 229 | - if (!deviceAddressCode) { | |
| 230 | - result.flag = false | |
| 231 | - result.message = '当前设备未绑定设备地址码' | |
| 232 | - } | |
| 233 | - | |
| 234 | - if (result.message && createValidateMessage) | |
| 235 | - createMessage.warning(result.message) | |
| 236 | - | |
| 237 | - return result | |
| 238 | - } | |
| 239 | - | |
| 240 | - return { | |
| 241 | - getModbusCommand, | |
| 242 | - validateCanGetCommand, | |
| 243 | - } | |
| 244 | -} |
| ... | ... | @@ -6,7 +6,7 @@ import { JSONEditor } from '@/components/CodeEditor/src/JSONEditor' |
| 6 | 6 | import { BasicModal, useModalInner } from '@/components/Modal' |
| 7 | 7 | import { useMessage } from '@/hooks/web/useMessage' |
| 8 | 8 | import { useJsonParse } from '@/hooks/business/useJSONParse' |
| 9 | -import { TransportTypeEnum } from '@/enums/datasource' | |
| 9 | +import { TransportTypeEnum } from '@/enums/deviceEnum' | |
| 10 | 10 | |
| 11 | 11 | const emit = defineEmits(['register', 'editComplete']) |
| 12 | 12 | ... | ... |
| ... | ... | @@ -2,8 +2,9 @@ import type { FormSchema } from '@/components/Form' |
| 2 | 2 | import { ComponentEnum } from '@/components/Form/src/enum' |
| 3 | 3 | import type { BasicColumn } from '@/components/Table' |
| 4 | 4 | import type { CommandWayEnum } from '@/enums/commandEnum' |
| 5 | -import type { CodeTypeEnum, ContentDataFieldsEnum, DeviceTypeEnum, TransportTypeEnum } from '@/enums/datasource' | |
| 5 | +import type { ContentDataFieldsEnum } from '@/enums/datasource' | |
| 6 | 6 | import { EventActionTypeEnum, EventActionTypeNameEnum, EventTypeEnum, EventTypeNameEnum } from '@/enums/datasource' |
| 7 | +import type { DeviceTypeEnum, TCPProtocolTypeEnum } from '@/enums/deviceEnum' | |
| 7 | 8 | |
| 8 | 9 | export enum TableColumnFieldEnum { |
| 9 | 10 | DEVICE_ID = 'deviceId', |
| ... | ... | @@ -18,9 +19,8 @@ export interface TableRecordItemType { |
| 18 | 19 | [TableColumnFieldEnum.WAY]?: Nullable<CommandWayEnum> |
| 19 | 20 | [TableColumnFieldEnum.COMMAND]?: Nullable<string | Recordable> |
| 20 | 21 | [ContentDataFieldsEnum.DEVICE_TYPE]?: Nullable<DeviceTypeEnum> |
| 21 | - [ContentDataFieldsEnum.CODE_TYPE]?: Nullable<CodeTypeEnum> | |
| 22 | + [ContentDataFieldsEnum.CODE_TYPE]?: Nullable<TCPProtocolTypeEnum> | |
| 22 | 23 | [ContentDataFieldsEnum.DEVICE_PROFILE_ID]: Nullable<string> |
| 23 | - [ContentDataFieldsEnum.TRANSPORT_TYPE]?: Nullable<TransportTypeEnum> | |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { | ... | ... |
| ... | ... | @@ -4,13 +4,13 @@ import { usePublicFormContext } from '../../../usePublicFormContext' |
| 4 | 4 | import { getThingsModelServices } from '@/api/device' |
| 5 | 5 | import { type FormSchema } from '@/components/Form' |
| 6 | 6 | import { ComponentEnum } from '@/components/Form/src/enum' |
| 7 | -import { CommandWayEnum, CommandWayNameEnum } from '@/enums/commandEnum' | |
| 8 | -import { CommandDeliveryWayEnum, CommandDeliveryWayNameEnum, DeviceTypeEnum, EventActionTypeEnum, EventActionTypeNameEnum, EventTypeEnum, EventTypeNameEnum, TransportTypeEnum } from '@/enums/datasource' | |
| 7 | +import { CommandDeliveryWayEnum, CommandDeliveryWayNameEnum, CommandWayEnum, CommandWayNameEnum } from '@/enums/commandEnum' | |
| 8 | +import { EventActionTypeEnum, EventActionTypeNameEnum, EventTypeEnum, EventTypeNameEnum } from '@/enums/datasource' | |
| 9 | 9 | import type { ThingsModel } from '@/api/device/model' |
| 10 | 10 | import { PackageCategoryEnum } from '@/core/Library/enum' |
| 11 | -import { useContentDataStoreWithOut } from '@/store/modules/contentData' | |
| 12 | 11 | import { validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm' |
| 13 | 12 | import { JSONEditorValidator } from '@/components/CodeEditor/src/JSONEditor' |
| 13 | +import { DeviceTypeEnum, TCPProtocolTypeEnum, TransportTypeEnum } from '@/enums/deviceEnum' | |
| 14 | 14 | |
| 15 | 15 | export enum FormFieldsEnum { |
| 16 | 16 | ACTION_TYPE = 'actionType', |
| ... | ... | @@ -35,16 +35,11 @@ export enum FormFieldsNameEnum { |
| 35 | 35 | SERVICE = '服务', |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | -const contentDataStore = useContentDataStoreWithOut() | |
| 39 | - | |
| 40 | 38 | export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { |
| 41 | 39 | const { getNodeData, getCellInfo, getDeviceInfo } = usePublicFormContext() |
| 42 | 40 | const { dataSourceJson } = unref(getNodeData) || {} |
| 43 | 41 | const { deviceProfileId } = dataSourceJson || {} |
| 44 | - // transportType:判断是什么类型的设备 code:设备地址码 deviceType:设备类型 | |
| 45 | - const { transportType, deviceType, codeType } = unref(getDeviceInfo) || {} | |
| 46 | - | |
| 47 | - const isTemplate = contentDataStore.isTemplate // 判断是否是模板 | |
| 42 | + const { transportType } = unref(getDeviceInfo) || {} | |
| 48 | 43 | |
| 49 | 44 | return [ |
| 50 | 45 | { |
| ... | ... | @@ -115,39 +110,30 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { |
| 115 | 110 | component: ComponentEnum.SELECT, |
| 116 | 111 | required: true, |
| 117 | 112 | ifShow: ({ model }) => model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.PARAMS_SETTING, |
| 113 | + defaultValue: CommandDeliveryWayEnum.CUSTOM, | |
| 118 | 114 | componentProps: ({ formActionType }) => { |
| 119 | 115 | function getOptions() { |
| 120 | 116 | const options = [ |
| 121 | 117 | { label: CommandDeliveryWayNameEnum.CUSTOM, value: CommandDeliveryWayEnum.CUSTOM }, |
| 122 | 118 | ] |
| 123 | 119 | |
| 124 | - const serviceOption = { label: CommandDeliveryWayNameEnum.SERVICE, value: CommandDeliveryWayEnum.SERVICE } | |
| 125 | - const modbusOption = { label: CommandDeliveryWayNameEnum.MODBUS, value: CommandDeliveryWayEnum.MODBUS } | |
| 126 | - | |
| 127 | - function setOptions() { | |
| 128 | - // 是模板的话选择不到设备标识符类型所以就判断为放开自定义命令 | |
| 129 | - if (isTemplate) { | |
| 130 | - if (transportType !== TransportTypeEnum.TCP) | |
| 131 | - options.push(serviceOption) | |
| 132 | - return | |
| 133 | - } | |
| 134 | - | |
| 135 | - if (transportType !== TransportTypeEnum.TCP && deviceType === DeviceTypeEnum.SENSOR) { | |
| 136 | - // 判断不是TCP但是是网关子 | |
| 137 | - options.push(serviceOption) | |
| 138 | - return | |
| 139 | - } | |
| 140 | - | |
| 141 | - if (deviceType !== DeviceTypeEnum.SENSOR) | |
| 142 | - options.push(serviceOption) | |
| 120 | + const isTCPDevice = unref(getDeviceInfo)?.transportType === TransportTypeEnum.TCP | |
| 121 | + const isTCPModbusDevice = isTCPDevice && unref(getDeviceInfo)?.deviceProfile?.profileData?.transportConfiguration?.protocol === TCPProtocolTypeEnum.MODBUS_RTU | |
| 143 | 122 | |
| 144 | - if (transportType !== TransportTypeEnum.TCP) return | |
| 123 | + // TCP 自定义设备 自定义 | |
| 124 | + if (isTCPDevice && !isTCPModbusDevice && unref(getDeviceInfo)?.deviceType === DeviceTypeEnum.SENSOR) | |
| 125 | + return options | |
| 145 | 126 | |
| 146 | - if (codeType !== CommandDeliveryWayEnum.CUSTOM) | |
| 147 | - options.push(modbusOption) | |
| 127 | + // TCP Modbus设备 自定义&Modbus | |
| 128 | + if (isTCPModbusDevice) { | |
| 129 | + const modbusOption = { label: CommandDeliveryWayNameEnum.MODBUS, value: CommandDeliveryWayEnum.MODBUS } | |
| 130 | + options.push(modbusOption) | |
| 131 | + return options | |
| 148 | 132 | } |
| 149 | 133 | |
| 150 | - setOptions() | |
| 134 | + // 其他 自定义&服务 | |
| 135 | + options.push({ label: CommandDeliveryWayNameEnum.SERVICE, value: CommandDeliveryWayEnum.SERVICE }) | |
| 136 | + | |
| 151 | 137 | return options |
| 152 | 138 | } |
| 153 | 139 | ... | ... |
| ... | ... | @@ -3,11 +3,12 @@ import { getDeviceAttributes, getListByConfigurationId, getListByDeviceProfileId |
| 3 | 3 | import type { DeviceItemType, ThingsModelItemType } from '@/api/device/model' |
| 4 | 4 | import type { FormSchema } from '@/components/Form' |
| 5 | 5 | import { ComponentEnum } from '@/components/Form/src/enum' |
| 6 | -import { ContentDataFieldsEnum, ContentDataFieldsNameEnum, DataTypeEnum } from '@/enums/datasource' | |
| 6 | +import { ContentDataFieldsEnum, ContentDataFieldsNameEnum } from '@/enums/datasource' | |
| 7 | 7 | import { useContentDataStoreWithOut } from '@/store/modules/contentData' |
| 8 | 8 | import type { ProductAndDevice } from '@/api/content/model' |
| 9 | 9 | import { ControlComponentEnum } from '@/core/Library/packages/Control' |
| 10 | 10 | import { useParseParams } from '@/core/LoadData' |
| 11 | +import { DataTypeEnum } from '@/enums/objectModelEnum' | |
| 11 | 12 | |
| 12 | 13 | const contentDataStore = useContentDataStoreWithOut() |
| 13 | 14 | export const formSchemas = (componentKey?: string): FormSchema[] => { |
| ... | ... | @@ -88,12 +89,13 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { |
| 88 | 89 | showSearch: true, |
| 89 | 90 | api: async (params: string) => { |
| 90 | 91 | if (!deviceProfileId) return [] |
| 91 | - const options = await getDeviceAttributes(params) | |
| 92 | + let options = await getDeviceAttributes(params) | |
| 92 | 93 | if (componentKey === ControlComponentEnum.SWITCH) { // 开关只返回bool |
| 93 | - return options.filter((item) => { | |
| 94 | + options = options.filter((item) => { | |
| 94 | 95 | return item.detail.dataType.type === DataTypeEnum.BOOL |
| 95 | 96 | }) |
| 96 | 97 | } |
| 98 | + | |
| 97 | 99 | return options |
| 98 | 100 | }, |
| 99 | 101 | params: deviceProfileId, | ... | ... |
| ... | ... | @@ -2,7 +2,8 @@ import { validateTCPCustomCommand } from '.' |
| 2 | 2 | import type { Specs, StructJSON } from '@/api/device/model' |
| 3 | 3 | import { type FormSchema } from '@/components/Form' |
| 4 | 4 | import { ComponentEnum } from '@/components/Form/src/enum' |
| 5 | -import { DataTypeEnum, TransportTypeEnum } from '@/enums/datasource' | |
| 5 | +import { TransportTypeEnum } from '@/enums/deviceEnum' | |
| 6 | +import { DataTypeEnum } from '@/enums/objectModelEnum' | |
| 6 | 7 | |
| 7 | 8 | export enum FormFieldsEnum { |
| 8 | 9 | SERVICE_COMMAND = 'serviceCommand', | ... | ... |
| ... | ... | @@ -5,9 +5,9 @@ import { getFormSchemas } from './config' |
| 5 | 5 | import { ThingsModelForm } from '.' |
| 6 | 6 | import type { StructJSON } from '@/api/device/model' |
| 7 | 7 | import { BasicForm, useForm } from '@/components/Form' |
| 8 | -import { DataTypeEnum } from '@/enums/datasource' | |
| 9 | 8 | import { FormLabelAlignEnum, FormLayoutEnum } from '@/components/Form/src/enum' |
| 10 | 9 | import { deepMerge } from '@/utils' |
| 10 | +import { DataTypeEnum } from '@/enums/objectModelEnum' | |
| 11 | 11 | |
| 12 | 12 | interface ThingsModelFormPropsType { |
| 13 | 13 | value?: Recordable | ... | ... |
| ... | ... | @@ -67,7 +67,7 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { |
| 67 | 67 | }) |
| 68 | 68 | |
| 69 | 69 | const getDeviceDetail = async () => { |
| 70 | - if (!unref(nodeData)?.dataSourceJson.deviceId) return | |
| 70 | + if (!unref(nodeData)?.dataSourceJson?.deviceId) return | |
| 71 | 71 | deviceInfo.value = await getDeviceInfo(unref(nodeData)!.dataSourceJson.deviceId) |
| 72 | 72 | } |
| 73 | 73 | ... | ... |
| ... | ... | @@ -2,8 +2,8 @@ import { h, render, toRaw, unref } from 'vue' |
| 2 | 2 | import { ControlComponentEnum } from '../packages/Control' |
| 3 | 3 | import { doCommandDelivery, getDeviceActive } from '@/api/device' |
| 4 | 4 | import type { MouseUpEventDataType, NodeDataDataSourceJsonType, NodeDataEventJsonType, SingleClickEventDataType } from '@/api/node/model' |
| 5 | -import { CommandMethodEnum, CommandWayEnum } from '@/enums/commandEnum' | |
| 6 | -import { ActRangListItemTypeEnum, CommandTypeEnum, EventActionTypeEnum } from '@/enums/datasource' | |
| 5 | +import { CommandMethodEnum, CommandTypeEnum, CommandWayEnum } from '@/enums/commandEnum' | |
| 6 | +import { ActRangListItemTypeEnum, EventActionTypeEnum } from '@/enums/datasource' | |
| 7 | 7 | import { useMessage } from '@/hooks/web/useMessage' |
| 8 | 8 | import { AttributeDeliverModal, CommandDeliveryModal, SwitchCommandDeliveryModal } from '@/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal' |
| 9 | 9 | import type { MxCell } from '@/fitCore/types' | ... | ... |
| ... | ... | @@ -8,8 +8,10 @@ import { ComponentEnum } from '@/components/Form/src/enum' |
| 8 | 8 | import { useContentDataStoreWithOut } from '@/store/modules/contentData' |
| 9 | 9 | import { DateFormatEnum } from '@/enums/timeEnum' |
| 10 | 10 | import type { BasicColumn } from '@/components/Table' |
| 11 | -import { AlarmStatusEnum, type CodeTypeEnum, type ContentDataFieldsEnum } from '@/enums/datasource' | |
| 11 | +import { type ContentDataFieldsEnum } from '@/enums/datasource' | |
| 12 | 12 | import { useParseParams } from '@/core/LoadData' |
| 13 | +import type { TCPProtocolTypeEnum } from '@/enums/deviceEnum' | |
| 14 | +import { AlarmStatusEnum } from '@/enums/alarmEnum' | |
| 13 | 15 | |
| 14 | 16 | export const tableColumns: BasicColumn[] = [ |
| 15 | 17 | { |
| ... | ... | @@ -27,7 +29,7 @@ export const tableColumns: BasicColumn[] = [ |
| 27 | 29 | export interface TableRecordItemType { |
| 28 | 30 | uuid: string |
| 29 | 31 | [TableColumnFieldEnum.DEVICE_ID]?: Nullable<string> |
| 30 | - [ContentDataFieldsEnum.CODE_TYPE]?: Nullable<CodeTypeEnum> | |
| 32 | + [ContentDataFieldsEnum.CODE_TYPE]?: Nullable<TCPProtocolTypeEnum> | |
| 31 | 33 | } |
| 32 | 34 | |
| 33 | 35 | export const AlarmColorMap = { | ... | ... |
| ... | ... | @@ -8,8 +8,8 @@ import type { AlarmListItemType } from '@/api/alarm/model' |
| 8 | 8 | import type { CreateComponentType } from '@/core/Library/types' |
| 9 | 9 | import { useContentDataStore } from '@/store/modules/contentData' |
| 10 | 10 | import { isLightboxMode, isShareMode } from '@/utils/env' |
| 11 | -import { AlarmStatusColorEnum, AlarmStatusEnum, AlarmStatusNameEnum } from '@/enums/datasource' | |
| 12 | 11 | import { formatToDateTime } from '@/utils/dateUtil' |
| 12 | +import { AlarmStatusColorEnum, AlarmStatusNameEnum } from '@/enums/alarmEnum' | |
| 13 | 13 | |
| 14 | 14 | const props = defineProps<{ |
| 15 | 15 | config: CreateComponentType |
| ... | ... | @@ -67,7 +67,7 @@ onMounted(async () => { |
| 67 | 67 | useIntervalFn(initFetchAlarmList, (initOptions.polling || 30) * 1000) |
| 68 | 68 | } |
| 69 | 69 | else { |
| 70 | - const statusList = Object.values(AlarmStatusEnum) | |
| 70 | + const statusList = Object.values(AlarmStatusNameEnum) | |
| 71 | 71 | Object.assign(initOptions, { |
| 72 | 72 | scroll: false, |
| 73 | 73 | interval: 0, | ... | ... |
| ... | ... | @@ -75,6 +75,15 @@ const init = () => { |
| 75 | 75 | videoPlayInstance.value = videoJs(unref(videoPlayEl)!, unref(getOptions), () => { |
| 76 | 76 | emit('ready', unref(videoPlayInstance)) |
| 77 | 77 | }) |
| 78 | + | |
| 79 | + unref(videoPlayInstance)?.on('timeupdate', () => { | |
| 80 | + if (!unref(videoPlayInstance)) return | |
| 81 | + | |
| 82 | + const differTime = unref(videoPlayInstance)!.buffered().end(0) - unref(videoPlayInstance)!.currentTime() | |
| 83 | + | |
| 84 | + if (differTime > 10) | |
| 85 | + init() | |
| 86 | + }) | |
| 78 | 87 | } |
| 79 | 88 | |
| 80 | 89 | const customInit = (getOptionsFn: (optios: VideoJsPlayerOptions) => VideoJsPlayerOptions) => { | ... | ... |
| ... | ... | @@ -3,11 +3,12 @@ import { getDeviceAttributes, getListByConfigurationId, getListByDeviceProfileId |
| 3 | 3 | import type { DeviceItemType, ThingsModelItemType } from '@/api/device/model' |
| 4 | 4 | import type { FormSchema } from '@/components/Form' |
| 5 | 5 | import { ComponentEnum } from '@/components/Form/src/enum' |
| 6 | -import { ContentDataFieldsEnum, ContentDataFieldsNameEnum, DataTypeEnum } from '@/enums/datasource' | |
| 6 | +import { ContentDataFieldsEnum, ContentDataFieldsNameEnum } from '@/enums/datasource' | |
| 7 | 7 | import { useContentDataStoreWithOut } from '@/store/modules/contentData' |
| 8 | 8 | import type { ProductAndDevice } from '@/api/content/model' |
| 9 | 9 | import { ControlComponentEnum } from '@/core/Library/packages/Control' |
| 10 | 10 | import { useParseParams } from '@/core/LoadData' |
| 11 | +import { DataTypeEnum } from '@/enums/objectModelEnum' | |
| 11 | 12 | |
| 12 | 13 | const contentDataStore = useContentDataStoreWithOut() |
| 13 | 14 | export const formSchemas = (componentKey?: string): FormSchema[] => { | ... | ... |
| ... | ... | @@ -84,11 +84,13 @@ export class DataDynamicEffectHandler { |
| 84 | 84 | if (flag) { |
| 85 | 85 | const nodeEl = this.nodeUtils.getNodesForCells([cell]) |
| 86 | 86 | const { type } = record! |
| 87 | + | |
| 87 | 88 | if (type === ActRangListItemTypeEnum.SHOW) { |
| 88 | 89 | nodeEl.forEach((node) => { |
| 89 | 90 | node.classList.add(ActAnimationName.VISIBLE) |
| 90 | 91 | node.classList.remove(ActAnimationName.HIDDEN) |
| 91 | 92 | }) |
| 93 | + this.nodeUtils.updateCellValue(cell, record?.title ? record.title : latestValue) | |
| 92 | 94 | } |
| 93 | 95 | else if (type === ActRangListItemTypeEnum.HIDDEN) { |
| 94 | 96 | nodeEl.forEach((node) => { | ... | ... |
| ... | ... | @@ -55,9 +55,9 @@ export class MessageHandler { |
| 55 | 55 | const { data, node } = commandSource |
| 56 | 56 | const { attr, deviceProfileId } = data as NodeDataDataSourceJsonType |
| 57 | 57 | |
| 58 | - let { latestValue } = useLatestMessageValue(message.data, attr) | |
| 58 | + let { latestValue } = useLatestMessageValue(message.data, attr as string) | |
| 59 | 59 | if (isNull(latestValue)) return |
| 60 | - latestValue = useObjectModelValue(deviceProfileId, attr, latestValue) | |
| 60 | + latestValue = useObjectModelValue(deviceProfileId, attr as string, latestValue) | |
| 61 | 61 | const cell = this.nodeUtils.getCellById(node) |
| 62 | 62 | |
| 63 | 63 | const cellValue = cell.getValue() as Element | ... | ... |
| 1 | 1 | import type { DataType, Specs, StructJSON } from '@/api/device/model' |
| 2 | -import { DataTypeEnum } from '@/enums/datasource' | |
| 2 | +import { TransportTypeEnum } from '@/enums/deviceEnum' | |
| 3 | +import { DataTypeEnum } from '@/enums/objectModelEnum' | |
| 3 | 4 | import { useJsonParse } from '@/hooks/business/useJSONParse' |
| 4 | 5 | import { useProductsStoreWithOut } from '@/store/modules/products' |
| 5 | 6 | |
| ... | ... | @@ -45,6 +46,13 @@ function getValueByType(type: string, value: any, dataType: DataType) { |
| 45 | 46 | } |
| 46 | 47 | } |
| 47 | 48 | |
| 49 | +function getModbusDeviceValueByType(value: any, dataType: DataType) { | |
| 50 | + if (dataType.type === DataTypeEnum.BOOL && dataType.specsList && dataType.specsList.length) | |
| 51 | + return getEnumTypeValue(value, dataType.specsList as Specs[]) | |
| 52 | + | |
| 53 | + return value | |
| 54 | +} | |
| 55 | + | |
| 48 | 56 | export function useObjectModelValue(deviceProfileId: string, attr: string, value: any) { |
| 49 | 57 | const productsStore = useProductsStoreWithOut() |
| 50 | 58 | |
| ... | ... | @@ -52,5 +60,8 @@ export function useObjectModelValue(deviceProfileId: string, attr: string, value |
| 52 | 60 | if (!result) |
| 53 | 61 | return value |
| 54 | 62 | |
| 55 | - return getValueByType(result.specs.dataType.type as DataTypeEnum, value, result.specs.dataType) | |
| 63 | + const products = productsStore.getProductDetailById(deviceProfileId) | |
| 64 | + const isTCPModbus = products.transportType === TransportTypeEnum.TCP && result.extensionDesc?.originalDataType | |
| 65 | + | |
| 66 | + return isTCPModbus ? getModbusDeviceValueByType(value, result.specs.dataType) : getValueByType(result.specs.dataType.type as DataTypeEnum, value, result.specs.dataType) | |
| 56 | 67 | } | ... | ... |
src/enums/alarmEnum.ts
0 → 100644
| 1 | +export enum AlarmStatusEnum { | |
| 2 | + CLEARED_UN_ACK = 'CLEARED_UNACK', | |
| 3 | + ACTIVE_UN_ACK = 'ACTIVE_UNACK', | |
| 4 | + CLEARED_ACK = 'CLEARED_ACK', | |
| 5 | + ACTIVE_ACK = 'ACTIVE_ACK', | |
| 6 | +} | |
| 7 | + | |
| 8 | +export enum AlarmStatusColorEnum { | |
| 9 | + CLEARED_UNACK = 'red', | |
| 10 | + ACTIVE_UNACK = 'orange', | |
| 11 | + CLEARED_ACK = 'cyan', | |
| 12 | + ACTIVE_ACK = 'green', | |
| 13 | +} | |
| 14 | + | |
| 15 | +export enum AlarmStatusNameEnum { | |
| 16 | + CLEARED_UNACK = '清除未确认', | |
| 17 | + ACTIVE_UNACK = '激活未确认', | |
| 18 | + CLEARED_ACK = '清除已确认', | |
| 19 | + ACTIVE_ACK = '激活已确认', | |
| 20 | +} | ... | ... |
| ... | ... | @@ -13,6 +13,31 @@ export enum CommandCallWayEnum { |
| 13 | 13 | ASYNC = 'ASYNC', |
| 14 | 14 | } |
| 15 | 15 | |
| 16 | +export enum CommandTypeEnum { | |
| 17 | + CUSTOM = 0, | |
| 18 | + SERVICE = 1, | |
| 19 | + ATTRIBUTE = 2, | |
| 20 | + API = 'api', | |
| 21 | +} | |
| 22 | + | |
| 23 | +export enum CommandTypeNameEnum { | |
| 24 | + CUSTOM = '自定义', | |
| 25 | + SERVICE = '服务', | |
| 26 | + ATTRIBUTE = '属性', | |
| 27 | +} | |
| 28 | + | |
| 29 | +export enum CommandDeliveryWayEnum { | |
| 30 | + CUSTOM = 'CUSTOM', | |
| 31 | + SERVICE = 'SERVICE', | |
| 32 | + MODBUS = 'MODBUS', | |
| 33 | +} | |
| 34 | + | |
| 35 | +export enum CommandDeliveryWayNameEnum { | |
| 36 | + CUSTOM = '自定义命令', | |
| 37 | + SERVICE = '服务调用', | |
| 38 | + MODBUS = 'MODBUS', | |
| 39 | +} | |
| 40 | + | |
| 16 | 41 | export enum CommandMethodEnum { |
| 17 | 42 | THINGSKIT = 'methodThingskit', |
| 18 | 43 | } | ... | ... |
| ... | ... | @@ -64,50 +64,6 @@ export enum ActTypeNameEnum { |
| 64 | 64 | VARIABLE_IMAGE = '变量图片', |
| 65 | 65 | } |
| 66 | 66 | |
| 67 | -export enum DeviceTypeEnum { | |
| 68 | - /** | |
| 69 | - * @description 网关设备 | |
| 70 | - */ | |
| 71 | - GATEWAY = 'GATEWAY', | |
| 72 | - | |
| 73 | - /** | |
| 74 | - * @description 直连设备 | |
| 75 | - */ | |
| 76 | - DIRECT_CONNECTION = 'DIRECT_CONNECTION', | |
| 77 | - | |
| 78 | - /** | |
| 79 | - * @description 网关子设备 | |
| 80 | - */ | |
| 81 | - SENSOR = 'SENSOR', | |
| 82 | -} | |
| 83 | - | |
| 84 | -export enum DeviceTypeNameEnum { | |
| 85 | - /** | |
| 86 | - * @description 网关设备 | |
| 87 | - */ | |
| 88 | - GATEWAY = '网关设备', | |
| 89 | - | |
| 90 | - /** | |
| 91 | - * @description 直连设备 | |
| 92 | - */ | |
| 93 | - DIRECT_CONNECTION = '直连设备', | |
| 94 | - | |
| 95 | - /** | |
| 96 | - * @description 网关子设备 | |
| 97 | - */ | |
| 98 | - SENSOR = '网关子设备', | |
| 99 | -} | |
| 100 | - | |
| 101 | -export enum TransportTypeEnum { | |
| 102 | - MQTT = 'MQTT', | |
| 103 | - TCP = 'TCP', | |
| 104 | -} | |
| 105 | - | |
| 106 | -export enum CodeTypeEnum { | |
| 107 | - CUSTOM = 'CUSTOM', | |
| 108 | - MODBUS_RTU = 'MODBUS_RTU', | |
| 109 | -} | |
| 110 | - | |
| 111 | 67 | export enum EventTypeEnum { |
| 112 | 68 | /** |
| 113 | 69 | * @description 鼠标抬起 |
| ... | ... | @@ -271,46 +227,6 @@ export enum VariableImageSourceNameEnum { |
| 271 | 227 | GALLERY = '图库图形', |
| 272 | 228 | } |
| 273 | 229 | |
| 274 | -export enum CommandDeliveryWayEnum { | |
| 275 | - CUSTOM = 'CUSTOM', | |
| 276 | - SERVICE = 'SERVICE', | |
| 277 | - MODBUS = 'MODBUS', | |
| 278 | -} | |
| 279 | - | |
| 280 | -export enum CommandDeliveryWayNameEnum { | |
| 281 | - CUSTOM = '自定义命令', | |
| 282 | - SERVICE = '服务调用', | |
| 283 | - MODBUS = 'MODBUS', | |
| 284 | -} | |
| 285 | - | |
| 286 | -export enum FunctionTypeEnum { | |
| 287 | - PROPERTIES = 'properties', | |
| 288 | - EVENTS = 'events', | |
| 289 | - SERVICE = 'services', | |
| 290 | -} | |
| 291 | - | |
| 292 | -export enum AssessMode { | |
| 293 | - READ = 'r', | |
| 294 | - WRITE = 'w', | |
| 295 | -} | |
| 296 | - | |
| 297 | -export enum CommandTypeEnum { | |
| 298 | - SERVICE = 1, | |
| 299 | - API = 'API', | |
| 300 | -} | |
| 301 | - | |
| 302 | -/** | |
| 303 | - * 新增参数 动态显示表单 | |
| 304 | - */ | |
| 305 | -export enum DataTypeEnum { | |
| 306 | - NUMBER_INT = 'INT', | |
| 307 | - NUMBER_DOUBLE = 'DOUBLE', | |
| 308 | - STRING = 'TEXT', | |
| 309 | - STRUCT = 'STRUCT', | |
| 310 | - BOOL = 'BOOL', | |
| 311 | - ENUM = 'ENUM', | |
| 312 | -} | |
| 313 | - | |
| 314 | 230 | export enum AggregateTypeEnum { |
| 315 | 231 | MIN = 'MIN', |
| 316 | 232 | MAX = 'MAX', |
| ... | ... | @@ -333,24 +249,3 @@ export enum SocketSubscriberEnum { |
| 333 | 249 | HISTORY_CMDS = 'historyCmds', |
| 334 | 250 | } |
| 335 | 251 | |
| 336 | -export enum AlarmStatusEnum { | |
| 337 | - CLEARED_UN_ACK = 'CLEARED_UNACK', | |
| 338 | - ACTIVE_UN_ACK = 'ACTIVE_UNACK', | |
| 339 | - CLEARED_ACK = 'CLEARED_ACK', | |
| 340 | - ACTIVE_ACK = 'ACTIVE_ACK', | |
| 341 | -} | |
| 342 | - | |
| 343 | -export enum AlarmStatusColorEnum { | |
| 344 | - CLEARED_UNACK = 'red', | |
| 345 | - ACTIVE_UNACK = 'orange', | |
| 346 | - CLEARED_ACK = 'cyan', | |
| 347 | - ACTIVE_ACK = 'green', | |
| 348 | -} | |
| 349 | - | |
| 350 | -export enum AlarmStatusNameEnum { | |
| 351 | - CLEARED_UNACK = '清除未确认', | |
| 352 | - ACTIVE_UNACK = '激活未确认', | |
| 353 | - CLEARED_ACK = '清除已确认', | |
| 354 | - ACTIVE_ACK = '激活已确认', | |
| 355 | -} | |
| 356 | - | ... | ... |
src/enums/deviceEnum.ts
0 → 100644
| 1 | +export enum DeviceTypeEnum { | |
| 2 | + /** | |
| 3 | + * @description 网关设备 | |
| 4 | + */ | |
| 5 | + GATEWAY = 'GATEWAY', | |
| 6 | + | |
| 7 | + /** | |
| 8 | + * @description 直连设备 | |
| 9 | + */ | |
| 10 | + DIRECT_CONNECTION = 'DIRECT_CONNECTION', | |
| 11 | + | |
| 12 | + /** | |
| 13 | + * @description 网关子设备 | |
| 14 | + */ | |
| 15 | + SENSOR = 'SENSOR', | |
| 16 | +} | |
| 17 | + | |
| 18 | +export enum DeviceTypeNameEnum { | |
| 19 | + /** | |
| 20 | + * @description 网关设备 | |
| 21 | + */ | |
| 22 | + GATEWAY = '网关设备', | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * @description 直连设备 | |
| 26 | + */ | |
| 27 | + DIRECT_CONNECTION = '直连设备', | |
| 28 | + | |
| 29 | + /** | |
| 30 | + * @description 网关子设备 | |
| 31 | + */ | |
| 32 | + SENSOR = '网关子设备', | |
| 33 | +} | |
| 34 | + | |
| 35 | +export enum TransportTypeEnum { | |
| 36 | + DEFAULT = 'DEFAULT', | |
| 37 | + MQTT = 'MQTT', | |
| 38 | + COAP = 'COAP', | |
| 39 | + LWM2M = 'LWM2M', | |
| 40 | + SNMP = 'SNMP', | |
| 41 | + TCP = 'TCP', | |
| 42 | +} | |
| 43 | + | |
| 44 | +export enum TCPProtocolTypeEnum { | |
| 45 | + CUSTOM = 'CUSTOM', | |
| 46 | + MODBUS_RTU = 'MODBUS_RTU', | |
| 47 | +} | |
| 48 | + | |
| 49 | +export enum TCPProtocolTypeNameEnum { | |
| 50 | + CUSTOM = '自定义', | |
| 51 | + MODBUS_RTU = 'MODBUS_RTU', | |
| 52 | +} | ... | ... |
| 1 | 1 | /** |
| 2 | - * @description TCP物模型拓展描述符数据格式 | |
| 2 | + * 新增参数 动态显示表单 | |
| 3 | 3 | */ |
| 4 | -export enum TCPObjectModelActionTypeEnum { | |
| 5 | - BOOL = '05', | |
| 6 | - INT = '06', | |
| 7 | - DOUBLE = '16', | |
| 4 | +export enum DataTypeEnum { | |
| 5 | + NUMBER_INT = 'INT', | |
| 6 | + NUMBER_DOUBLE = 'DOUBLE', | |
| 7 | + STRING = 'TEXT', | |
| 8 | + STRUCT = 'STRUCT', | |
| 9 | + BOOL = 'BOOL', | |
| 10 | + ENUM = 'ENUM', | |
| 11 | +} | |
| 12 | + | |
| 13 | +export enum FunctionTypeEnum { | |
| 14 | + PROPERTIES = 'properties', | |
| 15 | + EVENTS = 'events', | |
| 16 | + SERVICE = 'services', | |
| 17 | +} | |
| 18 | + | |
| 19 | +export enum ObjectModelAccessModeEnum { | |
| 20 | + READ = 'r', | |
| 21 | + READ_AND_WRITE = 'rw', | |
| 22 | +} | |
| 23 | + | |
| 24 | +export enum OriginalDataTypeEnum { | |
| 25 | + INT16_AB = 'INT16_AB', | |
| 26 | + INT16_BA = 'INT16_BA', | |
| 27 | + UINT16_AB = 'UINT16_AB', | |
| 28 | + UINT16_BA = 'UINT16_BA', | |
| 29 | + INT32_AB_CD = 'INT32_AB_CD', | |
| 30 | + INT32_CD_AB = 'INT32_CD_AB', | |
| 31 | + INT32_BA_DC = 'INT32_BA_DC', | |
| 32 | + INT32_DC_BA = 'INT32_DC_BA', | |
| 33 | + UINT32_AB_CD = 'UINT32_AB_CD', | |
| 34 | + UINT32_CD_AB = 'UINT32_CD_AB', | |
| 35 | + UINT32_BA_DC = 'UINT32_BA_DC', | |
| 36 | + UINT32_DC_BA = 'UINT32_DC_BA', | |
| 37 | + FLOAT_AB_CD = 'FLOAT_AB_CD', | |
| 38 | + FLOAT_CD_AB = 'FLOAT_CD_AB', | |
| 39 | + FLOAT_BA_DC = 'FLOAT_BA_DC', | |
| 40 | + FLOAT_DC_BA = 'FLOAT_DC_BA', | |
| 41 | + DOUBLE = 'DOUBLE', | |
| 42 | + STRING = 'STRING', | |
| 43 | + BOOLEAN = 'BOOLEAN', | |
| 44 | + BITS = 'BITS', | |
| 45 | +} | |
| 46 | + | |
| 47 | +export enum OriginalDataTypeNameEnum { | |
| 48 | + INT16_AB = '16位有符号整数AB', | |
| 49 | + INT16_BA = '16位有符号整数BA', | |
| 50 | + UINT16_AB = '16位无符号整数AB', | |
| 51 | + UINT16_BA = '16位无符号整数BA', | |
| 52 | + INT32_AB_CD = '32位有符号整数AB_CD', | |
| 53 | + INT32_CD_AB = '32位有符号整数CD_AB', | |
| 54 | + INT32_BA_DC = '32位有符号整数BA_DC', | |
| 55 | + INT32_DC_BA = '32位有符号整数DC_BA', | |
| 56 | + UINT32_AB_CD = '32位无符号整数AB_CD', | |
| 57 | + UINT32_CD_AB = '32位无符号整数CD_AB', | |
| 58 | + UINT32_BA_DC = '32位无符号整数BA_DC', | |
| 59 | + UINT32_DC_BA = '32位无符号整数DC_BA', | |
| 60 | + FLOAT_AB_CD = '单精度浮点型AB_CD', | |
| 61 | + FLOAT_CD_AB = '单精度浮点型CD_AB', | |
| 62 | + FLOAT_BA_DC = '单精度浮点型BA_DC', | |
| 63 | + FLOAT_DC_BA = '单精度浮点型DC_BA', | |
| 64 | + DOUBLE = '双精度浮点型', | |
| 65 | + STRING = '字符串', | |
| 66 | + BOOLEAN = '布尔型', | |
| 67 | + BITS = '位', | |
| 68 | +} | |
| 69 | + | |
| 70 | +export enum ExtendDescOperationTypeEnum { | |
| 71 | + INPUT_STATUS_R_02 = 'inputStatus_r_02', | |
| 72 | + COIL_STATUS_R_01 = 'coilStatus_r_01', | |
| 73 | + COIL_STATUS_RW_01_05 = 'coilStatus_rw_01_05', | |
| 74 | + COIL_STATUS_RW_01_0F = 'coilStatus_rw_01_0F', | |
| 75 | + COIL_STATUS_W_05 = 'coilStatus_w_05', | |
| 76 | + COIL_STATUS_W_0F = 'coilStatus_w_0F', | |
| 77 | + HOLDING_REGISTER_R_03 = 'holdingRegister_r_03', | |
| 78 | + HOLDING_REGISTER_RW_03_06 = 'holdingRegister_rw_03_06', | |
| 79 | + HOLDING_REGISTER_RW_03_10 = 'holdingRegister_rw_03_10', | |
| 80 | + HOLDING_REGISTER_W_06 = 'holdingRegister_w_06', | |
| 81 | + HOLDING_REGISTER_W_10 = 'holdingRegister_w_10', | |
| 82 | + INPUT_REGISTER_R_04 = 'inputRegister_r_04', | |
| 83 | +} | |
| 84 | + | |
| 85 | +export enum ExtendDescOperationTypeNameEnum { | |
| 86 | + INPUT_STATUS_R_02 = '离散量输入(只读,0x02)', | |
| 87 | + COIL_STATUS_R_01 = '线圈状态(只读,0x01)', | |
| 88 | + COIL_STATUS_RW_01_05 = '线圈状态(读写,读取使用0x01,写入使用0x05)', | |
| 89 | + COIL_STATUS_RW_01_0F = '线圈状态(读写,读取使用0x01,写入使用0x0F)', | |
| 90 | + COIL_STATUS_W_05 = '线圈状态(只写,0x05)', | |
| 91 | + COIL_STATUS_W_0F = '线圈状态(只写,0x0F)', | |
| 92 | + HOLDING_REGISTER_R_03 = '保持寄存器(只读,0x03)', | |
| 93 | + HOLDING_REGISTER_RW_03_06 = '保持寄存器(读写,读取使用0x03,写入使用0x06)', | |
| 94 | + HOLDING_REGISTER_RW_03_10 = '保持寄存器(读写,读取使用0x03,写入使用0x10)', | |
| 95 | + HOLDING_REGISTER_W_06 = '保持寄存器(只写,0x06)', | |
| 96 | + HOLDING_REGISTER_W_10 = '保持寄存器(只写,0x10)', | |
| 97 | + INPUT_REGISTER_R_04 = '输入寄存器(只读,0x04)', | |
| 8 | 98 | } | ... | ... |
src/hooks/business/useBaseConversion.ts
0 → 100644
| 1 | +import { OriginalDataTypeEnum } from '@/enums/objectModelEnum' | |
| 2 | +import { useParseOriginalDataType } from '@/hooks/business/useParseOriginalDataType' | |
| 3 | + | |
| 4 | +export function useBaseConversion() { | |
| 5 | + function DecTo32Float(number: number) { | |
| 6 | + const arr = new Uint8Array(4) | |
| 7 | + const view = new DataView(arr.buffer) | |
| 8 | + view.setFloat32(0, +number) | |
| 9 | + return arr | |
| 10 | + } | |
| 11 | + | |
| 12 | + function DecTo64Double(number: number) { | |
| 13 | + const arr = new Uint8Array(8) | |
| 14 | + const view = new DataView(arr.buffer) | |
| 15 | + view.setFloat64(0, +number) | |
| 16 | + return arr | |
| 17 | + } | |
| 18 | + | |
| 19 | + function arrToBase(toBase: number, arr: Uint8Array) { | |
| 20 | + let result = '' | |
| 21 | + for (let i = 0; i < arr.length; i++) | |
| 22 | + result += (256 + arr[i]).toString(toBase).substring(1).toUpperCase() | |
| 23 | + | |
| 24 | + return result | |
| 25 | + } | |
| 26 | + | |
| 27 | + function DecTo16Uint(number: number) { | |
| 28 | + const arr = new Uint8Array(2) | |
| 29 | + const view = new DataView(arr.buffer) | |
| 30 | + view.setUint16(0, +number) | |
| 31 | + return arr | |
| 32 | + } | |
| 33 | + | |
| 34 | + function DecTo16Int(number: number) { | |
| 35 | + const arr = new Uint8Array(2) | |
| 36 | + const view = new DataView(arr.buffer) | |
| 37 | + view.setInt16(0, +number) | |
| 38 | + return arr | |
| 39 | + } | |
| 40 | + | |
| 41 | + function DecTo32Uint(number: number) { | |
| 42 | + const arr = new Uint8Array(4) | |
| 43 | + const view = new DataView(arr.buffer) | |
| 44 | + view.setUint32(0, +number) | |
| 45 | + return arr | |
| 46 | + } | |
| 47 | + | |
| 48 | + function DecTo32Int(number: number) { | |
| 49 | + const arr = new Uint8Array(4) | |
| 50 | + const view = new DataView(arr.buffer) | |
| 51 | + view.setInt32(0, +number) | |
| 52 | + return arr | |
| 53 | + } | |
| 54 | + | |
| 55 | + function DecToBinaryByType(type: OriginalDataTypeEnum, number: number) { | |
| 56 | + switch (type) { | |
| 57 | + case OriginalDataTypeEnum.INT16_AB: | |
| 58 | + case OriginalDataTypeEnum.INT16_BA: | |
| 59 | + return arrToBase(2, DecTo16Int(number)) | |
| 60 | + | |
| 61 | + case OriginalDataTypeEnum.UINT16_AB: | |
| 62 | + case OriginalDataTypeEnum.UINT16_BA: | |
| 63 | + return arrToBase(2, DecTo16Uint(number)) | |
| 64 | + | |
| 65 | + case OriginalDataTypeEnum.INT32_AB_CD: | |
| 66 | + case OriginalDataTypeEnum.INT32_CD_AB: | |
| 67 | + case OriginalDataTypeEnum.INT32_BA_DC: | |
| 68 | + case OriginalDataTypeEnum.INT32_DC_BA: | |
| 69 | + return arrToBase(2, DecTo32Int(number)) | |
| 70 | + | |
| 71 | + case OriginalDataTypeEnum.UINT32_AB_CD: | |
| 72 | + case OriginalDataTypeEnum.UINT32_CD_AB: | |
| 73 | + case OriginalDataTypeEnum.UINT32_BA_DC: | |
| 74 | + case OriginalDataTypeEnum.UINT32_DC_BA: | |
| 75 | + return arrToBase(2, DecTo32Uint(number)) | |
| 76 | + | |
| 77 | + case OriginalDataTypeEnum.FLOAT_AB_CD: | |
| 78 | + case OriginalDataTypeEnum.FLOAT_CD_AB: | |
| 79 | + case OriginalDataTypeEnum.FLOAT_BA_DC: | |
| 80 | + case OriginalDataTypeEnum.FLOAT_DC_BA: | |
| 81 | + return arrToBase(2, DecTo32Float(number)) | |
| 82 | + | |
| 83 | + case OriginalDataTypeEnum.DOUBLE: | |
| 84 | + return arrToBase(2, DecTo64Double(number)) | |
| 85 | + } | |
| 86 | + } | |
| 87 | + | |
| 88 | + function SplitStringToGroupByItemLength( | |
| 89 | + value: string, | |
| 90 | + itemLength = 8, | |
| 91 | + ignoreLessThan = false, | |
| 92 | + ): string[] { | |
| 93 | + const reg = new RegExp(`.{${ignoreLessThan ? '' : '1,'}${itemLength}}`, 'g') | |
| 94 | + return value.match(reg) || [] | |
| 95 | + } | |
| 96 | + | |
| 97 | + function ExchangeByteOrder(binary: string, order: string) { | |
| 98 | + const group = SplitStringToGroupByItemLength(binary) | |
| 99 | + | |
| 100 | + const BASE_ORDER = { | |
| 101 | + A: 0, | |
| 102 | + B: 1, | |
| 103 | + C: 2, | |
| 104 | + D: 3, | |
| 105 | + } | |
| 106 | + | |
| 107 | + const array: string[] = Array.from({ length: binary.length / 8 }) | |
| 108 | + | |
| 109 | + order | |
| 110 | + .split('') | |
| 111 | + .forEach((bytePosition, index) => (array[index] = group[BASE_ORDER[bytePosition as keyof typeof BASE_ORDER] as number])) | |
| 112 | + | |
| 113 | + return array.join('') | |
| 114 | + } | |
| 115 | + | |
| 116 | + function ByteToHex(binary: string) { | |
| 117 | + const group = SplitStringToGroupByItemLength(binary, 16) | |
| 118 | + return group.map(byte => parseInt(byte, 2).toString(16)) | |
| 119 | + } | |
| 120 | + | |
| 121 | + function ByteToDec(binary: string) { | |
| 122 | + const group = SplitStringToGroupByItemLength(binary, 16) | |
| 123 | + return group.map(byte => parseInt(byte, 2)) | |
| 124 | + } | |
| 125 | + | |
| 126 | + function StringToHEXBuffer(string: string | number) { | |
| 127 | + return string | |
| 128 | + .toString() | |
| 129 | + .split('') | |
| 130 | + .map(string => string.charCodeAt(0).toString(16)) | |
| 131 | + .reverse() | |
| 132 | + } | |
| 133 | + | |
| 134 | + function getRegisterValueByOriginalDataType( | |
| 135 | + value: number, | |
| 136 | + type: OriginalDataTypeEnum, | |
| 137 | + additional?: { bitMask?: number; registerNumber?: number }, | |
| 138 | + ) { | |
| 139 | + const { exchangeSortFlag } = useParseOriginalDataType(type) | |
| 140 | + // eslint-disable-next-line no-console | |
| 141 | + console.groupCollapsed('Modbus Debug') | |
| 142 | + | |
| 143 | + let result: number[] | |
| 144 | + | |
| 145 | + if (type === OriginalDataTypeEnum.BOOLEAN) { | |
| 146 | + result = [value] | |
| 147 | + } | |
| 148 | + else if (type === OriginalDataTypeEnum.BITS) { | |
| 149 | + const { bitMask = 0 } = additional || {} | |
| 150 | + const binaryArray = Array.from({ length: 16 }, () => 0) | |
| 151 | + binaryArray[15 - bitMask] = value | |
| 152 | + result = [parseInt(binaryArray.join(''), 2)] | |
| 153 | + } | |
| 154 | + else if (type === OriginalDataTypeEnum.STRING) { | |
| 155 | + let buffer = StringToHEXBuffer(value) | |
| 156 | + const { registerNumber = 0 } = additional || {} | |
| 157 | + | |
| 158 | + if (buffer.length < registerNumber * 2) { | |
| 159 | + buffer = [ | |
| 160 | + ...Array.from({ length: registerNumber * 2 - buffer.length }, () => '00'), | |
| 161 | + ...buffer, | |
| 162 | + ] | |
| 163 | + } | |
| 164 | + | |
| 165 | + result = SplitStringToGroupByItemLength(buffer.join(''), 4).map(hex => parseInt(hex, 16)) | |
| 166 | + } | |
| 167 | + else { | |
| 168 | + // eslint-disable-next-line no-console | |
| 169 | + console.table({ input: value, sort: exchangeSortFlag, type }) | |
| 170 | + | |
| 171 | + let binary = DecToBinaryByType(type, value)! | |
| 172 | + | |
| 173 | + // eslint-disable-next-line no-console | |
| 174 | + console.table({ beforeExchange: binary }) | |
| 175 | + | |
| 176 | + if (exchangeSortFlag) binary = ExchangeByteOrder(binary, exchangeSortFlag) | |
| 177 | + result = ByteToDec(binary) | |
| 178 | + | |
| 179 | + // eslint-disable-next-line no-console | |
| 180 | + console.table({ | |
| 181 | + afterEchange: binary, | |
| 182 | + dec: result.toString(), | |
| 183 | + hex: ByteToHex(binary).toString(), | |
| 184 | + }) | |
| 185 | + } | |
| 186 | + | |
| 187 | + // eslint-disable-next-line no-console | |
| 188 | + console.groupEnd() | |
| 189 | + | |
| 190 | + return result | |
| 191 | + } | |
| 192 | + | |
| 193 | + return { | |
| 194 | + DecToBinaryByType, | |
| 195 | + ByteToDec, | |
| 196 | + ByteToHex, | |
| 197 | + ExchangeByteOrder, | |
| 198 | + SplitStringToGroupByItemLength, | |
| 199 | + getRegisterValueByOriginalDataType, | |
| 200 | + StringToHEXBuffer, | |
| 201 | + } | |
| 202 | +} | ... | ... |
src/hooks/business/useCommandDelivery.ts
0 → 100644
| 1 | +import { ref } from 'vue' | |
| 2 | +import { isFunction } from '@wry-smile/utils-is' | |
| 3 | +import { useMessage } from '../web/useMessage' | |
| 4 | +import { useCoverModbusCommand } from './useCoverModbusCommand' | |
| 5 | +import { | |
| 6 | + TCPProtocolTypeEnum, | |
| 7 | + TransportTypeEnum, | |
| 8 | +} from '@/enums/deviceEnum' | |
| 9 | +import { FunctionTypeEnum } from '@/enums/objectModelEnum' | |
| 10 | +import { CommandCallWayEnum, CommandMethodEnum, CommandTypeEnum, CommandWayEnum } from '@/enums/commandEnum' | |
| 11 | +import type { DeviceItemType, DeviceProfileItemType, RpcCommandType, Tsl } from '@/api/device/model' | |
| 12 | +import { getDeviceActive, getDeviceInfo, doCommandDelivery as rpcCommandApi } from '@/api/device' | |
| 13 | +import { useProductsStoreWithOut } from '@/store/modules/products' | |
| 14 | + | |
| 15 | +interface SetupType { | |
| 16 | + entityId: string | |
| 17 | + transportType: TransportTypeEnum | |
| 18 | + isTCPModbus: boolean | |
| 19 | + deviceCode: string | undefined | |
| 20 | + deviceDetail: DeviceItemType | |
| 21 | + objectModel: Tsl | undefined | |
| 22 | + identifier: string | |
| 23 | +} | |
| 24 | + | |
| 25 | +export interface DoCommandDeliverParamsType { | |
| 26 | + value: any | |
| 27 | + deviceDetail?: DeviceItemType | |
| 28 | + deviceId?: string | |
| 29 | + identifier?: string | |
| 30 | + objectModel?: Tsl | |
| 31 | + deviceProfileId?: string | |
| 32 | + deviceProfileDetail?: DeviceProfileItemType | |
| 33 | + way?: CommandWayEnum | |
| 34 | + cmdType?: CommandTypeEnum | |
| 35 | + transportType?: TransportTypeEnum | |
| 36 | + penetration?: boolean | |
| 37 | + | |
| 38 | + beforeFetch?: ( | |
| 39 | + rpcCommand: RpcCommandType, | |
| 40 | + setup: SetupType | |
| 41 | + ) => RpcCommandType | Promise<RpcCommandType> | |
| 42 | +} | |
| 43 | + | |
| 44 | +export function useCommandDelivery() { | |
| 45 | + const loading = ref(false) | |
| 46 | + async function doSetup(params: DoCommandDeliverParamsType) { | |
| 47 | + let { deviceDetail, identifier, deviceProfileId, objectModel, transportType } = params | |
| 48 | + const { deviceId, deviceProfileDetail } = params | |
| 49 | + | |
| 50 | + const entityId = deviceId || deviceDetail?.tbDeviceId | |
| 51 | + if (!entityId) | |
| 52 | + throw new Error('not found entityId') | |
| 53 | + | |
| 54 | + identifier = identifier || objectModel?.identifier | |
| 55 | + | |
| 56 | + if (!identifier) | |
| 57 | + throw new Error('not found identifier') | |
| 58 | + | |
| 59 | + transportType = transportType || (deviceDetail?.transportType as TransportTypeEnum) | |
| 60 | + | |
| 61 | + if ( | |
| 62 | + !transportType | |
| 63 | + || (transportType === TransportTypeEnum.TCP && !deviceDetail) | |
| 64 | + || !deviceDetail?.deviceProfile | |
| 65 | + ) { | |
| 66 | + deviceDetail = await getDeviceInfo(entityId) | |
| 67 | + transportType = deviceDetail.transportType as TransportTypeEnum | |
| 68 | + } | |
| 69 | + | |
| 70 | + const isTCPModbus | |
| 71 | + = transportType === TransportTypeEnum.TCP | |
| 72 | + && deviceDetail?.deviceProfile?.profileData?.transportConfiguration?.protocol | |
| 73 | + === TCPProtocolTypeEnum.MODBUS_RTU | |
| 74 | + | |
| 75 | + if (isTCPModbus && !objectModel?.extensionDesc) { | |
| 76 | + deviceProfileId = deviceDetail.deviceProfileId || deviceProfileId || deviceProfileDetail?.id | |
| 77 | + if (!deviceProfileId) | |
| 78 | + throw new Error('not found deviceProfile') | |
| 79 | + | |
| 80 | + const productStore = useProductsStoreWithOut() | |
| 81 | + objectModel = productStore.getObjectModelByIdWithIdentifier(deviceProfileId!, identifier)! | |
| 82 | + } | |
| 83 | + | |
| 84 | + const deviceCode = deviceDetail.code | |
| 85 | + return { | |
| 86 | + entityId, | |
| 87 | + transportType, | |
| 88 | + isTCPModbus, | |
| 89 | + deviceCode, | |
| 90 | + deviceDetail, | |
| 91 | + objectModel, | |
| 92 | + identifier, | |
| 93 | + } | |
| 94 | + } | |
| 95 | + | |
| 96 | + async function doCommandDelivery(params: DoCommandDeliverParamsType) { | |
| 97 | + try { | |
| 98 | + loading.value = true | |
| 99 | + | |
| 100 | + const setupResult = await doSetup(params) | |
| 101 | + | |
| 102 | + const { entityId, transportType, isTCPModbus, deviceCode, objectModel, identifier } | |
| 103 | + = setupResult | |
| 104 | + | |
| 105 | + let command = params.value | |
| 106 | + | |
| 107 | + let rpcCommand: RpcCommandType = { | |
| 108 | + persistent: true, | |
| 109 | + method: CommandMethodEnum.THINGSKIT, | |
| 110 | + additionalInfo: { | |
| 111 | + cmdType: params.cmdType ?? CommandTypeEnum.API, | |
| 112 | + }, | |
| 113 | + params: command, | |
| 114 | + } | |
| 115 | + | |
| 116 | + let way = params.way ?? CommandWayEnum.ONE_WAY | |
| 117 | + | |
| 118 | + if (!params.penetration) { | |
| 119 | + if (transportType === TransportTypeEnum.TCP) { | |
| 120 | + command = params.value | |
| 121 | + if (isTCPModbus) { | |
| 122 | + const { doCoverCommand } = useCoverModbusCommand() | |
| 123 | + command = await doCoverCommand(params.value, objectModel!, deviceCode, entityId) | |
| 124 | + } | |
| 125 | + } | |
| 126 | + else { | |
| 127 | + command = { | |
| 128 | + [identifier]: command, | |
| 129 | + } | |
| 130 | + } | |
| 131 | + | |
| 132 | + if (objectModel?.functionType === FunctionTypeEnum.SERVICE) { | |
| 133 | + rpcCommand.additionalInfo.cmdType = CommandTypeEnum.SERVICE | |
| 134 | + way | |
| 135 | + = objectModel.callType === CommandCallWayEnum.ASYNC | |
| 136 | + ? CommandWayEnum.ONE_WAY | |
| 137 | + : CommandWayEnum.TWO_WAY | |
| 138 | + } | |
| 139 | + | |
| 140 | + rpcCommand.params = command | |
| 141 | + } | |
| 142 | + | |
| 143 | + const sendApi = rpcCommandApi | |
| 144 | + | |
| 145 | + if (params.beforeFetch && isFunction(params.beforeFetch)) | |
| 146 | + rpcCommand = await params.beforeFetch(rpcCommand, setupResult) | |
| 147 | + | |
| 148 | + if (way === CommandWayEnum.TWO_WAY) { | |
| 149 | + const result = await getDeviceActive(entityId) | |
| 150 | + const [firsetItem] = result || [] | |
| 151 | + | |
| 152 | + if (!firsetItem.value) { | |
| 153 | + const { createMessage } = useMessage() | |
| 154 | + createMessage.warning('当前设备不在线') | |
| 155 | + return | |
| 156 | + } | |
| 157 | + } | |
| 158 | + await sendApi({ way, deviceId: entityId, command: rpcCommand }) | |
| 159 | + } | |
| 160 | + finally { | |
| 161 | + loading.value = false | |
| 162 | + } | |
| 163 | + } | |
| 164 | + | |
| 165 | + return { | |
| 166 | + loading, | |
| 167 | + doCommandDelivery, | |
| 168 | + } | |
| 169 | +} | ... | ... |
src/hooks/business/useCoverModbusCommand.ts
0 → 100644
| 1 | +import { isNullOrUnDef } from '@wry-smile/utils-is' | |
| 2 | +import { useParseOriginalDataType } from './useParseOriginalDataType' | |
| 3 | + | |
| 4 | +import { isFloatType, isNumberType, useParseOperationType } from './useParseOperationType' | |
| 5 | +import { useBaseConversion } from '@/hooks/business/useBaseConversion' | |
| 6 | +import { useMessage } from '@/hooks/web/useMessage' | |
| 7 | +import type { ExtensionDesc, Specs, Tsl } from '@/api/device/model' | |
| 8 | +import { type GenModbusCommandType, genModbusCommand, getDeviceInfo, getDeviceTelemetryValue } from '@/api/device' | |
| 9 | +import { ModbusCRCEnum } from '@/enums/commandEnum' | |
| 10 | +import { OriginalDataTypeEnum } from '@/enums/objectModelEnum' | |
| 11 | + | |
| 12 | +const getFloatPart = (number: string | number) => { | |
| 13 | + const isLessZero = Number(number) < 0 | |
| 14 | + number = number.toString() | |
| 15 | + const floatPartStartIndex = number.indexOf('.') | |
| 16 | + const value = ~floatPartStartIndex | |
| 17 | + ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}` | |
| 18 | + : '0' | |
| 19 | + return Number(value) | |
| 20 | +} | |
| 21 | + | |
| 22 | +function getValueFromValueRange(value: number, valueRange: Record<'min' | 'max', number>) { | |
| 23 | + const { min, max } = valueRange || {} | |
| 24 | + if (!isNullOrUnDef(min) && value < min) return min | |
| 25 | + if (!isNullOrUnDef(max) && value > max) return max | |
| 26 | + return value | |
| 27 | +} | |
| 28 | + | |
| 29 | +async function getCurrentBitCommand(entityId: string, objectModel: Tsl, value: number) { | |
| 30 | + const deviceDetail = await getDeviceInfo(entityId) | |
| 31 | + const thingsModels = deviceDetail.deviceProfile.profileData.thingsModel | |
| 32 | + | |
| 33 | + const { registerAddress } = objectModel.extensionDesc || {} | |
| 34 | + | |
| 35 | + const bitsModel = thingsModels?.filter( | |
| 36 | + item => | |
| 37 | + item.extensionDesc?.originalDataType === OriginalDataTypeEnum.BITS | |
| 38 | + && item.extensionDesc.registerAddress === registerAddress, | |
| 39 | + ) | |
| 40 | + | |
| 41 | + const valuePositionMap | |
| 42 | + = bitsModel?.reduce((prev, next) => { | |
| 43 | + return { ...prev, [next.identifier]: next.extensionDesc?.bitMask } | |
| 44 | + }, {} as Record<string, number>) || {} | |
| 45 | + | |
| 46 | + const attrKeys = Object.keys(valuePositionMap) | |
| 47 | + | |
| 48 | + const latestBitsValues = await getDeviceTelemetryValue({ entityId, keys: attrKeys.join(',') }) | |
| 49 | + | |
| 50 | + const binaryArr = Array.from({ length: 16 }, () => 0) | |
| 51 | + | |
| 52 | + for (const key of attrKeys) { | |
| 53 | + const index = valuePositionMap[key] | |
| 54 | + | |
| 55 | + if (!isNullOrUnDef(index)) { | |
| 56 | + const [latest] = latestBitsValues[key] | |
| 57 | + const { value } = latest | |
| 58 | + binaryArr[index] = Number(value) | |
| 59 | + } | |
| 60 | + } | |
| 61 | + | |
| 62 | + if (!isNullOrUnDef(objectModel.extensionDesc?.bitMask)) | |
| 63 | + binaryArr[objectModel.extensionDesc.bitMask] = value | |
| 64 | + | |
| 65 | + return [parseInt(binaryArr.reverse().join(''), 2)] | |
| 66 | +} | |
| 67 | + | |
| 68 | +export function useCoverModbusCommand() { | |
| 69 | + const { createMessage } = useMessage() | |
| 70 | + | |
| 71 | + const doCoverCommand = async (value: number, objectModel: Tsl, deviceAddressCode?: string, entityId?: string) => { | |
| 72 | + if (!deviceAddressCode) { | |
| 73 | + const message = '当前设备未绑定设备地址码' | |
| 74 | + createMessage.warning(message) | |
| 75 | + throw new Error(message) | |
| 76 | + } | |
| 77 | + | |
| 78 | + const { | |
| 79 | + registerAddress, | |
| 80 | + operationType, | |
| 81 | + scaling, | |
| 82 | + originalDataType, | |
| 83 | + bitMask, | |
| 84 | + registerCount: registerNumber, | |
| 85 | + } = objectModel.extensionDesc as Required<ExtensionDesc> | |
| 86 | + | |
| 87 | + const { writeRegisterAddress } = useParseOperationType(operationType) | |
| 88 | + | |
| 89 | + const { unsigned, exchangeSortFlag, registerCount } | |
| 90 | + = useParseOriginalDataType(originalDataType) | |
| 91 | + | |
| 92 | + const params: GenModbusCommandType = { | |
| 93 | + crc: ModbusCRCEnum.CRC_16_LOWER, | |
| 94 | + registerNumber: registerCount || registerNumber, | |
| 95 | + deviceCode: deviceAddressCode, | |
| 96 | + registerAddress: parseInt(registerAddress, 16), | |
| 97 | + method: writeRegisterAddress!, | |
| 98 | + registerValues: [value], | |
| 99 | + } | |
| 100 | + | |
| 101 | + if (exchangeSortFlag) params.hexByteOrderEnum = exchangeSortFlag | |
| 102 | + | |
| 103 | + const { getRegisterValueByOriginalDataType } = useBaseConversion() | |
| 104 | + | |
| 105 | + if (isNumberType(originalDataType)) { | |
| 106 | + let newValue = Math.trunc(value) * scaling + getFloatPart(value) * scaling | |
| 107 | + | |
| 108 | + newValue = unsigned ? newValue : Math.abs(newValue) | |
| 109 | + | |
| 110 | + newValue = getValueFromValueRange( | |
| 111 | + newValue, | |
| 112 | + (objectModel.specs?.dataType.specs as Specs).valueRange!, | |
| 113 | + ) | |
| 114 | + | |
| 115 | + if (!isFloatType(originalDataType) && newValue % 1 !== 0) { | |
| 116 | + const message = `属性下发类型必须是整数,缩放因子为${scaling}` | |
| 117 | + createMessage.warning(message) | |
| 118 | + throw Error(message) | |
| 119 | + } | |
| 120 | + | |
| 121 | + value = newValue | |
| 122 | + } | |
| 123 | + | |
| 124 | + params.registerValues = originalDataType === OriginalDataTypeEnum.BITS | |
| 125 | + ? await getCurrentBitCommand(entityId!, objectModel, value) | |
| 126 | + : getRegisterValueByOriginalDataType(value, originalDataType, { | |
| 127 | + bitMask, | |
| 128 | + registerNumber, | |
| 129 | + }) | |
| 130 | + | |
| 131 | + if (!params.method) { | |
| 132 | + const message = '物模型操作类型无法进行写入' | |
| 133 | + createMessage.warning(message) | |
| 134 | + throw Error(message) | |
| 135 | + } | |
| 136 | + | |
| 137 | + return await genModbusCommand(params) | |
| 138 | + } | |
| 139 | + | |
| 140 | + return { | |
| 141 | + doCoverCommand, | |
| 142 | + } | |
| 143 | +} | ... | ... |
src/hooks/business/useParseOperationType.ts
0 → 100644
| 1 | +import type { ExtendDescOperationTypeEnum } from '@/enums/objectModelEnum' | |
| 2 | +import { | |
| 3 | + DataTypeEnum, | |
| 4 | + ObjectModelAccessModeEnum, | |
| 5 | + OriginalDataTypeEnum, | |
| 6 | +} from '@/enums/objectModelEnum' | |
| 7 | + | |
| 8 | +export function isNumberType(originalDataType: OriginalDataTypeEnum) { | |
| 9 | + return ![ | |
| 10 | + OriginalDataTypeEnum.BITS, | |
| 11 | + OriginalDataTypeEnum.BOOLEAN, | |
| 12 | + OriginalDataTypeEnum.STRING, | |
| 13 | + ].includes(originalDataType) | |
| 14 | +} | |
| 15 | + | |
| 16 | +export function isFloatType(originalDataType: OriginalDataTypeEnum) { | |
| 17 | + return [ | |
| 18 | + OriginalDataTypeEnum.FLOAT_AB_CD, | |
| 19 | + OriginalDataTypeEnum.FLOAT_BA_DC, | |
| 20 | + OriginalDataTypeEnum.FLOAT_CD_AB, | |
| 21 | + OriginalDataTypeEnum.FLOAT_DC_BA, | |
| 22 | + OriginalDataTypeEnum.DOUBLE, | |
| 23 | + ].includes(originalDataType) | |
| 24 | +} | |
| 25 | + | |
| 26 | +export function getDataTypeByOriginalDataType(originalDataType: OriginalDataTypeEnum) { | |
| 27 | + if (originalDataType === OriginalDataTypeEnum.STRING) return DataTypeEnum.STRING | |
| 28 | + else if (originalDataType === OriginalDataTypeEnum.BOOLEAN) return DataTypeEnum.BOOL | |
| 29 | + else if (isFloatType(originalDataType)) return DataTypeEnum.NUMBER_DOUBLE | |
| 30 | + else return DataTypeEnum.NUMBER_INT | |
| 31 | +} | |
| 32 | + | |
| 33 | +export function useParseOperationType(actionType: ExtendDescOperationTypeEnum) { | |
| 34 | + const [, accessMode, inputOrOutput, output = undefined] = actionType.split('_') | |
| 35 | + | |
| 36 | + const _accessMode | |
| 37 | + = accessMode === ObjectModelAccessModeEnum.READ | |
| 38 | + ? ObjectModelAccessModeEnum.READ | |
| 39 | + : ObjectModelAccessModeEnum.READ_AND_WRITE | |
| 40 | + | |
| 41 | + const writeOnly | |
| 42 | + = _accessMode === ObjectModelAccessModeEnum.READ_AND_WRITE | |
| 43 | + && accessMode !== ObjectModelAccessModeEnum.READ_AND_WRITE | |
| 44 | + | |
| 45 | + return { | |
| 46 | + accessMode: _accessMode, | |
| 47 | + originAccessMode: accessMode, | |
| 48 | + writeOnly, | |
| 49 | + readRegisterAddress: writeOnly ? undefined : inputOrOutput, | |
| 50 | + writeRegisterAddress: writeOnly ? inputOrOutput : output, | |
| 51 | + } | |
| 52 | +} | ... | ... |
| 1 | +import { OriginalDataTypeEnum } from '@/enums/objectModelEnum' | |
| 2 | + | |
| 3 | +export type OriginalDataTypePrefixType<S = `${OriginalDataTypeEnum}`> = S extends string | |
| 4 | + ? S extends `${infer D}_${string}` | |
| 5 | + ? D | |
| 6 | + : S | |
| 7 | + : '' | |
| 8 | + | |
| 9 | +function getRegisterCount(originalDataType: OriginalDataTypeEnum) { | |
| 10 | + switch (originalDataType) { | |
| 11 | + case OriginalDataTypeEnum.INT16_AB: | |
| 12 | + case OriginalDataTypeEnum.INT16_BA: | |
| 13 | + case OriginalDataTypeEnum.UINT16_AB: | |
| 14 | + case OriginalDataTypeEnum.UINT16_BA: | |
| 15 | + case OriginalDataTypeEnum.BITS: | |
| 16 | + case OriginalDataTypeEnum.BOOLEAN: | |
| 17 | + return 1 | |
| 18 | + | |
| 19 | + case OriginalDataTypeEnum.INT32_AB_CD: | |
| 20 | + case OriginalDataTypeEnum.INT32_BA_DC: | |
| 21 | + case OriginalDataTypeEnum.INT32_CD_AB: | |
| 22 | + case OriginalDataTypeEnum.INT32_DC_BA: | |
| 23 | + case OriginalDataTypeEnum.UINT32_AB_CD: | |
| 24 | + case OriginalDataTypeEnum.UINT32_BA_DC: | |
| 25 | + case OriginalDataTypeEnum.UINT32_CD_AB: | |
| 26 | + case OriginalDataTypeEnum.UINT32_DC_BA: | |
| 27 | + return 2 | |
| 28 | + | |
| 29 | + case OriginalDataTypeEnum.FLOAT_AB_CD: | |
| 30 | + case OriginalDataTypeEnum.FLOAT_BA_DC: | |
| 31 | + case OriginalDataTypeEnum.FLOAT_CD_AB: | |
| 32 | + case OriginalDataTypeEnum.FLOAT_DC_BA: | |
| 33 | + return 2 | |
| 34 | + | |
| 35 | + case OriginalDataTypeEnum.DOUBLE: | |
| 36 | + return 4 | |
| 37 | + } | |
| 38 | +} | |
| 39 | + | |
| 40 | +export function useParseOriginalDataType(originalDataType: OriginalDataTypeEnum) { | |
| 41 | + const signedMatchRef = /^UN/ | |
| 42 | + | |
| 43 | + const splitArray = originalDataType.split('_') as [OriginalDataTypePrefixType] | |
| 44 | + | |
| 45 | + const [dataType] = splitArray | |
| 46 | + | |
| 47 | + const exchangeSortFlag = splitArray.slice(1).join('_') | |
| 48 | + | |
| 49 | + return { | |
| 50 | + registerCount: getRegisterCount(originalDataType), | |
| 51 | + unsigned: !signedMatchRef.test(originalDataType), | |
| 52 | + dataType, | |
| 53 | + exchangeSortFlag: exchangeSortFlag || null, | |
| 54 | + } | |
| 55 | +} | ... | ... |