Commit 1281e6e8d9f370174387405d02ef35d0c5fe93e7
1 parent
283688d5
fix: DEFECT-1118 看板管理指令下发修复tcp设备下发指令逻辑
Showing
9 changed files
with
143 additions
and
58 deletions
| ... | ... | @@ -86,6 +86,12 @@ export interface DataSource { |
| 86 | 86 | deviceName: string; |
| 87 | 87 | deviceProfileId: string; |
| 88 | 88 | tbDeviceId: string; |
| 89 | + customCommand: { | |
| 90 | + transportType?: string; | |
| 91 | + commandType?: string; | |
| 92 | + command?: string; | |
| 93 | + service?: string; | |
| 94 | + }; | |
| 89 | 95 | |
| 90 | 96 | // front usage |
| 91 | 97 | uuid?: string; |
| ... | ... | @@ -93,6 +99,7 @@ export interface DataSource { |
| 93 | 99 | height?: number; |
| 94 | 100 | radio?: RadioRecord; |
| 95 | 101 | deviceType?: DeviceTypeEnum; |
| 102 | + [key: string]: any; | |
| 96 | 103 | } |
| 97 | 104 | |
| 98 | 105 | export interface DataComponentRecord { | ... | ... |
| ... | ... | @@ -17,6 +17,9 @@ export interface ControlComponentValue { |
| 17 | 17 | deviceProfileId?: string; |
| 18 | 18 | deviceType?: DeviceTypeEnum; |
| 19 | 19 | organizationId?: string; |
| 20 | + | |
| 21 | + dataSource?: DataSource; | |
| 22 | + [key: string]: any; | |
| 20 | 23 | } |
| 21 | 24 | |
| 22 | 25 | export const ControlComponentDefaultConfig: ControlComponentValue = { |
| ... | ... | @@ -40,6 +43,7 @@ export const transformControlConfig = ( |
| 40 | 43 | deviceType: dataSourceRecord.deviceType, |
| 41 | 44 | slaveDeviceId: dataSourceRecord.slaveDeviceId, |
| 42 | 45 | organizationId: dataSourceRecord.organizationId, |
| 46 | + dataSource: dataSourceRecord, | |
| 43 | 47 | } as ControlComponentValue, |
| 44 | 48 | }; |
| 45 | 49 | }; | ... | ... |
| 1 | 1 | import { ControlComponentValue } from './control.config'; |
| 2 | -import { getDeviceProfile } from '/@/api/alarm/position'; | |
| 3 | -import { getDeviceRelation, sendCommandOneway } from '/@/api/dataBoard'; | |
| 4 | -import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | |
| 5 | -import { getModelServices } from '/@/api/device/modelOfMatter'; | |
| 2 | +import { sendCommandOneway } from '/@/api/dataBoard'; | |
| 6 | 3 | import { useMessage } from '/@/hooks/web/useMessage'; |
| 7 | -import { isString } from '/@/utils/is'; | |
| 8 | 4 | import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; |
| 9 | 5 | |
| 10 | 6 | const { createMessage } = useMessage(); |
| ... | ... | @@ -15,31 +11,20 @@ export function useSendCommand() { |
| 15 | 11 | }; |
| 16 | 12 | const sendCommand = async (record: ControlComponentValue, value: any) => { |
| 17 | 13 | if (!record) return error(); |
| 18 | - const { deviceProfileId, attribute, deviceType } = record; | |
| 19 | - let { deviceId } = record; | |
| 14 | + const { attribute } = record; | |
| 15 | + const { dataSource } = record; | |
| 16 | + const { customCommand } = dataSource || {}; | |
| 17 | + | |
| 18 | + const { deviceId } = record; | |
| 20 | 19 | if (!deviceId) return error(); |
| 21 | 20 | try { |
| 22 | - const list = await getDeviceProfile(); | |
| 23 | - const deviceProfile = list.find((item) => item.id === deviceProfileId); | |
| 24 | - if (!deviceProfile) return error(); | |
| 25 | - | |
| 26 | 21 | let params: string | Recordable = { |
| 27 | 22 | [attribute!]: Number(value), |
| 28 | 23 | }; |
| 29 | 24 | |
| 30 | 25 | // 如果是TCP设备从物模型中获取下发命令(TCP网关子设备无物模型服务与事件) |
| 31 | - if (deviceProfile!.transportType === TransportTypeEnum.TCP) { | |
| 32 | - const serviceList = (await getModelServices({ deviceProfileId: deviceProfileId! })) || []; | |
| 33 | - const record = serviceList.find((item) => item.identifier === attribute); | |
| 34 | - const sendCommand = record?.functionJson.inputData?.at(0)?.serviceCommand || ''; | |
| 35 | - params = isString(sendCommand) ? sendCommand : JSON.stringify(sendCommand); | |
| 36 | - } | |
| 37 | - | |
| 38 | - if (deviceType === DeviceTypeEnum.SENSOR) { | |
| 39 | - deviceId = await getDeviceRelation({ | |
| 40 | - deviceId, | |
| 41 | - isSlave: deviceType === DeviceTypeEnum.SENSOR, | |
| 42 | - }); | |
| 26 | + if (customCommand?.transportType === TransportTypeEnum.TCP) { | |
| 27 | + params = customCommand.command!; | |
| 43 | 28 | } |
| 44 | 29 | |
| 45 | 30 | // 控制按钮下发命令为0 或 1 |
| ... | ... | @@ -57,7 +42,6 @@ export function useSendCommand() { |
| 57 | 42 | createMessage.success('命令下发成功'); |
| 58 | 43 | return true; |
| 59 | 44 | } catch (msg) { |
| 60 | - console.log('enter'); | |
| 61 | 45 | return error(); |
| 62 | 46 | } |
| 63 | 47 | }; | ... | ... |
| ... | ... | @@ -61,7 +61,13 @@ |
| 61 | 61 | const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]); |
| 62 | 62 | for (const id of hasExistEl) { |
| 63 | 63 | const oldValues = dataSourceEl[id]?.getFieldsValue(); |
| 64 | - dataSourceEl[id]?.setFieldsValue({ ...oldValues, [DataSourceField.ATTRIBUTE]: null }); | |
| 64 | + dataSourceEl[id]?.setFieldsValue({ | |
| 65 | + ...oldValues, | |
| 66 | + [DataSourceField.ATTRIBUTE]: null, | |
| 67 | + [DataSourceField.COMMAND_TYPE]: null, | |
| 68 | + [DataSourceField.SERVICE]: null, | |
| 69 | + }); | |
| 70 | + dataSourceEl[id]?.clearValidate(); | |
| 65 | 71 | } |
| 66 | 72 | }; |
| 67 | 73 | |
| ... | ... | @@ -125,6 +131,12 @@ |
| 125 | 131 | _dataSource[index] = { |
| 126 | 132 | ...value, |
| 127 | 133 | componentInfo: { ...(props.defaultConfig || {}), ...componentInfo }, |
| 134 | + customCommand: { | |
| 135 | + transportType: value.transportType, | |
| 136 | + service: value.service, | |
| 137 | + command: value.command, | |
| 138 | + commandType: value.commandType, | |
| 139 | + }, | |
| 128 | 140 | }; |
| 129 | 141 | } |
| 130 | 142 | |
| ... | ... | @@ -197,13 +209,26 @@ |
| 197 | 209 | dataSource.value = props.record.record.dataSource.map((item) => { |
| 198 | 210 | const id = buildUUID(); |
| 199 | 211 | |
| 212 | + const customCommand = item.customCommand || {}; | |
| 213 | + | |
| 200 | 214 | nextTick(() => { |
| 201 | - (dataSourceEl[id] as FormActionType).setFieldsValue(item); | |
| 215 | + (dataSourceEl[id] as FormActionType).setFieldsValue({ | |
| 216 | + ...item, | |
| 217 | + transportType: customCommand.transportType, | |
| 218 | + command: customCommand?.command, | |
| 219 | + commandType: customCommand.commandType, | |
| 220 | + service: customCommand.service, | |
| 221 | + }); | |
| 202 | 222 | (dataSourceEl[id] as FormActionType).clearValidate(); |
| 203 | 223 | }); |
| 224 | + | |
| 204 | 225 | return { |
| 205 | 226 | id, |
| 206 | 227 | ...item, |
| 228 | + transportType: customCommand.transportType, | |
| 229 | + command: customCommand?.command, | |
| 230 | + commandType: customCommand.commandType, | |
| 231 | + service: customCommand.service, | |
| 207 | 232 | }; |
| 208 | 233 | }); |
| 209 | 234 | }; | ... | ... |
| ... | ... | @@ -72,7 +72,6 @@ |
| 72 | 72 | const { getAllDataSourceFieldValue, validate } = unref(basicConfigurationEl)!; |
| 73 | 73 | await validate(); |
| 74 | 74 | const value = getAllDataSourceFieldValue(); |
| 75 | - | |
| 76 | 75 | unref(isEdit) ? handleUpdateComponent(value) : handleAddComponent(value); |
| 77 | 76 | resetForm(); |
| 78 | 77 | } catch (error: unknown) { | ... | ... |
| 1 | 1 | import { getDeviceAttributes, getMeetTheConditionsDevice } from '/@/api/dataBoard'; |
| 2 | 2 | import { getOrganizationList } from '/@/api/system/system'; |
| 3 | -import { FormSchema } from '/@/components/Form'; | |
| 3 | +import { FormSchema, useComponentRegister } from '/@/components/Form'; | |
| 4 | 4 | import { copyTransFun } from '/@/utils/fnUtils'; |
| 5 | 5 | import { DeviceAttributeParams, MasterDeviceList } from '/@/api/dataBoard/model'; |
| 6 | 6 | import { FrontComponent } from '../../const/const'; |
| ... | ... | @@ -10,6 +10,11 @@ import { findDictItemByCode } from '/@/api/system/dict'; |
| 10 | 10 | import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; |
| 11 | 11 | import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config'; |
| 12 | 12 | import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; |
| 13 | +import { JSONEditor } from '/@/components/CodeEditor'; | |
| 14 | +import { CommandTypeEnum } from '/@/views/rule/linkedge/config/config.data'; | |
| 15 | +import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | |
| 16 | + | |
| 17 | +useComponentRegister('JSONEditor', JSONEditor); | |
| 13 | 18 | |
| 14 | 19 | export enum BasicConfigField { |
| 15 | 20 | NAME = 'name', |
| ... | ... | @@ -47,6 +52,10 @@ export enum DataSourceField { |
| 47 | 52 | DEVICE_RENAME = 'deviceRename', |
| 48 | 53 | LONGITUDE_ATTRIBUTE = 'longitudeAttribute', |
| 49 | 54 | LATITUDE_ATTRIBUTE = 'latitudeAttribute', |
| 55 | + | |
| 56 | + COMMAND = 'command', | |
| 57 | + COMMAND_TYPE = 'commandType', | |
| 58 | + SERVICE = 'service', | |
| 50 | 59 | } |
| 51 | 60 | |
| 52 | 61 | export const isControlComponent = (frontId: FrontComponent) => { |
| ... | ... | @@ -139,6 +148,7 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For |
| 139 | 148 | [DataSourceField.TRANSPORT_TYPE]: null, |
| 140 | 149 | }); |
| 141 | 150 | }, |
| 151 | + getPopupContainer: () => document.body, | |
| 142 | 152 | }; |
| 143 | 153 | }, |
| 144 | 154 | }, |
| ... | ... | @@ -172,6 +182,7 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For |
| 172 | 182 | [DataSourceField.TRANSPORT_TYPE]: option[DataSourceField.TRANSPORT_TYPE], |
| 173 | 183 | }); |
| 174 | 184 | }, |
| 185 | + getPopupContainer: () => document.body, | |
| 175 | 186 | }; |
| 176 | 187 | }, |
| 177 | 188 | }, |
| ... | ... | @@ -252,31 +263,14 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For |
| 252 | 263 | component: 'ApiSelect', |
| 253 | 264 | label: '属性', |
| 254 | 265 | colProps: { span: 8 }, |
| 255 | - dynamicRules: ({ model }) => { | |
| 256 | - const transportType = model[DataSourceField.TRANSPORT_TYPE]; | |
| 257 | - return [ | |
| 258 | - { | |
| 259 | - required: true, | |
| 260 | - message: `${ | |
| 261 | - isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType) | |
| 262 | - ? '服务' | |
| 263 | - : '属性' | |
| 264 | - }为必填项`, | |
| 265 | - }, | |
| 266 | - ]; | |
| 267 | - }, | |
| 266 | + rules: [{ required: true, message: '请选择属性' }], | |
| 267 | + ifShow: ({ model }) => | |
| 268 | + !(isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && isControlComponent(frontId!)), | |
| 268 | 269 | componentProps({ formModel }) { |
| 269 | 270 | const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; |
| 270 | - const transportType = formModel[DataSourceField.TRANSPORT_TYPE]; | |
| 271 | - if (isEdit && ![deviceProfileId, transportType].every(Boolean)) | |
| 272 | - return { placeholder: '请选择属性', getPopupContainer: () => document.body }; | |
| 273 | 271 | return { |
| 274 | 272 | api: async () => { |
| 275 | 273 | try { |
| 276 | - if (isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType)) { | |
| 277 | - return await getDeviceService(deviceProfileId); | |
| 278 | - } | |
| 279 | - | |
| 280 | 274 | if (deviceProfileId) { |
| 281 | 275 | return await getDeviceAttribute({ |
| 282 | 276 | deviceProfileId, |
| ... | ... | @@ -284,18 +278,87 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For |
| 284 | 278 | }); |
| 285 | 279 | } |
| 286 | 280 | } catch (error) {} |
| 287 | - | |
| 288 | 281 | return []; |
| 289 | 282 | }, |
| 290 | - placeholder: | |
| 291 | - isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType) | |
| 292 | - ? '请选择服务' | |
| 293 | - : '请选择属性', | |
| 283 | + placeholder: '请选择属性', | |
| 284 | + getPopupContainer: () => document.body, | |
| 285 | + }; | |
| 286 | + }, | |
| 287 | + }, | |
| 288 | + { | |
| 289 | + field: DataSourceField.COMMAND_TYPE, | |
| 290 | + component: 'ApiSelect', | |
| 291 | + label: '命令类型', | |
| 292 | + defaultValue: CommandTypeEnum.CUSTOM.toString(), | |
| 293 | + colProps: { span: 8 }, | |
| 294 | + ifShow: ({ model }) => | |
| 295 | + isControlComponent(frontId!) && isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), | |
| 296 | + componentProps: ({ formActionType }) => { | |
| 297 | + const { setFieldsValue } = formActionType; | |
| 298 | + return { | |
| 299 | + api: findDictItemByCode, | |
| 300 | + params: { | |
| 301 | + dictCode: 'custom_define', | |
| 302 | + }, | |
| 303 | + labelField: 'itemText', | |
| 304 | + valueField: 'itemValue', | |
| 305 | + placeholder: '请选择命令类型', | |
| 306 | + onChange() { | |
| 307 | + setFieldsValue({ [DataSourceField.COMMAND]: null, [DataSourceField.SERVICE]: null }); | |
| 308 | + }, | |
| 309 | + }; | |
| 310 | + }, | |
| 311 | + }, | |
| 312 | + { | |
| 313 | + field: DataSourceField.SERVICE, | |
| 314 | + component: 'ApiSelect', | |
| 315 | + label: '服务', | |
| 316 | + colProps: { span: 8 }, | |
| 317 | + rules: [{ required: true, message: '请选择服务' }], | |
| 318 | + ifShow: ({ model }) => | |
| 319 | + isControlComponent(frontId as FrontComponent) && | |
| 320 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE.toString() && | |
| 321 | + isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), | |
| 322 | + componentProps({ formModel, formActionType }) { | |
| 323 | + const { setFieldsValue } = formActionType; | |
| 324 | + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | |
| 325 | + const transportType = formModel[DataSourceField.TRANSPORT_TYPE]; | |
| 326 | + if (isEdit && ![deviceProfileId, transportType].every(Boolean)) | |
| 327 | + return { placeholder: '请选择服务', getPopupContainer: () => document.body }; | |
| 328 | + return { | |
| 329 | + api: async () => { | |
| 330 | + try { | |
| 331 | + if (deviceProfileId) { | |
| 332 | + return await getDeviceService(deviceProfileId); | |
| 333 | + } | |
| 334 | + } catch (error) {} | |
| 335 | + return []; | |
| 336 | + }, | |
| 337 | + placeholder: '请选择服务', | |
| 294 | 338 | getPopupContainer: () => document.body, |
| 339 | + onChange(value: string, options: ModelOfMatterParams) { | |
| 340 | + const command = value ? (options.functionJson.inputData || [])[0].serviceCommand : null; | |
| 341 | + setFieldsValue({ [DataSourceField.COMMAND]: command }); | |
| 342 | + }, | |
| 295 | 343 | }; |
| 296 | 344 | }, |
| 297 | 345 | }, |
| 298 | 346 | { |
| 347 | + field: DataSourceField.COMMAND, | |
| 348 | + component: 'Input', | |
| 349 | + label: '命令', | |
| 350 | + colProps: { span: 8 }, | |
| 351 | + // 是控制组件 && 自定义命令 && 传输协议为TCP | |
| 352 | + ifShow: ({ model }) => | |
| 353 | + isControlComponent(frontId!) && | |
| 354 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM.toString() && | |
| 355 | + model[DataSourceField.TRANSPORT_TYPE] && | |
| 356 | + isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), | |
| 357 | + componentProps: { | |
| 358 | + placeholder: '请输入下发命令', | |
| 359 | + }, | |
| 360 | + }, | |
| 361 | + { | |
| 299 | 362 | field: DataSourceField.DEVICE_RENAME, |
| 300 | 363 | component: 'Input', |
| 301 | 364 | label: '设备名', | ... | ... |
| ... | ... | @@ -127,13 +127,15 @@ export function useSocketConnect(dataSourceRef: Ref<DataBoardLayoutInfo[]>) { |
| 127 | 127 | if (isNullAndUnDef(subscriptionId)) return; |
| 128 | 128 | const mappingRecord = cmdIdMapping.get(subscriptionId); |
| 129 | 129 | if (!mappingRecord || !data) return; |
| 130 | - mappingRecord.forEach((item) => { | |
| 130 | + | |
| 131 | + for (const item of mappingRecord) { | |
| 131 | 132 | const { attribute, recordIndex, dataSourceIndex } = item; |
| 133 | + if (!attribute) continue; | |
| 132 | 134 | const [[timespan, value]] = data[attribute]; |
| 133 | 135 | const record = getNeedUpdateValueByIndex(recordIndex, dataSourceIndex); |
| 134 | 136 | record.componentInfo.value = value; |
| 135 | 137 | record.componentInfo.updateTime = timespan; |
| 136 | - }); | |
| 138 | + } | |
| 137 | 139 | return; |
| 138 | 140 | } catch (error) { |
| 139 | 141 | throw Error(error as string); | ... | ... |