Commit 846eb423c2b0c38f7ce168977f9749a40265cc13
Merge branch 'fix/DEFECT-1855' into 'main_dev'
fix: 修复组态属性下发tcp设备没有缩放因子 See merge request yunteng/thingskit-scada!205
Showing
9 changed files
with
264 additions
and
72 deletions
@@ -27,7 +27,7 @@ export interface GenModbusCommandType { | @@ -27,7 +27,7 @@ export interface GenModbusCommandType { | ||
27 | crc: string | 27 | crc: string |
28 | deviceCode: string | 28 | deviceCode: string |
29 | method: string | 29 | method: string |
30 | - registerAddress: string | 30 | + registerAddress: number |
31 | registerNumber?: number | 31 | registerNumber?: number |
32 | registerValues?: number[] | 32 | registerValues?: number[] |
33 | } | 33 | } |
1 | +import type { Specs, StructJSON } from '@/api/device/model' | ||
2 | +import type { NodeDataDataSourceJsonType } from '@/api/node/model' | ||
3 | +import { type FormSchema, useComponentRegister } from '@/components/Form' | ||
4 | +import { ComponentEnum } from '@/components/Form/src/enum' | ||
5 | +import { ThingsModelForm, validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm' | ||
6 | +import { getFormSchemas } from '@/core/Library/components/ThingsModelForm/config' | ||
7 | +import { CodeTypeEnum, DataTypeEnum, TransportTypeEnum } from '@/enums/datasource' | ||
8 | +import { TCPObjectModelActionTypeEnum } from '@/enums/objectModelEnum' | ||
9 | + | ||
10 | +useComponentRegister(ComponentEnum.THINGS_MODEL_FORM, ThingsModelForm) | ||
11 | + | ||
12 | +export enum FormFieldsEnum { | ||
13 | + ATTR_VALUE = 'attrValue', | ||
14 | + PASSWORD = 'password', | ||
15 | +} | ||
16 | + | ||
17 | +export interface AttributeDeliverModalOpenParamsType { | ||
18 | + title?: string | ||
19 | + operationPassword?: string | ||
20 | + operationPasswordEnable?: boolean | ||
21 | + dataSourceJson: NodeDataDataSourceJsonType | ||
22 | +} | ||
23 | + | ||
24 | +function getStructJsonFromDataSourceJson(dataSourceJson: NodeDataDataSourceJsonType): StructJSON { | ||
25 | + const { attrInfo } = dataSourceJson | ||
26 | + const { identifier, name, detail } = attrInfo || {} | ||
27 | + return { | ||
28 | + functionName: name, | ||
29 | + identifier, | ||
30 | + dataType: detail.dataType, | ||
31 | + } | ||
32 | +} | ||
33 | + | ||
34 | +function getTCPModbusSchemas({ structJson, required, actionType }: { structJson: StructJSON; required?: boolean; actionType: string }): FormSchema { | ||
35 | + const { dataType } = structJson | ||
36 | + const { specs, type } = dataType || {} | ||
37 | + const { valueRange, length = 10240 } = specs! as Specs | ||
38 | + const { max = Number.MAX_SAFE_INTEGER, min = Number.MIN_SAFE_INTEGER } = valueRange || {} | ||
39 | + | ||
40 | + function createInputNumber({ | ||
41 | + identifier, | ||
42 | + functionName, | ||
43 | + }: StructJSON): FormSchema { | ||
44 | + return { | ||
45 | + field: identifier, | ||
46 | + label: functionName, | ||
47 | + component: ComponentEnum.INPUT_NUMBER, | ||
48 | + required, | ||
49 | + componentProps: { | ||
50 | + max: actionType === TCPObjectModelActionTypeEnum.BOOL ? 1 : max, | ||
51 | + min: actionType === TCPObjectModelActionTypeEnum.BOOL ? 0 : min, | ||
52 | + precision: actionType === TCPObjectModelActionTypeEnum.BOOL ? 0 : 2, | ||
53 | + }, | ||
54 | + } | ||
55 | + } | ||
56 | + | ||
57 | + const createInput = ({ identifier, functionName }: StructJSON): FormSchema => { | ||
58 | + return { | ||
59 | + field: identifier, | ||
60 | + label: functionName, | ||
61 | + component: ComponentEnum.INPUT, | ||
62 | + rules: [ | ||
63 | + { | ||
64 | + required, | ||
65 | + message: `${functionName}是必填项`, | ||
66 | + }, | ||
67 | + { | ||
68 | + validator: validateTCPCustomCommand, | ||
69 | + }, | ||
70 | + ], | ||
71 | + componentProps: { | ||
72 | + maxLength: length, | ||
73 | + }, | ||
74 | + } as FormSchema | ||
75 | + } | ||
76 | + | ||
77 | + return type === DataTypeEnum.STRING ? createInput(structJson) : createInputNumber(structJson) | ||
78 | +} | ||
79 | + | ||
80 | +export const createFormSchemas = ({ operationPassword, operationPasswordEnable, dataSourceJson }: AttributeDeliverModalOpenParamsType): FormSchema[] => { | ||
81 | + const schemas: FormSchema[] = [] | ||
82 | + | ||
83 | + const { deviceInfo } = dataSourceJson | ||
84 | + if (deviceInfo?.transportType === TransportTypeEnum.TCP && deviceInfo.codeType === CodeTypeEnum.MODBUS_RTU && dataSourceJson.deviceInfo?.codeType) { | ||
85 | + schemas.push(getTCPModbusSchemas({ required: true, structJson: getStructJsonFromDataSourceJson(dataSourceJson), actionType: dataSourceJson.deviceInfo?.codeType })) | ||
86 | + } | ||
87 | + else { | ||
88 | + const isStructType = dataSourceJson.attrInfo?.detail?.dataType?.type === DataTypeEnum.STRUCT | ||
89 | + schemas.push( | ||
90 | + ...getFormSchemas({ structJSON: isStructType ? dataSourceJson?.attrInfo?.detail?.dataType?.specs as StructJSON[] || [] : [getStructJsonFromDataSourceJson(dataSourceJson)], required: !isStructType }), | ||
91 | + ) | ||
92 | + } | ||
93 | + | ||
94 | + if (operationPassword && operationPasswordEnable) { | ||
95 | + schemas.unshift({ | ||
96 | + field: FormFieldsEnum.PASSWORD, | ||
97 | + label: '操作密码', | ||
98 | + component: ComponentEnum.INPUT_PAWSSWORD, | ||
99 | + required: true, | ||
100 | + rules: [ | ||
101 | + { | ||
102 | + validator(_rule, value) { | ||
103 | + if (value && value !== operationPassword) return Promise.reject(new Error('操作密码不正确')) | ||
104 | + return Promise.resolve() | ||
105 | + }, | ||
106 | + }, | ||
107 | + ], | ||
108 | + }) | ||
109 | + } | ||
110 | + | ||
111 | + return schemas | ||
112 | +} |
1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
2 | import { Modal } from 'ant-design-vue' | 2 | import { Modal } from 'ant-design-vue' |
3 | import { nextTick, ref, unref } from 'vue' | 3 | import { nextTick, ref, unref } from 'vue' |
4 | -import type { FormSchema } from '@/components/Form' | 4 | +import type { AttributeDeliverModalOpenParamsType } from './AttributeDeliverModal.config' |
5 | +import { createFormSchemas } from './AttributeDeliverModal.config' | ||
6 | +import { useGetModbusCommand } from './useGetModbusCommand' | ||
5 | import { BasicForm, useForm } from '@/components/Form' | 7 | import { BasicForm, useForm } from '@/components/Form' |
6 | -import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum' | 8 | +import { FormLayoutEnum } from '@/components/Form/src/enum' |
9 | +import type { NodeDataDataSourceJsonType } from '@/api/node/model' | ||
10 | +import { CodeTypeEnum, DataTypeEnum, TransportTypeEnum } from '@/enums/datasource' | ||
7 | 11 | ||
8 | const resolveFn = ref<Fn>() | 12 | const resolveFn = ref<Fn>() |
9 | 13 | ||
@@ -11,61 +15,50 @@ const visible = ref(false) | @@ -11,61 +15,50 @@ const visible = ref(false) | ||
11 | 15 | ||
12 | const password = ref() | 16 | const password = ref() |
13 | 17 | ||
18 | +const currentDataSourceJson = ref<NodeDataDataSourceJsonType>() | ||
19 | + | ||
14 | const [register, { getFieldsValue, resetFields, validate, setProps, clearValidate }] = useForm({ | 20 | const [register, { getFieldsValue, resetFields, validate, setProps, clearValidate }] = useForm({ |
15 | layout: FormLayoutEnum.VERTICAL, | 21 | layout: FormLayoutEnum.VERTICAL, |
16 | showActionButtonGroup: false, | 22 | showActionButtonGroup: false, |
17 | }) | 23 | }) |
18 | 24 | ||
19 | -enum FormFieldsEnum { | ||
20 | - ATTR_VALUE = 'attrValue', | ||
21 | - PASSWORD = 'password', | ||
22 | -} | ||
23 | - | ||
24 | -const createFormSchemas = (title?: string, password?: string, operationPasswordEnable?: boolean): FormSchema[] => { | ||
25 | - const schemas: FormSchema[] = [ | ||
26 | - { | ||
27 | - field: FormFieldsEnum.ATTR_VALUE, | ||
28 | - label: title || '属性值', | ||
29 | - component: ComponentEnum.INPUT, | ||
30 | - required: true, | ||
31 | - }, | ||
32 | - ] | ||
33 | - | ||
34 | - if (password && operationPasswordEnable) { | ||
35 | - schemas.unshift({ | ||
36 | - field: FormFieldsEnum.PASSWORD, | ||
37 | - label: '操作密码', | ||
38 | - component: ComponentEnum.INPUT_PAWSSWORD, | ||
39 | - required: true, | ||
40 | - rules: [ | ||
41 | - { | ||
42 | - validator(_rule, value) { | ||
43 | - if (value && value !== password) return Promise.reject(new Error('操作密码不正确')) | ||
44 | - return Promise.resolve() | ||
45 | - }, | ||
46 | - }, | ||
47 | - ], | ||
48 | - }) | ||
49 | - } | ||
50 | - | ||
51 | - return schemas | ||
52 | -} | ||
53 | - | ||
54 | -const open = async ({ title, operationPassword, operationPasswordEnable }: Partial<Record<'operationPassword' | 'title', string>> & { operationPasswordEnable: boolean }) => { | 25 | +const open = async ({ title, operationPassword, operationPasswordEnable, dataSourceJson }: AttributeDeliverModalOpenParamsType) => { |
55 | visible.value = true | 26 | visible.value = true |
56 | password.value = operationPassword | 27 | password.value = operationPassword |
28 | + currentDataSourceJson.value = dataSourceJson | ||
57 | return new Promise((resolve) => { | 29 | return new Promise((resolve) => { |
58 | resolveFn.value = resolve | 30 | resolveFn.value = resolve |
59 | nextTick(() => { | 31 | nextTick(() => { |
60 | - setProps({ schemas: createFormSchemas(title, operationPassword, operationPasswordEnable) }) | 32 | + setProps({ schemas: createFormSchemas({ title, operationPassword, operationPasswordEnable, dataSourceJson }) }) |
61 | }) | 33 | }) |
62 | }) | 34 | }) |
63 | } | 35 | } |
64 | 36 | ||
37 | +async function getResult() { | ||
38 | + const result = getFieldsValue() | ||
39 | + const isTCPModbusDevice = unref(currentDataSourceJson)?.deviceInfo?.transportType === TransportTypeEnum.TCP && unref(currentDataSourceJson)?.deviceInfo?.codeType === CodeTypeEnum.MODBUS_RTU | ||
40 | + const isStructJSON = unref(currentDataSourceJson)?.attrInfo?.detail?.dataType?.type === DataTypeEnum.STRUCT | ||
41 | + const attrKey = unref(currentDataSourceJson)!.attr | ||
42 | + if (!isTCPModbusDevice) { return isStructJSON ? result : result[attrKey] } | ||
43 | + else { | ||
44 | + const value = result[attrKey] | ||
45 | + const isString = unref(currentDataSourceJson)?.attrInfo?.detail?.dataType?.type === DataTypeEnum.STRING | ||
46 | + | ||
47 | + if (isString) return value | ||
48 | + | ||
49 | + const { getModbusCommand, validateCanGetCommand } = useGetModbusCommand() | ||
50 | + if (!validateCanGetCommand(unref(currentDataSourceJson)!.attrInfo.extensionDesc, unref(currentDataSourceJson)!.deviceInfo?.code).flag) return | ||
51 | + | ||
52 | + const res = await getModbusCommand(value as unknown as number, unref(currentDataSourceJson)!.attrInfo.extensionDesc!, unref(currentDataSourceJson)!.deviceInfo!.code!) | ||
53 | + return res | ||
54 | + } | ||
55 | +} | ||
56 | + | ||
65 | const handleOk = async () => { | 57 | const handleOk = async () => { |
66 | await validate() | 58 | await validate() |
67 | - const value = getFieldsValue() | ||
68 | - unref(resolveFn)?.(value[FormFieldsEnum.ATTR_VALUE]) | 59 | + const result = await getResult() |
60 | + if (!result) return | ||
61 | + unref(resolveFn)?.(result) | ||
69 | visible.value = false | 62 | visible.value = false |
70 | resetFields() | 63 | resetFields() |
71 | } | 64 | } |
@@ -80,17 +73,9 @@ defineExpose({ open }) | @@ -80,17 +73,9 @@ defineExpose({ open }) | ||
80 | 73 | ||
81 | <template> | 74 | <template> |
82 | <Modal | 75 | <Modal |
83 | - v-model:open="visible" title="属性下发" ok-text="确认" :width="400" cancel-text="取消" @cancel="handleCancel" @ok="handleOk" | 76 | + v-model:open="visible" title="属性下发" ok-text="确认" :width="400" cancel-text="取消" @cancel="handleCancel" |
77 | + @ok="handleOk" | ||
84 | > | 78 | > |
85 | - <!-- <section> | ||
86 | - <FormItem label="操作密码" :label-col="{ span: 24 }"> | ||
87 | - <Input v-model:value="value" placeholder="请输入下发值" /> | ||
88 | - </FormItem> | ||
89 | - <FormItem :label="fieldTitle" :label-col="{ span: 24 }"> | ||
90 | - <Input v-model:value="value" placeholder="请输入下发值" /> | ||
91 | - </FormItem> | ||
92 | - </section> --> | ||
93 | - | ||
94 | <BasicForm @register="register" /> | 79 | <BasicForm @register="register" /> |
95 | </Modal> | 80 | </Modal> |
96 | </template> | 81 | </template> |
1 | +import { SingleToHex, getArray } from './config' | ||
2 | +import { type GenModbusCommandType, genModbusCommand } from '@/api/device' | ||
3 | +import type { ExtensionDesc } from '@/api/device/model' | ||
4 | +import { TCPObjectModelActionTypeEnum } from '@/enums/objectModelEnum' | ||
5 | +import { useMessage } from '@/hooks/web/useMessage' | ||
6 | + | ||
7 | +const getFloatPart = (number: string | number) => { | ||
8 | + const isLessZero = Number(number) < 0 | ||
9 | + number = number.toString() | ||
10 | + const floatPartStartIndex = number.indexOf('.') | ||
11 | + const value = ~floatPartStartIndex | ||
12 | + ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}` | ||
13 | + : '0' | ||
14 | + return Number(value) | ||
15 | +} | ||
16 | + | ||
17 | +const REGISTER_MAX_VALUE = Number(0xffff) | ||
18 | + | ||
19 | +export function useGetModbusCommand() { | ||
20 | + const { createMessage } = useMessage() | ||
21 | + | ||
22 | + const getModbusCommand = async (value: number, extensionDesc: ExtensionDesc, deviceAddressCode: string) => { | ||
23 | + const { registerAddress, actionType, zoomFactor } = extensionDesc as Required<ExtensionDesc> | ||
24 | + const params: GenModbusCommandType = { | ||
25 | + crc: 'CRC_16_LOWER', | ||
26 | + registerNumber: 1, | ||
27 | + deviceCode: deviceAddressCode, | ||
28 | + registerAddress, | ||
29 | + method: actionType, | ||
30 | + registerValues: [value], | ||
31 | + } | ||
32 | + | ||
33 | + if (actionType === TCPObjectModelActionTypeEnum.INT) { | ||
34 | + const newValue | ||
35 | + = Math.trunc(value) * zoomFactor | ||
36 | + + getFloatPart(value) * zoomFactor | ||
37 | + | ||
38 | + if (newValue % 1 !== 0) { | ||
39 | + createMessage.warning(`属性下发类型必须是整数,缩放因子为${zoomFactor}`) | ||
40 | + return | ||
41 | + } | ||
42 | + | ||
43 | + if (value * zoomFactor > REGISTER_MAX_VALUE) { | ||
44 | + createMessage.warning(`属性下发值不能超过${REGISTER_MAX_VALUE},缩放因子是${zoomFactor}`) | ||
45 | + return | ||
46 | + } | ||
47 | + } | ||
48 | + else if (actionType === TCPObjectModelActionTypeEnum.DOUBLE) { | ||
49 | + const regex = /^-?\d+(\.\d{0,2})?$/ | ||
50 | + const values | ||
51 | + = Math.trunc(value) * zoomFactor | ||
52 | + + getFloatPart(value) * zoomFactor | ||
53 | + | ||
54 | + if (!regex.test(values.toString())) { | ||
55 | + createMessage.warning(`属性下发值精确到两位小数,缩放因子是${zoomFactor}`) | ||
56 | + return | ||
57 | + } | ||
58 | + | ||
59 | + const newValue | ||
60 | + = values === 0 ? [0, 0] : getArray(SingleToHex(values)) | ||
61 | + params.registerValues = newValue | ||
62 | + params.registerNumber = 2 | ||
63 | + } | ||
64 | + | ||
65 | + return await genModbusCommand(params) | ||
66 | + } | ||
67 | + | ||
68 | + /** | ||
69 | + * | ||
70 | + * @param extensionDesc 物模型拓展描述符 | ||
71 | + * @param deviceAddressCode 设备地址码 | ||
72 | + */ | ||
73 | + const validateCanGetCommand = (extensionDesc?: ExtensionDesc, deviceAddressCode?: string, createValidateMessage = true) => { | ||
74 | + const result = { flag: true, message: '' } | ||
75 | + if (!extensionDesc) { | ||
76 | + result.flag = false | ||
77 | + result.message = '当前物模型扩展描述没有填写' | ||
78 | + } | ||
79 | + | ||
80 | + if (!deviceAddressCode) { | ||
81 | + result.flag = false | ||
82 | + result.message = '当前设备未绑定设备地址码' | ||
83 | + } | ||
84 | + | ||
85 | + if (result.message && createValidateMessage) | ||
86 | + createMessage.warning(result.message) | ||
87 | + | ||
88 | + return result | ||
89 | + } | ||
90 | + | ||
91 | + return { | ||
92 | + getModbusCommand, | ||
93 | + validateCanGetCommand, | ||
94 | + } | ||
95 | +} |
@@ -9,7 +9,7 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType | @@ -9,7 +9,7 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType | ||
9 | functionName, | 9 | functionName, |
10 | dataType, | 10 | dataType, |
11 | }: StructJSON): FormSchema => { | 11 | }: StructJSON): FormSchema => { |
12 | - const { specs } = dataType || {} | 12 | + const { specs, type } = dataType || {} |
13 | const { valueRange } = specs! as Specs | 13 | const { valueRange } = specs! as Specs |
14 | const { max = Number.MAX_SAFE_INTEGER, min = Number.MIN_SAFE_INTEGER } = valueRange || {} | 14 | const { max = Number.MAX_SAFE_INTEGER, min = Number.MIN_SAFE_INTEGER } = valueRange || {} |
15 | return { | 15 | return { |
@@ -21,22 +21,11 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType | @@ -21,22 +21,11 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType | ||
21 | required, | 21 | required, |
22 | message: `${functionName}是必填项`, | 22 | message: `${functionName}是必填项`, |
23 | }, | 23 | }, |
24 | - { | ||
25 | - type: 'number', | ||
26 | - trigger: 'change', | ||
27 | - validator: (_rule, value) => { | ||
28 | - const reg = /^[0-9]*$/ | ||
29 | - if (!reg.test(value)) return Promise.reject(new Error(`${functionName}不是一个有效的数字`)) | ||
30 | - if (value < min || value > max) | ||
31 | - return Promise.reject(new Error(`${functionName}取值范围在${min}~${max}之间`)) | ||
32 | - | ||
33 | - return Promise.resolve(value) | ||
34 | - }, | ||
35 | - }, | ||
36 | ], | 24 | ], |
37 | componentProps: { | 25 | componentProps: { |
38 | max, | 26 | max, |
39 | min, | 27 | min, |
28 | + precision: type === DataTypeEnum.NUMBER_INT ? 0 : 2, | ||
40 | }, | 29 | }, |
41 | } as FormSchema | 30 | } as FormSchema |
42 | } | 31 | } |
@@ -142,7 +131,6 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType | @@ -142,7 +131,6 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType | ||
142 | } | 131 | } |
143 | 132 | ||
144 | const schemas: FormSchema[] = [] | 133 | const schemas: FormSchema[] = [] |
145 | - | ||
146 | for (const item of structJson) { | 134 | for (const item of structJson) { |
147 | const { dataType } = item | 135 | const { dataType } = item |
148 | const { type } = dataType || {} | 136 | const { type } = dataType || {} |
1 | +import type { ValidatorRule } from 'ant-design-vue/lib/form/interface' | ||
2 | + | ||
1 | export { default as ThingsModelForm } from './index.vue' | 3 | export { default as ThingsModelForm } from './index.vue' |
4 | + | ||
5 | +export const validateTCPCustomCommand: ValidatorRule['validator'] = (_rule, value) => { | ||
6 | + const reg = /^[\s0-9a-fA-F]+$/ | ||
7 | + if (reg.test(value)) return Promise.resolve() | ||
8 | + return Promise.reject(new Error('请输入ASCII或HEX服务命令(0~9/A~F)')) | ||
9 | +} |
@@ -22,10 +22,6 @@ const props = withDefaults(defineProps<{ | @@ -22,10 +22,6 @@ const props = withDefaults(defineProps<{ | ||
22 | 22 | ||
23 | const thingsModelFormListElMap = reactive<Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }>>({}) | 23 | const thingsModelFormListElMap = reactive<Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }>>({}) |
24 | 24 | ||
25 | -// const getLabelWidth = () => { | ||
26 | -// return Math.max(...((props.inputData || [])?.map(item => item?.functionName?.length))) * 12 | ||
27 | -// } | ||
28 | - | ||
29 | const [register, formActionType] = useForm({ | 25 | const [register, formActionType] = useForm({ |
30 | schemas: getFormSchemas({ structJSON: props.inputData || [], required: props.required, transportType: props.transportType }), | 26 | schemas: getFormSchemas({ structJSON: props.inputData || [], required: props.required, transportType: props.transportType }), |
31 | showActionButtonGroup: false, | 27 | showActionButtonGroup: false, |
@@ -118,9 +118,9 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N | @@ -118,9 +118,9 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N | ||
118 | else { | 118 | else { |
119 | const instance = h(AttributeDeliverModal) | 119 | const instance = h(AttributeDeliverModal) |
120 | render(instance, document.body) | 120 | render(instance, document.body) |
121 | - const value = await (instance.component?.exposed as InstanceType<typeof AttributeDeliverModal>)?.open({ title: `${alias || deviceName}-${attrInfo.name}`, operationPassword, operationPasswordEnable }) as string | ||
122 | - | 121 | + const value = await (instance.component?.exposed as InstanceType<typeof AttributeDeliverModal>)?.open({ title: `${alias || deviceName}-${attrInfo.name}`, operationPassword, operationPasswordEnable, dataSourceJson }) as string |
123 | command.params = transportType === TransportTypeEnum.TCP ? value : { [attr]: value } | 122 | command.params = transportType === TransportTypeEnum.TCP ? value : { [attr]: value } |
123 | + if (!command.params) return | ||
124 | await doCommandDelivery({ way, command, deviceId }) | 124 | await doCommandDelivery({ way, command, deviceId }) |
125 | createMessage.success('命令下发成功') | 125 | createMessage.success('命令下发成功') |
126 | } | 126 | } |