Commit 341ec85140d2f80d497a3f7f513c1fc33173ac21
Merge branch 'perf/command-delivery' into 'main_dev'
perf: 优化命令下发&&物模型枚举与结构体回显 See merge request yunteng/thingskit-scada!211
Showing
32 changed files
with
935 additions
and
763 deletions
1 | -import type { DeviceActiveType, DeviceAttributeItemType, DeviceItemType, DeviceProfileItemType, OrganizationItemType, RpcCommandType, SendValue, ThingsModel, ThingsModelItemType } from './model' | 1 | +import type { DeviceActiveType, DeviceAttributeItemType, DeviceItemType, DeviceProfileItemType, OrganizationItemType, ProductsDetailWithThingsModelType, RpcCommandType, SendValue, ThingsModel, ThingsModelItemType } from './model' |
2 | import { CommandWayEnum } from '@/enums/commandEnum' | 2 | import { CommandWayEnum } from '@/enums/commandEnum' |
3 | import type { DeviceTypeEnum } from '@/enums/datasource' | 3 | import type { DeviceTypeEnum } from '@/enums/datasource' |
4 | +import { FunctionTypeEnum } from '@/enums/datasource' | ||
4 | import { isShareMode } from '@/utils/env' | 5 | import { isShareMode } from '@/utils/env' |
5 | import { defHttp } from '@/utils/http' | 6 | import { defHttp } from '@/utils/http' |
6 | 7 | ||
@@ -21,6 +22,8 @@ enum Api { | @@ -21,6 +22,8 @@ enum Api { | ||
21 | GET_DEVICE_DETAIL = '/device/', // 获取设备详情 | 22 | GET_DEVICE_DETAIL = '/device/', // 获取设备详情 |
22 | 23 | ||
23 | GET_LIST_BY_CONFIGURATION_ID = '/configuration/center/getListByConfigurationId', | 24 | GET_LIST_BY_CONFIGURATION_ID = '/configuration/center/getListByConfigurationId', |
25 | + | ||
26 | + GET_PRODUCTS_DETAIL_WITH_THINGS_MODEL = '/things_model/batch/get_tsl', | ||
24 | } | 27 | } |
25 | 28 | ||
26 | export interface GenModbusCommandType { | 29 | export interface GenModbusCommandType { |
@@ -139,3 +142,13 @@ export const getListByConfigurationId = (configurationId: string) => { | @@ -139,3 +142,13 @@ export const getListByConfigurationId = (configurationId: string) => { | ||
139 | url: `${Api.GET_LIST_BY_CONFIGURATION_ID}?configurationId=${configurationId}`, | 142 | url: `${Api.GET_LIST_BY_CONFIGURATION_ID}?configurationId=${configurationId}`, |
140 | }) | 143 | }) |
141 | } | 144 | } |
145 | + | ||
146 | +export const getProductsDetailWithThingsModel = ({ deviceProfileIds, functionTypeEnum }: { deviceProfileIds: string[]; functionTypeEnum?: FunctionTypeEnum | 'all' }) => { | ||
147 | + return defHttp.post<ProductsDetailWithThingsModelType[]>({ | ||
148 | + url: Api.GET_PRODUCTS_DETAIL_WITH_THINGS_MODEL, | ||
149 | + data: { | ||
150 | + deviceProfileIds, | ||
151 | + functionTypeEnum: functionTypeEnum || FunctionTypeEnum.PROPERTIES, | ||
152 | + }, | ||
153 | + }) | ||
154 | +} |
1 | -import type { DataTypeEnum, FunctionType, TransportTypeEnum } from '@/enums/datasource' | 1 | +import type { DataTypeEnum, FunctionTypeEnum, TransportTypeEnum } from '@/enums/datasource' |
2 | 2 | ||
3 | export interface DeviceProfileItemType { | 3 | export interface DeviceProfileItemType { |
4 | id: string | 4 | id: string |
@@ -27,6 +27,7 @@ export interface ProfileData { | @@ -27,6 +27,7 @@ export interface ProfileData { | ||
27 | interface AdditionalInfo { | 27 | interface AdditionalInfo { |
28 | cmdType: string | 28 | cmdType: string |
29 | } | 29 | } |
30 | + | ||
30 | export interface SendValue { | 31 | export interface SendValue { |
31 | additionalInfo: AdditionalInfo | 32 | additionalInfo: AdditionalInfo |
32 | method: string | 33 | method: string |
@@ -57,7 +58,7 @@ export interface StructJSON { | @@ -57,7 +58,7 @@ export interface StructJSON { | ||
57 | } | 58 | } |
58 | 59 | ||
59 | export interface ThingsModel { | 60 | export interface ThingsModel { |
60 | - functionType: FunctionType | 61 | + functionType: FunctionTypeEnum |
61 | functionName: string | 62 | functionName: string |
62 | identifier: string | 63 | identifier: string |
63 | callType: any | 64 | callType: any |
@@ -71,7 +72,7 @@ export interface ThingsModel { | @@ -71,7 +72,7 @@ export interface ThingsModel { | ||
71 | } | 72 | } |
72 | 73 | ||
73 | export interface FunctionJson { | 74 | export interface FunctionJson { |
74 | - dataType: DataType | DataType[] | 75 | + dataType: DataType |
75 | inputData?: StructJSON[] | 76 | inputData?: StructJSON[] |
76 | outputData?: StructJSON[] | 77 | outputData?: StructJSON[] |
77 | serviceCommand?: string | 78 | serviceCommand?: string |
@@ -160,7 +161,7 @@ export interface Specs { | @@ -160,7 +161,7 @@ export interface Specs { | ||
160 | min: string | 161 | min: string |
161 | max: string | 162 | max: string |
162 | name?: string | 163 | name?: string |
163 | - value?: string | 164 | + value?: any |
164 | dataType?: DataTypeEnum | 165 | dataType?: DataTypeEnum |
165 | } | 166 | } |
166 | 167 | ||
@@ -193,3 +194,23 @@ export interface DeviceActiveType { | @@ -193,3 +194,23 @@ export interface DeviceActiveType { | ||
193 | lastUpdateTs: number | 194 | lastUpdateTs: number |
194 | value: boolean | 195 | value: boolean |
195 | } | 196 | } |
197 | + | ||
198 | +export interface ProductsDetailWithThingsModelType { | ||
199 | + id: string | ||
200 | + name: string | ||
201 | + transportType: string | ||
202 | + deviceType: string | ||
203 | + tsl: Tsl[] | ||
204 | +} | ||
205 | + | ||
206 | +export interface Tsl { | ||
207 | + functionName: string | ||
208 | + identifier: string | ||
209 | + accessMode: string | ||
210 | + specs: { | ||
211 | + dataType: DataType | ||
212 | + } | ||
213 | + extensionDesc?: ExtensionDesc | ||
214 | + inputData?: StructJSON[] | ||
215 | + outputData?: StructJSON[] | ||
216 | +} |
1 | import type { ThingsModelItemType } from '@/api/device/model' | 1 | import type { ThingsModelItemType } from '@/api/device/model' |
2 | import type { ImageSelectorDataType } from '@/core/Library/components/ImageSelector' | 2 | import type { ImageSelectorDataType } from '@/core/Library/components/ImageSelector' |
3 | import type { CommandWayEnum } from '@/enums/commandEnum' | 3 | import type { CommandWayEnum } from '@/enums/commandEnum' |
4 | -import type { ActRangListItemTypeEnum, ActTypeEnum, AggregateTypeEnum, CommandDeliveryWayEnum, DeviceTypeEnum, EventActionTypeEnum, EventTypeEnum, SocketSubscriberEnum, TransportTypeEnum } from '@/enums/datasource' | 4 | +import type { ActRangListItemTypeEnum, ActTypeEnum, AggregateTypeEnum, CommandDeliveryWayEnum, DeviceTypeEnum, EventActionTypeEnum, EventTypeEnum, SocketSubscriberEnum } from '@/enums/datasource' |
5 | 5 | ||
6 | export enum DeleteNodeDataTypeEnum { | 6 | export enum DeleteNodeDataTypeEnum { |
7 | DATASOURCE = 'DATASOURCE', | 7 | DATASOURCE = 'DATASOURCE', |
@@ -93,18 +93,13 @@ export interface NodeDataDataSourceJsonType { | @@ -93,18 +93,13 @@ export interface NodeDataDataSourceJsonType { | ||
93 | deviceProfileId: string | 93 | deviceProfileId: string |
94 | deviceProfileTemplateId?: string | 94 | deviceProfileTemplateId?: string |
95 | attr: string | 95 | attr: string |
96 | - attrInfo: ThingsModelItemType | ||
97 | - | ||
98 | - deviceType?: DeviceTypeEnum | ||
99 | - transportType?: TransportTypeEnum | ||
100 | 96 | ||
101 | enable: boolean | 97 | enable: boolean |
102 | chartOption?: ChartOptionType | 98 | chartOption?: ChartOptionType |
103 | videoOption?: VideoOptionType | 99 | videoOption?: VideoOptionType |
104 | alarmListOption?: AlarmListOptionType | 100 | alarmListOption?: AlarmListOptionType |
105 | - deviceInfo?: DeviceInfoType | ||
106 | - circularFlowMeterOption?: FlowMeterColorItemType[] // 圆形水球图数据暂定any | ||
107 | - rectFlowMeterOption?: FlowMeterColorItemType[] // 方形水球图颜色配置数据暂定any | 101 | + circularFlowMeterOption?: FlowMeterColorItemType[] |
102 | + rectFlowMeterOption?: FlowMeterColorItemType[] | ||
108 | 103 | ||
109 | } | 104 | } |
110 | 105 | ||
@@ -128,17 +123,12 @@ export interface DoubleClickEventDataType { | @@ -128,17 +123,12 @@ export interface DoubleClickEventDataType { | ||
128 | openPage?: string | 123 | openPage?: string |
129 | enable: boolean | 124 | enable: boolean |
130 | service?: string | 125 | service?: string |
126 | + | ||
131 | serviceCommand?: Recordable | 127 | serviceCommand?: Recordable |
132 | way?: CommandWayEnum | 128 | way?: CommandWayEnum |
133 | customCommand?: string | 129 | customCommand?: string |
134 | - deviceInfo?: DeviceInfoType | ||
135 | commandWay?: CommandDeliveryWayEnum | 130 | commandWay?: CommandDeliveryWayEnum |
136 | callType?: string | 131 | callType?: string |
137 | - operationPassword?: { | ||
138 | - label: string | ||
139 | - value: string | number | ||
140 | - checked: boolean | ||
141 | - } | ||
142 | } | 132 | } |
143 | 133 | ||
144 | export interface SingleClickEventDataType extends DoubleClickEventDataType { } | 134 | export interface SingleClickEventDataType extends DoubleClickEventDataType { } |
@@ -155,7 +145,7 @@ export interface NodeDataEventJsonType { | @@ -155,7 +145,7 @@ export interface NodeDataEventJsonType { | ||
155 | [EventTypeEnum.SINGLE]: SingleClickEventDataType | 145 | [EventTypeEnum.SINGLE]: SingleClickEventDataType |
156 | [EventTypeEnum.DOWN]: MouseDownEventDataType | 146 | [EventTypeEnum.DOWN]: MouseDownEventDataType |
157 | [EventTypeEnum.UP]: MouseUpEventDataType | 147 | [EventTypeEnum.UP]: MouseUpEventDataType |
158 | - [EventTypeEnum.OPERATION_PASSWORD]: OperationPasswordDataType | 148 | + [EventTypeEnum.OPERATION_PASSWORD]?: OperationPasswordDataType |
159 | } | 149 | } |
160 | 150 | ||
161 | export interface RangeItemType { | 151 | export interface RangeItemType { |
1 | -import type { Specs, StructJSON } from '@/api/device/model' | ||
2 | -import type { NodeDataDataSourceJsonType } from '@/api/node/model' | 1 | +import type { DeviceItemType, Specs, StructJSON, Tsl } from '@/api/device/model' |
2 | +import type { NodeDataDataSourceJsonType, OperationPasswordDataType, SingleClickEventDataType } from '@/api/node/model' | ||
3 | import { type FormSchema, useComponentRegister } from '@/components/Form' | 3 | import { type FormSchema, useComponentRegister } from '@/components/Form' |
4 | import { ComponentEnum } from '@/components/Form/src/enum' | 4 | import { ComponentEnum } from '@/components/Form/src/enum' |
5 | import { ThingsModelForm, validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm' | 5 | import { ThingsModelForm, validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm' |
@@ -15,32 +15,38 @@ export enum FormFieldsEnum { | @@ -15,32 +15,38 @@ export enum FormFieldsEnum { | ||
15 | } | 15 | } |
16 | 16 | ||
17 | export interface AttributeDeliverModalOpenParamsType { | 17 | export interface AttributeDeliverModalOpenParamsType { |
18 | - title?: string | ||
19 | - operationPassword?: string | ||
20 | - operationPasswordEnable?: boolean | 18 | + operationPasswordInfo?: OperationPasswordDataType |
21 | dataSourceJson: NodeDataDataSourceJsonType | 19 | dataSourceJson: NodeDataDataSourceJsonType |
20 | + eventBindData: SingleClickEventDataType | ||
22 | } | 21 | } |
23 | 22 | ||
24 | -function getStructJsonFromDataSourceJson(dataSourceJson: NodeDataDataSourceJsonType): StructJSON { | ||
25 | - const { attrInfo } = dataSourceJson | ||
26 | - const { identifier, name, detail } = attrInfo || {} | 23 | +export interface CreateFormSchemasParamsType { |
24 | + deviceInfo: DeviceItemType | ||
25 | + objectModelTsl: Tsl | ||
26 | + operationPasswordInfo?: OperationPasswordDataType | ||
27 | +} | ||
28 | + | ||
29 | +function getStructJsonFromDataSourceJson(objectModelTsl: Tsl): StructJSON { | ||
30 | + const { identifier, functionName, specs } = objectModelTsl || {} | ||
27 | return { | 31 | return { |
28 | - functionName: name, | 32 | + functionName, |
29 | identifier, | 33 | identifier, |
30 | - dataType: detail.dataType, | 34 | + dataType: specs.dataType, |
31 | } | 35 | } |
32 | } | 36 | } |
33 | 37 | ||
34 | -function getTCPModbusSchemas({ structJson, required, actionType }: { structJson: StructJSON; required?: boolean; actionType: string }): FormSchema { | ||
35 | - const { dataType } = structJson | 38 | +function getTCPModbusSchemas({ objectModelTsl, required }: { objectModelTsl: Tsl; required?: boolean }): FormSchema { |
39 | + const { specs: tslSpecs, extensionDesc } = objectModelTsl | ||
40 | + const { dataType } = tslSpecs | ||
36 | const { specs, type } = dataType || {} | 41 | const { specs, type } = dataType || {} |
37 | const { valueRange, length = 10240 } = specs! as Specs | 42 | const { valueRange, length = 10240 } = specs! as Specs |
38 | const { max = Number.MAX_SAFE_INTEGER, min = Number.MIN_SAFE_INTEGER } = valueRange || {} | 43 | const { max = Number.MAX_SAFE_INTEGER, min = Number.MIN_SAFE_INTEGER } = valueRange || {} |
44 | + const { actionType } = extensionDesc || {} | ||
39 | 45 | ||
40 | function createInputNumber({ | 46 | function createInputNumber({ |
41 | identifier, | 47 | identifier, |
42 | functionName, | 48 | functionName, |
43 | - }: StructJSON): FormSchema { | 49 | + }: Tsl): FormSchema { |
44 | return { | 50 | return { |
45 | field: identifier, | 51 | field: identifier, |
46 | label: functionName, | 52 | label: functionName, |
@@ -54,7 +60,7 @@ function getTCPModbusSchemas({ structJson, required, actionType }: { structJson: | @@ -54,7 +60,7 @@ function getTCPModbusSchemas({ structJson, required, actionType }: { structJson: | ||
54 | } | 60 | } |
55 | } | 61 | } |
56 | 62 | ||
57 | - const createInput = ({ identifier, functionName }: StructJSON): FormSchema => { | 63 | + const createInput = ({ identifier, functionName }: Tsl): FormSchema => { |
58 | return { | 64 | return { |
59 | field: identifier, | 65 | field: identifier, |
60 | label: functionName, | 66 | label: functionName, |
@@ -74,24 +80,23 @@ function getTCPModbusSchemas({ structJson, required, actionType }: { structJson: | @@ -74,24 +80,23 @@ function getTCPModbusSchemas({ structJson, required, actionType }: { structJson: | ||
74 | } as FormSchema | 80 | } as FormSchema |
75 | } | 81 | } |
76 | 82 | ||
77 | - return type === DataTypeEnum.STRING ? createInput(structJson) : createInputNumber(structJson) | 83 | + return type === DataTypeEnum.STRING ? createInput(objectModelTsl) : createInputNumber(objectModelTsl) |
78 | } | 84 | } |
79 | 85 | ||
80 | -export const createFormSchemas = ({ operationPassword, operationPasswordEnable, dataSourceJson }: AttributeDeliverModalOpenParamsType): FormSchema[] => { | 86 | +export const createFormSchemas = ({ operationPasswordInfo, objectModelTsl, deviceInfo }: CreateFormSchemasParamsType): FormSchema[] => { |
81 | const schemas: FormSchema[] = [] | 87 | const schemas: FormSchema[] = [] |
82 | 88 | ||
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 })) | 89 | + if (deviceInfo?.transportType === TransportTypeEnum.TCP && deviceInfo.codeType === CodeTypeEnum.MODBUS_RTU && deviceInfo.code) { |
90 | + schemas.push(getTCPModbusSchemas({ required: true, objectModelTsl })) | ||
86 | } | 91 | } |
87 | else { | 92 | else { |
88 | - const isStructType = dataSourceJson.attrInfo?.detail?.dataType?.type === DataTypeEnum.STRUCT | 93 | + const isStructType = objectModelTsl.specs.dataType.type === DataTypeEnum.STRUCT |
89 | schemas.push( | 94 | schemas.push( |
90 | - ...getFormSchemas({ structJSON: isStructType ? dataSourceJson?.attrInfo?.detail?.dataType?.specs as StructJSON[] || [] : [getStructJsonFromDataSourceJson(dataSourceJson)], required: !isStructType }), | 95 | + ...getFormSchemas({ structJSON: isStructType ? objectModelTsl.specs.dataType.specs as StructJSON[] || [] : [getStructJsonFromDataSourceJson(objectModelTsl)], required: !isStructType }), |
91 | ) | 96 | ) |
92 | } | 97 | } |
93 | 98 | ||
94 | - if (operationPassword && operationPasswordEnable) { | 99 | + if (operationPasswordInfo?.value && operationPasswordInfo.checked) { |
95 | schemas.unshift({ | 100 | schemas.unshift({ |
96 | field: FormFieldsEnum.PASSWORD, | 101 | field: FormFieldsEnum.PASSWORD, |
97 | label: '操作密码', | 102 | label: '操作密码', |
@@ -100,7 +105,7 @@ export const createFormSchemas = ({ operationPassword, operationPasswordEnable, | @@ -100,7 +105,7 @@ export const createFormSchemas = ({ operationPassword, operationPasswordEnable, | ||
100 | rules: [ | 105 | rules: [ |
101 | { | 106 | { |
102 | validator(_rule, value) { | 107 | validator(_rule, value) { |
103 | - if (value && value !== operationPassword) return Promise.reject(new Error('操作密码不正确')) | 108 | + if (value && value !== operationPasswordInfo.value) return Promise.reject(new Error('操作密码不正确')) |
104 | return Promise.resolve() | 109 | return Promise.resolve() |
105 | }, | 110 | }, |
106 | }, | 111 | }, |
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, toRaw, unref } from 'vue' |
4 | import type { AttributeDeliverModalOpenParamsType } from './AttributeDeliverModal.config' | 4 | import type { AttributeDeliverModalOpenParamsType } from './AttributeDeliverModal.config' |
5 | import { createFormSchemas } from './AttributeDeliverModal.config' | 5 | import { createFormSchemas } from './AttributeDeliverModal.config' |
6 | -import { useGetModbusCommand } from './useGetModbusCommand' | 6 | +import { useCommandDeliver } from './useCommadDeliver' |
7 | import { BasicForm, useForm } from '@/components/Form' | 7 | import { BasicForm, useForm } from '@/components/Form' |
8 | import { 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' | ||
11 | - | ||
12 | -const resolveFn = ref<Fn>() | 9 | +import { DataTypeEnum } from '@/enums/datasource' |
13 | 10 | ||
14 | const visible = ref(false) | 11 | const visible = ref(false) |
15 | 12 | ||
16 | -const password = ref() | ||
17 | - | ||
18 | -const currentDataSourceJson = ref<NodeDataDataSourceJsonType>() | ||
19 | - | ||
20 | const [register, { getFieldsValue, resetFields, validate, setProps, clearValidate }] = useForm({ | 13 | const [register, { getFieldsValue, resetFields, validate, setProps, clearValidate }] = useForm({ |
21 | layout: FormLayoutEnum.VERTICAL, | 14 | layout: FormLayoutEnum.VERTICAL, |
22 | showActionButtonGroup: false, | 15 | showActionButtonGroup: false, |
23 | }) | 16 | }) |
24 | 17 | ||
25 | -const open = async ({ title, operationPassword, operationPasswordEnable, dataSourceJson }: AttributeDeliverModalOpenParamsType) => { | 18 | +const { setup, getDeviceInfo, getObjectModelTsl, doCommandDeliver } = useCommandDeliver() |
19 | + | ||
20 | +const open = async ({ operationPasswordInfo, dataSourceJson, eventBindData }: AttributeDeliverModalOpenParamsType) => { | ||
26 | visible.value = true | 21 | visible.value = true |
27 | - password.value = operationPassword | ||
28 | - currentDataSourceJson.value = dataSourceJson | ||
29 | - return new Promise((resolve) => { | ||
30 | - resolveFn.value = resolve | ||
31 | - nextTick(() => { | ||
32 | - setProps({ schemas: createFormSchemas({ title, operationPassword, operationPasswordEnable, dataSourceJson }) }) | ||
33 | - }) | 22 | + const { deviceId, deviceProfileId, attr } = dataSourceJson |
23 | + const { way } = eventBindData | ||
24 | + await setup({ deviceId, deviceProfileId, attr, callType: way }) | ||
25 | + | ||
26 | + nextTick(() => { | ||
27 | + const schemas = createFormSchemas({ operationPasswordInfo, deviceInfo: toRaw(unref(getDeviceInfo)!), objectModelTsl: toRaw(unref(getObjectModelTsl)!) }) | ||
28 | + setProps({ schemas }) | ||
34 | }) | 29 | }) |
35 | } | 30 | } |
36 | 31 | ||
37 | async function getResult() { | 32 | async function getResult() { |
38 | const result = getFieldsValue() | 33 | 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 | - } | 34 | + const isStructJSON = unref(getObjectModelTsl)?.specs.dataType.type === DataTypeEnum.STRUCT |
35 | + const identifier = unref(getObjectModelTsl)!.identifier | ||
36 | + await doCommandDeliver(isStructJSON ? result : result[identifier]) | ||
55 | } | 37 | } |
56 | 38 | ||
57 | const handleOk = async () => { | 39 | const handleOk = async () => { |
58 | await validate() | 40 | await validate() |
59 | - const result = await getResult() | ||
60 | - if (!result) return | ||
61 | - unref(resolveFn)?.(result) | 41 | + await getResult() |
62 | visible.value = false | 42 | visible.value = false |
63 | resetFields() | 43 | resetFields() |
64 | } | 44 | } |
@@ -73,7 +53,7 @@ defineExpose({ open }) | @@ -73,7 +53,7 @@ defineExpose({ open }) | ||
73 | 53 | ||
74 | <template> | 54 | <template> |
75 | <Modal | 55 | <Modal |
76 | - v-model:open="visible" title="属性下发" ok-text="确认" :width="400" cancel-text="取消" @cancel="handleCancel" | 56 | + v-model:open="visible" :title="`属性下发 / ${getDeviceInfo?.alias || getDeviceInfo?.name}`" ok-text="确认" :width="400" cancel-text="取消" @cancel="handleCancel" |
77 | @ok="handleOk" | 57 | @ok="handleOk" |
78 | > | 58 | > |
79 | <BasicForm @register="register" /> | 59 | <BasicForm @register="register" /> |
src/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal/ConfirmModal.vue
deleted
100644 → 0
1 | -<script setup lang="ts"> | ||
2 | -import { Icon } from '@iconify/vue' | ||
3 | -import { Modal } from 'ant-design-vue' | ||
4 | -import { ref, unref } from 'vue' | ||
5 | - | ||
6 | -const visible = ref(false) | ||
7 | - | ||
8 | -const resolveFn = ref<Fn>() | ||
9 | - | ||
10 | -const rejectFn = ref<Fn>() | ||
11 | - | ||
12 | -const open = async () => { | ||
13 | - visible.value = true | ||
14 | - return new Promise((resolve, reject) => { | ||
15 | - resolveFn.value = resolve | ||
16 | - rejectFn.value = reject | ||
17 | - }) | ||
18 | -} | ||
19 | - | ||
20 | -const handleOk = () => { | ||
21 | - unref(resolveFn)?.() | ||
22 | - visible.value = false | ||
23 | -} | ||
24 | - | ||
25 | -const handleCancel = () => { | ||
26 | - unref(rejectFn)?.() | ||
27 | - visible.value = false | ||
28 | -} | ||
29 | - | ||
30 | -defineExpose({ open }) | ||
31 | -</script> | ||
32 | - | ||
33 | -<template> | ||
34 | - <Modal v-model:open="visible" :width="400" ok-text="确认" cancel-text="取消" @ok="handleOk" @cancel="handleCancel"> | ||
35 | - <template #title> | ||
36 | - <div class="flex"> | ||
37 | - <span><Icon icon="ant-design:exclamation-circle-filled" class="text-yellow-300 text-xl" /></span> | ||
38 | - <span class="font-bold ml-2">提示</span> | ||
39 | - </div> | ||
40 | - </template> | ||
41 | - <div class="ml-5"> | ||
42 | - 是否确认此操作? | ||
43 | - </div> | ||
44 | - </Modal> | ||
45 | -</template> |
src/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal/SwitchCommandDeliveryModal.vue
renamed from
src/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal/CommandDeliveryConfirmModal.vue
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 { computed, nextTick, ref, unref } from 'vue' |
4 | +import { Icon } from '@iconify/vue' | ||
5 | +import { useCommandDeliver } from './useCommadDeliver' | ||
4 | import { BasicForm, type FormSchema, useForm } from '@/components/Form' | 6 | import { BasicForm, type FormSchema, useForm } from '@/components/Form' |
5 | import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum' | 7 | import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum' |
8 | +import type { NodeDataDataSourceJsonType, OperationPasswordDataType, SingleClickEventDataType } from '@/api/node/model' | ||
6 | 9 | ||
7 | -const resolveFn = ref<Fn>() | 10 | +interface SwitchCommandDeliveryModalParamsType { |
11 | + operationPasswordInfo?: OperationPasswordDataType | ||
12 | + dataSourceJson: NodeDataDataSourceJsonType | ||
13 | + eventBindData: SingleClickEventDataType | ||
14 | + sendValue: any | ||
15 | +} | ||
8 | 16 | ||
9 | const visible = ref(false) | 17 | const visible = ref(false) |
10 | 18 | ||
19 | +const currentOperationPasswordInfo = ref<OperationPasswordDataType>() | ||
20 | + | ||
21 | +const currendSendValue = ref() | ||
22 | + | ||
11 | const [register, { resetFields, validate, setProps }] = useForm({ | 23 | const [register, { resetFields, validate, setProps }] = useForm({ |
12 | showActionButtonGroup: false, | 24 | showActionButtonGroup: false, |
13 | layout: FormLayoutEnum.VERTICAL, | 25 | layout: FormLayoutEnum.VERTICAL, |
@@ -18,7 +30,7 @@ const createFormSchemas = (password: string): FormSchema[] => { | @@ -18,7 +30,7 @@ const createFormSchemas = (password: string): FormSchema[] => { | ||
18 | { | 30 | { |
19 | field: 'password', | 31 | field: 'password', |
20 | component: ComponentEnum.INPUT_PAWSSWORD, | 32 | component: ComponentEnum.INPUT_PAWSSWORD, |
21 | - label: '', | 33 | + label: '操作密码', |
22 | required: true, | 34 | required: true, |
23 | rules: [{ | 35 | rules: [{ |
24 | validator(_rule, value) { | 36 | validator(_rule, value) { |
@@ -33,21 +45,27 @@ const createFormSchemas = (password: string): FormSchema[] => { | @@ -33,21 +45,27 @@ const createFormSchemas = (password: string): FormSchema[] => { | ||
33 | ] | 45 | ] |
34 | } | 46 | } |
35 | 47 | ||
36 | -const open = async (pwd: string) => { | ||
37 | - visible.value = true | ||
38 | - return new Promise((resolve) => { | ||
39 | - resolveFn.value = resolve | 48 | +const getHasEnableOperationPassword = computed(() => !!(unref(currentOperationPasswordInfo)?.checked && unref(currentOperationPasswordInfo)?.value)) |
49 | + | ||
50 | +const { setup, doCommandDeliver, getDeviceInfo } = useCommandDeliver() | ||
40 | 51 | ||
41 | - nextTick(() => { | ||
42 | - setProps({ schemas: createFormSchemas(pwd) }) | ||
43 | - }) | 52 | +const open = async ({ operationPasswordInfo, dataSourceJson, eventBindData, sendValue }: SwitchCommandDeliveryModalParamsType) => { |
53 | + visible.value = true | ||
54 | + 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 }) | ||
59 | + nextTick(() => { | ||
60 | + unref(getHasEnableOperationPassword) && setProps({ schemas: createFormSchemas(operationPasswordInfo!.value!) }) | ||
44 | }) | 61 | }) |
45 | } | 62 | } |
46 | 63 | ||
47 | const handlerOk = async () => { | 64 | const handlerOk = async () => { |
48 | - await validate() | ||
49 | - unref(resolveFn)?.(true) | ||
50 | - resetFields() | 65 | + if (unref(getHasEnableOperationPassword)) |
66 | + await validate() | ||
67 | + await doCommandDeliver(unref(currendSendValue)) | ||
68 | + if (unref(getHasEnableOperationPassword)) resetFields() | ||
51 | visible.value = false | 69 | visible.value = false |
52 | } | 70 | } |
53 | 71 | ||
@@ -56,10 +74,27 @@ defineExpose({ open }) | @@ -56,10 +74,27 @@ defineExpose({ open }) | ||
56 | 74 | ||
57 | <template> | 75 | <template> |
58 | <Modal | 76 | <Modal |
59 | - v-model:open="visible" :width="300" ok-text="确认" cancel-text="取消" | ||
60 | - title="操作密码" | 77 | + v-model:open="visible" |
78 | + :title="`属性下发 / ${getDeviceInfo?.alias || getDeviceInfo?.name}`" | ||
79 | + :width="300" | ||
80 | + ok-text="确认" | ||
81 | + cancel-text="取消" | ||
82 | + destroy-on-close | ||
61 | @ok="handlerOk" | 83 | @ok="handlerOk" |
62 | > | 84 | > |
63 | - <BasicForm @register="register" /> | 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> --> | ||
92 | + <BasicForm v-if="getHasEnableOperationPassword" @register="register" /> | ||
93 | + <div v-else class="flex items-center"> | ||
94 | + <Icon icon="ant-design:exclamation-circle-filled" class="text-yellow-300 text-xl" /> | ||
95 | + <span class="ml-2"> | ||
96 | + 是否确认此操作? | ||
97 | + </span> | ||
98 | + </div> | ||
64 | </Modal> | 99 | </Modal> |
65 | </template> | 100 | </template> |
1 | import { ComponentEnum } from '@/components/Form/src/enum' | 1 | import { ComponentEnum } from '@/components/Form/src/enum' |
2 | import type { FormSchema } from '@/components/Form' | 2 | import type { FormSchema } from '@/components/Form' |
3 | -import type { StructJSON } from '@/api/device/model' | 3 | +import type { Tsl } from '@/api/device/model' |
4 | +import type { OperationPasswordDataType } from '@/api/node/model' | ||
5 | +import { validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm' | ||
6 | +import { JSONEditorValidator } from '@/components/CodeEditor/src/JSONEditor' | ||
7 | +import { DataTypeEnum } from '@/enums/datasource' | ||
8 | + | ||
9 | +export enum FormFieldsEnum { | ||
10 | + CUSTOM_COMMAND = 'customCommand', | ||
11 | + OPERATION_PASSWORD = 'password', | ||
12 | + | ||
13 | + SERVICE_COMMAND = 'serviceCommand', | ||
14 | +} | ||
15 | + | ||
4 | export const formSchemas: FormSchema[] = [ | 16 | export const formSchemas: FormSchema[] = [ |
5 | { | 17 | { |
6 | field: 'sendValue', | 18 | field: 'sendValue', |
@@ -10,219 +22,122 @@ export const formSchemas: FormSchema[] = [ | @@ -10,219 +22,122 @@ export const formSchemas: FormSchema[] = [ | ||
10 | }, | 22 | }, |
11 | ] | 23 | ] |
12 | 24 | ||
13 | -const InsertString = (t: any, c: any, n: any) => { | ||
14 | - const r: string | number[] = [] | ||
15 | - | ||
16 | - for (let i = 0; i * 2 < t.length; i++) | ||
17 | - r.push(t.substr(i * 2, n)) | ||
18 | - | ||
19 | - return r.join(c) | ||
20 | -} | ||
21 | -const FillString = (t: any, c: any, n: any, b: any) => { | ||
22 | - if (t === '' || c.length !== 1 || n <= t.length) | ||
23 | - return t | ||
24 | - | ||
25 | - const l = t.length | ||
26 | - | ||
27 | - for (let i = 0; i < n - l; i++) { | ||
28 | - if (b === true) | ||
29 | - t = c + t | 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) | ||
30 | 29 | ||
31 | - else | ||
32 | - t += c | 30 | + const _formSchema: FormSchema = { |
31 | + field: identifier, | ||
32 | + label: functionName, | ||
33 | + component: ComponentEnum.INPUT_NUMBER, | ||
33 | } | 34 | } |
34 | - return t | ||
35 | -} | ||
36 | -const SingleToHex = (t: any) => { | ||
37 | - if (t === '') | ||
38 | - return '' | ||
39 | - | ||
40 | - t = parseFloat(t) | ||
41 | - | ||
42 | - if (isNaN(t) === true) | ||
43 | - return 'Error' | ||
44 | - | ||
45 | - if (t === 0) | ||
46 | - return '00000000' | ||
47 | - | ||
48 | - let s, e, m | ||
49 | 35 | ||
50 | - if (t > 0) { | ||
51 | - s = 0 | 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>) | ||
52 | } | 46 | } |
53 | - else { | ||
54 | - s = 1 | ||
55 | 47 | ||
56 | - t = 0 - t | 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>) | ||
57 | } | 58 | } |
58 | - m = t.toString(2) | ||
59 | 59 | ||
60 | - if (m >= 1) { | ||
61 | - if (m.indexOf('.') === -1) | ||
62 | - m = `${m}.0` | ||
63 | - | ||
64 | - e = m.indexOf('.') - 1 | ||
65 | - } | ||
66 | - else { | ||
67 | - e = 1 - m.indexOf('1') | 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>) | ||
68 | } | 69 | } |
69 | - if (e >= 0) | ||
70 | - m = m.replace('.', '') | ||
71 | - | ||
72 | - else | ||
73 | - m = m.substring(m.indexOf('1')) | ||
74 | - | ||
75 | - if (m.length > 24) | ||
76 | - m = m.substr(0, 24) | ||
77 | - | ||
78 | - else | ||
79 | - m = FillString(m, '0', 24, false) | ||
80 | - | ||
81 | - m = m.substring(1) | ||
82 | - | ||
83 | - e = (e + 127).toString(2) | ||
84 | - | ||
85 | - e = FillString(e, '0', 8, true) | ||
86 | - | ||
87 | - let r = parseInt(s + e + m, 2).toString(16) | ||
88 | - | ||
89 | - r = FillString(r, '0', 8, true) | ||
90 | 70 | ||
91 | - return InsertString(r, ' ', 2).toUpperCase() | ||
92 | -} | ||
93 | - | ||
94 | -const FormatHex = (t: any, n: any, ie: any) => { | ||
95 | - const r: string[] = [] | ||
96 | - | ||
97 | - let s = '' | ||
98 | - | ||
99 | - let c = 0 | ||
100 | - | ||
101 | - for (let i = 0; i < t.length; i++) { | ||
102 | - if (t.charAt(i) !== ' ') { | ||
103 | - s += t.charAt(i) | ||
104 | - | ||
105 | - c += 1 | ||
106 | - | ||
107 | - if (c === n) { | ||
108 | - r.push(s) | ||
109 | - | ||
110 | - s = '' | ||
111 | - | ||
112 | - c = 0 | ||
113 | - } | ||
114 | - } | ||
115 | - if (ie === false) { | ||
116 | - if (i === t.length - 1 && s !== '') | ||
117 | - r.push(s) | ||
118 | - } | 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>) | ||
119 | } | 80 | } |
120 | - return r.join('\n') | ||
121 | -} | ||
122 | -const FormatHexBatch = (t: any, n: any, ie: any) => { | ||
123 | - const a = t.split('\n') | ||
124 | - | ||
125 | - const r: string[] = [] | ||
126 | - | ||
127 | - for (let i = 0; i < a.length; i++) | ||
128 | - r[i] = FormatHex(a[i], n, ie) | ||
129 | 81 | ||
130 | - return r.join('\n') | ||
131 | -} | ||
132 | -const SingleToHexBatch = (t: any) => { | ||
133 | - const a = t.split('\n') | ||
134 | - | ||
135 | - const r: string[] = [] | 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 | + } | ||
136 | 88 | ||
137 | - for (let i = 0; i < a.length; i++) | ||
138 | - r[i] = SingleToHex(a[i]) | 89 | + const schemas = schemasMap[type as DataTypeEnum]?.() |
139 | 90 | ||
140 | - return r.join('\r\n') | 91 | + return [schemas!] |
141 | } | 92 | } |
142 | 93 | ||
143 | -const formSchemasConfig = (schemas: StructJSON, actionType: any): FormSchema[] => { | ||
144 | - const { identifier, functionName } = schemas | ||
145 | - if (actionType === '06') { | ||
146 | - return [ | ||
147 | - { | ||
148 | - field: identifier, | ||
149 | - label: functionName, | ||
150 | - component: ComponentEnum.INPUT_NUMBER, | ||
151 | - rules: [{ required: true, message: '请输入正数' }], | ||
152 | - componentProps: { | ||
153 | - min: 0, | ||
154 | - formatter: (e: any) => { | ||
155 | - const value = `${e}`.replace('-', '').replace(/^(-)*(\d+)\.(\d\d).*$/, '$1$2.$3') | ||
156 | - return value | 94 | +export const getOperationPasswordSchemas = (operationPasswordInfo: OperationPasswordDataType): FormSchema[] => { |
95 | + return [ | ||
96 | + { | ||
97 | + field: FormFieldsEnum.OPERATION_PASSWORD, | ||
98 | + label: '操作密码', | ||
99 | + component: ComponentEnum.INPUT_PAWSSWORD, | ||
100 | + required: true, | ||
101 | + rules: [ | ||
102 | + { | ||
103 | + validator(_rule, value: string) { | ||
104 | + if (value !== operationPasswordInfo.value) | ||
105 | + return Promise.reject(Error('操作密码不正确')) | ||
106 | + return Promise.resolve() | ||
157 | }, | 107 | }, |
158 | - placeholder: '请输入正数', | ||
159 | }, | 108 | }, |
109 | + ], | ||
110 | + componentProps: { | ||
111 | + placeholder: '请输入操作密码', | ||
112 | + max: 120, | ||
113 | + autocomplete: 'cc-number', | ||
160 | }, | 114 | }, |
161 | - ] | ||
162 | - } | ||
163 | - else if (actionType === '05') { | ||
164 | - return [ | ||
165 | - { | ||
166 | - field: identifier, | ||
167 | - label: functionName, | ||
168 | - component: ComponentEnum.INPUT_NUMBER, | ||
169 | - rules: [{ required: true, message: '请输入值' }], | ||
170 | - componentProps: { | ||
171 | - min: 0, | ||
172 | - max: 1, | ||
173 | - precision: 0, | ||
174 | - placeholder: '请输入0或1', | ||
175 | - }, | ||
176 | - }, | ||
177 | - ] | ||
178 | - } | ||
179 | - else { | ||
180 | - return [ | ||
181 | - { | ||
182 | - field: identifier, | ||
183 | - label: functionName, | ||
184 | - component: ComponentEnum.INPUT_NUMBER, | ||
185 | - rules: [{ required: true, message: '请输入值' }], | ||
186 | - componentProps: { | ||
187 | - placeholder: '请输入数字', | ||
188 | - formatter: (e: any) => | ||
189 | - `${e}`.replace(/\B(?=(\d{3})+(?!\d))/g, '').replace(/^(-)*(\d+)\.(\d\d).*$/, '$1$2.$3'), | ||
190 | - }, | ||
191 | - }, | ||
192 | - ] | ||
193 | - } | 115 | + }, |
116 | + ] | ||
194 | } | 117 | } |
195 | 118 | ||
196 | -const getArray = (values: any) => { | ||
197 | - const str = values.replace(/\s+/g, '') | ||
198 | - const array: any = [] | ||
199 | - | ||
200 | - for (let i = 0; i < str.length; i += 4) { | ||
201 | - const chunk = parseInt(str.substring(i, i + 4), 16) | ||
202 | - array.push(chunk) | ||
203 | - } | ||
204 | - return array | 119 | +export const getTcpCustomCommandSchemas = (): FormSchema[] => { |
120 | + return [ | ||
121 | + { | ||
122 | + field: FormFieldsEnum.CUSTOM_COMMAND, | ||
123 | + label: '自定义命令', | ||
124 | + component: ComponentEnum.INPUT, | ||
125 | + required: true, | ||
126 | + rules: [{ validator: validateTCPCustomCommand }], | ||
127 | + }, | ||
128 | + ] | ||
205 | } | 129 | } |
206 | 130 | ||
207 | -// 获取小数 | ||
208 | -const getFloatPart = (number: string | number) => { | ||
209 | - const isLessZero = Number(number) < 0 | ||
210 | - number = number.toString() | ||
211 | - const floatPartStartIndex = number.indexOf('.') | ||
212 | - const value = ~floatPartStartIndex | ||
213 | - ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}` | ||
214 | - : '0' | ||
215 | - return Number(value) | 131 | +export const getJSONCommandSchemas = (): FormSchema[] => { |
132 | + return [ | ||
133 | + { | ||
134 | + field: FormFieldsEnum.CUSTOM_COMMAND, | ||
135 | + label: '自定义命令', | ||
136 | + component: ComponentEnum.JSON_EDITOR, | ||
137 | + required: true, | ||
138 | + changeEvent: 'update:value', | ||
139 | + rules: JSONEditorValidator(), | ||
140 | + }, | ||
141 | + ] | ||
216 | } | 142 | } |
217 | 143 | ||
218 | -export { | ||
219 | - InsertString, | ||
220 | - FillString, | ||
221 | - SingleToHex, | ||
222 | - FormatHex, | ||
223 | - FormatHexBatch, | ||
224 | - SingleToHexBatch, | ||
225 | - formSchemasConfig, | ||
226 | - getArray, | ||
227 | - getFloatPart, | ||
228 | -} |
1 | export { default as CommandDeliveryModal } from './index.vue' | 1 | export { default as CommandDeliveryModal } from './index.vue' |
2 | -export { default as CommandDeliveryConfirmModal } from './CommandDeliveryConfirmModal.vue' | ||
3 | -export { default as ConfirmModal } from './ConfirmModal.vue' | 2 | +export { default as SwitchCommandDeliveryModal } from './SwitchCommandDeliveryModal.vue' |
4 | export { default as AttributeDeliverModal } from './AttributeDeliverModal.vue' | 3 | export { default as AttributeDeliverModal } from './AttributeDeliverModal.vue' |
1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
2 | -import { nextTick, reactive, ref, unref } from 'vue' | 2 | +import { nextTick, reactive, ref, toRaw, unref } from 'vue' |
3 | import { Button } from 'ant-design-vue' | 3 | import { Button } from 'ant-design-vue' |
4 | -import { SingleToHex, formSchemas, formSchemasConfig, getArray, getFloatPart } from './config.ts' | ||
5 | -import { BasicModal } from '@/components/Modal' | ||
6 | -import type { SingleClickEventDataType } from '@/api/node/model' | 4 | +import { FormFieldsEnum, formSchemas, getJSONCommandSchemas, getModbusSchemas, getOperationPasswordSchemas, getTcpCustomCommandSchemas } from './config.ts' |
5 | +import { useCommandDeliver } from './useCommadDeliver' | ||
6 | +import { BasicModal, useModalInner } from '@/components/Modal' | ||
7 | +import type { NodeDataDataSourceJsonType, OperationPasswordDataType, SingleClickEventDataType } from '@/api/node/model' | ||
7 | import { BasicForm, useForm } from '@/components/Form' | 8 | import { BasicForm, useForm } from '@/components/Form' |
8 | -import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum' | ||
9 | -import { CommandDeliveryWayEnum, CommandTypeEnum, TransportTypeEnum } from '@/enums/datasource' | ||
10 | -import { genModbusCommand, getDeviceActiveTime, getDeviceInfo, getThingsModelServices, sendRpcOneway } from '@/api/device' | 9 | +import { FormLayoutEnum } from '@/components/Form/src/enum' |
10 | +import { CommandDeliveryWayEnum, TransportTypeEnum } from '@/enums/datasource' | ||
11 | import { ThingsModelForm } from '@/core/Library/components/ThingsModelForm' | 11 | import { ThingsModelForm } from '@/core/Library/components/ThingsModelForm' |
12 | -import { useMessage } from '@/hooks/web/useMessage' | ||
13 | -import type { StructJSON } from '@/api/device/model' | ||
14 | - | ||
15 | -interface ServiceInfo { | ||
16 | - title?: string | ||
17 | - serviceCommand?: Object | ||
18 | - inputData?: [] | ||
19 | - transportType?: string | ||
20 | - callType?: string | ||
21 | - code?: string | ||
22 | - way?: string | 12 | +import type { DeviceItemType, Tsl } from '@/api/device/model' |
13 | +import { useProductsStoreWithOut } from '@/store/modules/products' | ||
14 | +import { useJsonParse } from '@/hooks/business/useJSONParse' | ||
15 | +import { CommandCallWayEnum, CommandWayEnum } from '@/enums/commandEnum' | ||
16 | + | ||
17 | +interface OpenParamsType { | ||
18 | + dataSource: NodeDataDataSourceJsonType | ||
19 | + eventBindData: SingleClickEventDataType | ||
20 | + operationPasswordInfo?: OperationPasswordDataType | ||
23 | } | 21 | } |
24 | 22 | ||
25 | -const visible = ref<boolean>(false) | 23 | +const thingsModelElRef = ref<InstanceType<typeof ThingsModelForm>>() |
26 | 24 | ||
27 | -const { createMessage } = useMessage() | ||
28 | -const loading = ref<boolean>(false) | ||
29 | - | ||
30 | -const dataSourceJson = ref<any>({}) | ||
31 | -const thingsModelElRef = ref() | ||
32 | - | ||
33 | -const isInputData = ref<boolean>(false)// 判断使用那个表单 true: 用封装的表单 false:用自己写的表单 | ||
34 | - | ||
35 | -const zoomFactorValue = ref<number>(1) // 缩放因子 | ||
36 | -const isShowMultiply = ref<boolean>(false) // 只有tcp --> int和double类型才相乘缩放因子 | ||
37 | -const isShowActionType = ref<boolean>(true) // 判断设备属性标识符为modBus时没有填写扩展描述 | ||
38 | -const isShowModBUS = ref<boolean>(false) // 用于判断标识符类型是否时自定义还是modBUS | ||
39 | -const modBUSForm = ref<any>({}) | ||
40 | -const formField = ref('') // 存一个表单取值的field | ||
41 | -const isCommandWay = ref<undefined | string>('') // 存取命令下发方式的值 | ||
42 | -const deviceTitle = ref<string | undefined>()// 设备名称 | 25 | +const getDefaultConfiguration = () => ({ |
26 | + modalVisible: false, | ||
27 | + commandDeliveryWay: CommandDeliveryWayEnum.CUSTOM, | ||
28 | + operationPasswordEnable: false, | ||
29 | +}) | ||
43 | 30 | ||
44 | -const isPasswordInfo = ref<{ checked?: boolean; value?: string | number; label?: string }>({})// 是否需要操作密码才进行命令下发 | 31 | +const configuration = reactive(getDefaultConfiguration()) |
45 | 32 | ||
46 | -const serviceInfo = reactive<ServiceInfo>({ | ||
47 | - title: '', | ||
48 | - serviceCommand: {}, | ||
49 | - inputData: [], | ||
50 | - transportType: '', | ||
51 | - callType: '', | ||
52 | - code: '', | ||
53 | - way: '', // 单向还是双向 | ||
54 | -}) | 33 | +const [registerModal, { setModalProps, closeModal }] = useModalInner() |
55 | 34 | ||
56 | -const [register, { getFieldsValue, validate, setFieldsValue, updateSchema, setProps }] = useForm({ // 自定义下发值 | ||
57 | - schemas: formSchemas, | 35 | +// 操作密码 |
36 | +const [registerPassword, operationPasswordFormAction] = useForm({ | ||
58 | showActionButtonGroup: false, | 37 | showActionButtonGroup: false, |
59 | layout: FormLayoutEnum.VERTICAL, | 38 | layout: FormLayoutEnum.VERTICAL, |
60 | labelWidth: 110, | 39 | labelWidth: 110, |
61 | }) | 40 | }) |
62 | 41 | ||
63 | -const [registerPassword, { getFieldsValue: getPasswordValue, validate: validatePassword }] = useForm({ // 操作密码 | ||
64 | - schemas: [{ | ||
65 | - field: 'password', | ||
66 | - label: '操作密码', | ||
67 | - component: ComponentEnum.INPUT_PAWSSWORD, | ||
68 | - required: true, | ||
69 | - componentProps: { | ||
70 | - placeholder: '请输入操作密码', | ||
71 | - max: 120, | ||
72 | - }, | ||
73 | - }], | 42 | +// 自定义下发值 |
43 | +const [register, customCommandFormAction] = useForm({ | ||
44 | + schemas: formSchemas, | ||
74 | showActionButtonGroup: false, | 45 | showActionButtonGroup: false, |
75 | layout: FormLayoutEnum.VERTICAL, | 46 | layout: FormLayoutEnum.VERTICAL, |
76 | - labelWidth: 110, | ||
77 | }) | 47 | }) |
78 | 48 | ||
79 | -const getServiceInfo = async (deviceProfileId: string, service?: string, serviceCommand?: Recordable) => { | ||
80 | - isInputData.value = true | ||
81 | - const functionJson: any = await getThingsModelServices(deviceProfileId) | ||
82 | - serviceInfo.inputData = functionJson.filter((item: any) => item.identifier === service)?.[0].functionJson.inputData | ||
83 | - serviceInfo.title = functionJson[0].functionName | ||
84 | - serviceInfo.serviceCommand = serviceCommand | 49 | +function handleHasOperationPassword(operationPasswordInfo: OperationPasswordDataType) { |
50 | + configuration.operationPasswordEnable = true | ||
51 | + nextTick(() => { | ||
52 | + operationPasswordFormAction.setProps({ | ||
53 | + schemas: getOperationPasswordSchemas(operationPasswordInfo), | ||
54 | + }) | ||
55 | + }) | ||
56 | +} | ||
57 | + | ||
58 | +function handleOpenCustomCommandModal(deviceInfo: DeviceItemType, eventBindData: SingleClickEventDataType) { | ||
59 | + const { transportType } = deviceInfo | ||
60 | + | ||
61 | + const { setProps, setFieldsValue } = customCommandFormAction | ||
62 | + if (transportType === TransportTypeEnum.TCP) | ||
63 | + setProps({ schemas: getTcpCustomCommandSchemas() }) | ||
64 | + else | ||
65 | + setProps({ schemas: getJSONCommandSchemas() }) | ||
66 | + | ||
67 | + setFieldsValue({ [FormFieldsEnum.CUSTOM_COMMAND]: eventBindData.customCommand }) | ||
68 | +} | ||
69 | + | ||
70 | +function handleOpenServiceCommandModal(deviceInfo: DeviceItemType, eventBindData: SingleClickEventDataType) { | ||
71 | + const { service, serviceCommand = {} } = eventBindData | ||
72 | + | ||
73 | + const productsStore = useProductsStoreWithOut() | ||
74 | + | ||
75 | + const tsl = productsStore.getObjectModelByIdWithIdentifier(deviceInfo.deviceProfileId, service!) | ||
76 | + unref(thingsModelElRef)?.setProps({ inputData: tsl?.inputData, transportType: deviceInfo.transportType, title: tsl?.functionName }) | ||
77 | + | ||
78 | + nextTick(() => { | ||
79 | + unref(thingsModelElRef)?.setFieldsValue(serviceCommand) | ||
80 | + }) | ||
85 | } | 81 | } |
86 | 82 | ||
87 | -// 获取设备信息 | ||
88 | -const getDeviceDetail = async (deviceId: string) => { | ||
89 | - return await getDeviceInfo(deviceId) | 83 | +function handleOpenModbusCommandModal(objectModelTsl: Tsl) { |
84 | + const { setProps } = customCommandFormAction | ||
85 | + setProps({ schemas: getModbusSchemas(objectModelTsl) }) | ||
90 | } | 86 | } |
91 | 87 | ||
92 | -const open = async (_data: SingleClickEventDataType) => { | ||
93 | - const { operationPassword } = _data || {} | ||
94 | - dataSourceJson.value = _data.deviceInfo | ||
95 | - isPasswordInfo.value = operationPassword || {} | ||
96 | - // commandWay-->命令下发方式 | ||
97 | - const { commandWay, customCommand, serviceCommand, service, callType, way } = _data || {} | ||
98 | - isCommandWay.value = commandWay | ||
99 | - const { attrInfo, deviceId } = unref(dataSourceJson) | ||
100 | - const { identifier, extensionDesc, detail, name } = attrInfo || {}// 属性信息 | ||
101 | - const { dataType } = detail || {} | ||
102 | - const { type } = dataType || {} | ||
103 | - | ||
104 | - const { code, transportType, deviceProfileId, alias, name: deviceName } = await getDeviceDetail(deviceId) || {}// 设备信息 | ||
105 | - deviceTitle.value = alias || deviceName// 产品名称 | ||
106 | - const { registerAddress, actionType, zoomFactor } = extensionDesc || {} // 获取扩展描述内容 | ||
107 | - | ||
108 | - const schemas = [{ dataType, identifier, functionName: name } as StructJSON] | ||
109 | - | ||
110 | - zoomFactorValue.value = zoomFactor ? Number(zoomFactor) : 1 | ||
111 | - isShowMultiply.value = !!(type === 'INT' || type === 'DOUBLE') | ||
112 | - formField.value = identifier | ||
113 | - isShowActionType.value = !!actionType // 判断modBUS类型时 物模型是否填写扩展描述 | ||
114 | - serviceInfo.callType = callType // 服务命令调用方式 是同步还是异步 | ||
115 | - serviceInfo.way = way || 'oneway' // 命令下发是双向还是单向 | ||
116 | - | ||
117 | - serviceInfo.transportType = transportType | ||
118 | - serviceInfo.code = code | ||
119 | - | ||
120 | - visible.value = true | ||
121 | - | ||
122 | - if (transportType === TransportTypeEnum.TCP) { | ||
123 | - if (commandWay === CommandDeliveryWayEnum.SERVICE) { // 判断命令下发方式是服务或者自定义调用 服务调用用封装的组件 自定义和modbus就用输入框 | ||
124 | - getServiceInfo(deviceProfileId, service, serviceCommand) | ||
125 | - } | ||
126 | - else if (commandWay === CommandDeliveryWayEnum.CUSTOM) { // 自定义 | ||
127 | - await nextTick() | ||
128 | - updateSchema([ | ||
129 | - { | ||
130 | - field: 'sendValue', | ||
131 | - required: true, | ||
132 | - label: '自定义下发值', | ||
133 | - component: ComponentEnum.INPUT, | ||
134 | - }, | ||
135 | - ]) | ||
136 | - setFieldsValue({ sendValue: customCommand }) | ||
137 | - } | ||
138 | - else { // modBus调用 | ||
139 | - isShowModBUS.value = true | ||
140 | - modBUSForm.value = { | ||
141 | - crc: 'CRC_16_LOWER', | ||
142 | - deviceCode: code, | ||
143 | - method: actionType === '16' ? '10' : actionType, | ||
144 | - registerAddress, | ||
145 | - registerNumber: 1, | ||
146 | - registerValues: [], | ||
147 | - } | ||
148 | - await nextTick() | ||
149 | - setProps({ schemas: formSchemasConfig(schemas[0], actionType) }) | ||
150 | - } | ||
151 | - } | ||
152 | - else { | ||
153 | - isShowModBUS.value = false | ||
154 | - | ||
155 | - // 命令下发方式是服务或则自定义调用 | ||
156 | - if (commandWay === CommandDeliveryWayEnum.SERVICE) { getServiceInfo(deviceProfileId, service, serviceCommand) } | ||
157 | - | ||
158 | - else if (commandWay === CommandDeliveryWayEnum.CUSTOM) { | ||
159 | - await nextTick() | ||
160 | - updateSchema([ | ||
161 | - { | ||
162 | - field: 'sendValue', | ||
163 | - component: ComponentEnum.JSON_EDITOR, | ||
164 | - changeEvent: 'update:value', | ||
165 | - label: '自定义下发值', | ||
166 | - required: true, | ||
167 | - }, | ||
168 | - ]) | ||
169 | - setFieldsValue({ sendValue: customCommand }) | ||
170 | - } | ||
171 | - } | 88 | +const { setup, getDeviceInfo, getObjectModelTsl, doCommandDeliver } = useCommandDeliver() |
89 | + | ||
90 | +const open = async ({ eventBindData, operationPasswordInfo, dataSource }: OpenParamsType) => { | ||
91 | + const { deviceId, deviceProfileId, attr } = dataSource | ||
92 | + const { commandWay, callType, way, service } = eventBindData | ||
93 | + const { checked, value: operationPassword } = operationPasswordInfo || {} | ||
94 | + configuration.commandDeliveryWay = commandWay! | ||
95 | + | ||
96 | + setModalProps({ | ||
97 | + open: true, | ||
98 | + loading: true, | ||
99 | + }) | ||
100 | + | ||
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 | + if (checked && operationPassword) | ||
115 | + handleHasOperationPassword(operationPasswordInfo!) | ||
116 | + | ||
117 | + if (commandWay === CommandDeliveryWayEnum.CUSTOM) | ||
118 | + handleOpenCustomCommandModal(toRaw(unref(getDeviceInfo)!), eventBindData) | ||
119 | + else if (commandWay === CommandDeliveryWayEnum.SERVICE) | ||
120 | + handleOpenServiceCommandModal(toRaw(unref(getDeviceInfo)!), eventBindData) | ||
121 | + else if (commandWay === CommandDeliveryWayEnum.MODBUS) | ||
122 | + handleOpenModbusCommandModal(toRaw(unref(getObjectModelTsl)!)) | ||
123 | + | ||
124 | + setModalProps({ | ||
125 | + loading: false, | ||
126 | + title: `参数设置 / ${unref(getDeviceInfo)?.alias || unref(getDeviceInfo)?.name}`, | ||
127 | + }) | ||
128 | +} | ||
129 | + | ||
130 | +function handleGetCustomCommand() { | ||
131 | + const res = customCommandFormAction.getFieldsValue() | ||
132 | + const command = res[FormFieldsEnum.CUSTOM_COMMAND] | ||
133 | + return unref(getDeviceInfo)?.transportType === TransportTypeEnum.TCP ? command : useJsonParse(command).value | ||
134 | +} | ||
135 | + | ||
136 | +function handleGetServiceCommand() { | ||
137 | + const res = unref(thingsModelElRef)!.getFieldsValue() | ||
138 | + | ||
139 | + return unref(getDeviceInfo)?.transportType === TransportTypeEnum.TCP ? res[FormFieldsEnum.SERVICE_COMMAND] : res | ||
172 | } | 140 | } |
173 | 141 | ||
174 | -const error = () => { | ||
175 | - // createMessage.error('下发指令失败') | ||
176 | - return false | 142 | +function handleGetModbusCommand() { |
143 | + const res = customCommandFormAction.getFieldsValue() | ||
144 | + const identifier = unref(getObjectModelTsl)!.identifier | ||
145 | + return res[identifier] | ||
177 | } | 146 | } |
178 | 147 | ||
179 | const handleSubmit = async () => { | 148 | const handleSubmit = async () => { |
180 | - unref(isPasswordInfo)?.checked && await validatePassword()// 操作密码 | ||
181 | - !unref(isInputData) && await validate() | ||
182 | - unref(isInputData) && await unref(thingsModelElRef).validate() | ||
183 | - | ||
184 | - if (unref(isPasswordInfo)?.checked) { | ||
185 | - const { password } = getPasswordValue() | ||
186 | - if (unref(isPasswordInfo)?.value !== password) { | ||
187 | - createMessage.warning('操作密码不正确') | ||
188 | - return | ||
189 | - } | ||
190 | - } | ||
191 | - if (serviceInfo.callType === 'SYNC') { // 服务命令调用方式 是同步 需要调用设备是否在线才能下发 | ||
192 | - const res = await getDeviceActiveTime(dataSourceJson.value.deviceId) | ||
193 | - const { value } = res?.[0] || {} | ||
194 | - if (!value) { | ||
195 | - createMessage.error('当前设备不在线') | ||
196 | - return | ||
197 | - } | ||
198 | - } | ||
199 | - | ||
200 | - const sendValue = ref({}) | ||
201 | - try { | ||
202 | - loading.value = true | ||
203 | - if (unref(isShowModBUS)) { | ||
204 | - if (!unref(isShowActionType)) { | ||
205 | - createMessage.warning('当前物模型扩展描述没有填写') | ||
206 | - return | ||
207 | - } | ||
208 | - | ||
209 | - if (!serviceInfo.code) { | ||
210 | - createMessage.error('当前缺少设备地址码') | ||
211 | - return | ||
212 | - } | ||
213 | - const oldValue = getFieldsValue()[unref(formField)] | ||
214 | - modBUSForm.value.registerNumber = 1 | ||
215 | - modBUSForm.value.registerValues = [oldValue] | ||
216 | - | ||
217 | - if (unref(isShowMultiply) && unref(modBUSForm).method === '06') { | ||
218 | - const newValue | ||
219 | - = Math.trunc(oldValue) * unref(zoomFactorValue) | ||
220 | - + getFloatPart(oldValue) * unref(zoomFactorValue) | ||
221 | - if (newValue % 1 !== 0) { | ||
222 | - createMessage.warning(`属性下发类型必须是整数,缩放因子为${unref(zoomFactorValue)}`) | ||
223 | - return | ||
224 | - } | ||
225 | - | ||
226 | - if (oldValue * unref(zoomFactorValue) > 65535) { | ||
227 | - createMessage.warning(`属性下发值不能超过65535,缩放因子是${unref(zoomFactorValue)}`) | ||
228 | - return | ||
229 | - } | ||
230 | - // bool类型的就不用去乘缩放因子了 | ||
231 | - modBUSForm.value.registerValues = [newValue] | ||
232 | - } | ||
233 | - | ||
234 | - if (unref(modBUSForm).method === '16' || unref(modBUSForm).method === '10') { | ||
235 | - const regex = /^-?\d+(\.\d{0,2})?$/ | ||
236 | - const values | ||
237 | - = Math.trunc(oldValue) * unref(zoomFactorValue) | ||
238 | - + getFloatPart(oldValue) * unref(zoomFactorValue) | ||
239 | - | ||
240 | - if (!regex.test(values as any)) { | ||
241 | - createMessage.warning(`属性下发值精确到两位小数,缩放因子是${unref(zoomFactorValue)}`) | ||
242 | - return | ||
243 | - } | ||
244 | - | ||
245 | - const newValue | ||
246 | - = values === 0 ? [0, 0] : getArray(SingleToHex(unref(isShowMultiply) ? values : oldValue)) | ||
247 | - modBUSForm.value.registerValues = newValue | ||
248 | - modBUSForm.value.registerNumber = 2 | ||
249 | - modBUSForm.value.method = '10' | ||
250 | - } | ||
251 | - sendValue.value = await genModbusCommand(unref(modBUSForm)) | ||
252 | - } | ||
253 | - else { | ||
254 | - if (unref(isInputData)) { | ||
255 | - const values = serviceInfo.transportType === TransportTypeEnum.TCP ? Object.values(unref(thingsModelElRef)?.getFieldsValue()).join('').replace(/\s/g, '') : unref(thingsModelElRef)?.getFieldsValue() | ||
256 | - sendValue.value = values | ||
257 | - } | ||
258 | - else { | ||
259 | - const values = serviceInfo.transportType === TransportTypeEnum.TCP ? getFieldsValue().sendValue : JSON.parse(getFieldsValue().sendValue) || {} | ||
260 | - sendValue.value = values | ||
261 | - } | ||
262 | - } | ||
263 | - await sendRpcOneway({ | ||
264 | - additionalInfo: { | ||
265 | - cmdType: | ||
266 | - unref(isCommandWay) === CommandDeliveryWayEnum.SERVICE | ||
267 | - ? CommandTypeEnum.SERVICE | ||
268 | - : CommandTypeEnum.API, | ||
269 | - }, | ||
270 | - persistent: true, | ||
271 | - method: 'methodThingskit', | ||
272 | - params: serviceInfo.transportType !== TransportTypeEnum.TCP && unref(isCommandWay) === CommandDeliveryWayEnum.SERVICE ? { service: unref(sendValue) } : unref(sendValue), | ||
273 | - }, dataSourceJson.value.deviceId, serviceInfo.callType ? serviceInfo.callType === 'SYNC' ? 'twoway' : 'oneway' : serviceInfo.way) | ||
274 | - createMessage.success('命令下发成功') | ||
275 | - visible.value = false | ||
276 | - isInputData.value = false | ||
277 | - } | ||
278 | - catch (msg) { | ||
279 | - console.error(msg) | ||
280 | - return error() | ||
281 | - } | ||
282 | - finally { | ||
283 | - loading.value = false | ||
284 | - } | 149 | + if (configuration.operationPasswordEnable) await operationPasswordFormAction.validate() |
150 | + configuration.commandDeliveryWay === CommandDeliveryWayEnum.SERVICE ? await unref(thingsModelElRef)?.validate() : await customCommandFormAction.validate() | ||
151 | + | ||
152 | + const res | ||
153 | + = configuration.commandDeliveryWay === CommandDeliveryWayEnum.CUSTOM | ||
154 | + ? handleGetCustomCommand() | ||
155 | + : configuration.commandDeliveryWay === CommandDeliveryWayEnum.MODBUS | ||
156 | + ? handleGetModbusCommand() | ||
157 | + : handleGetServiceCommand() | ||
158 | + | ||
159 | + await doCommandDeliver(res) | ||
160 | + closeModal() | ||
285 | } | 161 | } |
286 | 162 | ||
287 | const handleCancel = () => { | 163 | const handleCancel = () => { |
288 | - visible.value = false | ||
289 | - isInputData.value = false | 164 | + closeModal() |
165 | + Object.assign(configuration, getDefaultConfiguration()) | ||
290 | } | 166 | } |
291 | 167 | ||
292 | defineExpose({ | 168 | defineExpose({ |
@@ -295,28 +171,15 @@ defineExpose({ | @@ -295,28 +171,15 @@ defineExpose({ | ||
295 | </script> | 171 | </script> |
296 | 172 | ||
297 | <template> | 173 | <template> |
298 | - <BasicModal | ||
299 | - v-model:open="visible" :title="`参数设置-${deviceTitle}`" :destroy-on-close="true" :confirm-loading="loading" | ||
300 | - :ok-button-props="{ type: 'primary' }" @ok="handleSubmit" @cancel="handleCancel" | ||
301 | - > | ||
302 | - <BasicForm v-if="isPasswordInfo?.checked" @register="registerPassword" /> | ||
303 | - <BasicForm v-if="!isInputData" @register="register" /> | ||
304 | - <ThingsModelForm | ||
305 | - v-else ref="thingsModelElRef" v-model:value="serviceInfo.serviceCommand" :title="serviceInfo.title" | ||
306 | - :input-data="serviceInfo.inputData || []" :transport-type="serviceInfo.transportType" | ||
307 | - /> | 174 | + <BasicModal :destroy-on-close="true" @register="registerModal" @ok="handleSubmit" @cancel="handleCancel"> |
175 | + <BasicForm v-if="configuration.operationPasswordEnable" @register="registerPassword" /> | ||
176 | + <BasicForm v-if="configuration.commandDeliveryWay !== CommandDeliveryWayEnum.SERVICE" @register="register" /> | ||
177 | + <ThingsModelForm v-if="configuration.commandDeliveryWay === CommandDeliveryWayEnum.SERVICE" ref="thingsModelElRef" /> | ||
308 | <template #footer> | 178 | <template #footer> |
309 | - <Button | ||
310 | - style="background-color: #ffffff; | ||
311 | - border-color: #d9d9d9; | ||
312 | - box-shadow: 0 2px 0 rgba(0, 0, 0, 0.02);" @click="handleCancel" | ||
313 | - > | 179 | + <Button @click="handleCancel"> |
314 | 取消 | 180 | 取消 |
315 | </Button> | 181 | </Button> |
316 | - <Button | ||
317 | - type="primary" :loading="loading" | ||
318 | - style="background: #1677ff !important; box-shadow: 0 2px 0 rgba(5, 145, 255, 0.1)" @click="handleSubmit" | ||
319 | - > | 182 | + <Button type="primary" @click="handleSubmit"> |
320 | 确定 | 183 | 确定 |
321 | </Button> | 184 | </Button> |
322 | </template> | 185 | </template> |
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 { SingleToHex, getArray } from './config' | ||
2 | import { type GenModbusCommandType, genModbusCommand } from '@/api/device' | 1 | import { type GenModbusCommandType, genModbusCommand } from '@/api/device' |
3 | import type { ExtensionDesc } from '@/api/device/model' | 2 | import type { ExtensionDesc } from '@/api/device/model' |
3 | +import { ModbusCRCEnum } from '@/enums/commandEnum' | ||
4 | import { TCPObjectModelActionTypeEnum } from '@/enums/objectModelEnum' | 4 | import { TCPObjectModelActionTypeEnum } from '@/enums/objectModelEnum' |
5 | import { useMessage } from '@/hooks/web/useMessage' | 5 | import { useMessage } from '@/hooks/web/useMessage' |
6 | 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 | + | ||
7 | const getFloatPart = (number: string | number) => { | 152 | const getFloatPart = (number: string | number) => { |
8 | const isLessZero = Number(number) < 0 | 153 | const isLessZero = Number(number) < 0 |
9 | number = number.toString() | 154 | number = number.toString() |
@@ -21,8 +166,9 @@ export function useGetModbusCommand() { | @@ -21,8 +166,9 @@ export function useGetModbusCommand() { | ||
21 | 166 | ||
22 | const getModbusCommand = async (value: number, extensionDesc: ExtensionDesc, deviceAddressCode: string) => { | 167 | const getModbusCommand = async (value: number, extensionDesc: ExtensionDesc, deviceAddressCode: string) => { |
23 | const { registerAddress, actionType, zoomFactor } = extensionDesc as Required<ExtensionDesc> | 168 | const { registerAddress, actionType, zoomFactor } = extensionDesc as Required<ExtensionDesc> |
169 | + | ||
24 | const params: GenModbusCommandType = { | 170 | const params: GenModbusCommandType = { |
25 | - crc: 'CRC_16_LOWER', | 171 | + crc: ModbusCRCEnum.CRC_16_LOWER, |
26 | registerNumber: 1, | 172 | registerNumber: 1, |
27 | deviceCode: deviceAddressCode, | 173 | deviceCode: deviceAddressCode, |
28 | registerAddress, | 174 | registerAddress, |
@@ -41,7 +187,7 @@ export function useGetModbusCommand() { | @@ -41,7 +187,7 @@ export function useGetModbusCommand() { | ||
41 | } | 187 | } |
42 | 188 | ||
43 | if (value * zoomFactor > REGISTER_MAX_VALUE) { | 189 | if (value * zoomFactor > REGISTER_MAX_VALUE) { |
44 | - createMessage.warning(`属性下发值不能超过${REGISTER_MAX_VALUE},缩放因子是${zoomFactor}`) | 190 | + createMessage.warning(`属性下发值不能超过${REGISTER_MAX_VALUE},缩放因子为${zoomFactor}`) |
45 | return | 191 | return |
46 | } | 192 | } |
47 | 193 | ||
@@ -54,7 +200,7 @@ export function useGetModbusCommand() { | @@ -54,7 +200,7 @@ export function useGetModbusCommand() { | ||
54 | + getFloatPart(value) * zoomFactor | 200 | + getFloatPart(value) * zoomFactor |
55 | 201 | ||
56 | if (!regex.test(values.toString())) { | 202 | if (!regex.test(values.toString())) { |
57 | - createMessage.warning(`属性下发值精确到两位小数,缩放因子是${zoomFactor}`) | 203 | + createMessage.warning(`属性下发值精确到两位小数,缩放因子为${zoomFactor}`) |
58 | return | 204 | return |
59 | } | 205 | } |
60 | 206 |
@@ -9,6 +9,8 @@ import { CommandDeliveryWayEnum, CommandDeliveryWayNameEnum, DeviceTypeEnum, Eve | @@ -9,6 +9,8 @@ import { CommandDeliveryWayEnum, CommandDeliveryWayNameEnum, DeviceTypeEnum, Eve | ||
9 | import type { ThingsModel } from '@/api/device/model' | 9 | import type { ThingsModel } from '@/api/device/model' |
10 | import { PackageCategoryEnum } from '@/core/Library/enum' | 10 | import { PackageCategoryEnum } from '@/core/Library/enum' |
11 | import { useContentDataStoreWithOut } from '@/store/modules/contentData' | 11 | import { useContentDataStoreWithOut } from '@/store/modules/contentData' |
12 | +import { validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm' | ||
13 | +import { JSONEditorValidator } from '@/components/CodeEditor/src/JSONEditor' | ||
12 | 14 | ||
13 | export enum FormFieldsEnum { | 15 | export enum FormFieldsEnum { |
14 | ACTION_TYPE = 'actionType', | 16 | ACTION_TYPE = 'actionType', |
@@ -36,14 +38,15 @@ export enum FormFieldsNameEnum { | @@ -36,14 +38,15 @@ export enum FormFieldsNameEnum { | ||
36 | const contentDataStore = useContentDataStoreWithOut() | 38 | const contentDataStore = useContentDataStoreWithOut() |
37 | 39 | ||
38 | export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { | 40 | export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { |
39 | - const { getNodeData, getCellInfo } = usePublicFormContext() | 41 | + const { getNodeData, getCellInfo, getDeviceInfo } = usePublicFormContext() |
40 | const { dataSourceJson } = unref(getNodeData) || {} | 42 | const { dataSourceJson } = unref(getNodeData) || {} |
41 | - const { deviceProfileId, deviceInfo, deviceId } = dataSourceJson || {} | 43 | + const { deviceProfileId, deviceId } = dataSourceJson || {} |
42 | // transportType:判断是什么类型的设备 code:设备地址码 deviceType:设备类型 | 44 | // transportType:判断是什么类型的设备 code:设备地址码 deviceType:设备类型 |
43 | let codeType: string | null = '' | 45 | let codeType: string | null = '' |
44 | - const { transportType, deviceType, codeType: deviceCodeType } = deviceInfo || {} | 46 | + const { transportType, deviceType, codeType: deviceCodeType } = unref(getDeviceInfo) || {} |
45 | codeType = deviceCodeType || (deviceId ? contentDataStore.diveceDetailMap?.[deviceId]?.codeType : null) | 47 | codeType = deviceCodeType || (deviceId ? contentDataStore.diveceDetailMap?.[deviceId]?.codeType : null) |
46 | const isTemplate = contentDataStore.isTemplate // 判断是否是模板 | 48 | const isTemplate = contentDataStore.isTemplate // 判断是否是模板 |
49 | + | ||
47 | return [ | 50 | return [ |
48 | { | 51 | { |
49 | field: 'eventName', | 52 | field: 'eventName', |
@@ -68,7 +71,6 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { | @@ -68,7 +71,6 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { | ||
68 | const options: DefaultOptionType[] = [ | 71 | const options: DefaultOptionType[] = [ |
69 | { label: EventActionTypeNameEnum.OPEN_LINK, value: EventActionTypeEnum.OPEN_LINK }, | 72 | { label: EventActionTypeNameEnum.OPEN_LINK, value: EventActionTypeEnum.OPEN_LINK }, |
70 | { label: EventActionTypeNameEnum.OPEN_PAGE, value: EventActionTypeEnum.OPEN_PAGE }, | 73 | { label: EventActionTypeNameEnum.OPEN_PAGE, value: EventActionTypeEnum.OPEN_PAGE }, |
71 | - | ||
72 | ] | 74 | ] |
73 | 75 | ||
74 | if (unref(getCellInfo).category.toUpperCase() === PackageCategoryEnum.CONTROL) | 76 | if (unref(getCellInfo).category.toUpperCase() === PackageCategoryEnum.CONTROL) |
@@ -93,7 +95,6 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { | @@ -93,7 +95,6 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { | ||
93 | field: FormFieldsEnum.OPEN_LINK, | 95 | field: FormFieldsEnum.OPEN_LINK, |
94 | label: '链接', | 96 | label: '链接', |
95 | component: ComponentEnum.INPUT, | 97 | component: ComponentEnum.INPUT, |
96 | - // dynamicDisabled: () => !deviceProfileId, | ||
97 | required: true, | 98 | required: true, |
98 | ifShow: ({ model }) => model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.OPEN_LINK, | 99 | ifShow: ({ model }) => model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.OPEN_LINK, |
99 | }, | 100 | }, |
@@ -187,20 +188,16 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { | @@ -187,20 +188,16 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { | ||
187 | component: ComponentEnum.JSON_EDITOR, | 188 | component: ComponentEnum.JSON_EDITOR, |
188 | changeEvent: 'update:value', | 189 | changeEvent: 'update:value', |
189 | required: true, | 190 | required: true, |
191 | + rules: JSONEditorValidator(), | ||
190 | ifShow: ({ model }) => transportType !== TransportTypeEnum.TCP && model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.PARAMS_SETTING && model[FormFieldsEnum.COMMAND_WAY] === CommandDeliveryWayEnum.CUSTOM, | 192 | ifShow: ({ model }) => transportType !== TransportTypeEnum.TCP && model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.PARAMS_SETTING && model[FormFieldsEnum.COMMAND_WAY] === CommandDeliveryWayEnum.CUSTOM, |
191 | - componentProps: () => { | ||
192 | - return {} | ||
193 | - }, | ||
194 | }, | 193 | }, |
195 | { | 194 | { |
196 | field: FormFieldsEnum.CUSTOM_COMMAND, | 195 | field: FormFieldsEnum.CUSTOM_COMMAND, |
197 | label: FormFieldsNameEnum.COMMAND, | 196 | label: FormFieldsNameEnum.COMMAND, |
198 | component: ComponentEnum.INPUT, | 197 | component: ComponentEnum.INPUT, |
199 | required: true, | 198 | required: true, |
199 | + rules: [{ validator: validateTCPCustomCommand }], | ||
200 | ifShow: ({ model }) => transportType === TransportTypeEnum.TCP && model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.PARAMS_SETTING && model[FormFieldsEnum.COMMAND_WAY] === CommandDeliveryWayEnum.CUSTOM, | 200 | ifShow: ({ model }) => transportType === TransportTypeEnum.TCP && model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.PARAMS_SETTING && model[FormFieldsEnum.COMMAND_WAY] === CommandDeliveryWayEnum.CUSTOM, |
201 | - componentProps: () => { | ||
202 | - return {} | ||
203 | - }, | ||
204 | }, | 201 | }, |
205 | { | 202 | { |
206 | field: FormFieldsEnum.SERVICE, | 203 | field: FormFieldsEnum.SERVICE, |
@@ -224,7 +221,6 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { | @@ -224,7 +221,6 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { | ||
224 | const service = formModel[FormFieldsEnum.SERVICE] | 221 | const service = formModel[FormFieldsEnum.SERVICE] |
225 | if (service) { | 222 | if (service) { |
226 | const thingsModel = unref(options).find(item => item.identifier === service) || null | 223 | const thingsModel = unref(options).find(item => item.identifier === service) || null |
227 | - // if (thingsModel) | ||
228 | formModel[FormFieldsEnum.THINGS_MODEL] = thingsModel | 224 | formModel[FormFieldsEnum.THINGS_MODEL] = thingsModel |
229 | } | 225 | } |
230 | }, | 226 | }, |
@@ -243,15 +239,8 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { | @@ -243,15 +239,8 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { | ||
243 | component: ComponentEnum.THINGS_MODEL_FORM, | 239 | component: ComponentEnum.THINGS_MODEL_FORM, |
244 | ifShow: ({ model }) => model[FormFieldsEnum.THINGS_MODEL] && model[FormFieldsEnum.COMMAND_WAY] === CommandDeliveryWayEnum.SERVICE, | 240 | ifShow: ({ model }) => model[FormFieldsEnum.THINGS_MODEL] && model[FormFieldsEnum.COMMAND_WAY] === CommandDeliveryWayEnum.SERVICE, |
245 | changeEvent: 'update:value', | 241 | changeEvent: 'update:value', |
246 | - colSlot: 'serviceCommand', | 242 | + slot: 'serviceCommand', |
247 | }, | 243 | }, |
248 | - // { | ||
249 | - // field: 'deviceInfo', | ||
250 | - // label: '设备属性数据', | ||
251 | - // component: ComponentEnum.INPUT, | ||
252 | - // defaultValue: dataSourceJson, | ||
253 | - // ifShow: false, | ||
254 | - // }, | ||
255 | { | 244 | { |
256 | field: 'transportType', | 245 | field: 'transportType', |
257 | label: '', | 246 | label: '', |
1 | -import { toRaw, unref } from 'vue' | 1 | +import { unref } from 'vue' |
2 | import { getDeviceAttributes, getListByConfigurationId, getListByDeviceProfileIds } from '@/api/device' | 2 | import { getDeviceAttributes, getListByConfigurationId, getListByDeviceProfileIds } from '@/api/device' |
3 | import type { DeviceItemType, ThingsModelItemType } from '@/api/device/model' | 3 | import type { DeviceItemType, ThingsModelItemType } from '@/api/device/model' |
4 | import type { FormSchema } from '@/components/Form' | 4 | import type { FormSchema } from '@/components/Form' |
@@ -32,9 +32,8 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { | @@ -32,9 +32,8 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { | ||
32 | return { | 32 | return { |
33 | options: (unref(contentDataStore.getProductAndDevice) || []).map((item: ProductAndDevice) => ({ label: item.profileName || item.name, value: item.profileId, transportType: item?.transportType, deviceType: item?.deviceType })), | 33 | options: (unref(contentDataStore.getProductAndDevice) || []).map((item: ProductAndDevice) => ({ label: item.profileName || item.name, value: item.profileId, transportType: item?.transportType, deviceType: item?.deviceType })), |
34 | placeholder: '请选择产品', | 34 | placeholder: '请选择产品', |
35 | - onSelect(value: string, option: any) { | 35 | + onSelect(value: string) { |
36 | formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] = value | 36 | formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] = value |
37 | - formModel[ContentDataFieldsEnum.DEVICE_INFO] = value && option ? { transportType: option.transportType, deviceType: option.deviceType, codeType: option?.codeType, deviceName: null } : null | ||
38 | }, | 37 | }, |
39 | getPopupContainer: () => document.body, | 38 | getPopupContainer: () => document.body, |
40 | } | 39 | } |
@@ -63,7 +62,6 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { | @@ -63,7 +62,6 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { | ||
63 | fieldNames: { label: 'name', value: 'tbDeviceId' }, | 62 | fieldNames: { label: 'name', value: 'tbDeviceId' }, |
64 | onSelect(value: string, option: DeviceItemType) { | 63 | onSelect(value: string, option: DeviceItemType) { |
65 | formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] = value ? option.deviceProfileId : null | 64 | formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] = value ? option.deviceProfileId : null |
66 | - formModel[ContentDataFieldsEnum.DEVICE_INFO] = value && option ? { code: option.code, transportType: option.transportType, deviceType: option.deviceType, deviceProfileId: option.deviceProfileId, deviceName: option.alias || option.name || null, codeType: option?.codeType } : null | ||
67 | formModel[ContentDataFieldsEnum.ATTR] = null | 65 | formModel[ContentDataFieldsEnum.ATTR] = null |
68 | }, | 66 | }, |
69 | filterOption: (inputValue: string, option: DeviceItemType) => { | 67 | filterOption: (inputValue: string, option: DeviceItemType) => { |
@@ -93,26 +91,11 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { | @@ -93,26 +91,11 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { | ||
93 | }, | 91 | }, |
94 | params: deviceProfileId, | 92 | params: deviceProfileId, |
95 | fieldNames: { label: 'name', value: 'identifier' }, | 93 | fieldNames: { label: 'name', value: 'identifier' }, |
96 | - onSelect(value: string, option: ThingsModelItemType) { | ||
97 | - formModel[ContentDataFieldsEnum.ATTR_INFO] = value && option ? toRaw(unref(option)) : null | ||
98 | - }, | ||
99 | filterOption: (inputValue: string, option: ThingsModelItemType) => { | 94 | filterOption: (inputValue: string, option: ThingsModelItemType) => { |
100 | return option.name.includes(inputValue) | 95 | return option.name.includes(inputValue) |
101 | }, | 96 | }, |
102 | } | 97 | } |
103 | }, | 98 | }, |
104 | }, | 99 | }, |
105 | - { | ||
106 | - field: ContentDataFieldsEnum.ATTR_INFO, | ||
107 | - label: ContentDataFieldsNameEnum.ATTR_INFO, | ||
108 | - component: ComponentEnum.INPUT, | ||
109 | - ifShow: false, | ||
110 | - }, | ||
111 | - { | ||
112 | - field: ContentDataFieldsEnum.DEVICE_INFO, | ||
113 | - label: ContentDataFieldsNameEnum.DEVICE_INFO, | ||
114 | - component: ComponentEnum.INPUT, | ||
115 | - ifShow: false, | ||
116 | - }, | ||
117 | ] | 100 | ] |
118 | } | 101 | } |
@@ -12,5 +12,4 @@ export interface ComponentExposeType { | @@ -12,5 +12,4 @@ export interface ComponentExposeType { | ||
12 | getFieldsValue: () => any | 12 | getFieldsValue: () => any |
13 | setFieldsValue: (value: any) => void | 13 | setFieldsValue: (value: any) => void |
14 | validate: () => Promise<RuleError | any> | 14 | validate: () => Promise<RuleError | any> |
15 | - updateSchema?: any | ||
16 | } | 15 | } |
@@ -58,7 +58,7 @@ const getEventJson = (): NodeDataEventJsonType => { | @@ -58,7 +58,7 @@ const getEventJson = (): NodeDataEventJsonType => { | ||
58 | 58 | ||
59 | Object.keys(status).forEach((key) => { | 59 | Object.keys(status).forEach((key) => { |
60 | if (eventJson[key as EventTypeEnum]) | 60 | if (eventJson[key as EventTypeEnum]) |
61 | - eventJson[key as EventTypeEnum].enable = status[key as EventTypeEnum] | 61 | + eventJson[key as EventTypeEnum]!.enable = status[key as EventTypeEnum] |
62 | }) | 62 | }) |
63 | 63 | ||
64 | return eventJson | 64 | return eventJson |
@@ -190,7 +190,7 @@ createPublicFormContext(nodeDataActinType) | @@ -190,7 +190,7 @@ createPublicFormContext(nodeDataActinType) | ||
190 | </div> | 190 | </div> |
191 | </Checkbox> | 191 | </Checkbox> |
192 | <Form> | 192 | <Form> |
193 | - <FormItem :validate-status="getValidateStatus(operationPassword.value)"> | 193 | + <FormItem :validate-status="getValidateStatus(operationPassword.value!)"> |
194 | <InputPassword | 194 | <InputPassword |
195 | v-model:value="operationPassword.value" class="mt-1" :disabled="!operationPassword.checked" | 195 | v-model:value="operationPassword.value" class="mt-1" :disabled="!operationPassword.checked" |
196 | placeholder="请输入操作密码" | 196 | placeholder="请输入操作密码" |
1 | +import { validateTCPCustomCommand } from '.' | ||
1 | import type { Specs, StructJSON } from '@/api/device/model' | 2 | import type { Specs, StructJSON } from '@/api/device/model' |
2 | import { type FormSchema } from '@/components/Form' | 3 | import { type FormSchema } from '@/components/Form' |
3 | import { ComponentEnum } from '@/components/Form/src/enum' | 4 | import { ComponentEnum } from '@/components/Form/src/enum' |
4 | -import { DataTypeEnum } from '@/enums/datasource' | 5 | +import { DataTypeEnum, TransportTypeEnum } from '@/enums/datasource' |
6 | + | ||
7 | +export enum FormFieldsEnum { | ||
8 | + SERVICE_COMMAND = 'serviceCommand', | ||
9 | +} | ||
5 | 10 | ||
6 | export const getFormSchemas = ({ structJSON: structJson, required, transportType }: { structJSON: StructJSON[]; required?: boolean; transportType?: string }): FormSchema[] => { | 11 | export const getFormSchemas = ({ structJSON: structJson, required, transportType }: { structJSON: StructJSON[]; required?: boolean; transportType?: string }): FormSchema[] => { |
7 | const createInputNumber = ({ | 12 | const createInputNumber = ({ |
@@ -120,10 +125,11 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType | @@ -120,10 +125,11 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType | ||
120 | 125 | ||
121 | const createTCPServiceCommandInput = ({ serviceCommand }: StructJSON): FormSchema => { | 126 | const createTCPServiceCommandInput = ({ serviceCommand }: StructJSON): FormSchema => { |
122 | return { | 127 | return { |
123 | - field: 'serviceCommand', | 128 | + field: FormFieldsEnum.SERVICE_COMMAND, |
124 | label: '服务命令', | 129 | label: '服务命令', |
125 | component: ComponentEnum.INPUT, | 130 | component: ComponentEnum.INPUT, |
126 | required, | 131 | required, |
132 | + rules: [{ validator: validateTCPCustomCommand }], | ||
127 | componentProps: { | 133 | componentProps: { |
128 | defaultValue: serviceCommand, | 134 | defaultValue: serviceCommand, |
129 | }, | 135 | }, |
@@ -134,8 +140,12 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType | @@ -134,8 +140,12 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType | ||
134 | for (const item of structJson) { | 140 | for (const item of structJson) { |
135 | const { dataType } = item | 141 | const { dataType } = item |
136 | const { type } = dataType || {} | 142 | const { type } = dataType || {} |
137 | - if (transportType === 'TCP') | ||
138 | - schemas.push(createTCPServiceCommandInput(item)) | 143 | + |
144 | + if (transportType === TransportTypeEnum.TCP) { | ||
145 | + item.serviceCommand && schemas.push(createTCPServiceCommandInput(item)) | ||
146 | + break | ||
147 | + } | ||
148 | + | ||
139 | if (type === DataTypeEnum.BOOL) | 149 | if (type === DataTypeEnum.BOOL) |
140 | schemas.push(createSelect(item)) | 150 | schemas.push(createSelect(item)) |
141 | else if (type === DataTypeEnum.ENUM) | 151 | else if (type === DataTypeEnum.ENUM) |
1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
2 | import { Card } from 'ant-design-vue' | 2 | import { Card } from 'ant-design-vue' |
3 | -import { computed, nextTick, reactive, unref, watch } from 'vue' | ||
4 | -import type { ComponentExposeType } from '../PublicForm' | 3 | +import { computed, nextTick, reactive, ref, unref, watch } from 'vue' |
5 | import { getFormSchemas } from './config' | 4 | import { getFormSchemas } from './config' |
6 | import { ThingsModelForm } from '.' | 5 | import { ThingsModelForm } from '.' |
7 | import type { StructJSON } from '@/api/device/model' | 6 | import type { StructJSON } from '@/api/device/model' |
8 | import { BasicForm, useForm } from '@/components/Form' | 7 | import { BasicForm, useForm } from '@/components/Form' |
9 | import { DataTypeEnum } from '@/enums/datasource' | 8 | import { DataTypeEnum } from '@/enums/datasource' |
10 | import { FormLabelAlignEnum, FormLayoutEnum } from '@/components/Form/src/enum' | 9 | import { FormLabelAlignEnum, FormLayoutEnum } from '@/components/Form/src/enum' |
10 | +import { deepMerge } from '@/utils' | ||
11 | 11 | ||
12 | -const props = withDefaults(defineProps<{ | ||
13 | - value: Recordable | 12 | +interface ThingsModelFormPropsType { |
13 | + value?: Recordable | ||
14 | inputData?: StructJSON[] | 14 | inputData?: StructJSON[] |
15 | required?: boolean | 15 | required?: boolean |
16 | title?: string | 16 | title?: string |
17 | transportType?: string | 17 | transportType?: string |
18 | -}>(), { | 18 | +} |
19 | + | ||
20 | +const props = withDefaults(defineProps<ThingsModelFormPropsType>(), { | ||
19 | inputData: () => [], | 21 | inputData: () => [], |
20 | required: true, | 22 | required: true, |
21 | }) | 23 | }) |
22 | 24 | ||
25 | +const propsRef = ref<Partial<ThingsModelFormPropsType>>({}) | ||
26 | + | ||
27 | +const getProps = computed<ThingsModelFormPropsType>(() => ({ ...props, ...unref(propsRef) })) | ||
28 | + | ||
23 | const thingsModelFormListElMap = reactive<Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }>>({}) | 29 | const thingsModelFormListElMap = reactive<Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }>>({}) |
24 | 30 | ||
25 | const [register, formActionType] = useForm({ | 31 | const [register, formActionType] = useForm({ |
26 | - schemas: getFormSchemas({ structJSON: props.inputData || [], required: props.required, transportType: props.transportType }), | 32 | + schemas: getFormSchemasByProps(), |
27 | showActionButtonGroup: false, | 33 | showActionButtonGroup: false, |
34 | + name: Math.random().toString(16).substring(2), | ||
28 | labelWidth: 100, | 35 | labelWidth: 100, |
29 | labelAlign: FormLabelAlignEnum.RIGHT, | 36 | labelAlign: FormLabelAlignEnum.RIGHT, |
30 | layout: FormLayoutEnum.HORIZONTAL, | 37 | layout: FormLayoutEnum.HORIZONTAL, |
31 | }) | 38 | }) |
32 | 39 | ||
33 | const getStructFormItem = computed(() => { | 40 | const getStructFormItem = computed(() => { |
34 | - const { inputData } = props | ||
35 | - return inputData.filter(item => item.dataType?.type === DataTypeEnum.STRUCT) | 41 | + const { inputData } = unref(getProps) |
42 | + return (inputData || []).filter(item => item.dataType?.type === DataTypeEnum.STRUCT) | ||
36 | }) | 43 | }) |
37 | 44 | ||
38 | const setFormElRef = (el: InstanceType<typeof ThingsModelForm>, structJSON: StructJSON) => { | 45 | const setFormElRef = (el: InstanceType<typeof ThingsModelForm>, structJSON: StructJSON) => { |
@@ -68,11 +75,7 @@ const validate = async () => { | @@ -68,11 +75,7 @@ const validate = async () => { | ||
68 | await thingsModelFormListElMap[key].el.validate() | 75 | await thingsModelFormListElMap[key].el.validate() |
69 | } | 76 | } |
70 | 77 | ||
71 | -const updateSchema = (value: Recordable) => { | ||
72 | - return formActionType.updateSchema(value) | ||
73 | -} | ||
74 | - | ||
75 | -watch(() => props.value, | 78 | +watch(() => unref(getProps).value, |
76 | async (value) => { | 79 | async (value) => { |
77 | await nextTick() | 80 | await nextTick() |
78 | formActionType.setFieldsValue(value || {}) | 81 | formActionType.setFieldsValue(value || {}) |
@@ -80,25 +83,31 @@ watch(() => props.value, | @@ -80,25 +83,31 @@ watch(() => props.value, | ||
80 | { immediate: true }, | 83 | { immediate: true }, |
81 | ) | 84 | ) |
82 | 85 | ||
86 | +function getFormSchemasByProps() { | ||
87 | + return getFormSchemas({ structJSON: unref(getProps).inputData || [], required: unref(getProps).required, transportType: unref(getProps).transportType }) | ||
88 | +} | ||
89 | + | ||
83 | watch( | 90 | watch( |
84 | - () => props.inputData, | 91 | + () => unref(getProps).inputData, |
85 | (value) => { | 92 | (value) => { |
86 | - if (value && value.length) { | ||
87 | - const schemas = getFormSchemas({ structJSON: props.inputData || [], required: props.required, transportType: props.transportType }) | ||
88 | - formActionType.setProps({ schemas }) | ||
89 | - } | 93 | + if (value && value.length) |
94 | + formActionType.setProps({ schemas: getFormSchemasByProps() }) | ||
90 | }) | 95 | }) |
91 | 96 | ||
92 | -defineExpose<ComponentExposeType>({ | 97 | +function setProps(props: Partial<ThingsModelFormPropsType>) { |
98 | + propsRef.value = deepMerge(unref(propsRef) || {}, props) as any | ||
99 | +} | ||
100 | + | ||
101 | +defineExpose({ | ||
93 | getFieldsValue, | 102 | getFieldsValue, |
94 | setFieldsValue, | 103 | setFieldsValue, |
95 | validate, | 104 | validate, |
96 | - updateSchema, | 105 | + setProps, |
97 | }) | 106 | }) |
98 | </script> | 107 | </script> |
99 | 108 | ||
100 | <template> | 109 | <template> |
101 | - <Card class="!border-2 !border-dashed" :title="title"> | 110 | + <Card class="!border-2 !border-dashed" :title="getProps.title"> |
102 | <BasicForm class="things-model-form" @register="register"> | 111 | <BasicForm class="things-model-form" @register="register"> |
103 | <template v-for="item in getStructFormItem" #[item.identifier]="{ model, field }" :key="item.identifier"> | 112 | <template v-for="item in getStructFormItem" #[item.identifier]="{ model, field }" :key="item.identifier"> |
104 | <ThingsModelForm | 113 | <ThingsModelForm |
@@ -7,6 +7,8 @@ import { CellAttributeKeyEnum } from '@/enums/cellAttributeEnum' | @@ -7,6 +7,8 @@ import { CellAttributeKeyEnum } from '@/enums/cellAttributeEnum' | ||
7 | import { useContentDataStoreWithOut } from '@/store/modules/contentData' | 7 | import { useContentDataStoreWithOut } from '@/store/modules/contentData' |
8 | import { useMessage } from '@/hooks/web/useMessage' | 8 | import { useMessage } from '@/hooks/web/useMessage' |
9 | import { MessageEnum } from '@/enums/messageEnum' | 9 | import { MessageEnum } from '@/enums/messageEnum' |
10 | +import type { DeviceItemType } from '@/api/device/model' | ||
11 | +import { getDeviceInfo } from '@/api/device' | ||
10 | 12 | ||
11 | interface UseNodeDataParamsType { | 13 | interface UseNodeDataParamsType { |
12 | cell: MxCell | 14 | cell: MxCell |
@@ -35,9 +37,9 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { | @@ -35,9 +37,9 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { | ||
35 | const { createMessage } = useMessage() | 37 | const { createMessage } = useMessage() |
36 | 38 | ||
37 | const nodeData = ref<NodeDataType>() | 39 | const nodeData = ref<NodeDataType>() |
40 | + const deviceInfo = ref<DeviceItemType>() | ||
38 | 41 | ||
39 | const contentDataStore = useContentDataStoreWithOut() | 42 | const contentDataStore = useContentDataStoreWithOut() |
40 | - // const contentId = window.DrawApp.configurationContentId! | ||
41 | const { configurationId } = useParseParams() | 43 | const { configurationId } = useParseParams() |
42 | 44 | ||
43 | const getCellID = () => { | 45 | const getCellID = () => { |
@@ -64,9 +66,15 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { | @@ -64,9 +66,15 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { | ||
64 | } | 66 | } |
65 | }) | 67 | }) |
66 | 68 | ||
69 | + const getDeviceDetail = async () => { | ||
70 | + if (!unref(nodeData)?.dataSourceJson.deviceId) return | ||
71 | + deviceInfo.value = await getDeviceInfo(unref(nodeData)!.dataSourceJson.deviceId) | ||
72 | + } | ||
73 | + | ||
67 | const getNodeAllData = async () => { | 74 | const getNodeAllData = async () => { |
68 | const result = await doGetNodeBindData(basicNodeBindData(ActionType.GET)) | 75 | const result = await doGetNodeBindData(basicNodeBindData(ActionType.GET)) |
69 | nodeData.value = result || getDefaultNodeData() | 76 | nodeData.value = result || getDefaultNodeData() |
77 | + await getDeviceDetail() | ||
70 | return result | 78 | return result |
71 | } | 79 | } |
72 | 80 | ||
@@ -79,6 +87,8 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { | @@ -79,6 +87,8 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { | ||
79 | const result = await doSaveNodeAllData(Object.assign(data, basicNodeBindData(ActionType.SAVE))) | 87 | const result = await doSaveNodeAllData(Object.assign(data, basicNodeBindData(ActionType.SAVE))) |
80 | removeCellSourceAttribute(cell) | 88 | removeCellSourceAttribute(cell) |
81 | nodeData.value = result || getDefaultNodeData() | 89 | nodeData.value = result || getDefaultNodeData() |
90 | + | ||
91 | + await getDeviceDetail() | ||
82 | return result | 92 | return result |
83 | } | 93 | } |
84 | 94 | ||
@@ -123,6 +133,7 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { | @@ -123,6 +133,7 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { | ||
123 | }) | 133 | }) |
124 | 134 | ||
125 | return { | 135 | return { |
136 | + getDeviceInfo: computed(() => unref(deviceInfo)), | ||
126 | getNodeData: computed(() => unref(nodeData)), | 137 | getNodeData: computed(() => unref(nodeData)), |
127 | getCellInfo, | 138 | getCellInfo, |
128 | getNodeAllData, | 139 | getNodeAllData, |
1 | -import { h, render, unref } from 'vue' | 1 | +import { h, render, toRaw, unref } from 'vue' |
2 | import { ControlComponentEnum } from '../packages/Control' | 2 | import { ControlComponentEnum } from '../packages/Control' |
3 | -import { useGetModbusCommand } from '../components/PublicForm/components/DataEvents/CommandDeliveryModal/useGetModbusCommand' | ||
4 | -import { doCommandDelivery, getDeviceActive, getDeviceInfo } from '@/api/device' | 3 | +import { doCommandDelivery, getDeviceActive } from '@/api/device' |
5 | import type { MouseUpEventDataType, NodeDataDataSourceJsonType, NodeDataEventJsonType, SingleClickEventDataType } from '@/api/node/model' | 4 | import type { MouseUpEventDataType, NodeDataDataSourceJsonType, NodeDataEventJsonType, SingleClickEventDataType } from '@/api/node/model' |
6 | -import { CommandWayEnum } from '@/enums/commandEnum' | ||
7 | -import { ActRangListItemTypeEnum, CodeTypeEnum, EventActionTypeEnum, TransportTypeEnum } from '@/enums/datasource' | 5 | +import { CommandMethodEnum, CommandWayEnum } from '@/enums/commandEnum' |
6 | +import { ActRangListItemTypeEnum, CommandTypeEnum, EventActionTypeEnum } from '@/enums/datasource' | ||
8 | import { useMessage } from '@/hooks/web/useMessage' | 7 | import { useMessage } from '@/hooks/web/useMessage' |
9 | -import { AttributeDeliverModal, CommandDeliveryConfirmModal, CommandDeliveryModal, ConfirmModal } from '@/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal' | 8 | +import { AttributeDeliverModal, CommandDeliveryModal, SwitchCommandDeliveryModal } from '@/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal' |
10 | import type { MxCell } from '@/fitCore/types' | 9 | import type { MxCell } from '@/fitCore/types' |
11 | import { NodeUtils } from '@/hooks/business/useNodeUtils' | 10 | import { NodeUtils } from '@/hooks/business/useNodeUtils' |
12 | import { useContentDataStoreWithOut } from '@/store/modules/contentData' | 11 | import { useContentDataStoreWithOut } from '@/store/modules/contentData' |
13 | -import type { RpcCommandType } from '@/api/device/model' | ||
14 | 12 | ||
15 | export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: NodeDataDataSourceJsonType, cell: MxCell) { | 13 | export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: NodeDataDataSourceJsonType, cell: MxCell) { |
16 | const { createMessage } = useMessage() | 14 | const { createMessage } = useMessage() |
17 | 15 | ||
16 | + /** | ||
17 | + * @description 多命令下发 | ||
18 | + * @param data | ||
19 | + * @returns | ||
20 | + */ | ||
18 | const handlerCommandDelivery = async (data: MouseUpEventDataType) => { | 21 | const handlerCommandDelivery = async (data: MouseUpEventDataType) => { |
19 | try { | 22 | try { |
20 | const promiseList: (() => Promise<any>)[] = [] | 23 | const promiseList: (() => Promise<any>)[] = [] |
@@ -34,8 +37,8 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N | @@ -34,8 +37,8 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N | ||
34 | way, | 37 | way, |
35 | deviceId, | 38 | deviceId, |
36 | command: { | 39 | command: { |
37 | - additionalInfo: { cmdType: 'API' }, | ||
38 | - method: 'methodThingskit', | 40 | + additionalInfo: { cmdType: CommandTypeEnum.API }, |
41 | + method: CommandMethodEnum.THINGSKIT, | ||
39 | params: command, | 42 | params: command, |
40 | persistent: true, | 43 | persistent: true, |
41 | }, | 44 | }, |
@@ -51,11 +54,19 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N | @@ -51,11 +54,19 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N | ||
51 | } | 54 | } |
52 | } | 55 | } |
53 | 56 | ||
57 | + /** | ||
58 | + * @description 打开链接 | ||
59 | + * @param data | ||
60 | + */ | ||
54 | const handleOpenLink = (data: SingleClickEventDataType) => { | 61 | const handleOpenLink = (data: SingleClickEventDataType) => { |
55 | const { openLink } = data | 62 | const { openLink } = data |
56 | window.open(openLink) | 63 | window.open(openLink) |
57 | } | 64 | } |
58 | 65 | ||
66 | + /** | ||
67 | + * @description 跳转页面 | ||
68 | + * @param data | ||
69 | + */ | ||
59 | const handleSwitchPage = (data: SingleClickEventDataType) => { | 70 | const handleSwitchPage = (data: SingleClickEventDataType) => { |
60 | const { openPage } = data | 71 | const { openPage } = data |
61 | const pages = window.DrawApp.pages | 72 | const pages = window.DrawApp.pages |
@@ -64,75 +75,36 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N | @@ -64,75 +75,36 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N | ||
64 | window.DrawApp.selectPage(openPageFile) | 75 | window.DrawApp.selectPage(openPageFile) |
65 | } | 76 | } |
66 | 77 | ||
67 | - const handleParamsSetting = async (_data: SingleClickEventDataType) => { | ||
68 | - const deviceInfo = dataSourceJson | ||
69 | - | 78 | + const handleParamsSetting = async (data: SingleClickEventDataType) => { |
70 | const instance = h(CommandDeliveryModal) | 79 | const instance = h(CommandDeliveryModal) |
71 | render(instance, document.body); | 80 | render(instance, document.body); |
72 | - (instance.component?.exposed as InstanceType<typeof CommandDeliveryModal>)?.open({ ..._data, deviceInfo, operationPassword: eventJson?.OPERATION_PASSWORD }) | 81 | + (instance.component?.exposed as InstanceType<typeof CommandDeliveryModal>)?.open({ dataSource: toRaw(unref(dataSourceJson)), eventBindData: data, operationPasswordInfo: eventJson.OPERATION_PASSWORD }) |
73 | } | 82 | } |
74 | 83 | ||
75 | const handleAttributeDelivery = async (data: SingleClickEventDataType) => { | 84 | const handleAttributeDelivery = async (data: SingleClickEventDataType) => { |
76 | try { | 85 | try { |
77 | const nodeUtils = new NodeUtils() | 86 | const nodeUtils = new NodeUtils() |
78 | - const { way = CommandWayEnum.ONE_WAY } = data | ||
79 | - const { attr, deviceId, attrInfo } = dataSourceJson | ||
80 | - const { transportType, alias, name: deviceName, code, codeType } = await getDeviceInfo(dataSourceJson.deviceId) || {}// 设备信息 | ||
81 | - const contentDataStore = useContentDataStoreWithOut() | ||
82 | - const currentData = contentDataStore.contentData.find(item => item.id === cell.getId()) | ||
83 | - if (!currentData) return | ||
84 | - const { actJson, eventJson } = currentData | ||
85 | - const { value: operationPassword, checked: operationPasswordEnable } = eventJson.OPERATION_PASSWORD | 87 | + const operationPasswordInfo = toRaw(unref(eventJson.OPERATION_PASSWORD)) |
86 | 88 | ||
87 | - const command: RpcCommandType = { | ||
88 | - additionalInfo: { cmdType: 'API' }, | ||
89 | - method: 'methodThingskit', | ||
90 | - params: {}, | ||
91 | - persistent: true, | ||
92 | - } | ||
93 | if (nodeUtils.getNodeComponentKey(cell) === ControlComponentEnum.SWITCH) { | 89 | if (nodeUtils.getNodeComponentKey(cell) === ControlComponentEnum.SWITCH) { |
90 | + const contentDataStore = useContentDataStoreWithOut() | ||
91 | + const currentData = contentDataStore.contentData.find(item => item.id === cell.getId()) | ||
92 | + if (!currentData) return | ||
93 | + const { actJson } = currentData | ||
94 | const { rangeList } = actJson.STATUS_SETTING | 94 | const { rangeList } = actJson.STATUS_SETTING |
95 | const status = cell.getAttribute('SWITCH_STATUS') | 95 | const status = cell.getAttribute('SWITCH_STATUS') |
96 | const res = rangeList.find(item => item.type === (status === ActRangListItemTypeEnum.OPEN ? ActRangListItemTypeEnum.CLOSE : ActRangListItemTypeEnum.OPEN)) | 96 | const res = rangeList.find(item => item.type === (status === ActRangListItemTypeEnum.OPEN ? ActRangListItemTypeEnum.CLOSE : ActRangListItemTypeEnum.OPEN)) |
97 | if (!res) return | 97 | if (!res) return |
98 | const { statusValue } = res | 98 | const { statusValue } = res |
99 | 99 | ||
100 | - command.params = transportType === TransportTypeEnum.TCP | ||
101 | - ? statusValue! | ||
102 | - : { | ||
103 | - [attr]: statusValue, | ||
104 | - } | ||
105 | - | ||
106 | - if (operationPasswordEnable) { | ||
107 | - const instance = h(CommandDeliveryConfirmModal) | ||
108 | - render(instance, document.body) | ||
109 | - await (instance.component?.exposed as InstanceType<typeof CommandDeliveryConfirmModal>)?.open(operationPassword!) | ||
110 | - } | ||
111 | - else { | ||
112 | - const instance = h(ConfirmModal) | ||
113 | - render(instance, document.body) | ||
114 | - await (instance.component?.exposed as InstanceType<typeof ConfirmModal>)?.open() | ||
115 | - } | ||
116 | - | ||
117 | - if (transportType === TransportTypeEnum.TCP && codeType === CodeTypeEnum.MODBUS_RTU) { | ||
118 | - const { validateCanGetCommand, getModbusCommand } = useGetModbusCommand() | ||
119 | - if (!validateCanGetCommand(unref(dataSourceJson).attrInfo.extensionDesc, code).flag) return | ||
120 | - const res = await getModbusCommand(statusValue!, unref(dataSourceJson).attrInfo.extensionDesc!, code) | ||
121 | - if (!res) return | ||
122 | - command.params = res | ||
123 | - } | ||
124 | - | ||
125 | - await doCommandDelivery({ way, command, deviceId }) | ||
126 | - createMessage.success('命令下发成功') | 100 | + const instance = h(SwitchCommandDeliveryModal) |
101 | + render(instance, document.body) | ||
102 | + await (instance.component?.exposed as InstanceType<typeof SwitchCommandDeliveryModal>)?.open({ operationPasswordInfo, eventBindData: toRaw(unref(data)), dataSourceJson: toRaw(unref(dataSourceJson)), sendValue: statusValue }!) | ||
127 | } | 103 | } |
128 | else { | 104 | else { |
129 | const instance = h(AttributeDeliverModal) | 105 | const instance = h(AttributeDeliverModal) |
130 | - render(instance, document.body) | ||
131 | - const value = await (instance.component?.exposed as InstanceType<typeof AttributeDeliverModal>)?.open({ title: `${alias || deviceName}-${attrInfo.name}`, operationPassword, operationPasswordEnable, dataSourceJson }) as string | ||
132 | - command.params = transportType === TransportTypeEnum.TCP ? value : { [attr]: value } | ||
133 | - if (!command.params) return | ||
134 | - await doCommandDelivery({ way, command, deviceId }) | ||
135 | - createMessage.success('命令下发成功') | 106 | + render(instance, document.body); |
107 | + (instance.component?.exposed as InstanceType<typeof AttributeDeliverModal>)?.open({ dataSourceJson, operationPasswordInfo, eventBindData: data }) | ||
136 | } | 108 | } |
137 | } | 109 | } |
138 | catch (error) { | 110 | catch (error) { |
src/core/Library/hook/useProduct.ts
0 → 100644
1 | +import type { ConfigurationContentType } from '@/api/content/model' | ||
2 | +import { getProductsDetailWithThingsModel } from '@/api/device' | ||
3 | +import { useProductsStoreWithOut } from '@/store/modules/products' | ||
4 | + | ||
5 | +export function useProduct() { | ||
6 | + const getAllDeviceProfileIds = async (contentData: ConfigurationContentType) => { | ||
7 | + const deviceProfileIds = contentData.productAndDevice?.map(item => item.profileId) || [] | ||
8 | + const result = await getProductsDetailWithThingsModel({ deviceProfileIds, functionTypeEnum: 'all' }) | ||
9 | + const productsStore = useProductsStoreWithOut() | ||
10 | + productsStore.setProducts(result.reduce((prev, next) => ({ ...prev, [next.id]: next }), {})) | ||
11 | + } | ||
12 | + | ||
13 | + return { | ||
14 | + getAllDeviceProfileIds, | ||
15 | + } | ||
16 | +} |
@@ -57,9 +57,9 @@ const handleSubmit = async () => { | @@ -57,9 +57,9 @@ const handleSubmit = async () => { | ||
57 | 57 | ||
58 | const handleSetFormValues = async () => { | 58 | const handleSetFormValues = async () => { |
59 | const { dataSourceJson } = unref(getNodeData) || {} | 59 | const { dataSourceJson } = unref(getNodeData) || {} |
60 | - const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId, attrInfo, deviceInfo } = dataSourceJson || {} | 60 | + const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId } = dataSourceJson || {} |
61 | await nextTick() | 61 | await nextTick() |
62 | - unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, attrInfo, deviceProfileTemplateId, deviceInfo }) | 62 | + unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, deviceProfileTemplateId }) |
63 | setFieldsValue({ ...chartOption }) | 63 | setFieldsValue({ ...chartOption }) |
64 | } | 64 | } |
65 | 65 |
@@ -55,9 +55,9 @@ const handleSubmit = async () => { | @@ -55,9 +55,9 @@ const handleSubmit = async () => { | ||
55 | 55 | ||
56 | const handleSetFormValues = async () => { | 56 | const handleSetFormValues = async () => { |
57 | const { dataSourceJson } = unref(getNodeData) || {} | 57 | const { dataSourceJson } = unref(getNodeData) || {} |
58 | - const { deviceId, attr, chartOption, deviceProfileId, attrInfo, deviceProfileTemplateId, deviceInfo } = dataSourceJson || {} | 58 | + const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId } = dataSourceJson || {} |
59 | await nextTick() | 59 | await nextTick() |
60 | - unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, attrInfo, deviceInfo, deviceProfileTemplateId }) | 60 | + unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, deviceProfileTemplateId }) |
61 | setFieldsValue({ ...chartOption }) | 61 | setFieldsValue({ ...chartOption }) |
62 | } | 62 | } |
63 | 63 |
@@ -33,9 +33,9 @@ const dataSourceElRef = ref<Nullable<InstanceType<typeof DataSourceForm>>>() | @@ -33,9 +33,9 @@ const dataSourceElRef = ref<Nullable<InstanceType<typeof DataSourceForm>>>() | ||
33 | 33 | ||
34 | const handleSetFormValues = async () => { | 34 | const handleSetFormValues = async () => { |
35 | const { dataSourceJson } = unref(getNodeData) || {} | 35 | const { dataSourceJson } = unref(getNodeData) || {} |
36 | - const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId, attrInfo, deviceInfo } = dataSourceJson || {} | 36 | + const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId } = dataSourceJson || {} |
37 | await nextTick() | 37 | await nextTick() |
38 | - unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, deviceProfileTemplateId, attrInfo, deviceInfo }) | 38 | + unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, deviceProfileTemplateId }) |
39 | setFieldsValue({ ...chartOption }) | 39 | setFieldsValue({ ...chartOption }) |
40 | } | 40 | } |
41 | 41 |
@@ -8,6 +8,7 @@ import { useContentDataStoreWithOut } from '@/store/modules/contentData' | @@ -8,6 +8,7 @@ import { useContentDataStoreWithOut } from '@/store/modules/contentData' | ||
8 | import { useUserStoreWithOut } from '@/store/modules/user' | 8 | import { useUserStoreWithOut } from '@/store/modules/user' |
9 | import { useMessage } from '@/hooks/web/useMessage' | 9 | import { useMessage } from '@/hooks/web/useMessage' |
10 | import { MessageEnum } from '@/enums/messageEnum' | 10 | import { MessageEnum } from '@/enums/messageEnum' |
11 | +import { useProduct } from '@/core/Library/hook/useProduct' | ||
11 | 12 | ||
12 | export function useContentData() { | 13 | export function useContentData() { |
13 | const contentDataStore = useContentDataStoreWithOut() | 14 | const contentDataStore = useContentDataStoreWithOut() |
@@ -46,6 +47,9 @@ export function useContentData() { | @@ -46,6 +47,9 @@ export function useContentData() { | ||
46 | const result = mode === PageModeEnum.SHARE ? await shareModeBootstrap() : await getContent() | 47 | const result = mode === PageModeEnum.SHARE ? await shareModeBootstrap() : await getContent() |
47 | 48 | ||
48 | if (result) { | 49 | if (result) { |
50 | + const { getAllDeviceProfileIds } = useProduct() | ||
51 | + await getAllDeviceProfileIds(result) | ||
52 | + | ||
49 | const { productAndDevice, nodelist, isTemplate, templateId } = result | 53 | const { productAndDevice, nodelist, isTemplate, templateId } = result |
50 | if (nodelist) contentDataStore.saveContentData(nodelist) | 54 | if (nodelist) contentDataStore.saveContentData(nodelist) |
51 | if (isTemplate) contentDataStore.setIsTemplate(isTemplate) | 55 | if (isTemplate) contentDataStore.setIsTemplate(isTemplate) |
@@ -2,6 +2,7 @@ import { isNull } from 'lodash-es' | @@ -2,6 +2,7 @@ import { isNull } from 'lodash-es' | ||
2 | import type { App } from 'vue' | 2 | import type { App } from 'vue' |
3 | import type { SubscriptionUpdateMsg } from '../type/message' | 3 | import type { SubscriptionUpdateMsg } from '../type/message' |
4 | import { DataDynamicEffectHandler } from './dataDynamicEffectHandler' | 4 | import { DataDynamicEffectHandler } from './dataDynamicEffectHandler' |
5 | +import { useObjectModelValue } from './useObjectModelValue' | ||
5 | import type { CommandSource, LightboxModeWebsocketService } from '.' | 6 | import type { CommandSource, LightboxModeWebsocketService } from '.' |
6 | import type { ActTypeEnum } from '@/enums/datasource' | 7 | import type { ActTypeEnum } from '@/enums/datasource' |
7 | import { DataSourceTypeEnum } from '@/enums/datasource' | 8 | import { DataSourceTypeEnum } from '@/enums/datasource' |
@@ -52,10 +53,13 @@ export class MessageHandler { | @@ -52,10 +53,13 @@ export class MessageHandler { | ||
52 | 53 | ||
53 | defaultHandler(commandSource: CommandSource, message: SubscriptionUpdateMsg) { | 54 | defaultHandler(commandSource: CommandSource, message: SubscriptionUpdateMsg) { |
54 | const { data, node } = commandSource | 55 | const { data, node } = commandSource |
55 | - const { attr } = data as NodeDataDataSourceJsonType | ||
56 | - const { latestValue } = useLatestMessageValue(message.data, attr) | 56 | + const { attr, deviceProfileId } = data as NodeDataDataSourceJsonType |
57 | + | ||
58 | + let { latestValue } = useLatestMessageValue(message.data, attr) | ||
57 | if (isNull(latestValue)) return | 59 | if (isNull(latestValue)) return |
60 | + latestValue = useObjectModelValue(deviceProfileId, attr, latestValue) | ||
58 | const cell = this.nodeUtils.getCellById(node) | 61 | const cell = this.nodeUtils.getCellById(node) |
62 | + | ||
59 | const cellValue = cell.getValue() as Element | 63 | const cellValue = cell.getValue() as Element |
60 | cellValue.setAttribute('label', latestValue) | 64 | cellValue.setAttribute('label', latestValue) |
61 | this.nodeUtils.updateCellValue(cell, cellValue) | 65 | this.nodeUtils.updateCellValue(cell, cellValue) |
1 | +import type { DataType, Specs, StructJSON } from '@/api/device/model' | ||
2 | +import { DataTypeEnum } from '@/enums/datasource' | ||
3 | +import { useJsonParse } from '@/hooks/business/useJSONParse' | ||
4 | +import { useProductsStoreWithOut } from '@/store/modules/products' | ||
5 | + | ||
6 | +function getBoolTypeValue(value: number, Specs: Specs) { | ||
7 | + const { boolOpen, boolClose } = Specs | ||
8 | + | ||
9 | + return Number(value) ? boolOpen : boolClose | ||
10 | +} | ||
11 | + | ||
12 | +function getEnumTypeValue(value: number, specsList: Specs[]) { | ||
13 | + const res = specsList.find(item => item.value === Number(value)) | ||
14 | + | ||
15 | + return res?.name | ||
16 | +} | ||
17 | + | ||
18 | +function getStructTypeValue(value: string, specs: StructJSON[]): string { | ||
19 | + const res = useJsonParse(value).value | ||
20 | + | ||
21 | + function generateStruct(specs: StructJSON[], value: Recordable) { | ||
22 | + if (!value) return {} | ||
23 | + | ||
24 | + return specs.reduce((prev, next) => { | ||
25 | + return { | ||
26 | + ...prev, | ||
27 | + [next.functionName]: getValueByType(next.dataType!.type, value[next.identifier], next.dataType!), | ||
28 | + } | ||
29 | + }, {}) | ||
30 | + } | ||
31 | + | ||
32 | + return JSON.stringify(generateStruct(specs, res)) | ||
33 | +} | ||
34 | + | ||
35 | +function getValueByType(type: string, value: any, dataType: DataType) { | ||
36 | + switch (type) { | ||
37 | + case DataTypeEnum.BOOL: | ||
38 | + return getBoolTypeValue(value, dataType.specs as Specs) | ||
39 | + case DataTypeEnum.STRUCT: | ||
40 | + return getStructTypeValue(value, dataType.specs as StructJSON[]) | ||
41 | + case DataTypeEnum.ENUM: | ||
42 | + return getEnumTypeValue(value, dataType.specsList as Specs[]) | ||
43 | + default: | ||
44 | + return value | ||
45 | + } | ||
46 | +} | ||
47 | + | ||
48 | +export function useObjectModelValue(deviceProfileId: string, attr: string, value: any) { | ||
49 | + const productsStore = useProductsStoreWithOut() | ||
50 | + | ||
51 | + const result = productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, attr) | ||
52 | + if (!result) | ||
53 | + return value | ||
54 | + | ||
55 | + return getValueByType(result.specs.dataType.type as DataTypeEnum, value, result.specs.dataType) | ||
56 | +} |
src/enums/alarmListEnum.ts
deleted
100644 → 0
@@ -7,3 +7,16 @@ export enum CommandWayNameEnum { | @@ -7,3 +7,16 @@ export enum CommandWayNameEnum { | ||
7 | ONE_WAY = '单向', | 7 | ONE_WAY = '单向', |
8 | TWO_WAY = '双向', | 8 | TWO_WAY = '双向', |
9 | } | 9 | } |
10 | + | ||
11 | +export enum CommandCallWayEnum { | ||
12 | + SYNC = 'SYNC', | ||
13 | + ASYNC = 'ASYNC', | ||
14 | +} | ||
15 | + | ||
16 | +export enum CommandMethodEnum { | ||
17 | + THINGSKIT = 'methodThingskit', | ||
18 | +} | ||
19 | + | ||
20 | +export enum ModbusCRCEnum { | ||
21 | + CRC_16_LOWER = 'CRC_16_LOWER', | ||
22 | +} |
@@ -244,11 +244,7 @@ export enum ContentDataFieldsEnum { | @@ -244,11 +244,7 @@ export enum ContentDataFieldsEnum { | ||
244 | ACCESS_MODE = 'accessMode', | 244 | ACCESS_MODE = 'accessMode', |
245 | VIDEO_FLAG = 'videoComponentFlag', | 245 | VIDEO_FLAG = 'videoComponentFlag', |
246 | 246 | ||
247 | - ATTR_INFO = 'attrInfo', | ||
248 | - DEVICE_INFO = 'deviceInfo', | ||
249 | - TRANSPORT_TYPE = 'transportType', | ||
250 | CODE_TYPE = 'codeType', | 247 | CODE_TYPE = 'codeType', |
251 | - DEVICE_ADDITIONAL_INFO = 'deviceAdditionalInfo', | ||
252 | } | 248 | } |
253 | 249 | ||
254 | export enum ContentDataFieldsNameEnum { | 250 | export enum ContentDataFieldsNameEnum { |
@@ -261,10 +257,6 @@ export enum ContentDataFieldsNameEnum { | @@ -261,10 +257,6 @@ export enum ContentDataFieldsNameEnum { | ||
261 | ACCESS_MODE = 'ACCESS_MODE', | 257 | ACCESS_MODE = 'ACCESS_MODE', |
262 | VIDEO_URL = '视频地址', | 258 | VIDEO_URL = '视频地址', |
263 | 259 | ||
264 | - ATTR_INFO = '物模型属性详情', | ||
265 | - DEVICE_INFO = '设备详情', | ||
266 | - TRANSPORT_TYPE = '传输协议', | ||
267 | - DEVICE_ADDITIONAL_INFO = '设备额外信息', | ||
268 | } | 260 | } |
269 | 261 | ||
270 | export enum VariableImageSourceEnum { | 262 | export enum VariableImageSourceEnum { |
@@ -289,7 +281,7 @@ export enum CommandDeliveryWayNameEnum { | @@ -289,7 +281,7 @@ export enum CommandDeliveryWayNameEnum { | ||
289 | MODBUS = 'MODBUS', | 281 | MODBUS = 'MODBUS', |
290 | } | 282 | } |
291 | 283 | ||
292 | -export enum FunctionType { | 284 | +export enum FunctionTypeEnum { |
293 | PROPERTIES = 'properties', | 285 | PROPERTIES = 'properties', |
294 | EVENTS = 'events', | 286 | EVENTS = 'events', |
295 | SERVICE = 'services', | 287 | SERVICE = 'services', |
src/store/modules/products.ts
0 → 100644
1 | +import { defineStore } from 'pinia' | ||
2 | +import { store } from '..' | ||
3 | +import type { ProductsDetailWithThingsModelType, Tsl } from '@/api/device/model' | ||
4 | + | ||
5 | +interface ProductsStoreType { | ||
6 | + products: Record<string, ProductsDetailWithThingsModelType> | ||
7 | +} | ||
8 | + | ||
9 | +export const useProductsStore = defineStore('app-products', { | ||
10 | + state: (): ProductsStoreType => { | ||
11 | + return { | ||
12 | + products: {}, | ||
13 | + } | ||
14 | + }, | ||
15 | + actions: { | ||
16 | + setProducts(products: Record<string, ProductsDetailWithThingsModelType>) { | ||
17 | + this.products = products | ||
18 | + }, | ||
19 | + | ||
20 | + getProductDetailById(id: string) { | ||
21 | + return this.products[id] | ||
22 | + }, | ||
23 | + | ||
24 | + getObjectModelsById(id: string) { | ||
25 | + return this.products[id] | ||
26 | + }, | ||
27 | + | ||
28 | + getObjectModelByIdWithIdentifier(id: string, identifier: string): Nullable<Tsl> { | ||
29 | + const product = this.getObjectModelsById(id) | ||
30 | + if (!product) return null | ||
31 | + return product.tsl.filter(item => item.identifier === identifier)[0] | ||
32 | + }, | ||
33 | + }, | ||
34 | +}) | ||
35 | + | ||
36 | +export function useProductsStoreWithOut() { | ||
37 | + return useProductsStore(store) | ||
38 | +} |