Commit cd0263e1511050325cb6f1d67777dc2752f95275

Authored by xp.Huang
2 parents 36e69e2d a4392a5d

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 -import type { AlarmStatusEnum } from '@/enums/datasource' 1 +import type { AlarmStatusEnum } from '@/enums/alarmEnum'
2 2
3 /** 3 /**
4 * 告警列表 4 * 告警列表
1 import type { DeviceActiveType, DeviceAttributeItemType, DeviceItemType, DeviceProfileItemType, OrganizationItemType, ProductsDetailWithThingsModelType, RpcCommandType, SendValue, ThingsModel, ThingsModelItemType } from './model' 1 import type { DeviceActiveType, DeviceAttributeItemType, DeviceItemType, DeviceProfileItemType, OrganizationItemType, ProductsDetailWithThingsModelType, RpcCommandType, SendValue, ThingsModel, ThingsModelItemType } from './model'
2 import { CommandWayEnum } from '@/enums/commandEnum' 2 import { CommandWayEnum } from '@/enums/commandEnum'
3 -import type { DeviceTypeEnum } from '@/enums/datasource'  
4 -import { FunctionTypeEnum } from '@/enums/datasource' 3 +import type { DeviceTypeEnum } from '@/enums/deviceEnum'
  4 +import { FunctionTypeEnum } from '@/enums/objectModelEnum'
5 import { isShareMode } from '@/utils/env' 5 import { isShareMode } from '@/utils/env'
6 import { defHttp } from '@/utils/http' 6 import { defHttp } from '@/utils/http'
7 7
@@ -24,6 +24,7 @@ enum Api { @@ -24,6 +24,7 @@ enum Api {
24 GET_LIST_BY_CONFIGURATION_ID = '/configuration/center/getListByConfigurationId', 24 GET_LIST_BY_CONFIGURATION_ID = '/configuration/center/getListByConfigurationId',
25 25
26 GET_PRODUCTS_DETAIL_WITH_THINGS_MODEL = '/things_model/batch/get_tsl', 26 GET_PRODUCTS_DETAIL_WITH_THINGS_MODEL = '/things_model/batch/get_tsl',
  27 +
27 } 28 }
28 29
29 export interface GenModbusCommandType { 30 export interface GenModbusCommandType {
@@ -33,6 +34,7 @@ export interface GenModbusCommandType { @@ -33,6 +34,7 @@ export interface GenModbusCommandType {
33 registerAddress: number 34 registerAddress: number
34 registerNumber?: number 35 registerNumber?: number
35 registerValues?: number[] 36 registerValues?: number[]
  37 + hexByteOrderEnum?: string
36 } 38 }
37 39
38 export const getDeviceProfile = (deviceType?: DeviceTypeEnum) => { 40 export const getDeviceProfile = (deviceType?: DeviceTypeEnum) => {
@@ -128,6 +130,17 @@ export const getDeviceActiveTime = (entityId: string) => { @@ -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 export const getDeviceInfo = (deviceId: string) => { 145 export const getDeviceInfo = (deviceId: string) => {
133 return defHttp.get<DeviceItemType>( 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 export interface DeviceProfileItemType { 5 export interface DeviceProfileItemType {
4 id: string 6 id: string
@@ -41,6 +43,7 @@ export interface Configuration { @@ -41,6 +43,7 @@ export interface Configuration {
41 43
42 export interface TransportConfiguration { 44 export interface TransportConfiguration {
43 type: string 45 type: string
  46 + protocol: TCPProtocolTypeEnum
44 } 47 }
45 48
46 export interface ProvisionConfiguration { 49 export interface ProvisionConfiguration {
@@ -116,6 +119,10 @@ export interface DeviceItemType { @@ -116,6 +119,10 @@ export interface DeviceItemType {
116 deviceType: string 119 deviceType: string
117 alarmStatus: number 120 alarmStatus: number
118 enable: boolean 121 enable: boolean
  122 + deviceProfile: {
  123 + transportType: TransportTypeEnum
  124 + profileData: ProfileData
  125 + }
119 126
120 transportType: TransportTypeEnum 127 transportType: TransportTypeEnum
121 } 128 }
@@ -135,10 +142,14 @@ export interface ThingsModelItemType { @@ -135,10 +142,14 @@ export interface ThingsModelItemType {
135 } 142 }
136 143
137 export interface ExtensionDesc { 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 export interface Detail { 155 export interface Detail {
@@ -146,7 +157,7 @@ export interface Detail { @@ -146,7 +157,7 @@ export interface Detail {
146 } 157 }
147 158
148 export interface DataType { 159 export interface DataType {
149 - type: string 160 + type: DataTypeEnum
150 specs: Specs | StructJSON[] 161 specs: Specs | StructJSON[]
151 specsList: Specs[] 162 specsList: Specs[]
152 } 163 }
@@ -163,6 +174,7 @@ export interface Specs { @@ -163,6 +174,7 @@ export interface Specs {
163 name?: string 174 name?: string
164 value?: any 175 value?: any
165 dataType?: DataTypeEnum 176 dataType?: DataTypeEnum
  177 + step?: number
166 } 178 }
167 179
168 export interface ValueRange { 180 export interface ValueRange {
@@ -207,6 +219,8 @@ export interface Tsl { @@ -207,6 +219,8 @@ export interface Tsl {
207 functionName: string 219 functionName: string
208 identifier: string 220 identifier: string
209 accessMode: string 221 accessMode: string
  222 + functionType: FunctionTypeEnum
  223 + callType: CommandCallWayEnum
210 specs: { 224 specs: {
211 dataType: DataType 225 dataType: DataType
212 } 226 }
1 import type { ThingsModelItemType } from '@/api/device/model' 1 import type { ThingsModelItemType } from '@/api/device/model'
2 import type { ImageSelectorDataType } from '@/core/Library/components/ImageSelector' 2 import type { ImageSelectorDataType } from '@/core/Library/components/ImageSelector'
3 -import type { CommandWayEnum } from '@/enums/commandEnum'  
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 export enum DeleteNodeDataTypeEnum { 7 export enum DeleteNodeDataTypeEnum {
7 DATASOURCE = 'DATASOURCE', 8 DATASOURCE = 'DATASOURCE',
@@ -2,7 +2,9 @@ import type { FormSchema } from '@/components/Form' @@ -2,7 +2,9 @@ import type { FormSchema } from '@/components/Form'
2 import { ComponentEnum } from '@/components/Form/src/enum' 2 import { ComponentEnum } from '@/components/Form/src/enum'
3 import type { BasicColumn } from '@/components/Table' 3 import type { BasicColumn } from '@/components/Table'
4 import type { CommandWayEnum } from '@/enums/commandEnum' 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 export enum TableColumnFieldEnum { 8 export enum TableColumnFieldEnum {
7 DEVICE_ID = 'deviceId', 9 DEVICE_ID = 'deviceId',
8 WAY = 'way', 10 WAY = 'way',
@@ -15,7 +17,7 @@ export interface TableRecordItemType { @@ -15,7 +17,7 @@ export interface TableRecordItemType {
15 [TableColumnFieldEnum.WAY]?: Nullable<CommandWayEnum> 17 [TableColumnFieldEnum.WAY]?: Nullable<CommandWayEnum>
16 [TableColumnFieldEnum.COMMAND]?: Nullable<string | Recordable> 18 [TableColumnFieldEnum.COMMAND]?: Nullable<string | Recordable>
17 [ContentDataFieldsEnum.DEVICE_TYPE]?: Nullable<DeviceTypeEnum> 19 [ContentDataFieldsEnum.DEVICE_TYPE]?: Nullable<DeviceTypeEnum>
18 - [ContentDataFieldsEnum.CODE_TYPE]?: Nullable<CodeTypeEnum> 20 + [ContentDataFieldsEnum.CODE_TYPE]?: Nullable<TCPProtocolTypeEnum>
19 } 21 }
20 22
21 export const getFormSchemas = (): FormSchema[] => { 23 export const getFormSchemas = (): FormSchema[] => {
@@ -2,10 +2,10 @@ import type { DeviceItemType, Specs, StructJSON, Tsl } from '@/api/device/model' @@ -2,10 +2,10 @@ import type { DeviceItemType, Specs, StructJSON, Tsl } from '@/api/device/model'
2 import type { NodeDataDataSourceJsonType, OperationPasswordDataType, SingleClickEventDataType } from '@/api/node/model' 2 import type { NodeDataDataSourceJsonType, OperationPasswordDataType, SingleClickEventDataType } from '@/api/node/model'
3 import { type FormSchema, useComponentRegister } from '@/components/Form' 3 import { type FormSchema, useComponentRegister } from '@/components/Form'
4 import { ComponentEnum } from '@/components/Form/src/enum' 4 import { ComponentEnum } from '@/components/Form/src/enum'
5 -import { ThingsModelForm, validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm' 5 +import { ThingsModelForm } from '@/core/Library/components/ThingsModelForm'
6 import { getFormSchemas } from '@/core/Library/components/ThingsModelForm/config' 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 useComponentRegister(ComponentEnum.THINGS_MODEL_FORM, ThingsModelForm) 10 useComponentRegister(ComponentEnum.THINGS_MODEL_FORM, ThingsModelForm)
11 11
@@ -35,59 +35,57 @@ function getStructJsonFromDataSourceJson(objectModelTsl: Tsl): StructJSON { @@ -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 export const createFormSchemas = ({ operationPasswordInfo, objectModelTsl, deviceInfo }: CreateFormSchemasParamsType): FormSchema[] => { 84 export const createFormSchemas = ({ operationPasswordInfo, objectModelTsl, deviceInfo }: CreateFormSchemasParamsType): FormSchema[] => {
87 const schemas: FormSchema[] = [] 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 else { 90 else {
93 const isStructType = objectModelTsl.specs.dataType.type === DataTypeEnum.STRUCT 91 const isStructType = objectModelTsl.specs.dataType.type === DataTypeEnum.STRUCT
1 <script setup lang="ts"> 1 <script setup lang="ts">
2 import { Modal } from 'ant-design-vue' 2 import { Modal } from 'ant-design-vue'
3 import { nextTick, ref, toRaw, unref } from 'vue' 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 import { BasicForm, useForm } from '@/components/Form' 5 import { BasicForm, useForm } from '@/components/Form'
8 import { FormLayoutEnum } from '@/components/Form/src/enum' 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 const visible = ref(false) 15 const visible = ref(false)
12 16
@@ -15,25 +19,41 @@ const [register, { getFieldsValue, resetFields, validate, setProps, clearValidat @@ -15,25 +19,41 @@ const [register, { getFieldsValue, resetFields, validate, setProps, clearValidat
15 showActionButtonGroup: false, 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 const open = async ({ operationPasswordInfo, dataSourceJson, eventBindData }: AttributeDeliverModalOpenParamsType) => { 27 const open = async ({ operationPasswordInfo, dataSourceJson, eventBindData }: AttributeDeliverModalOpenParamsType) => {
21 visible.value = true 28 visible.value = true
22 const { deviceId, deviceProfileId, attr } = dataSourceJson 29 const { deviceId, deviceProfileId, attr } = dataSourceJson
23 const { way } = eventBindData 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 nextTick(() => { 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 setProps({ schemas }) 37 setProps({ schemas })
29 }) 38 })
30 } 39 }
31 40
32 async function getResult() { 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 const handleOk = async () => { 59 const handleOk = async () => {
@@ -53,7 +73,7 @@ defineExpose({ open }) @@ -53,7 +73,7 @@ defineExpose({ open })
53 73
54 <template> 74 <template>
55 <Modal 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 @ok="handleOk" 77 @ok="handleOk"
58 > 78 >
59 <BasicForm @register="register" /> 79 <BasicForm @register="register" />
@@ -2,10 +2,16 @@ @@ -2,10 +2,16 @@
2 import { Modal } from 'ant-design-vue' 2 import { Modal } from 'ant-design-vue'
3 import { computed, nextTick, ref, unref } from 'vue' 3 import { computed, nextTick, ref, unref } from 'vue'
4 import { Icon } from '@iconify/vue' 4 import { Icon } from '@iconify/vue'
5 -import { useCommandDeliver } from './useCommadDeliver'  
6 import { BasicForm, type FormSchema, useForm } from '@/components/Form' 5 import { BasicForm, type FormSchema, useForm } from '@/components/Form'
7 import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum' 6 import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum'
8 import type { NodeDataDataSourceJsonType, OperationPasswordDataType, SingleClickEventDataType } from '@/api/node/model' 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 interface SwitchCommandDeliveryModalParamsType { 16 interface SwitchCommandDeliveryModalParamsType {
11 operationPasswordInfo?: OperationPasswordDataType 17 operationPasswordInfo?: OperationPasswordDataType
@@ -18,8 +24,6 @@ const visible = ref(false) @@ -18,8 +24,6 @@ const visible = ref(false)
18 24
19 const currentOperationPasswordInfo = ref<OperationPasswordDataType>() 25 const currentOperationPasswordInfo = ref<OperationPasswordDataType>()
20 26
21 -const currendSendValue = ref()  
22 -  
23 const [register, { resetFields, validate, setProps }] = useForm({ 27 const [register, { resetFields, validate, setProps }] = useForm({
24 showActionButtonGroup: false, 28 showActionButtonGroup: false,
25 layout: FormLayoutEnum.VERTICAL, 29 layout: FormLayoutEnum.VERTICAL,
@@ -45,17 +49,17 @@ const createFormSchemas = (password: string): FormSchema[] => { @@ -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 visible.value = true 59 visible.value = true
  60 + currentParams.value = params
54 currentOperationPasswordInfo.value = operationPasswordInfo 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 nextTick(() => { 63 nextTick(() => {
60 unref(getHasEnableOperationPassword) && setProps({ schemas: createFormSchemas(operationPasswordInfo!.value!) }) 64 unref(getHasEnableOperationPassword) && setProps({ schemas: createFormSchemas(operationPasswordInfo!.value!) })
61 }) 65 })
@@ -64,7 +68,36 @@ const open = async ({ operationPasswordInfo, dataSourceJson, eventBindData, send @@ -64,7 +68,36 @@ const open = async ({ operationPasswordInfo, dataSourceJson, eventBindData, send
64 const handlerOk = async () => { 68 const handlerOk = async () => {
65 if (unref(getHasEnableOperationPassword)) 69 if (unref(getHasEnableOperationPassword))
66 await validate() 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 if (unref(getHasEnableOperationPassword)) resetFields() 101 if (unref(getHasEnableOperationPassword)) resetFields()
69 visible.value = false 102 visible.value = false
70 } 103 }
@@ -73,22 +106,7 @@ defineExpose({ open }) @@ -73,22 +106,7 @@ defineExpose({ open })
73 </script> 106 </script>
74 107
75 <template> 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 <BasicForm v-if="getHasEnableOperationPassword" @register="register" /> 110 <BasicForm v-if="getHasEnableOperationPassword" @register="register" />
93 <div v-else class="flex items-center"> 111 <div v-else class="flex items-center">
94 <Icon icon="ant-design:exclamation-circle-filled" class="text-yellow-300 text-xl" /> 112 <Icon icon="ant-design:exclamation-circle-filled" class="text-yellow-300 text-xl" />
1 import { ComponentEnum } from '@/components/Form/src/enum' 1 import { ComponentEnum } from '@/components/Form/src/enum'
2 import type { FormSchema } from '@/components/Form' 2 import type { FormSchema } from '@/components/Form'
3 -import type { Tsl } from '@/api/device/model'  
4 import type { OperationPasswordDataType } from '@/api/node/model' 3 import type { OperationPasswordDataType } from '@/api/node/model'
5 import { validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm' 4 import { validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm'
6 import { JSONEditorValidator } from '@/components/CodeEditor/src/JSONEditor' 5 import { JSONEditorValidator } from '@/components/CodeEditor/src/JSONEditor'
7 -import { DataTypeEnum } from '@/enums/datasource'  
8 6
9 export enum FormFieldsEnum { 7 export enum FormFieldsEnum {
10 CUSTOM_COMMAND = 'customCommand', 8 CUSTOM_COMMAND = 'customCommand',
@@ -22,75 +20,6 @@ export const formSchemas: FormSchema[] = [ @@ -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 export const getOperationPasswordSchemas = (operationPasswordInfo: OperationPasswordDataType): FormSchema[] => { 23 export const getOperationPasswordSchemas = (operationPasswordInfo: OperationPasswordDataType): FormSchema[] => {
95 return [ 24 return [
96 { 25 {
1 <script setup lang="ts"> 1 <script setup lang="ts">
2 import { nextTick, reactive, ref, toRaw, unref } from 'vue' 2 import { nextTick, reactive, ref, toRaw, unref } from 'vue'
3 import { Button } from 'ant-design-vue' 3 import { Button } from 'ant-design-vue'
4 -import { 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 import { BasicModal, useModalInner } from '@/components/Modal' 6 import { BasicModal, useModalInner } from '@/components/Modal'
7 import type { NodeDataDataSourceJsonType, OperationPasswordDataType, SingleClickEventDataType } from '@/api/node/model' 7 import type { NodeDataDataSourceJsonType, OperationPasswordDataType, SingleClickEventDataType } from '@/api/node/model'
8 import { BasicForm, useForm } from '@/components/Form' 8 import { BasicForm, useForm } from '@/components/Form'
9 import { FormLayoutEnum } from '@/components/Form/src/enum' 9 import { FormLayoutEnum } from '@/components/Form/src/enum'
10 -import { CommandDeliveryWayEnum, TransportTypeEnum } from '@/enums/datasource'  
11 import { ThingsModelForm } from '@/core/Library/components/ThingsModelForm' 10 import { ThingsModelForm } from '@/core/Library/components/ThingsModelForm'
12 import type { DeviceItemType, Tsl } from '@/api/device/model' 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 import { useJsonParse } from '@/hooks/business/useJSONParse' 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 interface OpenParamsType { 21 interface OpenParamsType {
18 dataSource: NodeDataDataSourceJsonType 22 dataSource: NodeDataDataSourceJsonType
@@ -82,66 +86,60 @@ function handleOpenServiceCommandModal(deviceInfo: DeviceItemType, eventBindData @@ -82,66 +86,60 @@ function handleOpenServiceCommandModal(deviceInfo: DeviceItemType, eventBindData
82 86
83 function handleOpenModbusCommandModal(objectModelTsl: Tsl) { 87 function handleOpenModbusCommandModal(objectModelTsl: Tsl) {
84 const { setProps } = customCommandFormAction 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 const { deviceId, deviceProfileId, attr } = dataSource 99 const { deviceId, deviceProfileId, attr } = dataSource
92 - const { commandWay, callType, way, service } = eventBindData 100 + const { commandWay } = eventBindData
93 const { checked, value: operationPassword } = operationPasswordInfo || {} 101 const { checked, value: operationPassword } = operationPasswordInfo || {}
94 configuration.commandDeliveryWay = commandWay! 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 setModalProps({ 106 setModalProps({
97 open: true, 107 open: true,
98 loading: true, 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 if (checked && operationPassword) 111 if (checked && operationPassword)
115 handleHasOperationPassword(operationPasswordInfo!) 112 handleHasOperationPassword(operationPasswordInfo!)
116 113
  114 + await nextTick()
  115 +
117 if (commandWay === CommandDeliveryWayEnum.CUSTOM) 116 if (commandWay === CommandDeliveryWayEnum.CUSTOM)
118 - handleOpenCustomCommandModal(toRaw(unref(getDeviceInfo)!), eventBindData) 117 + handleOpenCustomCommandModal(toRaw(unref(deviceInfo)!), eventBindData)
119 else if (commandWay === CommandDeliveryWayEnum.SERVICE) 118 else if (commandWay === CommandDeliveryWayEnum.SERVICE)
120 - handleOpenServiceCommandModal(toRaw(unref(getDeviceInfo)!), eventBindData) 119 + handleOpenServiceCommandModal(toRaw(unref(deviceInfo)!), eventBindData)
121 else if (commandWay === CommandDeliveryWayEnum.MODBUS) 120 else if (commandWay === CommandDeliveryWayEnum.MODBUS)
122 - handleOpenModbusCommandModal(toRaw(unref(getObjectModelTsl)!)) 121 + handleOpenModbusCommandModal(toRaw(unref(objectModelTsl)!))
123 122
124 setModalProps({ 123 setModalProps({
125 loading: false, 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 function handleGetCustomCommand() { 129 function handleGetCustomCommand() {
131 const res = customCommandFormAction.getFieldsValue() 130 const res = customCommandFormAction.getFieldsValue()
132 const command = res[FormFieldsEnum.CUSTOM_COMMAND] 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 function handleGetServiceCommand() { 135 function handleGetServiceCommand() {
137 const res = unref(thingsModelElRef)!.getFieldsValue() 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 function handleGetModbusCommand() { 140 function handleGetModbusCommand() {
143 const res = customCommandFormAction.getFieldsValue() 141 const res = customCommandFormAction.getFieldsValue()
144 - const identifier = unref(getObjectModelTsl)!.identifier 142 + const identifier = unref(objectModelTsl)!.identifier
145 return res[identifier] 143 return res[identifier]
146 } 144 }
147 145
@@ -156,7 +154,31 @@ const handleSubmit = async () => { @@ -156,7 +154,31 @@ const handleSubmit = async () => {
156 ? handleGetModbusCommand() 154 ? handleGetModbusCommand()
157 : handleGetServiceCommand() 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 closeModal() 182 closeModal()
161 } 183 }
162 184
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 +}
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,7 +6,7 @@ import { JSONEditor } from '@/components/CodeEditor/src/JSONEditor'
6 import { BasicModal, useModalInner } from '@/components/Modal' 6 import { BasicModal, useModalInner } from '@/components/Modal'
7 import { useMessage } from '@/hooks/web/useMessage' 7 import { useMessage } from '@/hooks/web/useMessage'
8 import { useJsonParse } from '@/hooks/business/useJSONParse' 8 import { useJsonParse } from '@/hooks/business/useJSONParse'
9 -import { TransportTypeEnum } from '@/enums/datasource' 9 +import { TransportTypeEnum } from '@/enums/deviceEnum'
10 10
11 const emit = defineEmits(['register', 'editComplete']) 11 const emit = defineEmits(['register', 'editComplete'])
12 12
@@ -2,8 +2,9 @@ import type { FormSchema } from '@/components/Form' @@ -2,8 +2,9 @@ import type { FormSchema } from '@/components/Form'
2 import { ComponentEnum } from '@/components/Form/src/enum' 2 import { ComponentEnum } from '@/components/Form/src/enum'
3 import type { BasicColumn } from '@/components/Table' 3 import type { BasicColumn } from '@/components/Table'
4 import type { CommandWayEnum } from '@/enums/commandEnum' 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 import { EventActionTypeEnum, EventActionTypeNameEnum, EventTypeEnum, EventTypeNameEnum } from '@/enums/datasource' 6 import { EventActionTypeEnum, EventActionTypeNameEnum, EventTypeEnum, EventTypeNameEnum } from '@/enums/datasource'
  7 +import type { DeviceTypeEnum, TCPProtocolTypeEnum } from '@/enums/deviceEnum'
7 8
8 export enum TableColumnFieldEnum { 9 export enum TableColumnFieldEnum {
9 DEVICE_ID = 'deviceId', 10 DEVICE_ID = 'deviceId',
@@ -18,9 +19,8 @@ export interface TableRecordItemType { @@ -18,9 +19,8 @@ export interface TableRecordItemType {
18 [TableColumnFieldEnum.WAY]?: Nullable<CommandWayEnum> 19 [TableColumnFieldEnum.WAY]?: Nullable<CommandWayEnum>
19 [TableColumnFieldEnum.COMMAND]?: Nullable<string | Recordable> 20 [TableColumnFieldEnum.COMMAND]?: Nullable<string | Recordable>
20 [ContentDataFieldsEnum.DEVICE_TYPE]?: Nullable<DeviceTypeEnum> 21 [ContentDataFieldsEnum.DEVICE_TYPE]?: Nullable<DeviceTypeEnum>
21 - [ContentDataFieldsEnum.CODE_TYPE]?: Nullable<CodeTypeEnum> 22 + [ContentDataFieldsEnum.CODE_TYPE]?: Nullable<TCPProtocolTypeEnum>
22 [ContentDataFieldsEnum.DEVICE_PROFILE_ID]: Nullable<string> 23 [ContentDataFieldsEnum.DEVICE_PROFILE_ID]: Nullable<string>
23 - [ContentDataFieldsEnum.TRANSPORT_TYPE]?: Nullable<TransportTypeEnum>  
24 } 24 }
25 25
26 export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { 26 export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => {
@@ -4,13 +4,13 @@ import { usePublicFormContext } from '../../../usePublicFormContext' @@ -4,13 +4,13 @@ import { usePublicFormContext } from '../../../usePublicFormContext'
4 import { getThingsModelServices } from '@/api/device' 4 import { getThingsModelServices } from '@/api/device'
5 import { type FormSchema } from '@/components/Form' 5 import { type FormSchema } from '@/components/Form'
6 import { ComponentEnum } from '@/components/Form/src/enum' 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 import type { ThingsModel } from '@/api/device/model' 9 import type { ThingsModel } from '@/api/device/model'
10 import { PackageCategoryEnum } from '@/core/Library/enum' 10 import { PackageCategoryEnum } from '@/core/Library/enum'
11 -import { useContentDataStoreWithOut } from '@/store/modules/contentData'  
12 import { validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm' 11 import { validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm'
13 import { JSONEditorValidator } from '@/components/CodeEditor/src/JSONEditor' 12 import { JSONEditorValidator } from '@/components/CodeEditor/src/JSONEditor'
  13 +import { DeviceTypeEnum, TCPProtocolTypeEnum, TransportTypeEnum } from '@/enums/deviceEnum'
14 14
15 export enum FormFieldsEnum { 15 export enum FormFieldsEnum {
16 ACTION_TYPE = 'actionType', 16 ACTION_TYPE = 'actionType',
@@ -35,16 +35,11 @@ export enum FormFieldsNameEnum { @@ -35,16 +35,11 @@ export enum FormFieldsNameEnum {
35 SERVICE = '服务', 35 SERVICE = '服务',
36 } 36 }
37 37
38 -const contentDataStore = useContentDataStoreWithOut()  
39 -  
40 export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { 38 export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => {
41 const { getNodeData, getCellInfo, getDeviceInfo } = usePublicFormContext() 39 const { getNodeData, getCellInfo, getDeviceInfo } = usePublicFormContext()
42 const { dataSourceJson } = unref(getNodeData) || {} 40 const { dataSourceJson } = unref(getNodeData) || {}
43 const { deviceProfileId } = dataSourceJson || {} 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 return [ 44 return [
50 { 45 {
@@ -115,39 +110,30 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { @@ -115,39 +110,30 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => {
115 component: ComponentEnum.SELECT, 110 component: ComponentEnum.SELECT,
116 required: true, 111 required: true,
117 ifShow: ({ model }) => model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.PARAMS_SETTING, 112 ifShow: ({ model }) => model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.PARAMS_SETTING,
  113 + defaultValue: CommandDeliveryWayEnum.CUSTOM,
118 componentProps: ({ formActionType }) => { 114 componentProps: ({ formActionType }) => {
119 function getOptions() { 115 function getOptions() {
120 const options = [ 116 const options = [
121 { label: CommandDeliveryWayNameEnum.CUSTOM, value: CommandDeliveryWayEnum.CUSTOM }, 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 return options 137 return options
152 } 138 }
153 139
@@ -3,11 +3,12 @@ import { getDeviceAttributes, getListByConfigurationId, getListByDeviceProfileId @@ -3,11 +3,12 @@ import { getDeviceAttributes, getListByConfigurationId, getListByDeviceProfileId
3 import type { DeviceItemType, ThingsModelItemType } from '@/api/device/model' 3 import type { DeviceItemType, ThingsModelItemType } from '@/api/device/model'
4 import type { FormSchema } from '@/components/Form' 4 import type { FormSchema } from '@/components/Form'
5 import { ComponentEnum } from '@/components/Form/src/enum' 5 import { ComponentEnum } from '@/components/Form/src/enum'
6 -import { ContentDataFieldsEnum, ContentDataFieldsNameEnum, DataTypeEnum } from '@/enums/datasource' 6 +import { ContentDataFieldsEnum, ContentDataFieldsNameEnum } from '@/enums/datasource'
7 import { useContentDataStoreWithOut } from '@/store/modules/contentData' 7 import { useContentDataStoreWithOut } from '@/store/modules/contentData'
8 import type { ProductAndDevice } from '@/api/content/model' 8 import type { ProductAndDevice } from '@/api/content/model'
9 import { ControlComponentEnum } from '@/core/Library/packages/Control' 9 import { ControlComponentEnum } from '@/core/Library/packages/Control'
10 import { useParseParams } from '@/core/LoadData' 10 import { useParseParams } from '@/core/LoadData'
  11 +import { DataTypeEnum } from '@/enums/objectModelEnum'
11 12
12 const contentDataStore = useContentDataStoreWithOut() 13 const contentDataStore = useContentDataStoreWithOut()
13 export const formSchemas = (componentKey?: string): FormSchema[] => { 14 export const formSchemas = (componentKey?: string): FormSchema[] => {
@@ -88,12 +89,13 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { @@ -88,12 +89,13 @@ export const formSchemas = (componentKey?: string): FormSchema[] => {
88 showSearch: true, 89 showSearch: true,
89 api: async (params: string) => { 90 api: async (params: string) => {
90 if (!deviceProfileId) return [] 91 if (!deviceProfileId) return []
91 - const options = await getDeviceAttributes(params) 92 + let options = await getDeviceAttributes(params)
92 if (componentKey === ControlComponentEnum.SWITCH) { // 开关只返回bool 93 if (componentKey === ControlComponentEnum.SWITCH) { // 开关只返回bool
93 - return options.filter((item) => { 94 + options = options.filter((item) => {
94 return item.detail.dataType.type === DataTypeEnum.BOOL 95 return item.detail.dataType.type === DataTypeEnum.BOOL
95 }) 96 })
96 } 97 }
  98 +
97 return options 99 return options
98 }, 100 },
99 params: deviceProfileId, 101 params: deviceProfileId,
@@ -2,7 +2,8 @@ import { validateTCPCustomCommand } from '.' @@ -2,7 +2,8 @@ import { validateTCPCustomCommand } from '.'
2 import type { Specs, StructJSON } from '@/api/device/model' 2 import type { Specs, StructJSON } from '@/api/device/model'
3 import { type FormSchema } from '@/components/Form' 3 import { type FormSchema } from '@/components/Form'
4 import { ComponentEnum } from '@/components/Form/src/enum' 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 export enum FormFieldsEnum { 8 export enum FormFieldsEnum {
8 SERVICE_COMMAND = 'serviceCommand', 9 SERVICE_COMMAND = 'serviceCommand',
@@ -5,9 +5,9 @@ import { getFormSchemas } from './config' @@ -5,9 +5,9 @@ import { getFormSchemas } from './config'
5 import { ThingsModelForm } from '.' 5 import { ThingsModelForm } from '.'
6 import type { StructJSON } from '@/api/device/model' 6 import type { StructJSON } from '@/api/device/model'
7 import { BasicForm, useForm } from '@/components/Form' 7 import { BasicForm, useForm } from '@/components/Form'
8 -import { DataTypeEnum } from '@/enums/datasource'  
9 import { FormLabelAlignEnum, FormLayoutEnum } from '@/components/Form/src/enum' 8 import { FormLabelAlignEnum, FormLayoutEnum } from '@/components/Form/src/enum'
10 import { deepMerge } from '@/utils' 9 import { deepMerge } from '@/utils'
  10 +import { DataTypeEnum } from '@/enums/objectModelEnum'
11 11
12 interface ThingsModelFormPropsType { 12 interface ThingsModelFormPropsType {
13 value?: Recordable 13 value?: Recordable
@@ -67,7 +67,7 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { @@ -67,7 +67,7 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) {
67 }) 67 })
68 68
69 const getDeviceDetail = async () => { 69 const getDeviceDetail = async () => {
70 - if (!unref(nodeData)?.dataSourceJson.deviceId) return 70 + if (!unref(nodeData)?.dataSourceJson?.deviceId) return
71 deviceInfo.value = await getDeviceInfo(unref(nodeData)!.dataSourceJson.deviceId) 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,8 +2,8 @@ import { h, render, toRaw, unref } from 'vue'
2 import { ControlComponentEnum } from '../packages/Control' 2 import { ControlComponentEnum } from '../packages/Control'
3 import { doCommandDelivery, getDeviceActive } from '@/api/device' 3 import { doCommandDelivery, getDeviceActive } from '@/api/device'
4 import type { MouseUpEventDataType, NodeDataDataSourceJsonType, NodeDataEventJsonType, SingleClickEventDataType } from '@/api/node/model' 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 import { useMessage } from '@/hooks/web/useMessage' 7 import { useMessage } from '@/hooks/web/useMessage'
8 import { AttributeDeliverModal, CommandDeliveryModal, SwitchCommandDeliveryModal } from '@/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal' 8 import { AttributeDeliverModal, CommandDeliveryModal, SwitchCommandDeliveryModal } from '@/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal'
9 import type { MxCell } from '@/fitCore/types' 9 import type { MxCell } from '@/fitCore/types'
@@ -8,8 +8,10 @@ import { ComponentEnum } from '@/components/Form/src/enum' @@ -8,8 +8,10 @@ import { ComponentEnum } from '@/components/Form/src/enum'
8 import { useContentDataStoreWithOut } from '@/store/modules/contentData' 8 import { useContentDataStoreWithOut } from '@/store/modules/contentData'
9 import { DateFormatEnum } from '@/enums/timeEnum' 9 import { DateFormatEnum } from '@/enums/timeEnum'
10 import type { BasicColumn } from '@/components/Table' 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 import { useParseParams } from '@/core/LoadData' 12 import { useParseParams } from '@/core/LoadData'
  13 +import type { TCPProtocolTypeEnum } from '@/enums/deviceEnum'
  14 +import { AlarmStatusEnum } from '@/enums/alarmEnum'
13 15
14 export const tableColumns: BasicColumn[] = [ 16 export const tableColumns: BasicColumn[] = [
15 { 17 {
@@ -27,7 +29,7 @@ export const tableColumns: BasicColumn[] = [ @@ -27,7 +29,7 @@ export const tableColumns: BasicColumn[] = [
27 export interface TableRecordItemType { 29 export interface TableRecordItemType {
28 uuid: string 30 uuid: string
29 [TableColumnFieldEnum.DEVICE_ID]?: Nullable<string> 31 [TableColumnFieldEnum.DEVICE_ID]?: Nullable<string>
30 - [ContentDataFieldsEnum.CODE_TYPE]?: Nullable<CodeTypeEnum> 32 + [ContentDataFieldsEnum.CODE_TYPE]?: Nullable<TCPProtocolTypeEnum>
31 } 33 }
32 34
33 export const AlarmColorMap = { 35 export const AlarmColorMap = {
@@ -8,8 +8,8 @@ import type { AlarmListItemType } from '@/api/alarm/model' @@ -8,8 +8,8 @@ import type { AlarmListItemType } from '@/api/alarm/model'
8 import type { CreateComponentType } from '@/core/Library/types' 8 import type { CreateComponentType } from '@/core/Library/types'
9 import { useContentDataStore } from '@/store/modules/contentData' 9 import { useContentDataStore } from '@/store/modules/contentData'
10 import { isLightboxMode, isShareMode } from '@/utils/env' 10 import { isLightboxMode, isShareMode } from '@/utils/env'
11 -import { AlarmStatusColorEnum, AlarmStatusEnum, AlarmStatusNameEnum } from '@/enums/datasource'  
12 import { formatToDateTime } from '@/utils/dateUtil' 11 import { formatToDateTime } from '@/utils/dateUtil'
  12 +import { AlarmStatusColorEnum, AlarmStatusNameEnum } from '@/enums/alarmEnum'
13 13
14 const props = defineProps<{ 14 const props = defineProps<{
15 config: CreateComponentType 15 config: CreateComponentType
@@ -67,7 +67,7 @@ onMounted(async () => { @@ -67,7 +67,7 @@ onMounted(async () => {
67 useIntervalFn(initFetchAlarmList, (initOptions.polling || 30) * 1000) 67 useIntervalFn(initFetchAlarmList, (initOptions.polling || 30) * 1000)
68 } 68 }
69 else { 69 else {
70 - const statusList = Object.values(AlarmStatusEnum) 70 + const statusList = Object.values(AlarmStatusNameEnum)
71 Object.assign(initOptions, { 71 Object.assign(initOptions, {
72 scroll: false, 72 scroll: false,
73 interval: 0, 73 interval: 0,
@@ -75,6 +75,15 @@ const init = () => { @@ -75,6 +75,15 @@ const init = () => {
75 videoPlayInstance.value = videoJs(unref(videoPlayEl)!, unref(getOptions), () => { 75 videoPlayInstance.value = videoJs(unref(videoPlayEl)!, unref(getOptions), () => {
76 emit('ready', unref(videoPlayInstance)) 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 const customInit = (getOptionsFn: (optios: VideoJsPlayerOptions) => VideoJsPlayerOptions) => { 89 const customInit = (getOptionsFn: (optios: VideoJsPlayerOptions) => VideoJsPlayerOptions) => {
@@ -3,11 +3,12 @@ import { getDeviceAttributes, getListByConfigurationId, getListByDeviceProfileId @@ -3,11 +3,12 @@ import { getDeviceAttributes, getListByConfigurationId, getListByDeviceProfileId
3 import type { DeviceItemType, ThingsModelItemType } from '@/api/device/model' 3 import type { DeviceItemType, ThingsModelItemType } from '@/api/device/model'
4 import type { FormSchema } from '@/components/Form' 4 import type { FormSchema } from '@/components/Form'
5 import { ComponentEnum } from '@/components/Form/src/enum' 5 import { ComponentEnum } from '@/components/Form/src/enum'
6 -import { ContentDataFieldsEnum, ContentDataFieldsNameEnum, DataTypeEnum } from '@/enums/datasource' 6 +import { ContentDataFieldsEnum, ContentDataFieldsNameEnum } from '@/enums/datasource'
7 import { useContentDataStoreWithOut } from '@/store/modules/contentData' 7 import { useContentDataStoreWithOut } from '@/store/modules/contentData'
8 import type { ProductAndDevice } from '@/api/content/model' 8 import type { ProductAndDevice } from '@/api/content/model'
9 import { ControlComponentEnum } from '@/core/Library/packages/Control' 9 import { ControlComponentEnum } from '@/core/Library/packages/Control'
10 import { useParseParams } from '@/core/LoadData' 10 import { useParseParams } from '@/core/LoadData'
  11 +import { DataTypeEnum } from '@/enums/objectModelEnum'
11 12
12 const contentDataStore = useContentDataStoreWithOut() 13 const contentDataStore = useContentDataStoreWithOut()
13 export const formSchemas = (componentKey?: string): FormSchema[] => { 14 export const formSchemas = (componentKey?: string): FormSchema[] => {
@@ -84,11 +84,13 @@ export class DataDynamicEffectHandler { @@ -84,11 +84,13 @@ export class DataDynamicEffectHandler {
84 if (flag) { 84 if (flag) {
85 const nodeEl = this.nodeUtils.getNodesForCells([cell]) 85 const nodeEl = this.nodeUtils.getNodesForCells([cell])
86 const { type } = record! 86 const { type } = record!
  87 +
87 if (type === ActRangListItemTypeEnum.SHOW) { 88 if (type === ActRangListItemTypeEnum.SHOW) {
88 nodeEl.forEach((node) => { 89 nodeEl.forEach((node) => {
89 node.classList.add(ActAnimationName.VISIBLE) 90 node.classList.add(ActAnimationName.VISIBLE)
90 node.classList.remove(ActAnimationName.HIDDEN) 91 node.classList.remove(ActAnimationName.HIDDEN)
91 }) 92 })
  93 + this.nodeUtils.updateCellValue(cell, record?.title ? record.title : latestValue)
92 } 94 }
93 else if (type === ActRangListItemTypeEnum.HIDDEN) { 95 else if (type === ActRangListItemTypeEnum.HIDDEN) {
94 nodeEl.forEach((node) => { 96 nodeEl.forEach((node) => {
@@ -55,9 +55,9 @@ export class MessageHandler { @@ -55,9 +55,9 @@ export class MessageHandler {
55 const { data, node } = commandSource 55 const { data, node } = commandSource
56 const { attr, deviceProfileId } = data as NodeDataDataSourceJsonType 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 if (isNull(latestValue)) return 59 if (isNull(latestValue)) return
60 - latestValue = useObjectModelValue(deviceProfileId, attr, latestValue) 60 + latestValue = useObjectModelValue(deviceProfileId, attr as string, latestValue)
61 const cell = this.nodeUtils.getCellById(node) 61 const cell = this.nodeUtils.getCellById(node)
62 62
63 const cellValue = cell.getValue() as Element 63 const cellValue = cell.getValue() as Element
1 import type { DataType, Specs, StructJSON } from '@/api/device/model' 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 import { useJsonParse } from '@/hooks/business/useJSONParse' 4 import { useJsonParse } from '@/hooks/business/useJSONParse'
4 import { useProductsStoreWithOut } from '@/store/modules/products' 5 import { useProductsStoreWithOut } from '@/store/modules/products'
5 6
@@ -45,6 +46,13 @@ function getValueByType(type: string, value: any, dataType: DataType) { @@ -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 export function useObjectModelValue(deviceProfileId: string, attr: string, value: any) { 56 export function useObjectModelValue(deviceProfileId: string, attr: string, value: any) {
49 const productsStore = useProductsStoreWithOut() 57 const productsStore = useProductsStoreWithOut()
50 58
@@ -52,5 +60,8 @@ export function useObjectModelValue(deviceProfileId: string, attr: string, value @@ -52,5 +60,8 @@ export function useObjectModelValue(deviceProfileId: string, attr: string, value
52 if (!result) 60 if (!result)
53 return value 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 }
  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,6 +13,31 @@ export enum CommandCallWayEnum {
13 ASYNC = 'ASYNC', 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 export enum CommandMethodEnum { 41 export enum CommandMethodEnum {
17 THINGSKIT = 'methodThingskit', 42 THINGSKIT = 'methodThingskit',
18 } 43 }
@@ -64,50 +64,6 @@ export enum ActTypeNameEnum { @@ -64,50 +64,6 @@ export enum ActTypeNameEnum {
64 VARIABLE_IMAGE = '变量图片', 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 export enum EventTypeEnum { 67 export enum EventTypeEnum {
112 /** 68 /**
113 * @description 鼠标抬起 69 * @description 鼠标抬起
@@ -271,46 +227,6 @@ export enum VariableImageSourceNameEnum { @@ -271,46 +227,6 @@ export enum VariableImageSourceNameEnum {
271 GALLERY = '图库图形', 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 export enum AggregateTypeEnum { 230 export enum AggregateTypeEnum {
315 MIN = 'MIN', 231 MIN = 'MIN',
316 MAX = 'MAX', 232 MAX = 'MAX',
@@ -333,24 +249,3 @@ export enum SocketSubscriberEnum { @@ -333,24 +249,3 @@ export enum SocketSubscriberEnum {
333 HISTORY_CMDS = 'historyCmds', 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 -  
  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 }
  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 +}
  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 +}
  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 +}
  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 +}
@@ -43,11 +43,11 @@ foreignObject[thingskit-component] > div > div > div { @@ -43,11 +43,11 @@ foreignObject[thingskit-component] > div > div > div {
43 } 43 }
44 44
45 .act-visible { 45 .act-visible {
46 - visibility: visible; 46 + display: block;
47 } 47 }
48 48
49 .act-hidden { 49 .act-hidden {
50 - visibility: hidden; 50 + display: none;
51 } 51 }
52 52
53 .act-spin-animation { 53 .act-spin-animation {