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