Commit b2bff1c5a02bdcd83240baf8c86094679b46587b

Authored by xp.Huang
2 parents e7195427 43a27418

Merge branch 'main_dev' into 'main'

Main dev

See merge request yunteng/thingskit-scada!224
Showing 64 changed files with 2252 additions and 1334 deletions

Too many changes to show.

To preserve performance only 64 of 71 files are displayed.

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, RpcCommandType, SendValue, ThingsModel, ThingsModelItemType } from './model' 1 +import type { DeviceActiveType, DeviceAttributeItemType, DeviceItemType, DeviceProfileItemType, OrganizationItemType, ProductsDetailWithThingsModelType, RpcCommandType, SendValue, ThingsModel, ThingsModelItemType } from './model'
2 import { CommandWayEnum } from '@/enums/commandEnum' 2 import { CommandWayEnum } from '@/enums/commandEnum'
3 -import type { DeviceTypeEnum } from '@/enums/datasource' 3 +import type { DeviceTypeEnum } from '@/enums/deviceEnum'
  4 +import { FunctionTypeEnum } from '@/enums/objectModelEnum'
4 import { isShareMode } from '@/utils/env' 5 import { isShareMode } from '@/utils/env'
5 import { defHttp } from '@/utils/http' 6 import { defHttp } from '@/utils/http'
6 7
@@ -13,7 +14,7 @@ enum Api { @@ -13,7 +14,7 @@ enum Api {
13 GET_THINGS_MODEL_SERVICES = '/things_model/get_services/', 14 GET_THINGS_MODEL_SERVICES = '/things_model/get_services/',
14 15
15 RPC_COMMAND = '/rpc/', 16 RPC_COMMAND = '/rpc/',
16 - GET_DEVICE_ACTIVE = 'plugins/telemetry/DEVICE/', 17 + GET_DEVICE_ACTIVE = '/plugins/telemetry/DEVICE/',
17 RPC_ONEWAY = '/rpc/oneway', 18 RPC_ONEWAY = '/rpc/oneway',
18 RPC_TWOWAY = '/rpc/twoway', 19 RPC_TWOWAY = '/rpc/twoway',
19 20
@@ -21,6 +22,9 @@ enum Api { @@ -21,6 +22,9 @@ enum Api {
21 GET_DEVICE_DETAIL = '/device/', // 获取设备详情 22 GET_DEVICE_DETAIL = '/device/', // 获取设备详情
22 23
23 GET_LIST_BY_CONFIGURATION_ID = '/configuration/center/getListByConfigurationId', 24 GET_LIST_BY_CONFIGURATION_ID = '/configuration/center/getListByConfigurationId',
  25 +
  26 + GET_PRODUCTS_DETAIL_WITH_THINGS_MODEL = '/things_model/batch/get_tsl',
  27 +
24 } 28 }
25 29
26 export interface GenModbusCommandType { 30 export interface GenModbusCommandType {
@@ -30,6 +34,7 @@ export interface GenModbusCommandType { @@ -30,6 +34,7 @@ export interface GenModbusCommandType {
30 registerAddress: number 34 registerAddress: number
31 registerNumber?: number 35 registerNumber?: number
32 registerValues?: number[] 36 registerValues?: number[]
  37 + hexByteOrderEnum?: string
33 } 38 }
34 39
35 export const getDeviceProfile = (deviceType?: DeviceTypeEnum) => { 40 export const getDeviceProfile = (deviceType?: DeviceTypeEnum) => {
@@ -125,6 +130,17 @@ export const getDeviceActiveTime = (entityId: string) => { @@ -125,6 +130,17 @@ export const getDeviceActiveTime = (entityId: string) => {
125 ) 130 )
126 } 131 }
127 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 +
128 // 获取设备详情 144 // 获取设备详情
129 export const getDeviceInfo = (deviceId: string) => { 145 export const getDeviceInfo = (deviceId: string) => {
130 return defHttp.get<DeviceItemType>( 146 return defHttp.get<DeviceItemType>(
@@ -139,3 +155,13 @@ export const getListByConfigurationId = (configurationId: string) => { @@ -139,3 +155,13 @@ export const getListByConfigurationId = (configurationId: string) => {
139 url: `${Api.GET_LIST_BY_CONFIGURATION_ID}?configurationId=${configurationId}`, 155 url: `${Api.GET_LIST_BY_CONFIGURATION_ID}?configurationId=${configurationId}`,
140 }) 156 })
141 } 157 }
  158 +
  159 +export const getProductsDetailWithThingsModel = ({ deviceProfileIds, functionTypeEnum }: { deviceProfileIds: string[]; functionTypeEnum?: FunctionTypeEnum | 'all' }) => {
  160 + return defHttp.post<ProductsDetailWithThingsModelType[]>({
  161 + url: Api.GET_PRODUCTS_DETAIL_WITH_THINGS_MODEL,
  162 + data: {
  163 + deviceProfileIds,
  164 + functionTypeEnum: functionTypeEnum || FunctionTypeEnum.PROPERTIES,
  165 + },
  166 + })
  167 +}
1 -import type { DataTypeEnum, FunctionType, 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
@@ -27,6 +29,7 @@ export interface ProfileData { @@ -27,6 +29,7 @@ export interface ProfileData {
27 interface AdditionalInfo { 29 interface AdditionalInfo {
28 cmdType: string 30 cmdType: string
29 } 31 }
  32 +
30 export interface SendValue { 33 export interface SendValue {
31 additionalInfo: AdditionalInfo 34 additionalInfo: AdditionalInfo
32 method: string 35 method: string
@@ -40,6 +43,7 @@ export interface Configuration { @@ -40,6 +43,7 @@ export interface Configuration {
40 43
41 export interface TransportConfiguration { 44 export interface TransportConfiguration {
42 type: string 45 type: string
  46 + protocol: TCPProtocolTypeEnum
43 } 47 }
44 48
45 export interface ProvisionConfiguration { 49 export interface ProvisionConfiguration {
@@ -57,7 +61,7 @@ export interface StructJSON { @@ -57,7 +61,7 @@ export interface StructJSON {
57 } 61 }
58 62
59 export interface ThingsModel { 63 export interface ThingsModel {
60 - functionType: FunctionType 64 + functionType: FunctionTypeEnum
61 functionName: string 65 functionName: string
62 identifier: string 66 identifier: string
63 callType: any 67 callType: any
@@ -71,7 +75,7 @@ export interface ThingsModel { @@ -71,7 +75,7 @@ export interface ThingsModel {
71 } 75 }
72 76
73 export interface FunctionJson { 77 export interface FunctionJson {
74 - dataType: DataType | DataType[] 78 + dataType: DataType
75 inputData?: StructJSON[] 79 inputData?: StructJSON[]
76 outputData?: StructJSON[] 80 outputData?: StructJSON[]
77 serviceCommand?: string 81 serviceCommand?: string
@@ -115,6 +119,10 @@ export interface DeviceItemType { @@ -115,6 +119,10 @@ export interface DeviceItemType {
115 deviceType: string 119 deviceType: string
116 alarmStatus: number 120 alarmStatus: number
117 enable: boolean 121 enable: boolean
  122 + deviceProfile: {
  123 + transportType: TransportTypeEnum
  124 + profileData: ProfileData
  125 + }
118 126
119 transportType: TransportTypeEnum 127 transportType: TransportTypeEnum
120 } 128 }
@@ -134,10 +142,14 @@ export interface ThingsModelItemType { @@ -134,10 +142,14 @@ export interface ThingsModelItemType {
134 } 142 }
135 143
136 export interface ExtensionDesc { 144 export interface ExtensionDesc {
137 - zoomFactor?: number  
138 - actionType?: string  
139 - dataType: string  
140 - 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
141 } 153 }
142 154
143 export interface Detail { 155 export interface Detail {
@@ -145,7 +157,7 @@ export interface Detail { @@ -145,7 +157,7 @@ export interface Detail {
145 } 157 }
146 158
147 export interface DataType { 159 export interface DataType {
148 - type: string 160 + type: DataTypeEnum
149 specs: Specs | StructJSON[] 161 specs: Specs | StructJSON[]
150 specsList: Specs[] 162 specsList: Specs[]
151 } 163 }
@@ -160,8 +172,9 @@ export interface Specs { @@ -160,8 +172,9 @@ export interface Specs {
160 min: string 172 min: string
161 max: string 173 max: string
162 name?: string 174 name?: string
163 - value?: string 175 + value?: any
164 dataType?: DataTypeEnum 176 dataType?: DataTypeEnum
  177 + step?: number
165 } 178 }
166 179
167 export interface ValueRange { 180 export interface ValueRange {
@@ -193,3 +206,25 @@ export interface DeviceActiveType { @@ -193,3 +206,25 @@ export interface DeviceActiveType {
193 lastUpdateTs: number 206 lastUpdateTs: number
194 value: boolean 207 value: boolean
195 } 208 }
  209 +
  210 +export interface ProductsDetailWithThingsModelType {
  211 + id: string
  212 + name: string
  213 + transportType: string
  214 + deviceType: string
  215 + tsl: Tsl[]
  216 +}
  217 +
  218 +export interface Tsl {
  219 + functionName: string
  220 + identifier: string
  221 + accessMode: string
  222 + functionType: FunctionTypeEnum
  223 + callType: CommandCallWayEnum
  224 + specs: {
  225 + dataType: DataType
  226 + }
  227 + extensionDesc?: ExtensionDesc
  228 + inputData?: StructJSON[]
  229 + outputData?: StructJSON[]
  230 +}
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, TransportTypeEnum } 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',
@@ -67,6 +68,8 @@ export interface VideoOptionType { @@ -67,6 +68,8 @@ export interface VideoOptionType {
67 orgId: string 68 orgId: string
68 videoComponentFlag: boolean 69 videoComponentFlag: boolean
69 videoUrl: string 70 videoUrl: string
  71 + channelId?: string
  72 + deviceId?: string
70 } 73 }
71 74
72 export interface AlarmListOptionType { 75 export interface AlarmListOptionType {
@@ -92,19 +95,15 @@ export interface NodeDataDataSourceJsonType { @@ -92,19 +95,15 @@ export interface NodeDataDataSourceJsonType {
92 deviceId: string 95 deviceId: string
93 deviceProfileId: string 96 deviceProfileId: string
94 deviceProfileTemplateId?: string 97 deviceProfileTemplateId?: string
95 - attr: string  
96 - attrInfo: ThingsModelItemType  
97 -  
98 - deviceType?: DeviceTypeEnum  
99 - transportType?: TransportTypeEnum 98 + attr: string | string[]
  99 + deviceName?: string
100 100
101 enable: boolean 101 enable: boolean
102 chartOption?: ChartOptionType 102 chartOption?: ChartOptionType
103 videoOption?: VideoOptionType 103 videoOption?: VideoOptionType
104 alarmListOption?: AlarmListOptionType 104 alarmListOption?: AlarmListOptionType
105 - deviceInfo?: DeviceInfoType  
106 - circularFlowMeterOption?: FlowMeterColorItemType[] // 圆形水球图数据暂定any  
107 - rectFlowMeterOption?: FlowMeterColorItemType[] // 方形水球图颜色配置数据暂定any 105 + circularFlowMeterOption?: FlowMeterColorItemType[]
  106 + rectFlowMeterOption?: FlowMeterColorItemType[]
108 107
109 } 108 }
110 109
@@ -128,17 +127,12 @@ export interface DoubleClickEventDataType { @@ -128,17 +127,12 @@ export interface DoubleClickEventDataType {
128 openPage?: string 127 openPage?: string
129 enable: boolean 128 enable: boolean
130 service?: string 129 service?: string
  130 +
131 serviceCommand?: Recordable 131 serviceCommand?: Recordable
132 way?: CommandWayEnum 132 way?: CommandWayEnum
133 customCommand?: string 133 customCommand?: string
134 - deviceInfo?: DeviceInfoType  
135 commandWay?: CommandDeliveryWayEnum 134 commandWay?: CommandDeliveryWayEnum
136 callType?: string 135 callType?: string
137 - operationPassword?: {  
138 - label: string  
139 - value: string | number  
140 - checked: boolean  
141 - }  
142 } 136 }
143 137
144 export interface SingleClickEventDataType extends DoubleClickEventDataType { } 138 export interface SingleClickEventDataType extends DoubleClickEventDataType { }
@@ -155,7 +149,7 @@ export interface NodeDataEventJsonType { @@ -155,7 +149,7 @@ export interface NodeDataEventJsonType {
155 [EventTypeEnum.SINGLE]: SingleClickEventDataType 149 [EventTypeEnum.SINGLE]: SingleClickEventDataType
156 [EventTypeEnum.DOWN]: MouseDownEventDataType 150 [EventTypeEnum.DOWN]: MouseDownEventDataType
157 [EventTypeEnum.UP]: MouseUpEventDataType 151 [EventTypeEnum.UP]: MouseUpEventDataType
158 - [EventTypeEnum.OPERATION_PASSWORD]: OperationPasswordDataType 152 + [EventTypeEnum.OPERATION_PASSWORD]?: OperationPasswordDataType
159 } 153 }
160 154
161 export interface RangeItemType { 155 export interface RangeItemType {
  1 +import type { VideoChannelPlayAddressType, VideoItemRecordType } from './model'
1 import { defHttp } from '@/utils/http' 2 import { defHttp } from '@/utils/http'
2 3
3 enum Api { 4 enum Api {
4 GET_VIDEO_LIST = '/video', 5 GET_VIDEO_LIST = '/video',
5 STREAMING_PLAY_GET_URL = '/video/url', 6 STREAMING_PLAY_GET_URL = '/video/url',
  7 + GET_VIDEO_ALL_LIST = '/video/list',
6 RTSP_CLOSEFLV = '/rtsp.closeFlv', 8 RTSP_CLOSEFLV = '/rtsp.closeFlv',
  9 + GET_VIDEO_CONTROL_START = '/video/control/start',
  10 + GET_VIDEO_CONTROL_STOP = '/video/control/stop',
7 } 11 }
8 12
9 // 获取视频组件--->频流 13 // 获取视频组件--->频流
10 export const getVideoList = (organizationId?: string) => { 14 export const getVideoList = (organizationId?: string) => {
11 - return defHttp.get({ 15 + return defHttp.get<{ items: VideoItemRecordType[] }>({
12 url: Api.GET_VIDEO_LIST, 16 url: Api.GET_VIDEO_LIST,
13 params: { 17 params: {
14 organizationId, 18 organizationId,
@@ -35,3 +39,21 @@ export const closeFlvPlay = (url: string, browserId: string) => { @@ -35,3 +39,21 @@ export const closeFlvPlay = (url: string, browserId: string) => {
35 }) 39 })
36 } 40 }
37 41
  42 +export const getVideoControlStart = ({
  43 + deviceId,
  44 + channelId,
  45 +}: Record<'deviceId' | 'channelId', string>) => {
  46 + return defHttp.get<VideoChannelPlayAddressType>(
  47 + {
  48 + url: `${Api.GET_VIDEO_CONTROL_START}/${deviceId}/${channelId}`,
  49 + timeout: 30 * 1000,
  50 + },
  51 + {},
  52 + )
  53 +}
  54 +export const getCameraList = (params: Record<'organizationId', string>) => {
  55 + return defHttp.get<{ data: VideoItemRecordType[] }>({
  56 + url: Api.GET_VIDEO_ALL_LIST,
  57 + params,
  58 + })
  59 +}
  1 +export interface VideoItemRecordType {
  2 + id: string
  3 + creator: string
  4 + createTime: string
  5 + name: string
  6 + enabled: boolean
  7 + tenantId: string
  8 + sn: string
  9 + organizationId: string
  10 + organizationName: string
  11 + status: boolean
  12 + accessMode: number
  13 + playProtocol: number
  14 + params: Params
  15 + videoUrl: string
  16 +}
  17 +
  18 +export interface Params {
  19 + channelNo: string
  20 + deviceId: string
  21 +}
  22 +
  23 +export interface VideoChannelPlayAddressType {
  24 + code: number
  25 + message: string
  26 + data: Data
  27 +}
  28 +
  29 +export interface Data {
  30 + app: string
  31 + stream: string
  32 + ip: any
  33 + flv: string
  34 + https_flv: string
  35 + ws_flv: string
  36 + wss_flv: string
  37 + fmp4: string
  38 + https_fmp4: string
  39 + ws_fmp4: string
  40 + wss_fmp4: string
  41 + hls: string
  42 + https_hls: string
  43 + ws_hls: string
  44 + wss_hls: string
  45 + ts: string
  46 + https_ts: string
  47 + ws_ts: string
  48 + wss_ts: any
  49 + rtmp: string
  50 + rtmps: string
  51 + rtsp: string
  52 + rtsps: string
  53 + rtc: string
  54 + rtcs: string
  55 + mediaServerId: string
  56 + tracks: Track[]
  57 + startTime: any
  58 + endTime: any
  59 + progress: number
  60 +}
  61 +
  62 +export interface Track {
  63 + channels: number
  64 + codecId: number
  65 + codecIdName: any
  66 + codecType: number
  67 + ready: boolean
  68 + sampleBit: number
  69 + sampleRate: number
  70 + fps: number
  71 + height: number
  72 + width: number
  73 +}
@@ -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[] => {
1 -import type { Specs, StructJSON } from '@/api/device/model'  
2 -import type { NodeDataDataSourceJsonType } from '@/api/node/model' 1 +import type { DeviceItemType, Specs, StructJSON, Tsl } from '@/api/device/model'
  2 +import type { NodeDataDataSourceJsonType, OperationPasswordDataType, SingleClickEventDataType } from '@/api/node/model'
3 import { type FormSchema, useComponentRegister } from '@/components/Form' 3 import { type FormSchema, useComponentRegister } from '@/components/Form'
4 import { ComponentEnum } from '@/components/Form/src/enum' 4 import { ComponentEnum } from '@/components/Form/src/enum'
5 -import { ThingsModelForm, validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm' 5 +import { ThingsModelForm } 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
@@ -15,83 +15,106 @@ export enum FormFieldsEnum { @@ -15,83 +15,106 @@ export enum FormFieldsEnum {
15 } 15 }
16 16
17 export interface AttributeDeliverModalOpenParamsType { 17 export interface AttributeDeliverModalOpenParamsType {
18 - title?: string  
19 - operationPassword?: string  
20 - operationPasswordEnable?: boolean 18 + operationPasswordInfo?: OperationPasswordDataType
21 dataSourceJson: NodeDataDataSourceJsonType 19 dataSourceJson: NodeDataDataSourceJsonType
  20 + eventBindData: SingleClickEventDataType
22 } 21 }
23 22
24 -function getStructJsonFromDataSourceJson(dataSourceJson: NodeDataDataSourceJsonType): StructJSON {  
25 - const { attrInfo } = dataSourceJson  
26 - const { identifier, name, detail } = attrInfo || {} 23 +export interface CreateFormSchemasParamsType {
  24 + deviceInfo: DeviceItemType
  25 + objectModelTsl: Tsl
  26 + operationPasswordInfo?: OperationPasswordDataType
  27 +}
  28 +
  29 +function getStructJsonFromDataSourceJson(objectModelTsl: Tsl): StructJSON {
  30 + const { identifier, functionName, specs } = objectModelTsl || {}
27 return { 31 return {
28 - functionName: name, 32 + functionName,
29 identifier, 33 identifier,
30 - dataType: detail.dataType, 34 + dataType: specs.dataType,
31 } 35 }
32 } 36 }
33 37
34 -function getTCPModbusSchemas({ structJson, required, actionType }: { structJson: StructJSON; required?: boolean; actionType: string }): FormSchema {  
35 - const { dataType } = structJson  
36 - const { specs, type } = dataType || {}  
37 - const { valueRange, length = 10240 } = specs! as Specs  
38 - const { max = Number.MAX_SAFE_INTEGER, min = Number.MIN_SAFE_INTEGER } = valueRange || {} 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
39 41
40 - function createInputNumber({  
41 - identifier,  
42 - functionName,  
43 - }: StructJSON): FormSchema {  
44 - return {  
45 - field: identifier,  
46 - label: functionName,  
47 - component: ComponentEnum.INPUT_NUMBER,  
48 - required,  
49 - componentProps: {  
50 - max: actionType === TCPObjectModelActionTypeEnum.BOOL ? 1 : max,  
51 - min: actionType === TCPObjectModelActionTypeEnum.BOOL ? 0 : min,  
52 - precision: actionType === TCPObjectModelActionTypeEnum.BOOL ? 0 : 2,  
53 - },  
54 - } 42 + return {
  43 + flag: value < min || value > max,
  44 + message: `取值范围在${min}~${max}之间`,
55 } 45 }
  46 +}
  47 +
  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 || {}
  55 +
  56 + if (extensionDesc?.originalDataType === OriginalDataTypeEnum.BOOLEAN) {
  57 + const options = [
  58 + { label: '闭合', value: parseInt('FF00', 16) },
  59 + { label: '断开', value: parseInt('0000', 16) },
  60 + ]
56 61
57 - const createInput = ({ identifier, functionName }: StructJSON): FormSchema => {  
58 return { 62 return {
59 field: identifier, 63 field: identifier,
60 label: functionName, 64 label: functionName,
61 - component: ComponentEnum.INPUT,  
62 - rules: [  
63 - {  
64 - required,  
65 - message: `${functionName}是必填项`,  
66 - },  
67 - {  
68 - validator: validateTCPCustomCommand,  
69 - },  
70 - ],  
71 - componentProps: {  
72 - maxLength: length, 65 + component: ComponentEnum.SELECT,
  66 + componentProps: () => {
  67 + return {
  68 + options,
  69 + placeholder: `请选择${functionName}`,
  70 + getPopupContainer: () => document.body,
  71 + }
73 }, 72 },
74 - } as FormSchema 73 + }
75 } 74 }
76 75
77 - return type === DataTypeEnum.STRING ? createInput(structJson) : createInputNumber(structJson) 76 + const isStringType = extensionDesc?.originalDataType === OriginalDataTypeEnum.STRING
  77 + return {
  78 + field: identifier,
  79 + label: functionName,
  80 + component: isStringType ? ComponentEnum.INPUT : ComponentEnum.INPUT_NUMBER,
  81 + rules: isStringType
  82 + ? []
  83 + : [
  84 + {
  85 + type: 'number',
  86 + validator: (_rule, value) => {
  87 + const { flag, message } = validateDouble(value, min, max)
  88 + if (flag)
  89 + return Promise.reject(Error(`${functionName}${message}`))
  90 +
  91 + return Promise.resolve(value)
  92 + },
  93 + },
  94 + ],
  95 + componentProps: {
  96 + // max: max ?? Number.MAX_SAFE_INTEGER,
  97 + // min: min ?? Number.MIN_SAFE_INTEGER,
  98 + placeholder: `请输入${functionName}`,
  99 + precision: 0,
  100 + },
  101 + }
78 } 102 }
79 103
80 -export const createFormSchemas = ({ operationPassword, operationPasswordEnable, dataSourceJson }: AttributeDeliverModalOpenParamsType): FormSchema[] => { 104 +export const createFormSchemas = ({ operationPasswordInfo, objectModelTsl, deviceInfo }: CreateFormSchemasParamsType): FormSchema[] => {
81 const schemas: FormSchema[] = [] 105 const schemas: FormSchema[] = []
82 106
83 - const { deviceInfo } = dataSourceJson  
84 - if (deviceInfo?.transportType === TransportTypeEnum.TCP && deviceInfo.codeType === CodeTypeEnum.MODBUS_RTU && dataSourceJson.deviceInfo?.codeType) {  
85 - schemas.push(getTCPModbusSchemas({ required: true, structJson: getStructJsonFromDataSourceJson(dataSourceJson), actionType: dataSourceJson.deviceInfo?.codeType })) 107 + if (deviceInfo?.transportType === TransportTypeEnum.TCP && deviceInfo.deviceProfile?.profileData?.transportConfiguration?.protocol === TCPProtocolTypeEnum.MODBUS_RTU) {
  108 + schemas.push(createModbusValueInput(objectModelTsl))
86 } 109 }
87 else { 110 else {
88 - const isStructType = dataSourceJson.attrInfo?.detail?.dataType?.type === DataTypeEnum.STRUCT 111 + const isStructType = objectModelTsl.specs.dataType.type === DataTypeEnum.STRUCT
89 schemas.push( 112 schemas.push(
90 - ...getFormSchemas({ structJSON: isStructType ? dataSourceJson?.attrInfo?.detail?.dataType?.specs as StructJSON[] || [] : [getStructJsonFromDataSourceJson(dataSourceJson)], required: !isStructType }), 113 + ...getFormSchemas({ structJSON: isStructType ? objectModelTsl.specs.dataType.specs as StructJSON[] || [] : [getStructJsonFromDataSourceJson(objectModelTsl)], required: !isStructType }),
91 ) 114 )
92 } 115 }
93 116
94 - if (operationPassword && operationPasswordEnable) { 117 + if (operationPasswordInfo?.value && operationPasswordInfo.checked) {
95 schemas.unshift({ 118 schemas.unshift({
96 field: FormFieldsEnum.PASSWORD, 119 field: FormFieldsEnum.PASSWORD,
97 label: '操作密码', 120 label: '操作密码',
@@ -100,7 +123,7 @@ export const createFormSchemas = ({ operationPassword, operationPasswordEnable, @@ -100,7 +123,7 @@ export const createFormSchemas = ({ operationPassword, operationPasswordEnable,
100 rules: [ 123 rules: [
101 { 124 {
102 validator(_rule, value) { 125 validator(_rule, value) {
103 - if (value && value !== operationPassword) return Promise.reject(new Error('操作密码不正确')) 126 + if (value && value !== operationPasswordInfo.value) return Promise.reject(new Error('操作密码不正确'))
104 return Promise.resolve() 127 return Promise.resolve()
105 }, 128 },
106 }, 129 },
1 <script setup lang="ts"> 1 <script setup lang="ts">
2 import { Modal } from 'ant-design-vue' 2 import { Modal } from 'ant-design-vue'
3 -import { nextTick, ref, unref } from 'vue'  
4 -import type { AttributeDeliverModalOpenParamsType } from './AttributeDeliverModal.config'  
5 -import { createFormSchemas } from './AttributeDeliverModal.config'  
6 -import { useGetModbusCommand } from './useGetModbusCommand' 3 +import { nextTick, ref, toRaw, unref } from 'vue'
  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 type { NodeDataDataSourceJsonType } from '@/api/node/model'  
10 -import { CodeTypeEnum, DataTypeEnum, TransportTypeEnum } from '@/enums/datasource'  
11 -  
12 -const resolveFn = ref<Fn>() 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'
13 14
14 const visible = ref(false) 15 const visible = ref(false)
15 16
16 -const password = ref()  
17 -  
18 -const currentDataSourceJson = ref<NodeDataDataSourceJsonType>()  
19 -  
20 const [register, { getFieldsValue, resetFields, validate, setProps, clearValidate }] = useForm({ 17 const [register, { getFieldsValue, resetFields, validate, setProps, clearValidate }] = useForm({
21 layout: FormLayoutEnum.VERTICAL, 18 layout: FormLayoutEnum.VERTICAL,
22 showActionButtonGroup: false, 19 showActionButtonGroup: false,
23 }) 20 })
24 21
25 -const open = async ({ title, operationPassword, operationPasswordEnable, dataSourceJson }: AttributeDeliverModalOpenParamsType) => { 22 +const deviceInfo = ref<DeviceItemType>()
  23 +const objectModelTsl = ref<Tsl>()
  24 +const productsStore = useProductsStore()
  25 +const commandWay = ref<CommandWayEnum>()
  26 +
  27 +const open = async ({ operationPasswordInfo, dataSourceJson, eventBindData }: AttributeDeliverModalOpenParamsType) => {
26 visible.value = true 28 visible.value = true
27 - password.value = operationPassword  
28 - currentDataSourceJson.value = dataSourceJson  
29 - return new Promise((resolve) => {  
30 - resolveFn.value = resolve  
31 - nextTick(() => {  
32 - setProps({ schemas: createFormSchemas({ title, operationPassword, operationPasswordEnable, dataSourceJson }) })  
33 - }) 29 + const { deviceId, deviceProfileId, attr } = dataSourceJson
  30 + const { way } = eventBindData
  31 + commandWay.value = way
  32 + objectModelTsl.value = productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, attr as string)!
  33 + deviceInfo.value = await getDeviceInfo(deviceId)
  34 +
  35 + nextTick(() => {
  36 + const schemas = createFormSchemas({ operationPasswordInfo, deviceInfo: toRaw(unref(deviceInfo)!), objectModelTsl: toRaw(unref(objectModelTsl)!) })
  37 + setProps({ schemas })
34 }) 38 })
35 } 39 }
36 40
37 async function getResult() { 41 async function getResult() {
38 - const result = getFieldsValue()  
39 - const isTCPModbusDevice = unref(currentDataSourceJson)?.deviceInfo?.transportType === TransportTypeEnum.TCP && unref(currentDataSourceJson)?.deviceInfo?.codeType === CodeTypeEnum.MODBUS_RTU  
40 - const isStructJSON = unref(currentDataSourceJson)?.attrInfo?.detail?.dataType?.type === DataTypeEnum.STRUCT  
41 - const attrKey = unref(currentDataSourceJson)!.attr  
42 - if (!isTCPModbusDevice) { return isStructJSON ? result : result[attrKey] }  
43 - else {  
44 - const value = result[attrKey]  
45 - const isString = unref(currentDataSourceJson)?.attrInfo?.detail?.dataType?.type === DataTypeEnum.STRING 42 + let value = getFieldsValue()
  43 + const { doCommandDelivery } = useCommandDelivery()
46 44
47 - if (isString) return value 45 + const isStructJSON = unref(objectModelTsl)?.specs.dataType.type === DataTypeEnum.STRUCT
  46 + const identifier = unref(objectModelTsl)!.identifier
  47 + value = isStructJSON ? value : value[identifier]
48 48
49 - const { getModbusCommand, validateCanGetCommand } = useGetModbusCommand()  
50 - if (!validateCanGetCommand(unref(currentDataSourceJson)!.attrInfo.extensionDesc, unref(currentDataSourceJson)!.deviceInfo?.code).flag) return 49 + await doCommandDelivery({
  50 + objectModel: unref(objectModelTsl),
  51 + deviceDetail: unref(deviceInfo),
  52 + value,
  53 + })
51 54
52 - const res = await getModbusCommand(value as unknown as number, unref(currentDataSourceJson)!.attrInfo.extensionDesc!, unref(currentDataSourceJson)!.deviceInfo!.code!)  
53 - return res  
54 - } 55 + const { createMessage } = useMessage()
  56 + createMessage.success('命令下发成功')
55 } 57 }
56 58
57 const handleOk = async () => { 59 const handleOk = async () => {
58 await validate() 60 await validate()
59 - const result = await getResult()  
60 - if (!result) return  
61 - unref(resolveFn)?.(result) 61 + await getResult()
62 visible.value = false 62 visible.value = false
63 resetFields() 63 resetFields()
64 } 64 }
@@ -73,7 +73,7 @@ defineExpose({ open }) @@ -73,7 +73,7 @@ defineExpose({ open })
73 73
74 <template> 74 <template>
75 <Modal 75 <Modal
76 - v-model:open="visible" title="属性下发" 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"
77 @ok="handleOk" 77 @ok="handleOk"
78 > 78 >
79 <BasicForm @register="register" /> 79 <BasicForm @register="register" />
1 -<script setup lang="ts">  
2 -import { Modal } from 'ant-design-vue'  
3 -import { nextTick, ref, unref } from 'vue'  
4 -import { BasicForm, type FormSchema, useForm } from '@/components/Form'  
5 -import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum'  
6 -  
7 -const resolveFn = ref<Fn>()  
8 -  
9 -const visible = ref(false)  
10 -  
11 -const [register, { resetFields, validate, setProps }] = useForm({  
12 - showActionButtonGroup: false,  
13 - layout: FormLayoutEnum.VERTICAL,  
14 -})  
15 -  
16 -const createFormSchemas = (password: string): FormSchema[] => {  
17 - return [  
18 - {  
19 - field: 'password',  
20 - component: ComponentEnum.INPUT_PAWSSWORD,  
21 - label: '',  
22 - required: true,  
23 - rules: [{  
24 - validator(_rule, value) {  
25 - if (value && value !== password) return Promise.reject(new Error('操作密码不正确'))  
26 - return Promise.resolve()  
27 - },  
28 - }],  
29 - componentProps: {  
30 - placeholder: '请输入操作密码',  
31 - },  
32 - },  
33 - ]  
34 -}  
35 -  
36 -const open = async (pwd: string) => {  
37 - visible.value = true  
38 - return new Promise((resolve) => {  
39 - resolveFn.value = resolve  
40 -  
41 - nextTick(() => {  
42 - setProps({ schemas: createFormSchemas(pwd) })  
43 - })  
44 - })  
45 -}  
46 -  
47 -const handlerOk = async () => {  
48 - await validate()  
49 - unref(resolveFn)?.(true)  
50 - resetFields()  
51 - visible.value = false  
52 -}  
53 -  
54 -defineExpose({ open })  
55 -</script>  
56 -  
57 -<template>  
58 - <Modal  
59 - v-model:open="visible" :width="300" ok-text="确认" cancel-text="取消"  
60 - title="操作密码"  
61 - @ok="handlerOk"  
62 - >  
63 - <BasicForm @register="register" />  
64 - </Modal>  
65 -</template>  
1 -<script setup lang="ts">  
2 -import { Icon } from '@iconify/vue'  
3 -import { Modal } from 'ant-design-vue'  
4 -import { ref, unref } from 'vue'  
5 -  
6 -const visible = ref(false)  
7 -  
8 -const resolveFn = ref<Fn>()  
9 -  
10 -const rejectFn = ref<Fn>()  
11 -  
12 -const open = async () => {  
13 - visible.value = true  
14 - return new Promise((resolve, reject) => {  
15 - resolveFn.value = resolve  
16 - rejectFn.value = reject  
17 - })  
18 -}  
19 -  
20 -const handleOk = () => {  
21 - unref(resolveFn)?.()  
22 - visible.value = false  
23 -}  
24 -  
25 -const handleCancel = () => {  
26 - unref(rejectFn)?.()  
27 - visible.value = false  
28 -}  
29 -  
30 -defineExpose({ open })  
31 -</script>  
32 -  
33 -<template>  
34 - <Modal v-model:open="visible" :width="400" ok-text="确认" cancel-text="取消" @ok="handleOk" @cancel="handleCancel">  
35 - <template #title>  
36 - <div class="flex">  
37 - <span><Icon icon="ant-design:exclamation-circle-filled" class="text-yellow-300 text-xl" /></span>  
38 - <span class="font-bold ml-2">提示</span>  
39 - </div>  
40 - </template>  
41 - <div class="ml-5">  
42 - 是否确认此操作?  
43 - </div>  
44 - </Modal>  
45 -</template>  
  1 +<script setup lang="ts">
  2 +import { Modal } from 'ant-design-vue'
  3 +import { computed, nextTick, ref, unref } from 'vue'
  4 +import { Icon } from '@iconify/vue'
  5 +import { BasicForm, type FormSchema, useForm } from '@/components/Form'
  6 +import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum'
  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'
  15 +
  16 +interface SwitchCommandDeliveryModalParamsType {
  17 + operationPasswordInfo?: OperationPasswordDataType
  18 + dataSourceJson: NodeDataDataSourceJsonType
  19 + eventBindData: SingleClickEventDataType
  20 + sendValue: any
  21 +}
  22 +
  23 +const visible = ref(false)
  24 +
  25 +const currentOperationPasswordInfo = ref<OperationPasswordDataType>()
  26 +
  27 +const [register, { resetFields, validate, setProps }] = useForm({
  28 + showActionButtonGroup: false,
  29 + layout: FormLayoutEnum.VERTICAL,
  30 +})
  31 +
  32 +const createFormSchemas = (password: string): FormSchema[] => {
  33 + return [
  34 + {
  35 + field: 'password',
  36 + component: ComponentEnum.INPUT_PAWSSWORD,
  37 + label: '操作密码',
  38 + required: true,
  39 + rules: [{
  40 + validator(_rule, value) {
  41 + if (value && value !== password) return Promise.reject(new Error('操作密码不正确'))
  42 + return Promise.resolve()
  43 + },
  44 + }],
  45 + componentProps: {
  46 + placeholder: '请输入操作密码',
  47 + },
  48 + },
  49 + ]
  50 +}
  51 +
  52 +const productsStore = useProductsStore()
  53 +
  54 +const getHasEnableOperationPassword = computed(() => !!(unref(currentOperationPasswordInfo)?.checked && unref(currentOperationPasswordInfo)?.value))
  55 +
  56 +const currentParams = ref<SwitchCommandDeliveryModalParamsType>()
  57 +const open = async (params: SwitchCommandDeliveryModalParamsType) => {
  58 + const { operationPasswordInfo } = params
  59 + visible.value = true
  60 + currentParams.value = params
  61 + currentOperationPasswordInfo.value = operationPasswordInfo
  62 +
  63 + nextTick(() => {
  64 + unref(getHasEnableOperationPassword) && setProps({ schemas: createFormSchemas(operationPasswordInfo!.value!) })
  65 + })
  66 +}
  67 +
  68 +const handlerOk = async () => {
  69 + if (unref(getHasEnableOperationPassword))
  70 + await validate()
  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 +
  101 + if (unref(getHasEnableOperationPassword)) resetFields()
  102 + visible.value = false
  103 +}
  104 +
  105 +defineExpose({ open })
  106 +</script>
  107 +
  108 +<template>
  109 + <Modal v-model:open="visible" :width="300" ok-text="确认" cancel-text="取消" destroy-on-close @ok="handlerOk">
  110 + <BasicForm v-if="getHasEnableOperationPassword" @register="register" />
  111 + <div v-else class="flex items-center">
  112 + <Icon icon="ant-design:exclamation-circle-filled" class="text-yellow-300 text-xl" />
  113 + <span class="ml-2">
  114 + 是否确认此操作?
  115 + </span>
  116 + </div>
  117 + </Modal>
  118 +</template>
1 import { ComponentEnum } from '@/components/Form/src/enum' 1 import { ComponentEnum } from '@/components/Form/src/enum'
2 import type { FormSchema } from '@/components/Form' 2 import type { FormSchema } from '@/components/Form'
3 -import type { StructJSON } from '@/api/device/model' 3 +import type { OperationPasswordDataType } from '@/api/node/model'
  4 +import { validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm'
  5 +import { JSONEditorValidator } from '@/components/CodeEditor/src/JSONEditor'
  6 +
  7 +export enum FormFieldsEnum {
  8 + CUSTOM_COMMAND = 'customCommand',
  9 + OPERATION_PASSWORD = 'password',
  10 +
  11 + SERVICE_COMMAND = 'serviceCommand',
  12 +}
  13 +
4 export const formSchemas: FormSchema[] = [ 14 export const formSchemas: FormSchema[] = [
5 { 15 {
6 field: 'sendValue', 16 field: 'sendValue',
@@ -10,219 +20,53 @@ export const formSchemas: FormSchema[] = [ @@ -10,219 +20,53 @@ export const formSchemas: FormSchema[] = [
10 }, 20 },
11 ] 21 ]
12 22
13 -const InsertString = (t: any, c: any, n: any) => {  
14 - const r: string | number[] = []  
15 -  
16 - for (let i = 0; i * 2 < t.length; i++)  
17 - r.push(t.substr(i * 2, n))  
18 -  
19 - return r.join(c)  
20 -}  
21 -const FillString = (t: any, c: any, n: any, b: any) => {  
22 - if (t === '' || c.length !== 1 || n <= t.length)  
23 - return t  
24 -  
25 - const l = t.length  
26 -  
27 - for (let i = 0; i < n - l; i++) {  
28 - if (b === true)  
29 - t = c + t  
30 -  
31 - else  
32 - t += c  
33 - }  
34 - return t  
35 -}  
36 -const SingleToHex = (t: any) => {  
37 - if (t === '')  
38 - return ''  
39 -  
40 - t = parseFloat(t)  
41 -  
42 - if (isNaN(t) === true)  
43 - return 'Error'  
44 -  
45 - if (t === 0)  
46 - return '00000000'  
47 -  
48 - let s, e, m  
49 -  
50 - if (t > 0) {  
51 - s = 0  
52 - }  
53 - else {  
54 - s = 1  
55 -  
56 - t = 0 - t  
57 - }  
58 - m = t.toString(2)  
59 -  
60 - if (m >= 1) {  
61 - if (m.indexOf('.') === -1)  
62 - m = `${m}.0`  
63 -  
64 - e = m.indexOf('.') - 1  
65 - }  
66 - else {  
67 - e = 1 - m.indexOf('1')  
68 - }  
69 - if (e >= 0)  
70 - m = m.replace('.', '')  
71 -  
72 - else  
73 - m = m.substring(m.indexOf('1'))  
74 -  
75 - if (m.length > 24)  
76 - m = m.substr(0, 24)  
77 -  
78 - else  
79 - m = FillString(m, '0', 24, false)  
80 -  
81 - m = m.substring(1)  
82 -  
83 - e = (e + 127).toString(2)  
84 -  
85 - e = FillString(e, '0', 8, true)  
86 -  
87 - let r = parseInt(s + e + m, 2).toString(16)  
88 -  
89 - r = FillString(r, '0', 8, true)  
90 -  
91 - return InsertString(r, ' ', 2).toUpperCase()  
92 -}  
93 -  
94 -const FormatHex = (t: any, n: any, ie: any) => {  
95 - const r: string[] = []  
96 -  
97 - let s = ''  
98 -  
99 - let c = 0  
100 -  
101 - for (let i = 0; i < t.length; i++) {  
102 - if (t.charAt(i) !== ' ') {  
103 - s += t.charAt(i)  
104 -  
105 - c += 1  
106 -  
107 - if (c === n) {  
108 - r.push(s)  
109 -  
110 - s = ''  
111 -  
112 - c = 0  
113 - }  
114 - }  
115 - if (ie === false) {  
116 - if (i === t.length - 1 && s !== '')  
117 - r.push(s)  
118 - }  
119 - }  
120 - return r.join('\n')  
121 -}  
122 -const FormatHexBatch = (t: any, n: any, ie: any) => {  
123 - const a = t.split('\n')  
124 -  
125 - const r: string[] = []  
126 -  
127 - for (let i = 0; i < a.length; i++)  
128 - r[i] = FormatHex(a[i], n, ie)  
129 -  
130 - return r.join('\n')  
131 -}  
132 -const SingleToHexBatch = (t: any) => {  
133 - const a = t.split('\n')  
134 -  
135 - const r: string[] = []  
136 -  
137 - for (let i = 0; i < a.length; i++)  
138 - r[i] = SingleToHex(a[i])  
139 -  
140 - return r.join('\r\n')  
141 -}  
142 -  
143 -const formSchemasConfig = (schemas: StructJSON, actionType: any): FormSchema[] => {  
144 - const { identifier, functionName } = schemas  
145 - if (actionType === '06') {  
146 - return [  
147 - {  
148 - field: identifier,  
149 - label: functionName,  
150 - component: ComponentEnum.INPUT_NUMBER,  
151 - rules: [{ required: true, message: '请输入正数' }],  
152 - componentProps: {  
153 - min: 0,  
154 - formatter: (e: any) => {  
155 - const value = `${e}`.replace('-', '').replace(/^(-)*(\d+)\.(\d\d).*$/, '$1$2.$3')  
156 - return value 23 +export const getOperationPasswordSchemas = (operationPasswordInfo: OperationPasswordDataType): FormSchema[] => {
  24 + return [
  25 + {
  26 + field: FormFieldsEnum.OPERATION_PASSWORD,
  27 + label: '操作密码',
  28 + component: ComponentEnum.INPUT_PAWSSWORD,
  29 + required: true,
  30 + rules: [
  31 + {
  32 + validator(_rule, value: string) {
  33 + if (value !== operationPasswordInfo.value)
  34 + return Promise.reject(Error('操作密码不正确'))
  35 + return Promise.resolve()
157 }, 36 },
158 - placeholder: '请输入正数',  
159 }, 37 },
  38 + ],
  39 + componentProps: {
  40 + placeholder: '请输入操作密码',
  41 + max: 120,
  42 + autocomplete: 'cc-number',
160 }, 43 },
161 - ]  
162 - }  
163 - else if (actionType === '05') {  
164 - return [  
165 - {  
166 - field: identifier,  
167 - label: functionName,  
168 - component: ComponentEnum.INPUT_NUMBER,  
169 - rules: [{ required: true, message: '请输入值' }],  
170 - componentProps: {  
171 - min: 0,  
172 - max: 1,  
173 - precision: 0,  
174 - placeholder: '请输入0或1',  
175 - },  
176 - },  
177 - ]  
178 - }  
179 - else {  
180 - return [  
181 - {  
182 - field: identifier,  
183 - label: functionName,  
184 - component: ComponentEnum.INPUT_NUMBER,  
185 - rules: [{ required: true, message: '请输入值' }],  
186 - componentProps: {  
187 - placeholder: '请输入数字',  
188 - formatter: (e: any) =>  
189 - `${e}`.replace(/\B(?=(\d{3})+(?!\d))/g, '').replace(/^(-)*(\d+)\.(\d\d).*$/, '$1$2.$3'),  
190 - },  
191 - },  
192 - ]  
193 - } 44 + },
  45 + ]
194 } 46 }
195 47
196 -const getArray = (values: any) => {  
197 - const str = values.replace(/\s+/g, '')  
198 - const array: any = []  
199 -  
200 - for (let i = 0; i < str.length; i += 4) {  
201 - const chunk = parseInt(str.substring(i, i + 4), 16)  
202 - array.push(chunk)  
203 - }  
204 - return array 48 +export const getTcpCustomCommandSchemas = (): FormSchema[] => {
  49 + return [
  50 + {
  51 + field: FormFieldsEnum.CUSTOM_COMMAND,
  52 + label: '自定义命令',
  53 + component: ComponentEnum.INPUT,
  54 + required: true,
  55 + rules: [{ validator: validateTCPCustomCommand }],
  56 + },
  57 + ]
205 } 58 }
206 59
207 -// 获取小数  
208 -const getFloatPart = (number: string | number) => {  
209 - const isLessZero = Number(number) < 0  
210 - number = number.toString()  
211 - const floatPartStartIndex = number.indexOf('.')  
212 - const value = ~floatPartStartIndex  
213 - ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}`  
214 - : '0'  
215 - return Number(value) 60 +export const getJSONCommandSchemas = (): FormSchema[] => {
  61 + return [
  62 + {
  63 + field: FormFieldsEnum.CUSTOM_COMMAND,
  64 + label: '自定义命令',
  65 + component: ComponentEnum.JSON_EDITOR,
  66 + required: true,
  67 + changeEvent: 'update:value',
  68 + rules: JSONEditorValidator(),
  69 + },
  70 + ]
216 } 71 }
217 72
218 -export {  
219 - InsertString,  
220 - FillString,  
221 - SingleToHex,  
222 - FormatHex,  
223 - FormatHexBatch,  
224 - SingleToHexBatch,  
225 - formSchemasConfig,  
226 - getArray,  
227 - getFloatPart,  
228 -}  
1 export { default as CommandDeliveryModal } from './index.vue' 1 export { default as CommandDeliveryModal } from './index.vue'
2 -export { default as CommandDeliveryConfirmModal } from './CommandDeliveryConfirmModal.vue'  
3 -export { default as ConfirmModal } from './ConfirmModal.vue' 2 +export { default as SwitchCommandDeliveryModal } from './SwitchCommandDeliveryModal.vue'
4 export { default as AttributeDeliverModal } from './AttributeDeliverModal.vue' 3 export { default as AttributeDeliverModal } from './AttributeDeliverModal.vue'
1 <script setup lang="ts"> 1 <script setup lang="ts">
2 -import { nextTick, reactive, ref, unref } from 'vue' 2 +import { nextTick, reactive, ref, toRaw, unref } from 'vue'
3 import { Button } from 'ant-design-vue' 3 import { Button } from 'ant-design-vue'
4 -import { SingleToHex, formSchemas, formSchemasConfig, getArray, getFloatPart } from './config.ts'  
5 -import { BasicModal } from '@/components/Modal'  
6 -import type { SingleClickEventDataType } from '@/api/node/model' 4 +import { FormFieldsEnum, formSchemas, getJSONCommandSchemas, getOperationPasswordSchemas, getTcpCustomCommandSchemas } from './config.ts'
  5 +import { createModbusValueInput } from './AttributeDeliverModal.config'
  6 +import { BasicModal, useModalInner } from '@/components/Modal'
  7 +import type { NodeDataDataSourceJsonType, OperationPasswordDataType, SingleClickEventDataType } from '@/api/node/model'
7 import { BasicForm, useForm } from '@/components/Form' 8 import { BasicForm, useForm } from '@/components/Form'
8 -import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum'  
9 -import { CommandDeliveryWayEnum, CommandTypeEnum, TransportTypeEnum } from '@/enums/datasource'  
10 -import { genModbusCommand, getDeviceActiveTime, getDeviceInfo, getThingsModelServices, sendRpcOneway } from '@/api/device' 9 +import { FormLayoutEnum } from '@/components/Form/src/enum'
11 import { ThingsModelForm } from '@/core/Library/components/ThingsModelForm' 10 import { ThingsModelForm } from '@/core/Library/components/ThingsModelForm'
  11 +import type { DeviceItemType, Tsl } from '@/api/device/model'
  12 +import { useProductsStore, useProductsStoreWithOut } from '@/store/modules/products'
  13 +import { useJsonParse } from '@/hooks/business/useJSONParse'
  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'
12 import { useMessage } from '@/hooks/web/useMessage' 19 import { useMessage } from '@/hooks/web/useMessage'
13 -import type { StructJSON } from '@/api/device/model'  
14 -  
15 -interface ServiceInfo {  
16 - title?: string  
17 - serviceCommand?: Object  
18 - inputData?: []  
19 - transportType?: string  
20 - callType?: string  
21 - code?: string  
22 - way?: string  
23 -}  
24 -  
25 -const visible = ref<boolean>(false)  
26 20
27 -const { createMessage } = useMessage()  
28 -const loading = ref<boolean>(false)  
29 -  
30 -const dataSourceJson = ref<any>({})  
31 -const thingsModelElRef = ref() 21 +interface OpenParamsType {
  22 + dataSource: NodeDataDataSourceJsonType
  23 + eventBindData: SingleClickEventDataType
  24 + operationPasswordInfo?: OperationPasswordDataType
  25 +}
32 26
33 -const isInputData = ref<boolean>(false)// 判断使用那个表单 true: 用封装的表单 false:用自己写的表单 27 +const thingsModelElRef = ref<InstanceType<typeof ThingsModelForm>>()
34 28
35 -const zoomFactorValue = ref<number>(1) // 缩放因子  
36 -const isShowMultiply = ref<boolean>(false) // 只有tcp --> int和double类型才相乘缩放因子  
37 -const isShowActionType = ref<boolean>(true) // 判断设备属性标识符为modBus时没有填写扩展描述  
38 -const isShowModBUS = ref<boolean>(false) // 用于判断标识符类型是否时自定义还是modBUS  
39 -const modBUSForm = ref<any>({})  
40 -const formField = ref('') // 存一个表单取值的field  
41 -const isCommandWay = ref<undefined | string>('') // 存取命令下发方式的值  
42 -const deviceTitle = ref<string | undefined>()// 设备名称 29 +const getDefaultConfiguration = () => ({
  30 + modalVisible: false,
  31 + commandDeliveryWay: CommandDeliveryWayEnum.CUSTOM,
  32 + operationPasswordEnable: false,
  33 +})
43 34
44 -const isPasswordInfo = ref<{ checked?: boolean; value?: string | number; label?: string }>({})// 是否需要操作密码才进行命令下发 35 +const configuration = reactive(getDefaultConfiguration())
45 36
46 -const serviceInfo = reactive<ServiceInfo>({  
47 - title: '',  
48 - serviceCommand: {},  
49 - inputData: [],  
50 - transportType: '',  
51 - callType: '',  
52 - code: '',  
53 - way: '', // 单向还是双向  
54 -}) 37 +const [registerModal, { setModalProps, closeModal }] = useModalInner()
55 38
56 -const [register, { getFieldsValue, validate, setFieldsValue, updateSchema, setProps }] = useForm({ // 自定义下发值  
57 - schemas: formSchemas, 39 +// 操作密码
  40 +const [registerPassword, operationPasswordFormAction] = useForm({
58 showActionButtonGroup: false, 41 showActionButtonGroup: false,
59 layout: FormLayoutEnum.VERTICAL, 42 layout: FormLayoutEnum.VERTICAL,
60 labelWidth: 110, 43 labelWidth: 110,
61 }) 44 })
62 45
63 -const [registerPassword, { getFieldsValue: getPasswordValue, validate: validatePassword }] = useForm({ // 操作密码  
64 - schemas: [{  
65 - field: 'password',  
66 - label: '操作密码',  
67 - component: ComponentEnum.INPUT_PAWSSWORD,  
68 - required: true,  
69 - componentProps: {  
70 - placeholder: '请输入操作密码',  
71 - max: 120,  
72 - },  
73 - }], 46 +// 自定义下发值
  47 +const [register, customCommandFormAction] = useForm({
  48 + schemas: formSchemas,
74 showActionButtonGroup: false, 49 showActionButtonGroup: false,
75 layout: FormLayoutEnum.VERTICAL, 50 layout: FormLayoutEnum.VERTICAL,
76 - labelWidth: 110,  
77 }) 51 })
78 52
79 -const getServiceInfo = async (deviceProfileId: string, service?: string, serviceCommand?: Recordable) => {  
80 - isInputData.value = true  
81 - const functionJson: any = await getThingsModelServices(deviceProfileId)  
82 - serviceInfo.inputData = functionJson.filter((item: any) => item.identifier === service)?.[0].functionJson.inputData  
83 - serviceInfo.title = functionJson[0].functionName  
84 - serviceInfo.serviceCommand = serviceCommand 53 +function handleHasOperationPassword(operationPasswordInfo: OperationPasswordDataType) {
  54 + configuration.operationPasswordEnable = true
  55 + nextTick(() => {
  56 + operationPasswordFormAction.setProps({
  57 + schemas: getOperationPasswordSchemas(operationPasswordInfo),
  58 + })
  59 + })
85 } 60 }
86 61
87 -// 获取设备信息  
88 -const getDeviceDetail = async (deviceId: string) => {  
89 - return await getDeviceInfo(deviceId) 62 +function handleOpenCustomCommandModal(deviceInfo: DeviceItemType, eventBindData: SingleClickEventDataType) {
  63 + const { transportType } = deviceInfo
  64 +
  65 + const { setProps, setFieldsValue } = customCommandFormAction
  66 + if (transportType === TransportTypeEnum.TCP)
  67 + setProps({ schemas: getTcpCustomCommandSchemas() })
  68 + else
  69 + setProps({ schemas: getJSONCommandSchemas() })
  70 +
  71 + setFieldsValue({ [FormFieldsEnum.CUSTOM_COMMAND]: eventBindData.customCommand })
90 } 72 }
91 73
92 -const open = async (_data: SingleClickEventDataType) => {  
93 - const { operationPassword } = _data || {}  
94 - dataSourceJson.value = _data.deviceInfo  
95 - isPasswordInfo.value = operationPassword || {}  
96 - // commandWay-->命令下发方式  
97 - const { commandWay, customCommand, serviceCommand, service, callType, way } = _data || {}  
98 - isCommandWay.value = commandWay  
99 - const { attrInfo, deviceId } = unref(dataSourceJson)  
100 - const { identifier, extensionDesc, detail, name } = attrInfo || {}// 属性信息  
101 - const { dataType } = detail || {}  
102 - const { type } = dataType || {}  
103 -  
104 - const { code, transportType, deviceProfileId, alias, name: deviceName } = await getDeviceDetail(deviceId) || {}// 设备信息  
105 - deviceTitle.value = alias || deviceName// 产品名称  
106 - const { registerAddress, actionType, zoomFactor } = extensionDesc || {} // 获取扩展描述内容  
107 -  
108 - const schemas = [{ dataType, identifier, functionName: name } as StructJSON]  
109 -  
110 - zoomFactorValue.value = zoomFactor ? Number(zoomFactor) : 1  
111 - isShowMultiply.value = !!(type === 'INT' || type === 'DOUBLE')  
112 - formField.value = identifier  
113 - isShowActionType.value = !!actionType // 判断modBUS类型时 物模型是否填写扩展描述  
114 - serviceInfo.callType = callType // 服务命令调用方式 是同步还是异步  
115 - serviceInfo.way = way || 'oneway' // 命令下发是双向还是单向  
116 -  
117 - serviceInfo.transportType = transportType  
118 - serviceInfo.code = code  
119 -  
120 - visible.value = true  
121 -  
122 - if (transportType === TransportTypeEnum.TCP) {  
123 - if (commandWay === CommandDeliveryWayEnum.SERVICE) { // 判断命令下发方式是服务或者自定义调用 服务调用用封装的组件 自定义和modbus就用输入框  
124 - getServiceInfo(deviceProfileId, service, serviceCommand)  
125 - }  
126 - else if (commandWay === CommandDeliveryWayEnum.CUSTOM) { // 自定义  
127 - await nextTick()  
128 - updateSchema([  
129 - {  
130 - field: 'sendValue',  
131 - required: true,  
132 - label: '自定义下发值',  
133 - component: ComponentEnum.INPUT,  
134 - },  
135 - ])  
136 - setFieldsValue({ sendValue: customCommand })  
137 - }  
138 - else { // modBus调用  
139 - isShowModBUS.value = true  
140 - modBUSForm.value = {  
141 - crc: 'CRC_16_LOWER',  
142 - deviceCode: code,  
143 - method: actionType === '16' ? '10' : actionType,  
144 - registerAddress,  
145 - registerNumber: 1,  
146 - registerValues: [],  
147 - }  
148 - await nextTick()  
149 - setProps({ schemas: formSchemasConfig(schemas[0], actionType) })  
150 - }  
151 - }  
152 - else {  
153 - isShowModBUS.value = false  
154 -  
155 - // 命令下发方式是服务或则自定义调用  
156 - if (commandWay === CommandDeliveryWayEnum.SERVICE) { getServiceInfo(deviceProfileId, service, serviceCommand) }  
157 -  
158 - else if (commandWay === CommandDeliveryWayEnum.CUSTOM) {  
159 - await nextTick()  
160 - updateSchema([  
161 - {  
162 - field: 'sendValue',  
163 - component: ComponentEnum.JSON_EDITOR,  
164 - changeEvent: 'update:value',  
165 - label: '自定义下发值',  
166 - required: true,  
167 - },  
168 - ])  
169 - setFieldsValue({ sendValue: customCommand })  
170 - }  
171 - } 74 +function handleOpenServiceCommandModal(deviceInfo: DeviceItemType, eventBindData: SingleClickEventDataType) {
  75 + const { service, serviceCommand = {} } = eventBindData
  76 +
  77 + const productsStore = useProductsStoreWithOut()
  78 +
  79 + const tsl = productsStore.getObjectModelByIdWithIdentifier(deviceInfo.deviceProfileId, service!)
  80 + unref(thingsModelElRef)?.setProps({ inputData: tsl?.inputData, transportType: deviceInfo.transportType, title: tsl?.functionName })
  81 +
  82 + nextTick(() => {
  83 + unref(thingsModelElRef)?.setFieldsValue(serviceCommand)
  84 + })
  85 +}
  86 +
  87 +function handleOpenModbusCommandModal(objectModelTsl: Tsl) {
  88 + const { setProps } = customCommandFormAction
  89 + setProps({ schemas: [createModbusValueInput(objectModelTsl)] })
172 } 90 }
173 91
174 -const error = () => {  
175 - // createMessage.error('下发指令失败')  
176 - return false 92 +const deviceInfo = ref<DeviceItemType>()
  93 +const objectModelTsl = ref<Tsl>()
  94 +const currentParams = ref<OpenParamsType>()
  95 +const productsStore = useProductsStore()
  96 +
  97 +const open = async (params: OpenParamsType) => {
  98 + const { eventBindData, operationPasswordInfo, dataSource } = params
  99 + const { deviceId, deviceProfileId, attr } = dataSource
  100 + const { commandWay } = eventBindData
  101 + const { checked, value: operationPassword } = operationPasswordInfo || {}
  102 + configuration.commandDeliveryWay = commandWay!
  103 + currentParams.value = params
  104 + deviceInfo.value = await getDeviceInfo(deviceId)
  105 + objectModelTsl.value = productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, attr as string)!
  106 + setModalProps({
  107 + open: true,
  108 + loading: true,
  109 + })
  110 +
  111 + if (checked && operationPassword)
  112 + handleHasOperationPassword(operationPasswordInfo!)
  113 +
  114 + await nextTick()
  115 +
  116 + if (commandWay === CommandDeliveryWayEnum.CUSTOM)
  117 + handleOpenCustomCommandModal(toRaw(unref(deviceInfo)!), eventBindData)
  118 + else if (commandWay === CommandDeliveryWayEnum.SERVICE)
  119 + handleOpenServiceCommandModal(toRaw(unref(deviceInfo)!), eventBindData)
  120 + else if (commandWay === CommandDeliveryWayEnum.MODBUS)
  121 + handleOpenModbusCommandModal(toRaw(unref(objectModelTsl)!))
  122 +
  123 + setModalProps({
  124 + loading: false,
  125 + title: `参数设置 / ${unref(deviceInfo)?.alias || unref(deviceInfo)?.name}`,
  126 + })
  127 +}
  128 +
  129 +function handleGetCustomCommand() {
  130 + const res = customCommandFormAction.getFieldsValue()
  131 + const command = res[FormFieldsEnum.CUSTOM_COMMAND]
  132 + return unref(deviceInfo)?.transportType === TransportTypeEnum.TCP ? command : useJsonParse(command).value
  133 +}
  134 +
  135 +function handleGetServiceCommand() {
  136 + const res = unref(thingsModelElRef)!.getFieldsValue()
  137 + return unref(deviceInfo)?.transportType === TransportTypeEnum.TCP ? res[FormFieldsEnum.SERVICE_COMMAND] : res
  138 +}
  139 +
  140 +function handleGetModbusCommand() {
  141 + const res = customCommandFormAction.getFieldsValue()
  142 + const identifier = unref(objectModelTsl)!.identifier
  143 + return res[identifier]
177 } 144 }
178 145
179 const handleSubmit = async () => { 146 const handleSubmit = async () => {
180 - unref(isPasswordInfo)?.checked && await validatePassword()// 操作密码  
181 - !unref(isInputData) && await validate()  
182 - unref(isInputData) && await unref(thingsModelElRef).validate()  
183 -  
184 - if (unref(isPasswordInfo)?.checked) {  
185 - const { password } = getPasswordValue()  
186 - if (unref(isPasswordInfo)?.value !== password) {  
187 - createMessage.warning('操作密码不正确')  
188 - return  
189 - }  
190 - }  
191 - if (serviceInfo.callType === 'SYNC') { // 服务命令调用方式 是同步 需要调用设备是否在线才能下发  
192 - const res = await getDeviceActiveTime(dataSourceJson.value.deviceId)  
193 - const { value } = res?.[0] || {}  
194 - if (!value) {  
195 - createMessage.error('当前设备不在线')  
196 - return  
197 - } 147 + if (configuration.operationPasswordEnable) await operationPasswordFormAction.validate()
  148 + configuration.commandDeliveryWay === CommandDeliveryWayEnum.SERVICE ? await unref(thingsModelElRef)?.validate() : await customCommandFormAction.validate()
  149 +
  150 + const res
  151 + = configuration.commandDeliveryWay === CommandDeliveryWayEnum.CUSTOM
  152 + ? handleGetCustomCommand()
  153 + : configuration.commandDeliveryWay === CommandDeliveryWayEnum.MODBUS
  154 + ? handleGetModbusCommand()
  155 + : handleGetServiceCommand()
  156 +
  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,
198 } 168 }
199 169
200 - const sendValue = ref({})  
201 - try {  
202 - loading.value = true  
203 - if (unref(isShowModBUS)) {  
204 - if (!unref(isShowActionType)) {  
205 - createMessage.warning('当前物模型扩展描述没有填写')  
206 - return  
207 - }  
208 -  
209 - if (!serviceInfo.code) {  
210 - createMessage.error('当前缺少设备地址码')  
211 - return  
212 - }  
213 - const oldValue = getFieldsValue()[unref(formField)]  
214 - modBUSForm.value.registerNumber = 1  
215 - modBUSForm.value.registerValues = [oldValue]  
216 -  
217 - if (unref(isShowMultiply) && unref(modBUSForm).method === '06') {  
218 - const newValue  
219 - = Math.trunc(oldValue) * unref(zoomFactorValue)  
220 - + getFloatPart(oldValue) * unref(zoomFactorValue)  
221 - if (newValue % 1 !== 0) {  
222 - createMessage.warning(`属性下发类型必须是整数,缩放因子为${unref(zoomFactorValue)}`)  
223 - return  
224 - }  
225 -  
226 - if (oldValue * unref(zoomFactorValue) > 65535) {  
227 - createMessage.warning(`属性下发值不能超过65535,缩放因子是${unref(zoomFactorValue)}`)  
228 - return  
229 - }  
230 - // bool类型的就不用去乘缩放因子了  
231 - modBUSForm.value.registerValues = [newValue]  
232 - }  
233 -  
234 - if (unref(modBUSForm).method === '16' || unref(modBUSForm).method === '10') {  
235 - const regex = /^-?\d+(\.\d{0,2})?$/  
236 - const values  
237 - = Math.trunc(oldValue) * unref(zoomFactorValue)  
238 - + getFloatPart(oldValue) * unref(zoomFactorValue)  
239 -  
240 - if (!regex.test(values as any)) {  
241 - createMessage.warning(`属性下发值精确到两位小数,缩放因子是${unref(zoomFactorValue)}`)  
242 - return  
243 - }  
244 -  
245 - const newValue  
246 - = values === 0 ? [0, 0] : getArray(SingleToHex(unref(isShowMultiply) ? values : oldValue))  
247 - modBUSForm.value.registerValues = newValue  
248 - modBUSForm.value.registerNumber = 2  
249 - modBUSForm.value.method = '10'  
250 - }  
251 - sendValue.value = await genModbusCommand(unref(modBUSForm))  
252 - }  
253 - else {  
254 - if (unref(isInputData)) {  
255 - const values = serviceInfo.transportType === TransportTypeEnum.TCP ? Object.values(unref(thingsModelElRef)?.getFieldsValue()).join('').replace(/\s/g, '') : unref(thingsModelElRef)?.getFieldsValue()  
256 - sendValue.value = values  
257 - }  
258 - else {  
259 - const values = serviceInfo.transportType === TransportTypeEnum.TCP ? getFieldsValue().sendValue : JSON.parse(getFieldsValue().sendValue) || {}  
260 - sendValue.value = values  
261 - }  
262 - }  
263 - await sendRpcOneway({  
264 - additionalInfo: {  
265 - cmdType:  
266 - unref(isCommandWay) === CommandDeliveryWayEnum.SERVICE  
267 - ? CommandTypeEnum.SERVICE  
268 - : CommandTypeEnum.API,  
269 - },  
270 - persistent: true,  
271 - method: 'methodThingskit',  
272 - params: serviceInfo.transportType !== TransportTypeEnum.TCP && unref(isCommandWay) === CommandDeliveryWayEnum.SERVICE ? { service: unref(sendValue) } : unref(sendValue),  
273 - }, dataSourceJson.value.deviceId, serviceInfo.callType ? serviceInfo.callType === 'SYNC' ? 'twoway' : 'oneway' : serviceInfo.way)  
274 - createMessage.success('命令下发成功')  
275 - visible.value = false  
276 - isInputData.value = false  
277 - }  
278 - catch (msg) {  
279 - console.error(msg)  
280 - return error() 170 + if (commandWay === CommandDeliveryWayEnum.CUSTOM) {
  171 + params.penetration = true
281 } 172 }
282 - finally {  
283 - loading.value = false 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 }
284 } 177 }
  178 +
  179 + await doCommandDelivery(params)
  180 + const { createMessage } = useMessage()
  181 + createMessage.success('命令下发成功')
  182 + closeModal()
285 } 183 }
286 184
287 const handleCancel = () => { 185 const handleCancel = () => {
288 - visible.value = false  
289 - isInputData.value = false 186 + closeModal()
  187 + Object.assign(configuration, getDefaultConfiguration())
290 } 188 }
291 189
292 defineExpose({ 190 defineExpose({
@@ -295,28 +193,15 @@ defineExpose({ @@ -295,28 +193,15 @@ defineExpose({
295 </script> 193 </script>
296 194
297 <template> 195 <template>
298 - <BasicModal  
299 - v-model:open="visible" :title="`参数设置-${deviceTitle}`" :destroy-on-close="true" :confirm-loading="loading"  
300 - :ok-button-props="{ type: 'primary' }" @ok="handleSubmit" @cancel="handleCancel"  
301 - >  
302 - <BasicForm v-if="isPasswordInfo?.checked" @register="registerPassword" />  
303 - <BasicForm v-if="!isInputData" @register="register" />  
304 - <ThingsModelForm  
305 - v-else ref="thingsModelElRef" v-model:value="serviceInfo.serviceCommand" :title="serviceInfo.title"  
306 - :input-data="serviceInfo.inputData || []" :transport-type="serviceInfo.transportType"  
307 - /> 196 + <BasicModal :destroy-on-close="true" @register="registerModal" @ok="handleSubmit" @cancel="handleCancel">
  197 + <BasicForm v-if="configuration.operationPasswordEnable" @register="registerPassword" />
  198 + <BasicForm v-if="configuration.commandDeliveryWay !== CommandDeliveryWayEnum.SERVICE" @register="register" />
  199 + <ThingsModelForm v-if="configuration.commandDeliveryWay === CommandDeliveryWayEnum.SERVICE" ref="thingsModelElRef" />
308 <template #footer> 200 <template #footer>
309 - <Button  
310 - style="background-color: #ffffff;  
311 - border-color: #d9d9d9;  
312 - box-shadow: 0 2px 0 rgba(0, 0, 0, 0.02);" @click="handleCancel"  
313 - > 201 + <Button @click="handleCancel">
314 取消 202 取消
315 </Button> 203 </Button>
316 - <Button  
317 - type="primary" :loading="loading"  
318 - style="background: #1677ff !important; box-shadow: 0 2px 0 rgba(5, 145, 255, 0.1)" @click="handleSubmit"  
319 - > 204 + <Button type="primary" @click="handleSubmit">
320 确定 205 确定
321 </Button> 206 </Button>
322 </template> 207 </template>
  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 { SingleToHex, getArray } from './config'  
2 -import { type GenModbusCommandType, genModbusCommand } from '@/api/device'  
3 -import type { ExtensionDesc } from '@/api/device/model'  
4 -import { TCPObjectModelActionTypeEnum } from '@/enums/objectModelEnum'  
5 -import { useMessage } from '@/hooks/web/useMessage'  
6 -  
7 -const getFloatPart = (number: string | number) => {  
8 - const isLessZero = Number(number) < 0  
9 - number = number.toString()  
10 - const floatPartStartIndex = number.indexOf('.')  
11 - const value = ~floatPartStartIndex  
12 - ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}`  
13 - : '0'  
14 - return Number(value)  
15 -}  
16 -  
17 -const REGISTER_MAX_VALUE = Number(0xffff)  
18 -  
19 -export function useGetModbusCommand() {  
20 - const { createMessage } = useMessage()  
21 -  
22 - const getModbusCommand = async (value: number, extensionDesc: ExtensionDesc, deviceAddressCode: string) => {  
23 - const { registerAddress, actionType, zoomFactor } = extensionDesc as Required<ExtensionDesc>  
24 - const params: GenModbusCommandType = {  
25 - crc: 'CRC_16_LOWER',  
26 - registerNumber: 1,  
27 - deviceCode: deviceAddressCode,  
28 - registerAddress,  
29 - method: actionType,  
30 - registerValues: [value],  
31 - }  
32 -  
33 - if (actionType === TCPObjectModelActionTypeEnum.INT) {  
34 - const newValue  
35 - = Math.trunc(value) * zoomFactor  
36 - + getFloatPart(value) * zoomFactor  
37 -  
38 - if (newValue % 1 !== 0) {  
39 - createMessage.warning(`属性下发类型必须是整数,缩放因子为${zoomFactor}`)  
40 - return  
41 - }  
42 -  
43 - if (value * zoomFactor > REGISTER_MAX_VALUE) {  
44 - createMessage.warning(`属性下发值不能超过${REGISTER_MAX_VALUE},缩放因子是${zoomFactor}`)  
45 - return  
46 - }  
47 -  
48 - params.registerValues = [newValue]  
49 - }  
50 - else if (actionType === TCPObjectModelActionTypeEnum.DOUBLE) {  
51 - const regex = /^-?\d+(\.\d{0,2})?$/  
52 - const values  
53 - = Math.trunc(value) * zoomFactor  
54 - + getFloatPart(value) * zoomFactor  
55 -  
56 - if (!regex.test(values.toString())) {  
57 - createMessage.warning(`属性下发值精确到两位小数,缩放因子是${zoomFactor}`)  
58 - return  
59 - }  
60 -  
61 - const newValue  
62 - = values === 0 ? [0, 0] : getArray(SingleToHex(values))  
63 - params.registerValues = newValue  
64 - params.registerNumber = 2  
65 - params.method = '10'  
66 - }  
67 -  
68 - return await genModbusCommand(params)  
69 - }  
70 -  
71 - /**  
72 - *  
73 - * @param extensionDesc 物模型拓展描述符  
74 - * @param deviceAddressCode 设备地址码  
75 - */  
76 - const validateCanGetCommand = (extensionDesc?: ExtensionDesc, deviceAddressCode?: string, createValidateMessage = true) => {  
77 - const result = { flag: true, message: '' }  
78 - if (!extensionDesc) {  
79 - result.flag = false  
80 - result.message = '当前物模型扩展描述没有填写'  
81 - }  
82 -  
83 - if (!deviceAddressCode) {  
84 - result.flag = false  
85 - result.message = '当前设备未绑定设备地址码'  
86 - }  
87 -  
88 - if (result.message && createValidateMessage)  
89 - createMessage.warning(result.message)  
90 -  
91 - return result  
92 - }  
93 -  
94 - return {  
95 - getModbusCommand,  
96 - validateCanGetCommand,  
97 - }  
98 -}  
@@ -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,11 +4,14 @@ import { usePublicFormContext } from '../../../usePublicFormContext' @@ -4,11 +4,14 @@ 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' 11 +import { validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm'
  12 +import { JSONEditorValidator } from '@/components/CodeEditor/src/JSONEditor'
  13 +import { DeviceTypeEnum, TCPProtocolTypeEnum, TransportTypeEnum } from '@/enums/deviceEnum'
  14 +import { useProductsStoreWithOut } from '@/store/modules/products'
12 15
13 export enum FormFieldsEnum { 16 export enum FormFieldsEnum {
14 ACTION_TYPE = 'actionType', 17 ACTION_TYPE = 'actionType',
@@ -33,17 +36,14 @@ export enum FormFieldsNameEnum { @@ -33,17 +36,14 @@ export enum FormFieldsNameEnum {
33 SERVICE = '服务', 36 SERVICE = '服务',
34 } 37 }
35 38
36 -const contentDataStore = useContentDataStoreWithOut()  
37 -  
38 export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { 39 export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => {
39 - const { getNodeData, getCellInfo } = usePublicFormContext() 40 + const { getNodeData, getCellInfo, getDeviceInfo } = usePublicFormContext()
40 const { dataSourceJson } = unref(getNodeData) || {} 41 const { dataSourceJson } = unref(getNodeData) || {}
41 - const { deviceProfileId, deviceInfo, deviceId } = dataSourceJson || {}  
42 - // transportType:判断是什么类型的设备 code:设备地址码 deviceType:设备类型  
43 - let codeType: string | null = ''  
44 - const { transportType, deviceType, codeType: deviceCodeType } = deviceInfo || {}  
45 - codeType = deviceCodeType || (deviceId ? contentDataStore.diveceDetailMap?.[deviceId]?.codeType : null)  
46 - const isTemplate = contentDataStore.isTemplate // 判断是否是模板 42 + const { deviceProfileId } = dataSourceJson || {}
  43 + const productsStore = useProductsStoreWithOut()
  44 + const detail = productsStore.getProductDetailById(deviceProfileId!)
  45 + const { transportType } = unref(getDeviceInfo) || detail || {}
  46 +
47 return [ 47 return [
48 { 48 {
49 field: 'eventName', 49 field: 'eventName',
@@ -68,7 +68,6 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { @@ -68,7 +68,6 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => {
68 const options: DefaultOptionType[] = [ 68 const options: DefaultOptionType[] = [
69 { label: EventActionTypeNameEnum.OPEN_LINK, value: EventActionTypeEnum.OPEN_LINK }, 69 { label: EventActionTypeNameEnum.OPEN_LINK, value: EventActionTypeEnum.OPEN_LINK },
70 { label: EventActionTypeNameEnum.OPEN_PAGE, value: EventActionTypeEnum.OPEN_PAGE }, 70 { label: EventActionTypeNameEnum.OPEN_PAGE, value: EventActionTypeEnum.OPEN_PAGE },
71 -  
72 ] 71 ]
73 72
74 if (unref(getCellInfo).category.toUpperCase() === PackageCategoryEnum.CONTROL) 73 if (unref(getCellInfo).category.toUpperCase() === PackageCategoryEnum.CONTROL)
@@ -93,7 +92,6 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { @@ -93,7 +92,6 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => {
93 field: FormFieldsEnum.OPEN_LINK, 92 field: FormFieldsEnum.OPEN_LINK,
94 label: '链接', 93 label: '链接',
95 component: ComponentEnum.INPUT, 94 component: ComponentEnum.INPUT,
96 - // dynamicDisabled: () => !deviceProfileId,  
97 required: true, 95 required: true,
98 ifShow: ({ model }) => model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.OPEN_LINK, 96 ifShow: ({ model }) => model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.OPEN_LINK,
99 }, 97 },
@@ -115,39 +113,30 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { @@ -115,39 +113,30 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => {
115 component: ComponentEnum.SELECT, 113 component: ComponentEnum.SELECT,
116 required: true, 114 required: true,
117 ifShow: ({ model }) => model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.PARAMS_SETTING, 115 ifShow: ({ model }) => model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.PARAMS_SETTING,
  116 + defaultValue: CommandDeliveryWayEnum.CUSTOM,
118 componentProps: ({ formActionType }) => { 117 componentProps: ({ formActionType }) => {
119 function getOptions() { 118 function getOptions() {
120 const options = [ 119 const options = [
121 { label: CommandDeliveryWayNameEnum.CUSTOM, value: CommandDeliveryWayEnum.CUSTOM }, 120 { label: CommandDeliveryWayNameEnum.CUSTOM, value: CommandDeliveryWayEnum.CUSTOM },
122 ] 121 ]
123 122
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 - } 123 + const isTCPDevice = unref(getDeviceInfo)?.transportType === TransportTypeEnum.TCP
  124 + const isTCPModbusDevice = isTCPDevice && unref(getDeviceInfo)?.deviceProfile?.profileData?.transportConfiguration?.protocol === TCPProtocolTypeEnum.MODBUS_RTU
140 125
141 - if (deviceType !== DeviceTypeEnum.SENSOR)  
142 - options.push(serviceOption) 126 + // TCP 自定义设备 自定义
  127 + if (isTCPDevice && !isTCPModbusDevice && unref(getDeviceInfo)?.deviceType === DeviceTypeEnum.SENSOR)
  128 + return options
143 129
144 - if (transportType !== TransportTypeEnum.TCP) return  
145 -  
146 - if (codeType !== CommandDeliveryWayEnum.CUSTOM)  
147 - options.push(modbusOption) 130 + // TCP Modbus设备 自定义&Modbus
  131 + if (isTCPModbusDevice) {
  132 + const modbusOption = { label: CommandDeliveryWayNameEnum.MODBUS, value: CommandDeliveryWayEnum.MODBUS }
  133 + options.push(modbusOption)
  134 + return options
148 } 135 }
149 136
150 - setOptions() 137 + // 其他 自定义&服务
  138 + options.push({ label: CommandDeliveryWayNameEnum.SERVICE, value: CommandDeliveryWayEnum.SERVICE })
  139 +
151 return options 140 return options
152 } 141 }
153 142
@@ -187,20 +176,16 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { @@ -187,20 +176,16 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => {
187 component: ComponentEnum.JSON_EDITOR, 176 component: ComponentEnum.JSON_EDITOR,
188 changeEvent: 'update:value', 177 changeEvent: 'update:value',
189 required: true, 178 required: true,
  179 + rules: JSONEditorValidator(),
190 ifShow: ({ model }) => transportType !== TransportTypeEnum.TCP && model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.PARAMS_SETTING && model[FormFieldsEnum.COMMAND_WAY] === CommandDeliveryWayEnum.CUSTOM, 180 ifShow: ({ model }) => transportType !== TransportTypeEnum.TCP && model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.PARAMS_SETTING && model[FormFieldsEnum.COMMAND_WAY] === CommandDeliveryWayEnum.CUSTOM,
191 - componentProps: () => {  
192 - return {}  
193 - },  
194 }, 181 },
195 { 182 {
196 field: FormFieldsEnum.CUSTOM_COMMAND, 183 field: FormFieldsEnum.CUSTOM_COMMAND,
197 label: FormFieldsNameEnum.COMMAND, 184 label: FormFieldsNameEnum.COMMAND,
198 component: ComponentEnum.INPUT, 185 component: ComponentEnum.INPUT,
199 required: true, 186 required: true,
  187 + rules: [{ validator: validateTCPCustomCommand }],
200 ifShow: ({ model }) => transportType === TransportTypeEnum.TCP && model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.PARAMS_SETTING && model[FormFieldsEnum.COMMAND_WAY] === CommandDeliveryWayEnum.CUSTOM, 188 ifShow: ({ model }) => transportType === TransportTypeEnum.TCP && model[FormFieldsEnum.ACTION_TYPE] === EventActionTypeEnum.PARAMS_SETTING && model[FormFieldsEnum.COMMAND_WAY] === CommandDeliveryWayEnum.CUSTOM,
201 - componentProps: () => {  
202 - return {}  
203 - },  
204 }, 189 },
205 { 190 {
206 field: FormFieldsEnum.SERVICE, 191 field: FormFieldsEnum.SERVICE,
@@ -224,7 +209,6 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { @@ -224,7 +209,6 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => {
224 const service = formModel[FormFieldsEnum.SERVICE] 209 const service = formModel[FormFieldsEnum.SERVICE]
225 if (service) { 210 if (service) {
226 const thingsModel = unref(options).find(item => item.identifier === service) || null 211 const thingsModel = unref(options).find(item => item.identifier === service) || null
227 - // if (thingsModel)  
228 formModel[FormFieldsEnum.THINGS_MODEL] = thingsModel 212 formModel[FormFieldsEnum.THINGS_MODEL] = thingsModel
229 } 213 }
230 }, 214 },
@@ -243,15 +227,8 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { @@ -243,15 +227,8 @@ export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => {
243 component: ComponentEnum.THINGS_MODEL_FORM, 227 component: ComponentEnum.THINGS_MODEL_FORM,
244 ifShow: ({ model }) => model[FormFieldsEnum.THINGS_MODEL] && model[FormFieldsEnum.COMMAND_WAY] === CommandDeliveryWayEnum.SERVICE, 228 ifShow: ({ model }) => model[FormFieldsEnum.THINGS_MODEL] && model[FormFieldsEnum.COMMAND_WAY] === CommandDeliveryWayEnum.SERVICE,
245 changeEvent: 'update:value', 229 changeEvent: 'update:value',
246 - colSlot: 'serviceCommand', 230 + slot: 'serviceCommand',
247 }, 231 },
248 - // {  
249 - // field: 'deviceInfo',  
250 - // label: '设备属性数据',  
251 - // component: ComponentEnum.INPUT,  
252 - // defaultValue: dataSourceJson,  
253 - // ifShow: false,  
254 - // },  
255 { 232 {
256 field: 'transportType', 233 field: 'transportType',
257 label: '', 234 label: '',
1 -import { toRaw, unref } from 'vue' 1 +import { unref } from 'vue'
2 import { getDeviceAttributes, getListByConfigurationId, getListByDeviceProfileIds } from '@/api/device' 2 import { getDeviceAttributes, getListByConfigurationId, getListByDeviceProfileIds } from '@/api/device'
3 import type { DeviceItemType, ThingsModelItemType } from '@/api/device/model' 3 import type { DeviceItemType, ThingsModelItemType } from '@/api/device/model'
4 import type { FormSchema } from '@/components/Form' 4 import type { FormSchema } from '@/components/Form'
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[] => {
@@ -32,9 +33,8 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { @@ -32,9 +33,8 @@ export const formSchemas = (componentKey?: string): FormSchema[] => {
32 return { 33 return {
33 options: (unref(contentDataStore.getProductAndDevice) || []).map((item: ProductAndDevice) => ({ label: item.profileName || item.name, value: item.profileId, transportType: item?.transportType, deviceType: item?.deviceType })), 34 options: (unref(contentDataStore.getProductAndDevice) || []).map((item: ProductAndDevice) => ({ label: item.profileName || item.name, value: item.profileId, transportType: item?.transportType, deviceType: item?.deviceType })),
34 placeholder: '请选择产品', 35 placeholder: '请选择产品',
35 - onSelect(value: string, option: any) { 36 + onSelect(value: string) {
36 formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] = value 37 formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] = value
37 - formModel[ContentDataFieldsEnum.DEVICE_INFO] = value && option ? { transportType: option.transportType, deviceType: option.deviceType, codeType: option?.codeType, deviceName: null } : null  
38 }, 38 },
39 getPopupContainer: () => document.body, 39 getPopupContainer: () => document.body,
40 } 40 }
@@ -63,8 +63,8 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { @@ -63,8 +63,8 @@ export const formSchemas = (componentKey?: string): FormSchema[] => {
63 fieldNames: { label: 'name', value: 'tbDeviceId' }, 63 fieldNames: { label: 'name', value: 'tbDeviceId' },
64 onSelect(value: string, option: DeviceItemType) { 64 onSelect(value: string, option: DeviceItemType) {
65 formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] = value ? option.deviceProfileId : null 65 formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] = value ? option.deviceProfileId : null
66 - formModel[ContentDataFieldsEnum.DEVICE_INFO] = value && option ? { code: option.code, transportType: option.transportType, deviceType: option.deviceType, deviceProfileId: option.deviceProfileId, deviceName: option.alias || option.name || null, codeType: option?.codeType } : null  
67 formModel[ContentDataFieldsEnum.ATTR] = null 66 formModel[ContentDataFieldsEnum.ATTR] = null
  67 + formModel[ContentDataFieldsEnum.DEVICE_NAME] = value ? option.alias || option.name : ''
68 }, 68 },
69 filterOption: (inputValue: string, option: DeviceItemType) => { 69 filterOption: (inputValue: string, option: DeviceItemType) => {
70 return option.alias?.includes?.(inputValue) || option.name?.includes?.(inputValue) 70 return option.alias?.includes?.(inputValue) || option.name?.includes?.(inputValue)
@@ -73,6 +73,12 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { @@ -73,6 +73,12 @@ export const formSchemas = (componentKey?: string): FormSchema[] => {
73 }, 73 },
74 }, 74 },
75 { 75 {
  76 + field: ContentDataFieldsEnum.DEVICE_NAME,
  77 + label: ContentDataFieldsNameEnum.deviceName,
  78 + component: ComponentEnum.INPUT,
  79 + ifShow: false,
  80 + },
  81 + {
76 field: ContentDataFieldsEnum.ATTR, 82 field: ContentDataFieldsEnum.ATTR,
77 label: ContentDataFieldsNameEnum.ATTR, 83 label: ContentDataFieldsNameEnum.ATTR,
78 component: ComponentEnum.API_SELECT, 84 component: ComponentEnum.API_SELECT,
@@ -83,36 +89,22 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { @@ -83,36 +89,22 @@ export const formSchemas = (componentKey?: string): FormSchema[] => {
83 showSearch: true, 89 showSearch: true,
84 api: async (params: string) => { 90 api: async (params: string) => {
85 if (!deviceProfileId) return [] 91 if (!deviceProfileId) return []
86 - const options = await getDeviceAttributes(params) 92 + let options = await getDeviceAttributes(params)
87 if (componentKey === ControlComponentEnum.SWITCH) { // 开关只返回bool 93 if (componentKey === ControlComponentEnum.SWITCH) { // 开关只返回bool
88 - return options.filter((item) => { 94 + options = options.filter((item) => {
89 return item.detail.dataType.type === DataTypeEnum.BOOL 95 return item.detail.dataType.type === DataTypeEnum.BOOL
90 }) 96 })
91 } 97 }
  98 +
92 return options 99 return options
93 }, 100 },
94 params: deviceProfileId, 101 params: deviceProfileId,
95 fieldNames: { label: 'name', value: 'identifier' }, 102 fieldNames: { label: 'name', value: 'identifier' },
96 - onSelect(value: string, option: ThingsModelItemType) {  
97 - formModel[ContentDataFieldsEnum.ATTR_INFO] = value && option ? toRaw(unref(option)) : null  
98 - },  
99 filterOption: (inputValue: string, option: ThingsModelItemType) => { 103 filterOption: (inputValue: string, option: ThingsModelItemType) => {
100 return option.name.includes(inputValue) 104 return option.name.includes(inputValue)
101 }, 105 },
102 } 106 }
103 }, 107 },
104 }, 108 },
105 - {  
106 - field: ContentDataFieldsEnum.ATTR_INFO,  
107 - label: ContentDataFieldsNameEnum.ATTR_INFO,  
108 - component: ComponentEnum.INPUT,  
109 - ifShow: false,  
110 - },  
111 - {  
112 - field: ContentDataFieldsEnum.DEVICE_INFO,  
113 - label: ContentDataFieldsNameEnum.DEVICE_INFO,  
114 - component: ComponentEnum.INPUT,  
115 - ifShow: false,  
116 - },  
117 ] 109 ]
118 } 110 }
@@ -12,5 +12,4 @@ export interface ComponentExposeType { @@ -12,5 +12,4 @@ export interface ComponentExposeType {
12 getFieldsValue: () => any 12 getFieldsValue: () => any
13 setFieldsValue: (value: any) => void 13 setFieldsValue: (value: any) => void
14 validate: () => Promise<RuleError | any> 14 validate: () => Promise<RuleError | any>
15 - updateSchema?: any  
16 } 15 }
@@ -58,7 +58,7 @@ const getEventJson = (): NodeDataEventJsonType => { @@ -58,7 +58,7 @@ const getEventJson = (): NodeDataEventJsonType => {
58 58
59 Object.keys(status).forEach((key) => { 59 Object.keys(status).forEach((key) => {
60 if (eventJson[key as EventTypeEnum]) 60 if (eventJson[key as EventTypeEnum])
61 - eventJson[key as EventTypeEnum].enable = status[key as EventTypeEnum] 61 + eventJson[key as EventTypeEnum]!.enable = status[key as EventTypeEnum]
62 }) 62 })
63 63
64 return eventJson 64 return eventJson
@@ -111,7 +111,7 @@ const handleSave = async () => { @@ -111,7 +111,7 @@ const handleSave = async () => {
111 if (operationPassword.checked) 111 if (operationPassword.checked)
112 await validatePassword() 112 await validatePassword()
113 if (contentDataStore.getIsTemplate)// 判断组态是不是模板 113 if (contentDataStore.getIsTemplate)// 判断组态是不是模板
114 - dataSourceJson = { ...value, deviceProfileId: value?.deviceProfileTemplateId, deviceId: null } 114 + dataSourceJson = { ...value, deviceProfileId: value?.deviceProfileTemplateId, deviceId: null, deviceName: null }
115 await saveNodeAllData({ dataSourceJson, eventJson: { ...eventJson, OPERATION_PASSWORD: unref(getCellInfo).category === PackageCategoryEnum.CONTROL ? operationPassword : undefined }, actJson }) 115 await saveNodeAllData({ dataSourceJson, eventJson: { ...eventJson, OPERATION_PASSWORD: unref(getCellInfo).category === PackageCategoryEnum.CONTROL ? operationPassword : undefined }, actJson })
116 createMessage.success('操作成功') 116 createMessage.success('操作成功')
117 savePageContent() 117 savePageContent()
@@ -190,7 +190,7 @@ createPublicFormContext(nodeDataActinType) @@ -190,7 +190,7 @@ createPublicFormContext(nodeDataActinType)
190 </div> 190 </div>
191 </Checkbox> 191 </Checkbox>
192 <Form> 192 <Form>
193 - <FormItem :validate-status="getValidateStatus(operationPassword.value)"> 193 + <FormItem :validate-status="getValidateStatus(operationPassword.value!)">
194 <InputPassword 194 <InputPassword
195 v-model:value="operationPassword.value" class="mt-1" :disabled="!operationPassword.checked" 195 v-model:value="operationPassword.value" class="mt-1" :disabled="!operationPassword.checked"
196 placeholder="请输入操作密码" 196 placeholder="请输入操作密码"
  1 +import { validateTCPCustomCommand } from '.'
1 import type { Specs, StructJSON } from '@/api/device/model' 2 import type { Specs, StructJSON } from '@/api/device/model'
2 import { type FormSchema } from '@/components/Form' 3 import { type FormSchema } from '@/components/Form'
3 import { ComponentEnum } from '@/components/Form/src/enum' 4 import { ComponentEnum } from '@/components/Form/src/enum'
4 -import { DataTypeEnum } from '@/enums/datasource' 5 +import { TransportTypeEnum } from '@/enums/deviceEnum'
  6 +import { DataTypeEnum } from '@/enums/objectModelEnum'
  7 +
  8 +export enum FormFieldsEnum {
  9 + SERVICE_COMMAND = 'serviceCommand',
  10 +}
5 11
6 export const getFormSchemas = ({ structJSON: structJson, required, transportType }: { structJSON: StructJSON[]; required?: boolean; transportType?: string }): FormSchema[] => { 12 export const getFormSchemas = ({ structJSON: structJson, required, transportType }: { structJSON: StructJSON[]; required?: boolean; transportType?: string }): FormSchema[] => {
7 const createInputNumber = ({ 13 const createInputNumber = ({
@@ -120,10 +126,11 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType @@ -120,10 +126,11 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType
120 126
121 const createTCPServiceCommandInput = ({ serviceCommand }: StructJSON): FormSchema => { 127 const createTCPServiceCommandInput = ({ serviceCommand }: StructJSON): FormSchema => {
122 return { 128 return {
123 - field: 'serviceCommand', 129 + field: FormFieldsEnum.SERVICE_COMMAND,
124 label: '服务命令', 130 label: '服务命令',
125 component: ComponentEnum.INPUT, 131 component: ComponentEnum.INPUT,
126 required, 132 required,
  133 + rules: [{ validator: validateTCPCustomCommand }],
127 componentProps: { 134 componentProps: {
128 defaultValue: serviceCommand, 135 defaultValue: serviceCommand,
129 }, 136 },
@@ -134,8 +141,12 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType @@ -134,8 +141,12 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType
134 for (const item of structJson) { 141 for (const item of structJson) {
135 const { dataType } = item 142 const { dataType } = item
136 const { type } = dataType || {} 143 const { type } = dataType || {}
137 - if (transportType === 'TCP')  
138 - schemas.push(createTCPServiceCommandInput(item)) 144 +
  145 + if (transportType === TransportTypeEnum.TCP) {
  146 + item.serviceCommand && schemas.push(createTCPServiceCommandInput(item))
  147 + break
  148 + }
  149 +
139 if (type === DataTypeEnum.BOOL) 150 if (type === DataTypeEnum.BOOL)
140 schemas.push(createSelect(item)) 151 schemas.push(createSelect(item))
141 else if (type === DataTypeEnum.ENUM) 152 else if (type === DataTypeEnum.ENUM)
1 <script setup lang="ts"> 1 <script setup lang="ts">
2 import { Card } from 'ant-design-vue' 2 import { Card } from 'ant-design-vue'
3 -import { computed, nextTick, reactive, unref, watch } from 'vue'  
4 -import type { ComponentExposeType } from '../PublicForm' 3 +import { computed, nextTick, reactive, ref, unref, watch } from 'vue'
5 import { getFormSchemas } from './config' 4 import { getFormSchemas } from './config'
6 import { ThingsModelForm } from '.' 5 import { ThingsModelForm } from '.'
7 import type { StructJSON } from '@/api/device/model' 6 import type { StructJSON } from '@/api/device/model'
8 import { BasicForm, useForm } from '@/components/Form' 7 import { BasicForm, useForm } from '@/components/Form'
9 -import { DataTypeEnum } from '@/enums/datasource'  
10 import { FormLabelAlignEnum, FormLayoutEnum } from '@/components/Form/src/enum' 8 import { FormLabelAlignEnum, FormLayoutEnum } from '@/components/Form/src/enum'
  9 +import { deepMerge } from '@/utils'
  10 +import { DataTypeEnum } from '@/enums/objectModelEnum'
11 11
12 -const props = withDefaults(defineProps<{  
13 - value: Recordable 12 +interface ThingsModelFormPropsType {
  13 + value?: Recordable
14 inputData?: StructJSON[] 14 inputData?: StructJSON[]
15 required?: boolean 15 required?: boolean
16 title?: string 16 title?: string
17 transportType?: string 17 transportType?: string
18 -}>(), { 18 +}
  19 +
  20 +const props = withDefaults(defineProps<ThingsModelFormPropsType>(), {
19 inputData: () => [], 21 inputData: () => [],
20 required: true, 22 required: true,
21 }) 23 })
22 24
  25 +const propsRef = ref<Partial<ThingsModelFormPropsType>>({})
  26 +
  27 +const getProps = computed<ThingsModelFormPropsType>(() => ({ ...props, ...unref(propsRef) }))
  28 +
23 const thingsModelFormListElMap = reactive<Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }>>({}) 29 const thingsModelFormListElMap = reactive<Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }>>({})
24 30
25 const [register, formActionType] = useForm({ 31 const [register, formActionType] = useForm({
26 - schemas: getFormSchemas({ structJSON: props.inputData || [], required: props.required, transportType: props.transportType }), 32 + schemas: getFormSchemasByProps(),
27 showActionButtonGroup: false, 33 showActionButtonGroup: false,
  34 + name: Math.random().toString(16).substring(2),
28 labelWidth: 100, 35 labelWidth: 100,
29 labelAlign: FormLabelAlignEnum.RIGHT, 36 labelAlign: FormLabelAlignEnum.RIGHT,
30 layout: FormLayoutEnum.HORIZONTAL, 37 layout: FormLayoutEnum.HORIZONTAL,
31 }) 38 })
32 39
33 const getStructFormItem = computed(() => { 40 const getStructFormItem = computed(() => {
34 - const { inputData } = props  
35 - return inputData.filter(item => item.dataType?.type === DataTypeEnum.STRUCT) 41 + const { inputData } = unref(getProps)
  42 + return (inputData || []).filter(item => item.dataType?.type === DataTypeEnum.STRUCT)
36 }) 43 })
37 44
38 const setFormElRef = (el: InstanceType<typeof ThingsModelForm>, structJSON: StructJSON) => { 45 const setFormElRef = (el: InstanceType<typeof ThingsModelForm>, structJSON: StructJSON) => {
@@ -68,11 +75,7 @@ const validate = async () => { @@ -68,11 +75,7 @@ const validate = async () => {
68 await thingsModelFormListElMap[key].el.validate() 75 await thingsModelFormListElMap[key].el.validate()
69 } 76 }
70 77
71 -const updateSchema = (value: Recordable) => {  
72 - return formActionType.updateSchema(value)  
73 -}  
74 -  
75 -watch(() => props.value, 78 +watch(() => unref(getProps).value,
76 async (value) => { 79 async (value) => {
77 await nextTick() 80 await nextTick()
78 formActionType.setFieldsValue(value || {}) 81 formActionType.setFieldsValue(value || {})
@@ -80,25 +83,31 @@ watch(() => props.value, @@ -80,25 +83,31 @@ watch(() => props.value,
80 { immediate: true }, 83 { immediate: true },
81 ) 84 )
82 85
  86 +function getFormSchemasByProps() {
  87 + return getFormSchemas({ structJSON: unref(getProps).inputData || [], required: unref(getProps).required, transportType: unref(getProps).transportType })
  88 +}
  89 +
83 watch( 90 watch(
84 - () => props.inputData, 91 + () => unref(getProps).inputData,
85 (value) => { 92 (value) => {
86 - if (value && value.length) {  
87 - const schemas = getFormSchemas({ structJSON: props.inputData || [], required: props.required, transportType: props.transportType })  
88 - formActionType.setProps({ schemas })  
89 - } 93 + if (value && value.length)
  94 + formActionType.setProps({ schemas: getFormSchemasByProps() })
90 }) 95 })
91 96
92 -defineExpose<ComponentExposeType>({ 97 +function setProps(props: Partial<ThingsModelFormPropsType>) {
  98 + propsRef.value = deepMerge(unref(propsRef) || {}, props) as any
  99 +}
  100 +
  101 +defineExpose({
93 getFieldsValue, 102 getFieldsValue,
94 setFieldsValue, 103 setFieldsValue,
95 validate, 104 validate,
96 - updateSchema, 105 + setProps,
97 }) 106 })
98 </script> 107 </script>
99 108
100 <template> 109 <template>
101 - <Card class="!border-2 !border-dashed" :title="title"> 110 + <Card class="!border-2 !border-dashed" :title="getProps.title">
102 <BasicForm class="things-model-form" @register="register"> 111 <BasicForm class="things-model-form" @register="register">
103 <template v-for="item in getStructFormItem" #[item.identifier]="{ model, field }" :key="item.identifier"> 112 <template v-for="item in getStructFormItem" #[item.identifier]="{ model, field }" :key="item.identifier">
104 <ThingsModelForm 113 <ThingsModelForm
@@ -6,3 +6,10 @@ export function useLatestMessageValue(message: SubscriptionData, attr: string) { @@ -6,3 +6,10 @@ export function useLatestMessageValue(message: SubscriptionData, attr: string) {
6 const [ts, latestValue = null] = lateset || [] 6 const [ts, latestValue = null] = lateset || []
7 return { ts, latestValue } 7 return { ts, latestValue }
8 } 8 }
  9 +
  10 +export function useLatestMultipleMessageValue(message: SubscriptionData, attr: string[], Fn: (attribute: string, value: any, timespan: number) => void) {
  11 + attr.forEach((item) => {
  12 + const [ts = null, latestValue = null] = message[item][0] || []
  13 + Fn(item, ts, latestValue)
  14 + })
  15 +}
@@ -7,6 +7,8 @@ import { CellAttributeKeyEnum } from '@/enums/cellAttributeEnum' @@ -7,6 +7,8 @@ import { CellAttributeKeyEnum } from '@/enums/cellAttributeEnum'
7 import { useContentDataStoreWithOut } from '@/store/modules/contentData' 7 import { useContentDataStoreWithOut } from '@/store/modules/contentData'
8 import { useMessage } from '@/hooks/web/useMessage' 8 import { useMessage } from '@/hooks/web/useMessage'
9 import { MessageEnum } from '@/enums/messageEnum' 9 import { MessageEnum } from '@/enums/messageEnum'
  10 +import type { DeviceItemType } from '@/api/device/model'
  11 +import { getDeviceInfo } from '@/api/device'
10 12
11 interface UseNodeDataParamsType { 13 interface UseNodeDataParamsType {
12 cell: MxCell 14 cell: MxCell
@@ -35,9 +37,9 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { @@ -35,9 +37,9 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) {
35 const { createMessage } = useMessage() 37 const { createMessage } = useMessage()
36 38
37 const nodeData = ref<NodeDataType>() 39 const nodeData = ref<NodeDataType>()
  40 + const deviceInfo = ref<DeviceItemType>()
38 41
39 const contentDataStore = useContentDataStoreWithOut() 42 const contentDataStore = useContentDataStoreWithOut()
40 - // const contentId = window.DrawApp.configurationContentId!  
41 const { configurationId } = useParseParams() 43 const { configurationId } = useParseParams()
42 44
43 const getCellID = () => { 45 const getCellID = () => {
@@ -64,9 +66,15 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { @@ -64,9 +66,15 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) {
64 } 66 }
65 }) 67 })
66 68
  69 + const getDeviceDetail = async () => {
  70 + if (!unref(nodeData)?.dataSourceJson?.deviceId) return
  71 + deviceInfo.value = await getDeviceInfo(unref(nodeData)!.dataSourceJson.deviceId)
  72 + }
  73 +
67 const getNodeAllData = async () => { 74 const getNodeAllData = async () => {
68 const result = await doGetNodeBindData(basicNodeBindData(ActionType.GET)) 75 const result = await doGetNodeBindData(basicNodeBindData(ActionType.GET))
69 nodeData.value = result || getDefaultNodeData() 76 nodeData.value = result || getDefaultNodeData()
  77 + await getDeviceDetail()
70 return result 78 return result
71 } 79 }
72 80
@@ -79,6 +87,8 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { @@ -79,6 +87,8 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) {
79 const result = await doSaveNodeAllData(Object.assign(data, basicNodeBindData(ActionType.SAVE))) 87 const result = await doSaveNodeAllData(Object.assign(data, basicNodeBindData(ActionType.SAVE)))
80 removeCellSourceAttribute(cell) 88 removeCellSourceAttribute(cell)
81 nodeData.value = result || getDefaultNodeData() 89 nodeData.value = result || getDefaultNodeData()
  90 +
  91 + await getDeviceDetail()
82 return result 92 return result
83 } 93 }
84 94
@@ -123,6 +133,7 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) { @@ -123,6 +133,7 @@ export function useNodeData({ cell, immediate = true }: UseNodeDataParamsType) {
123 }) 133 })
124 134
125 return { 135 return {
  136 + getDeviceInfo: computed(() => unref(deviceInfo)),
126 getNodeData: computed(() => unref(nodeData)), 137 getNodeData: computed(() => unref(nodeData)),
127 getCellInfo, 138 getCellInfo,
128 getNodeAllData, 139 getNodeAllData,
1 -import { h, render, unref } from 'vue' 1 +import { h, render, toRaw, unref } from 'vue'
2 import { ControlComponentEnum } from '../packages/Control' 2 import { ControlComponentEnum } from '../packages/Control'
3 -import { useGetModbusCommand } from '../components/PublicForm/components/DataEvents/CommandDeliveryModal/useGetModbusCommand'  
4 -import { doCommandDelivery, getDeviceActive, getDeviceInfo } from '@/api/device' 3 +import { doCommandDelivery, getDeviceActive } from '@/api/device'
5 import type { MouseUpEventDataType, NodeDataDataSourceJsonType, NodeDataEventJsonType, SingleClickEventDataType } from '@/api/node/model' 4 import type { MouseUpEventDataType, NodeDataDataSourceJsonType, NodeDataEventJsonType, SingleClickEventDataType } from '@/api/node/model'
6 -import { CommandWayEnum } from '@/enums/commandEnum'  
7 -import { ActRangListItemTypeEnum, CodeTypeEnum, EventActionTypeEnum, TransportTypeEnum } from '@/enums/datasource' 5 +import { CommandMethodEnum, CommandTypeEnum, CommandWayEnum } from '@/enums/commandEnum'
  6 +import { ActRangListItemTypeEnum, EventActionTypeEnum } from '@/enums/datasource'
8 import { useMessage } from '@/hooks/web/useMessage' 7 import { useMessage } from '@/hooks/web/useMessage'
9 -import { AttributeDeliverModal, CommandDeliveryConfirmModal, CommandDeliveryModal, ConfirmModal } from '@/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal' 8 +import { AttributeDeliverModal, CommandDeliveryModal, SwitchCommandDeliveryModal } from '@/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal'
10 import type { MxCell } from '@/fitCore/types' 9 import type { MxCell } from '@/fitCore/types'
11 import { NodeUtils } from '@/hooks/business/useNodeUtils' 10 import { NodeUtils } from '@/hooks/business/useNodeUtils'
12 import { useContentDataStoreWithOut } from '@/store/modules/contentData' 11 import { useContentDataStoreWithOut } from '@/store/modules/contentData'
13 -import type { RpcCommandType } from '@/api/device/model'  
14 12
15 export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: NodeDataDataSourceJsonType, cell: MxCell) { 13 export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: NodeDataDataSourceJsonType, cell: MxCell) {
16 const { createMessage } = useMessage() 14 const { createMessage } = useMessage()
17 15
  16 + /**
  17 + * @description 多命令下发
  18 + * @param data
  19 + * @returns
  20 + */
18 const handlerCommandDelivery = async (data: MouseUpEventDataType) => { 21 const handlerCommandDelivery = async (data: MouseUpEventDataType) => {
19 try { 22 try {
20 const promiseList: (() => Promise<any>)[] = [] 23 const promiseList: (() => Promise<any>)[] = []
@@ -34,8 +37,8 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N @@ -34,8 +37,8 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N
34 way, 37 way,
35 deviceId, 38 deviceId,
36 command: { 39 command: {
37 - additionalInfo: { cmdType: 'API' },  
38 - method: 'methodThingskit', 40 + additionalInfo: { cmdType: CommandTypeEnum.API },
  41 + method: CommandMethodEnum.THINGSKIT,
39 params: command, 42 params: command,
40 persistent: true, 43 persistent: true,
41 }, 44 },
@@ -51,11 +54,19 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N @@ -51,11 +54,19 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N
51 } 54 }
52 } 55 }
53 56
  57 + /**
  58 + * @description 打开链接
  59 + * @param data
  60 + */
54 const handleOpenLink = (data: SingleClickEventDataType) => { 61 const handleOpenLink = (data: SingleClickEventDataType) => {
55 const { openLink } = data 62 const { openLink } = data
56 window.open(openLink) 63 window.open(openLink)
57 } 64 }
58 65
  66 + /**
  67 + * @description 跳转页面
  68 + * @param data
  69 + */
59 const handleSwitchPage = (data: SingleClickEventDataType) => { 70 const handleSwitchPage = (data: SingleClickEventDataType) => {
60 const { openPage } = data 71 const { openPage } = data
61 const pages = window.DrawApp.pages 72 const pages = window.DrawApp.pages
@@ -64,75 +75,36 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N @@ -64,75 +75,36 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N
64 window.DrawApp.selectPage(openPageFile) 75 window.DrawApp.selectPage(openPageFile)
65 } 76 }
66 77
67 - const handleParamsSetting = async (_data: SingleClickEventDataType) => {  
68 - const deviceInfo = dataSourceJson  
69 - 78 + const handleParamsSetting = async (data: SingleClickEventDataType) => {
70 const instance = h(CommandDeliveryModal) 79 const instance = h(CommandDeliveryModal)
71 render(instance, document.body); 80 render(instance, document.body);
72 - (instance.component?.exposed as InstanceType<typeof CommandDeliveryModal>)?.open({ ..._data, deviceInfo, operationPassword: eventJson?.OPERATION_PASSWORD }) 81 + (instance.component?.exposed as InstanceType<typeof CommandDeliveryModal>)?.open({ dataSource: toRaw(unref(dataSourceJson)), eventBindData: data, operationPasswordInfo: eventJson.OPERATION_PASSWORD })
73 } 82 }
74 83
75 const handleAttributeDelivery = async (data: SingleClickEventDataType) => { 84 const handleAttributeDelivery = async (data: SingleClickEventDataType) => {
76 try { 85 try {
77 const nodeUtils = new NodeUtils() 86 const nodeUtils = new NodeUtils()
78 - const { way = CommandWayEnum.ONE_WAY } = data  
79 - const { attr, deviceId, attrInfo } = dataSourceJson  
80 - const { transportType, alias, name: deviceName, code, codeType } = await getDeviceInfo(dataSourceJson.deviceId) || {}// 设备信息  
81 - const contentDataStore = useContentDataStoreWithOut()  
82 - const currentData = contentDataStore.contentData.find(item => item.id === cell.getId())  
83 - if (!currentData) return  
84 - const { actJson, eventJson } = currentData  
85 - const { value: operationPassword, checked: operationPasswordEnable } = eventJson.OPERATION_PASSWORD 87 + const operationPasswordInfo = toRaw(unref(eventJson.OPERATION_PASSWORD))
86 88
87 - const command: RpcCommandType = {  
88 - additionalInfo: { cmdType: 'API' },  
89 - method: 'methodThingskit',  
90 - params: {},  
91 - persistent: true,  
92 - }  
93 if (nodeUtils.getNodeComponentKey(cell) === ControlComponentEnum.SWITCH) { 89 if (nodeUtils.getNodeComponentKey(cell) === ControlComponentEnum.SWITCH) {
  90 + const contentDataStore = useContentDataStoreWithOut()
  91 + const currentData = contentDataStore.contentData.find(item => item.id === cell.getId())
  92 + if (!currentData) return
  93 + const { actJson } = currentData
94 const { rangeList } = actJson.STATUS_SETTING 94 const { rangeList } = actJson.STATUS_SETTING
95 const status = cell.getAttribute('SWITCH_STATUS') 95 const status = cell.getAttribute('SWITCH_STATUS')
96 const res = rangeList.find(item => item.type === (status === ActRangListItemTypeEnum.OPEN ? ActRangListItemTypeEnum.CLOSE : ActRangListItemTypeEnum.OPEN)) 96 const res = rangeList.find(item => item.type === (status === ActRangListItemTypeEnum.OPEN ? ActRangListItemTypeEnum.CLOSE : ActRangListItemTypeEnum.OPEN))
97 if (!res) return 97 if (!res) return
98 const { statusValue } = res 98 const { statusValue } = res
99 99
100 - command.params = transportType === TransportTypeEnum.TCP  
101 - ? statusValue!  
102 - : {  
103 - [attr]: statusValue,  
104 - }  
105 -  
106 - if (operationPasswordEnable) {  
107 - const instance = h(CommandDeliveryConfirmModal)  
108 - render(instance, document.body)  
109 - await (instance.component?.exposed as InstanceType<typeof CommandDeliveryConfirmModal>)?.open(operationPassword!)  
110 - }  
111 - else {  
112 - const instance = h(ConfirmModal)  
113 - render(instance, document.body)  
114 - await (instance.component?.exposed as InstanceType<typeof ConfirmModal>)?.open()  
115 - }  
116 -  
117 - if (transportType === TransportTypeEnum.TCP && codeType === CodeTypeEnum.MODBUS_RTU) {  
118 - const { validateCanGetCommand, getModbusCommand } = useGetModbusCommand()  
119 - if (!validateCanGetCommand(unref(dataSourceJson).attrInfo.extensionDesc, code).flag) return  
120 - const res = await getModbusCommand(statusValue!, unref(dataSourceJson).attrInfo.extensionDesc!, code)  
121 - if (!res) return  
122 - command.params = res  
123 - }  
124 -  
125 - await doCommandDelivery({ way, command, deviceId })  
126 - createMessage.success('命令下发成功') 100 + const instance = h(SwitchCommandDeliveryModal)
  101 + render(instance, document.body)
  102 + await (instance.component?.exposed as InstanceType<typeof SwitchCommandDeliveryModal>)?.open({ operationPasswordInfo, eventBindData: toRaw(unref(data)), dataSourceJson: toRaw(unref(dataSourceJson)), sendValue: statusValue }!)
127 } 103 }
128 else { 104 else {
129 const instance = h(AttributeDeliverModal) 105 const instance = h(AttributeDeliverModal)
130 - render(instance, document.body)  
131 - const value = await (instance.component?.exposed as InstanceType<typeof AttributeDeliverModal>)?.open({ title: `${alias || deviceName}-${attrInfo.name}`, operationPassword, operationPasswordEnable, dataSourceJson }) as string  
132 - command.params = transportType === TransportTypeEnum.TCP ? value : { [attr]: value }  
133 - if (!command.params) return  
134 - await doCommandDelivery({ way, command, deviceId })  
135 - createMessage.success('命令下发成功') 106 + render(instance, document.body);
  107 + (instance.component?.exposed as InstanceType<typeof AttributeDeliverModal>)?.open({ dataSourceJson, operationPasswordInfo, eventBindData: data })
136 } 108 }
137 } 109 }
138 catch (error) { 110 catch (error) {
  1 +import type { ConfigurationContentType } from '@/api/content/model'
  2 +import { getProductsDetailWithThingsModel } from '@/api/device'
  3 +import { useProductsStoreWithOut } from '@/store/modules/products'
  4 +
  5 +export function useProduct() {
  6 + const getAllDeviceProfileIds = async (contentData: ConfigurationContentType) => {
  7 + const deviceProfileIds = contentData.productAndDevice?.map(item => item.profileId) || []
  8 + const result = await getProductsDetailWithThingsModel({ deviceProfileIds, functionTypeEnum: 'all' })
  9 + const productsStore = useProductsStoreWithOut()
  10 + productsStore.setProducts(result.reduce((prev, next) => ({ ...prev, [next.id]: next }), {}))
  11 + }
  12 +
  13 + return {
  14 + getAllDeviceProfileIds,
  15 + }
  16 +}
@@ -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) => {
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 import { Button, Divider } from 'ant-design-vue' 2 import { Button, Divider } from 'ant-design-vue'
3 import { nextTick, onMounted, ref, unref } from 'vue' 3 import { nextTick, onMounted, ref, unref } from 'vue'
4 import { getOrganization } from '@/api/device' 4 import { getOrganization } from '@/api/device'
5 -import { getVideoList } from '@/api/video' 5 +import { getCameraList } from '@/api/video'
6 import { BasicForm, useForm } from '@/components/Form' 6 import { BasicForm, useForm } from '@/components/Form'
7 import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum' 7 import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum'
8 import { ContentDataFieldsEnum, ContentDataFieldsNameEnum } from '@/enums/datasource' 8 import { ContentDataFieldsEnum, ContentDataFieldsNameEnum } from '@/enums/datasource'
@@ -13,6 +13,8 @@ import { createPublicFormContext } from '@/core/Library/components/PublicForm/us @@ -13,6 +13,8 @@ import { createPublicFormContext } from '@/core/Library/components/PublicForm/us
13 import type { NodeDataDataSourceJsonType, VideoOptionType } from '@/api/node/model' 13 import type { NodeDataDataSourceJsonType, VideoOptionType } from '@/api/node/model'
14 import { useMessage } from '@/hooks/web/useMessage' 14 import { useMessage } from '@/hooks/web/useMessage'
15 import { useSavePageContent } from '@/core/Library/hook/useSavePageContent' 15 import { useSavePageContent } from '@/core/Library/hook/useSavePageContent'
  16 +import { VideoAccessModeEnum } from '@/enums/videoEnum'
  17 +import type { VideoItemRecordType } from '@/api/video/model'
16 18
17 const props = defineProps<ConfigComponentProps>() 19 const props = defineProps<ConfigComponentProps>()
18 const { organizationId } = useParseParams() 20 const { organizationId } = useParseParams()
@@ -20,6 +22,23 @@ const nodeDataActinType = useNodeData({ cell: props.cell!, immediate: false }) @@ -20,6 +22,23 @@ const nodeDataActinType = useNodeData({ cell: props.cell!, immediate: false })
20 22
21 const { getNodeData, getNodeAllData, saveNodeAllData } = nodeDataActinType 23 const { getNodeData, getNodeAllData, saveNodeAllData } = nodeDataActinType
22 24
  25 +enum VideoFormFieldsEnum {
  26 + VIDEO_ID = 'id',
  27 + VIDEO_URL = 'videoUrl',
  28 + ACCESS_MODE = 'accessMode',
  29 + VIDEO_FLAG = 'videoComponentFlag',
  30 + CHANNEL_ID = 'channelId',
  31 + DEVICE_ID = 'deviceId',
  32 +}
  33 +
  34 +enum VideoFormFieldsNameEnum {
  35 + VIDEO_ID = '视频流',
  36 + ACCESS_MODE = 'ACCESS_MODE',
  37 + VIDEO_URL = '视频地址',
  38 + CHANNEL_ID = '通道号',
  39 + DEVICE_ID = '设备id',
  40 +}
  41 +
23 const loading = ref(false) 42 const loading = ref(false)
24 43
25 const [register, { getFieldsValue, validate, setFieldsValue }] = useForm({ 44 const [register, { getFieldsValue, validate, setFieldsValue }] = useForm({
@@ -36,45 +55,67 @@ const [register, { getFieldsValue, validate, setFieldsValue }] = useForm({ @@ -36,45 +55,67 @@ const [register, { getFieldsValue, validate, setFieldsValue }] = useForm({
36 labelField: 'name', 55 labelField: 'name',
37 valueField: 'id', 56 valueField: 'id',
38 onChange() { 57 onChange() {
39 - formModel[ContentDataFieldsEnum.VIDEO_ID] = null 58 + formModel[VideoFormFieldsEnum.VIDEO_ID] = null
  59 + formModel[VideoFormFieldsEnum.CHANNEL_ID] = null
  60 + formModel[VideoFormFieldsEnum.DEVICE_ID] = null
40 }, 61 },
41 } 62 }
42 }, 63 },
43 }, 64 },
44 { 65 {
45 - field: ContentDataFieldsEnum.VIDEO_ID,  
46 - label: ContentDataFieldsNameEnum.VIDEO_ID, 66 + field: VideoFormFieldsEnum.VIDEO_ID,
  67 + label: VideoFormFieldsNameEnum.VIDEO_ID,
47 component: ComponentEnum.API_SELECT, 68 component: ComponentEnum.API_SELECT,
  69 + required: true,
48 componentProps: ({ formModel }) => { 70 componentProps: ({ formModel }) => {
49 return { 71 return {
50 - api: getVideoList,  
51 - params: formModel[ContentDataFieldsEnum.ORG_ID],  
52 - 72 + api: async (params: Record<'organizationId', string>) => {
  73 + if (!params.organizationId) return []
  74 + return await getCameraList(params)
  75 + },
  76 + params: {
  77 + organizationId: formModel[ContentDataFieldsEnum.ORG_ID],
  78 + },
53 fieldNames: { label: 'name', value: 'id' }, 79 fieldNames: { label: 'name', value: 'id' },
54 - resultField: 'items',  
55 - onSelect(value: string, option: any) {  
56 - formModel[ContentDataFieldsEnum.ACCESS_MODE] = value && option ? option.accessMode : null  
57 - formModel[ContentDataFieldsEnum.VIDEO_URL] = value && option ? option.videoUrl : null  
58 - formModel[ContentDataFieldsEnum.VIDEO_FLAG] = true 80 + resultField: 'data',
  81 + onSelect(_: string, option: VideoItemRecordType) {
  82 + const accessMode = option?.accessMode
  83 + formModel[VideoFormFieldsEnum.ACCESS_MODE] = accessMode
  84 + formModel[VideoFormFieldsEnum.VIDEO_URL] = option?.videoUrl
  85 + formModel[VideoFormFieldsEnum.CHANNEL_ID] = accessMode === VideoAccessModeEnum.GBT28181 ? option?.params?.channelNo : null
  86 + formModel[VideoFormFieldsEnum.DEVICE_ID] = accessMode === VideoAccessModeEnum.GBT28181 ? option?.params?.deviceId : null
  87 + formModel[VideoFormFieldsEnum.VIDEO_FLAG] = true
59 }, 88 },
60 } 89 }
61 }, 90 },
62 }, 91 },
63 { 92 {
64 - field: ContentDataFieldsEnum.ACCESS_MODE,  
65 - label: ContentDataFieldsNameEnum.ACCESS_MODE, 93 + field: VideoFormFieldsEnum.ACCESS_MODE,
  94 + label: VideoFormFieldsNameEnum.ACCESS_MODE,
  95 + component: ComponentEnum.INPUT,
  96 + ifShow: false,
  97 + },
  98 + {
  99 + field: VideoFormFieldsEnum.DEVICE_ID,
  100 + label: VideoFormFieldsNameEnum.DEVICE_ID,
66 component: ComponentEnum.INPUT, 101 component: ComponentEnum.INPUT,
67 ifShow: false, 102 ifShow: false,
68 }, 103 },
69 { 104 {
70 - field: ContentDataFieldsEnum.VIDEO_URL,  
71 - label: ContentDataFieldsNameEnum.VIDEO_URL, 105 + field: VideoFormFieldsEnum.CHANNEL_ID,
  106 + label: VideoFormFieldsNameEnum.CHANNEL_ID,
72 component: ComponentEnum.INPUT, 107 component: ComponentEnum.INPUT,
73 ifShow: false, 108 ifShow: false,
74 }, 109 },
75 { 110 {
76 - field: ContentDataFieldsEnum.VIDEO_FLAG,  
77 - label: ContentDataFieldsEnum.VIDEO_FLAG, 111 + field: VideoFormFieldsEnum.VIDEO_URL,
  112 + label: VideoFormFieldsNameEnum.VIDEO_URL,
  113 + component: ComponentEnum.INPUT,
  114 + ifShow: false,
  115 + },
  116 + {
  117 + field: VideoFormFieldsEnum.VIDEO_FLAG,
  118 + label: VideoFormFieldsEnum.VIDEO_FLAG,
78 component: ComponentEnum.INPUT, 119 component: ComponentEnum.INPUT,
79 ifShow: false, 120 ifShow: false,
80 }, 121 },
@@ -130,9 +171,10 @@ createPublicFormContext(nodeDataActinType) @@ -130,9 +171,10 @@ createPublicFormContext(nodeDataActinType)
130 171
131 <style lang="less" scoped> 172 <style lang="less" scoped>
132 .form-container { 173 .form-container {
  174 + @apply text-sm;
  175 +
  176 + :deep(.ant-divider) {
133 @apply text-sm; 177 @apply text-sm;
134 - :deep(.ant-divider) {  
135 - @apply text-sm;  
136 - }  
137 } 178 }
  179 +}
138 </style> 180 </style>
1 <script setup lang="ts"> 1 <script setup lang="ts">
  2 +import { Spin } from 'ant-design-vue'
2 import { computed, nextTick, onMounted, onUnmounted, ref, toRaw, unref } from 'vue' 3 import { computed, nextTick, onMounted, onUnmounted, ref, toRaw, unref } from 'vue'
3 import 'video.js/dist/video-js.css' 4 import 'video.js/dist/video-js.css'
4 5
5 import type { VideoJsPlayerOptions } from 'video.js' 6 import type { VideoJsPlayerOptions } from 'video.js'
6 import { BasicVideoPlay } from './component/index.ts' 7 import { BasicVideoPlay } from './component/index.ts'
7 -import { getVideoTypeByUrl, isRtspProtocol, useFingerprint } from './component/config' 8 +import { VideoPlayerType, getVideoTypeByUrl, isRtspProtocol, useFingerprint } from './component/config'
8 9
9 import { getJwtToken, getShareJwtToken } from '@/utils/auth' 10 import { getJwtToken, getShareJwtToken } from '@/utils/auth'
10 import { isLightboxMode, isShareMode } from '@/utils/env' 11 import { isLightboxMode, isShareMode } from '@/utils/env'
11 import type { CreateComponentType } from '@/core/Library/types' 12 import type { CreateComponentType } from '@/core/Library/types'
12 -import { closeFlvPlay, getFlvPlayUrl, getStreamingPlayUrl } from '@/api/video' 13 +import { closeFlvPlay, getFlvPlayUrl, getStreamingPlayUrl, getVideoControlStart } from '@/api/video'
13 import { useContentDataStore } from '@/store/modules/contentData' 14 import { useContentDataStore } from '@/store/modules/contentData'
  15 +import { VideoAccessModeEnum } from '@/enums/videoEnum'
14 16
15 const props = defineProps<{ 17 const props = defineProps<{
16 config: CreateComponentType 18 config: CreateComponentType
@@ -20,6 +22,7 @@ const props = defineProps<{ @@ -20,6 +22,7 @@ const props = defineProps<{
20 // 获取节点数据 22 // 获取节点数据
21 const contentDataStore = useContentDataStore() 23 const contentDataStore = useContentDataStore()
22 const { getResult } = useFingerprint() 24 const { getResult } = useFingerprint()
  25 +const loading = ref(false)
23 26
24 const getOptions = computed<VideoJsPlayerOptions>(() => { 27 const getOptions = computed<VideoJsPlayerOptions>(() => {
25 const { config } = props || {} 28 const { config } = props || {}
@@ -46,45 +49,58 @@ const playSource = ref<Record<'src' | 'type', string>>( @@ -46,45 +49,58 @@ const playSource = ref<Record<'src' | 'type', string>>(
46 49
47 // 数据绑定获取的url 50 // 数据绑定获取的url
48 const handleGetVideoPlay = async () => { 51 const handleGetVideoPlay = async () => {
49 - const { dataSourceJson } = unref(videoConfig)[0] || {}  
50 - const { videoOption } = dataSourceJson || {}  
51 - const { id, accessMode, videoUrl } = videoOption || {}  
52 - let type = getVideoTypeByUrl(videoUrl!)  
53 - let playUrl = videoUrl  
54 -  
55 - // 判断是否是流媒体播放  
56 - if (accessMode === 1 && id) {  
57 - const { data: { url } = { url: '' } } = await getStreamingPlayUrl(id!)  
58 - playUrl = url  
59 - playUrl && (type = getVideoTypeByUrl(playUrl!))  
60 - } 52 + try {
  53 + loading.value = true
  54 + const { dataSourceJson } = unref(videoConfig)[0] || {}
  55 + const { videoOption } = dataSourceJson || {}
  56 + const { id, accessMode, videoUrl, deviceId, channelId } = videoOption || {}
  57 + let type = getVideoTypeByUrl(videoUrl!)
  58 + let playUrl = videoUrl
  59 + // 判断是否是流媒体播放
  60 + if (accessMode === VideoAccessModeEnum.Streaming && id) {
  61 + const { data: { url } = { url: '' } } = await getStreamingPlayUrl(id!)
  62 + playUrl = url
  63 + playUrl && (type = getVideoTypeByUrl(playUrl!))
  64 + }
  65 + else if (accessMode === VideoAccessModeEnum.GBT28181 && deviceId && channelId) {
  66 + const {
  67 + data: { flv },
  68 + } = await getVideoControlStart({ channelId, deviceId })
61 69
62 - // 判断是否是rtsp播放  
63 - if (isRtspProtocol(videoUrl!)) {  
64 - const result = await getResult()  
65 - const { visitorId } = result  
66 - playUrl = getFlvPlayUrl(playUrl!, visitorId)  
67 - withToken.value = true  
68 - } 70 + playUrl = flv
  71 + type = VideoPlayerType.flv
  72 + }
69 73
70 - playSource.value = {  
71 - src: playUrl!,  
72 - type,  
73 - } 74 + // 判断是否是rtsp播放
  75 + if (isRtspProtocol(videoUrl!)) {
  76 + const result = await getResult()
  77 + const { visitorId } = result
  78 + playUrl = getFlvPlayUrl(playUrl!, visitorId)
  79 + withToken.value = true
  80 + }
74 81
75 - const instance = unref(basicVideoPlayEl)?.customInit((options) => {  
76 - if (unref(withToken)) {  
77 - (options as any).flvjs.config.headers = {  
78 - 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`,  
79 - } 82 + playSource.value = {
  83 + src: playUrl!,
  84 + type,
80 } 85 }
81 - return {  
82 - ...options,  
83 - sources: [toRaw(unref(playSource))],  
84 - } as VideoJsPlayerOptions  
85 - })  
86 86
87 - instance?.play() 87 + const instance = unref(basicVideoPlayEl)?.customInit((options) => {
  88 + if (unref(withToken)) {
  89 + (options as any).flvjs.config.headers = {
  90 + 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`,
  91 + }
  92 + }
  93 + return {
  94 + ...options,
  95 + sources: [toRaw(unref(playSource))],
  96 + } as VideoJsPlayerOptions
  97 + })
  98 +
  99 + instance?.play()
  100 + }
  101 + finally {
  102 + loading.value = false
  103 + }
88 } 104 }
89 105
90 // 默认播放 106 // 默认播放
@@ -126,23 +142,21 @@ onUnmounted(async () => { @@ -126,23 +142,21 @@ onUnmounted(async () => {
126 142
127 <template> 143 <template>
128 <div class="w-full h-full flex justify-center items-center"> 144 <div class="w-full h-full flex justify-center items-center">
129 - <!-- <Spin :spinning="loading" wrapper-class-name="video-spin" class="w-full h-full"> -->  
130 - <BasicVideoPlay  
131 - ref="basicVideoPlayEl"  
132 - :options="getOptions"  
133 - :with-token="withToken"  
134 - :immediate-init-on-mounted="false"  
135 - />  
136 - <!-- </Spin> --> 145 + <Spin :spinning="loading">
  146 + <BasicVideoPlay
  147 + ref="basicVideoPlayEl" :options="getOptions" :with-token="withToken"
  148 + :immediate-init-on-mounted="false"
  149 + />
  150 + </Spin>
137 </div> 151 </div>
138 </template> 152 </template>
139 153
140 <style lang="less" scoped> 154 <style lang="less" scoped>
141 .video-spin { 155 .video-spin {
142 - @apply w-full h-full; 156 + @apply w-full h-full;
143 157
144 - :deep(.ant-spin-container) {  
145 - @apply w-full h-full;  
146 - } 158 + :deep(.ant-spin-container) {
  159 + @apply w-full h-full;
147 } 160 }
  161 +}
148 </style> 162 </style>
@@ -34,7 +34,7 @@ const handleSubmit = async () => { @@ -34,7 +34,7 @@ const handleSubmit = async () => {
34 const formValues = unref(dataSourceFormRef)?.getFieldsValue() 34 const formValues = unref(dataSourceFormRef)?.getFieldsValue()
35 let dataSourceJson = formValues 35 let dataSourceJson = formValues
36 if (contentDataStore.getIsTemplate) 36 if (contentDataStore.getIsTemplate)
37 - dataSourceJson = { ...formValues, deviceProfileId: formValues?.deviceProfileTemplateId, deviceId: null } 37 + dataSourceJson = { ...formValues, deviceProfileId: formValues?.deviceProfileTemplateId, deviceId: null, deviceName: null }
38 38
39 saveNodeAllData({ dataSourceJson: { ...dataSourceJson, circularFlowMeterOption: unref(colorConfig) } }) 39 saveNodeAllData({ dataSourceJson: { ...dataSourceJson, circularFlowMeterOption: unref(colorConfig) } })
40 createMessage.success('保存成功~') 40 createMessage.success('保存成功~')
@@ -9,6 +9,7 @@ import { useOnMessage } from '@/core/Library/hook/useOnMessage' @@ -9,6 +9,7 @@ import { useOnMessage } from '@/core/Library/hook/useOnMessage'
9 import type { NodeDataDataSourceJsonType } from '@/api/node/model' 9 import type { NodeDataDataSourceJsonType } from '@/api/node/model'
10 import { useLatestMessageValue } from '@/core/Library/hook/useLatestMessageValue' 10 import { useLatestMessageValue } from '@/core/Library/hook/useLatestMessageValue'
11 import { useContentDataStore } from '@/store/modules/contentData' 11 import { useContentDataStore } from '@/store/modules/contentData'
  12 +import { useProductsStore } from '@/store/modules/products'
12 13
13 const props = defineProps<{ 14 const props = defineProps<{
14 config: CreateComponentType 15 config: CreateComponentType
@@ -24,6 +25,8 @@ const chartElRef = ref<Nullable<HTMLDivElement>>() @@ -24,6 +25,8 @@ const chartElRef = ref<Nullable<HTMLDivElement>>()
24 25
25 const chartInstance = ref<Nullable<ECharts>>() 26 const chartInstance = ref<Nullable<ECharts>>()
26 27
  28 +const productsStore = useProductsStore()
  29 +
27 function initChartInstance() { 30 function initChartInstance() {
28 const { dataSourceJson } = unref(getCurrentNodeData) || {} 31 const { dataSourceJson } = unref(getCurrentNodeData) || {}
29 const { circularFlowMeterOption } = dataSourceJson || {} 32 const { circularFlowMeterOption } = dataSourceJson || {}
@@ -34,13 +37,13 @@ function initChartInstance() { @@ -34,13 +37,13 @@ function initChartInstance() {
34 const { onMessage } = useOnMessage({ 37 const { onMessage } = useOnMessage({
35 onReceiveDataSourceMessage(commandSource, message) { 38 onReceiveDataSourceMessage(commandSource, message) {
36 const { data } = commandSource 39 const { data } = commandSource
37 - const { deviceInfo, attrInfo } = data || {}  
38 - const { deviceName } = deviceInfo || {}  
39 - const { attr } = data as NodeDataDataSourceJsonType  
40 - const { latestValue } = useLatestMessageValue(message.data, attr) 40 + const { attr, deviceName, deviceProfileId } = data as NodeDataDataSourceJsonType
  41 +
  42 + const { functionName } = productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, (attr) as string) || {}
  43 + const { latestValue } = useLatestMessageValue(message.data, (attr as string))
41 unref(chartInstance)?.setOption({ 44 unref(chartInstance)?.setOption({
42 title: { 45 title: {
43 - text: `${deviceName || ''}-${attrInfo.name || ''}`, 46 + text: `${deviceName || ''} - ${functionName || ''}`,
44 }, 47 },
45 series: [{ 48 series: [{
46 data: getSetValue(Number(latestValue)), 49 data: getSetValue(Number(latestValue)),
@@ -3,7 +3,8 @@ import { Button, Divider } from 'ant-design-vue' @@ -3,7 +3,8 @@ import { Button, Divider } from 'ant-design-vue'
3 import { nextTick, onMounted, ref, unref } from 'vue' 3 import { nextTick, onMounted, ref, unref } from 'vue'
4 import { formSchemas } from '../config' 4 import { formSchemas } from '../config'
5 import { ChartComponentEnum } from '..' 5 import { ChartComponentEnum } from '..'
6 -import { DataSourceForm } from '@/core/Library/components/PublicForm/components/DataSourceForm' 6 +import { DataSourceForm } from '../component/index'
  7 +// import { DataSourceForm } from '@/core/Library/components/PublicForm/components/DataSourceForm'
7 8
8 import { BasicForm, useForm } from '@/components/Form' 9 import { BasicForm, useForm } from '@/components/Form'
9 import { FormLayoutEnum } from '@/components/Form/src/enum' 10 import { FormLayoutEnum } from '@/components/Form/src/enum'
@@ -44,9 +45,9 @@ const handleSubmit = async () => { @@ -44,9 +45,9 @@ const handleSubmit = async () => {
44 const values = getFieldsValue() 45 const values = getFieldsValue()
45 let dataSourceJson = value 46 let dataSourceJson = value
46 if (contentDataStore.getIsTemplate) 47 if (contentDataStore.getIsTemplate)
47 - dataSourceJson = { ...value, deviceProfileId: value?.deviceProfileTemplateId, deviceId: null } 48 + dataSourceJson = { ...value, deviceProfileId: value?.deviceProfileTemplateId, deviceId: null, deviceName: null }
48 49
49 - await saveNodeAllData({ dataSourceJson: { ...dataSourceJson, chartOption: { ...values } } }) 50 + await saveNodeAllData({ dataSourceJson: { ...dataSourceJson, attr: typeof value.attr == 'string' ? [value.attr] : value.attr, chartOption: { ...values } } })
50 savePageContent() 51 savePageContent()
51 createMessage.success('操作成功~') 52 createMessage.success('操作成功~')
52 } 53 }
@@ -57,9 +58,9 @@ const handleSubmit = async () => { @@ -57,9 +58,9 @@ const handleSubmit = async () => {
57 58
58 const handleSetFormValues = async () => { 59 const handleSetFormValues = async () => {
59 const { dataSourceJson } = unref(getNodeData) || {} 60 const { dataSourceJson } = unref(getNodeData) || {}
60 - const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId, attrInfo, deviceInfo } = dataSourceJson || {} 61 + const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId, deviceName } = dataSourceJson || {}
61 await nextTick() 62 await nextTick()
62 - unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, attrInfo, deviceProfileTemplateId, deviceInfo }) 63 + unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceName, deviceProfileId, deviceProfileTemplateId })
63 setFieldsValue({ ...chartOption }) 64 setFieldsValue({ ...chartOption })
64 } 65 }
65 66
  1 +import type { EChartsOption } from 'echarts'
1 import { isLightboxMode } from '@/utils/env' 2 import { isLightboxMode } from '@/utils/env'
2 -  
3 -const data = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']  
4 -const seriesList = [  
5 - { value: 120, name: '123', itemStyle: { color: '#5470c6' } },  
6 - { value: 150, name: '456', itemStyle: { color: '#5470c6' } },  
7 - { value: 40, name: '789', itemStyle: { color: '#5470c6' } },  
8 - { value: 139, name: '111', itemStyle: { color: '#5470c6' } },  
9 - { value: 130, name: '222', itemStyle: { color: '#5470c6' } },  
10 - { value: 125, name: '333', itemStyle: { color: '#5470c6' } },  
11 - { value: 110, name: '444', itemStyle: { color: '#5470c6' } },  
12 -]  
13 -export const defaultOption = {  
14 - color: ['#3398DB'],  
15 - tooltip: {  
16 - // 提示框  
17 - trigger: 'item',  
18 - confine: true,  
19 - axisPointer: {  
20 - type: 'shadow', 3 +export const defaultOption = (): EChartsOption => {
  4 + return {
  5 + tooltip: {
21 }, 6 },
22 - },  
23 - xAxis: {  
24 - type: 'category',  
25 - data: isLightboxMode() ? [] : data,  
26 - axisTick: {  
27 - alignWithLabel: true, 7 + calculable: true,
  8 + xAxis:
  9 + {
  10 + type: 'category',
  11 + // prettier-ignore
  12 + data: isLightboxMode() ? [] : ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
  13 + },
  14 +
  15 + legend: {
  16 + top: '8%',
  17 + left: 'center',
  18 + data: [''],
28 }, 19 },
29 - },  
30 - grid: {  
31 - top: '10%',  
32 - left: '10%',  
33 - bottom: '10%',  
34 - },  
35 - yAxis: {  
36 - type: 'value',  
37 - },  
38 - series: [  
39 - {  
40 - name: '',  
41 - type: 'bar',  
42 - barWidth: '60%',  
43 - data: isLightboxMode () ? [] : seriesList, 20 +
  21 + grid: {
  22 + top: '25%',
  23 + left: '6%',
  24 + right: '10%',
  25 + bottom: '8%',
  26 + containLabel: true,
44 }, 27 },
45 - ], 28 + yAxis:
  29 + {
  30 + type: 'value',
  31 + },
  32 + series: [
  33 + {
  34 + name: 'Rainfall',
  35 + type: isLightboxMode() ? 'line' : 'bar',
  36 + data: isLightboxMode()
  37 + ? []
  38 + : [
  39 + 25.6, 76.7, 42.0, 27.0, 23.2,
  40 + ],
  41 + },
  42 + {
  43 + name: 'Evaporation',
  44 + type: 'bar',
  45 + data: isLightboxMode()
  46 + ? []
  47 + : [
  48 + 28.7, 32.6, 70.7, 29.0, 26.4,
  49 + ],
  50 + },
  51 + ],
  52 + }
46 } 53 }
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import { computed, onMounted, onUnmounted, ref, unref } from 'vue' 2 +import { computed, onMounted, onUnmounted, ref, toRaw, unref } from 'vue'
3 import { init } from 'echarts' 3 import { init } from 'echarts'
4 import type { ECharts, EChartsOption } from 'echarts' 4 import type { ECharts, EChartsOption } from 'echarts'
5 import { defaultOption } from './index.config' 5 import { defaultOption } from './index.config'
@@ -10,6 +10,7 @@ import { SocketSubscriberEnum } from '@/enums/datasource' @@ -10,6 +10,7 @@ import { SocketSubscriberEnum } from '@/enums/datasource'
10 import type { SubscriptionData } from '@/core/websocket/type/message' 10 import type { SubscriptionData } from '@/core/websocket/type/message'
11 import { formatToDateTime } from '@/utils/dateUtil' 11 import { formatToDateTime } from '@/utils/dateUtil'
12 import type { CommandSource } from '@/core/websocket/processor' 12 import type { CommandSource } from '@/core/websocket/processor'
  13 +import { useProductsStore } from '@/store/modules/products'
13 14
14 const props = defineProps<{ 15 const props = defineProps<{
15 config: CreateComponentType 16 config: CreateComponentType
@@ -21,40 +22,80 @@ const chartElRef = ref<Nullable<HTMLDivElement>>() @@ -21,40 +22,80 @@ const chartElRef = ref<Nullable<HTMLDivElement>>()
21 22
22 const chartInstance = ref<Nullable<ECharts>>() 23 const chartInstance = ref<Nullable<ECharts>>()
23 24
  25 +const seriesData = ref<any>([])
  26 +const XAxisData = ref<any>([])
  27 +const titleATTR = ref<string[] | any>([])
  28 +
24 function initChartInstance() { 29 function initChartInstance() {
25 chartInstance.value = init(unref(chartElRef)) 30 chartInstance.value = init(unref(chartElRef))
26 - chartInstance.value.setOption(defaultOption) 31 + chartInstance.value.setOption(defaultOption())
27 } 32 }
28 33
  34 +const productsStore = useProductsStore()
  35 +
29 const handleHistoryData = (commandSource: CommandSource, message: SubscriptionData) => { 36 const handleHistoryData = (commandSource: CommandSource, message: SubscriptionData) => {
30 const { data } = commandSource 37 const { data } = commandSource
31 - const { attrInfo, deviceInfo, attr } = data as NodeDataDataSourceJsonType  
32 - const { deviceName } = deviceInfo || {} 38 + const { attr, deviceName } = data as NodeDataDataSourceJsonType as any
33 39
34 - const historyData = message[attr]  
35 - const xAxisData: string[] = []  
36 - const seriesData: number[] = [] 40 + attr.forEach((item: any) => { // 得到所有时间
  41 + XAxisData.value.push(...message[item].map((item1) => {
  42 + const [ts] = item1
  43 + return ts
  44 + }))
  45 + })
  46 + XAxisData.value = Array.from(new Set(XAxisData.value)).sort((a: any, b: any) => a - b)// 去重获取到的时间
  47 + // 初始化有几个柱
  48 + seriesData.value.forEach((item: any) => {
  49 + item.data = unref(XAxisData).map((time: any) => ({ name: time, value: '' }))
  50 + })
37 51
38 - for (const item of historyData) {  
39 - const [ts, value] = item  
40 - xAxisData.push(formatToDateTime(ts))  
41 - seriesData.push(value)  
42 - } 52 + // 跟每个柱添加数据
  53 + seriesData.value.forEach((item: any) => {
  54 + item.data.forEach((item1: any) => {
  55 + message[item.attribute].forEach((messageItem) => {
  56 + const [ts, value] = messageItem
  57 + if (item1.name === ts)
  58 + item1.value = value || ''
  59 + // item1.name = formatToDateTime(item1.name)
  60 + })
  61 + })
  62 + })
43 63
44 unref(chartInstance)?.setOption({ 64 unref(chartInstance)?.setOption({
45 title: { 65 title: {
46 - text: `${deviceName || ''}-${attrInfo.name || ''}`, 66 + text: `${deviceName || ''}`,
  67 + },
  68 + xAxis: [
  69 + {
  70 + type: 'category',
  71 + data: toRaw(unref(XAxisData.value.map((item: string | number) => formatToDateTime(item)))),
  72 + },
  73 + ],
  74 + legend: {
  75 + data: unref(titleATTR),
47 }, 76 },
48 - xAxis: { data: xAxisData },  
49 - series: { data: seriesData }, 77 + series: toRaw(unref(seriesData).map((item: { type: string; name: string; data: any; attribute: string }) => {
  78 + const { type, name, data } = item
  79 + return {
  80 + type,
  81 + name,
  82 + data: data.map((item: any) => item.value),
  83 + }
  84 + })),
50 } as EChartsOption) 85 } as EChartsOption)
51 } 86 }
52 87
53 const { onMessage } = useOnMessage({ 88 const { onMessage } = useOnMessage({
54 onReceiveDataSourceMessage(commandSource, message) { 89 onReceiveDataSourceMessage(commandSource, message) {
55 const { data } = commandSource 90 const { data } = commandSource
56 - const { chartOption } = data as NodeDataDataSourceJsonType 91 + const { chartOption, attr, deviceProfileId } = data as NodeDataDataSourceJsonType
57 const { queryType } = chartOption || {} 92 const { queryType } = chartOption || {}
  93 + if (!seriesData.value.length) {
  94 + (attr as string[]).forEach((item: string) => {
  95 + titleATTR.value.push(productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, item)?.functionName)
  96 + seriesData.value.push({ attribute: item, data: [], type: 'bar', name: productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, item)?.functionName })
  97 + })
  98 + }
58 99
59 if (queryType === SocketSubscriberEnum.HISTORY_CMDS) 100 if (queryType === SocketSubscriberEnum.HISTORY_CMDS)
60 handleHistoryData(commandSource, message.data) 101 handleHistoryData(commandSource, message.data)
@@ -42,7 +42,7 @@ const handleSubmit = async () => { @@ -42,7 +42,7 @@ const handleSubmit = async () => {
42 const values = getFieldsValue() 42 const values = getFieldsValue()
43 let dataSourceJson = value 43 let dataSourceJson = value
44 if (contentDataStore.getIsTemplate) 44 if (contentDataStore.getIsTemplate)
45 - dataSourceJson = { ...value, deviceProfileId: value?.deviceProfileTemplateId, deviceId: null } 45 + dataSourceJson = { ...value, deviceProfileId: value?.deviceProfileTemplateId, deviceId: null, deviceName: null }
46 46
47 await saveNodeAllData({ dataSourceJson: { ...dataSourceJson, chartOption: values } }) 47 await saveNodeAllData({ dataSourceJson: { ...dataSourceJson, chartOption: values } })
48 createMessage.success('操作成功~') 48 createMessage.success('操作成功~')
@@ -55,9 +55,9 @@ const handleSubmit = async () => { @@ -55,9 +55,9 @@ const handleSubmit = async () => {
55 55
56 const handleSetFormValues = async () => { 56 const handleSetFormValues = async () => {
57 const { dataSourceJson } = unref(getNodeData) || {} 57 const { dataSourceJson } = unref(getNodeData) || {}
58 - const { deviceId, attr, chartOption, deviceProfileId, attrInfo, deviceProfileTemplateId, deviceInfo } = dataSourceJson || {} 58 + const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId, deviceName } = dataSourceJson || {}
59 await nextTick() 59 await nextTick()
60 - unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, attrInfo, deviceInfo, deviceProfileTemplateId }) 60 + unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, deviceName, deviceProfileTemplateId })
61 setFieldsValue({ ...chartOption }) 61 setFieldsValue({ ...chartOption })
62 } 62 }
63 63
@@ -10,6 +10,7 @@ import type { CommandSource } from '@/core/websocket/processor' @@ -10,6 +10,7 @@ import type { CommandSource } from '@/core/websocket/processor'
10 import type { NodeDataDataSourceJsonType } from '@/api/node/model' 10 import type { NodeDataDataSourceJsonType } from '@/api/node/model'
11 import type { SubscriptionUpdateMsg } from '@/core/websocket/type/message' 11 import type { SubscriptionUpdateMsg } from '@/core/websocket/type/message'
12 import { useContentDataStore } from '@/store/modules/contentData' 12 import { useContentDataStore } from '@/store/modules/contentData'
  13 +import { useProductsStore } from '@/store/modules/products'
13 14
14 const props = defineProps<{ 15 const props = defineProps<{
15 config: CreateComponentType 16 config: CreateComponentType
@@ -23,6 +24,8 @@ const chartInstance = ref<Nullable<ECharts>>() @@ -23,6 +24,8 @@ const chartInstance = ref<Nullable<ECharts>>()
23 24
24 const contentDataStore = useContentDataStore() 25 const contentDataStore = useContentDataStore()
25 26
  27 +const productsStore = useProductsStore()
  28 +
26 function initChartInstance() { 29 function initChartInstance() {
27 chartInstance.value = init(unref(chartElRef)) 30 chartInstance.value = init(unref(chartElRef))
28 const currentNodeData = unref(contentDataStore.getCurrentNodeDataById(props.config)) 31 const currentNodeData = unref(contentDataStore.getCurrentNodeDataById(props.config))
@@ -36,17 +39,16 @@ onMounted(() => { @@ -36,17 +39,16 @@ onMounted(() => {
36 39
37 const onReceiveDataSourceMessage = (commandSource: CommandSource, message: SubscriptionUpdateMsg) => { 40 const onReceiveDataSourceMessage = (commandSource: CommandSource, message: SubscriptionUpdateMsg) => {
38 const { data } = commandSource 41 const { data } = commandSource
39 - const { deviceInfo, attrInfo } = data || {}  
40 - const { deviceName } = deviceInfo || {}  
41 - const { attr } = data as NodeDataDataSourceJsonType  
42 - const { latestValue } = useLatestMessageValue(message.data, attr) 42 + const { attr, deviceName, deviceProfileId } = data as NodeDataDataSourceJsonType
  43 + const { functionName } = productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, (attr) as string) || {}
  44 + const { latestValue } = useLatestMessageValue(message.data, (attr as string))
43 unref(chartInstance)?.setOption({ 45 unref(chartInstance)?.setOption({
44 series: [{ 46 series: [{
45 data: [{ value: latestValue }], 47 data: [{ value: latestValue }],
46 48
47 }], 49 }],
48 title: { 50 title: {
49 - text: `${deviceName || ''}-${attrInfo.name || ''}`, 51 + text: `${deviceName || ''}- ${functionName || ''} `,
50 }, 52 },
51 } as EChartsOption) 53 } as EChartsOption)
52 } 54 }
@@ -3,7 +3,9 @@ import { Button, Divider } from 'ant-design-vue' @@ -3,7 +3,9 @@ import { Button, Divider } from 'ant-design-vue'
3 import { nextTick, onMounted, ref, unref } from 'vue' 3 import { nextTick, onMounted, ref, unref } from 'vue'
4 import { formSchemas } from '../config' 4 import { formSchemas } from '../config'
5 import { ChartComponentEnum } from '..' 5 import { ChartComponentEnum } from '..'
6 -import { DataSourceForm } from '@/core/Library/components/PublicForm/components/DataSourceForm' 6 +
  7 +import { DataSourceForm } from '../component/index'
  8 +// import { DataSourceForm } from '@/core/Library/components/PublicForm/components/DataSourceForm'
7 9
8 import { BasicForm, useForm } from '@/components/Form' 10 import { BasicForm, useForm } from '@/components/Form'
9 import { FormLayoutEnum } from '@/components/Form/src/enum' 11 import { FormLayoutEnum } from '@/components/Form/src/enum'
@@ -33,9 +35,9 @@ const dataSourceElRef = ref<Nullable<InstanceType<typeof DataSourceForm>>>() @@ -33,9 +35,9 @@ const dataSourceElRef = ref<Nullable<InstanceType<typeof DataSourceForm>>>()
33 35
34 const handleSetFormValues = async () => { 36 const handleSetFormValues = async () => {
35 const { dataSourceJson } = unref(getNodeData) || {} 37 const { dataSourceJson } = unref(getNodeData) || {}
36 - const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId, attrInfo, deviceInfo } = dataSourceJson || {} 38 + const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId, deviceName } = dataSourceJson || {}
37 await nextTick() 39 await nextTick()
38 - unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, deviceProfileTemplateId, attrInfo, deviceInfo }) 40 + unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, deviceProfileTemplateId, deviceName })
39 setFieldsValue({ ...chartOption }) 41 setFieldsValue({ ...chartOption })
40 } 42 }
41 43
@@ -52,9 +54,9 @@ const handleSubmit = async () => { @@ -52,9 +54,9 @@ const handleSubmit = async () => {
52 const values = getFieldsValue() 54 const values = getFieldsValue()
53 let dataSourceJson = value 55 let dataSourceJson = value
54 if (contentDataStore.getIsTemplate) 56 if (contentDataStore.getIsTemplate)
55 - dataSourceJson = { ...value, deviceProfileId: value?.deviceProfileTemplateId, deviceId: null } 57 + dataSourceJson = { ...value, deviceProfileId: value?.deviceProfileTemplateId, deviceId: null, deviceName: null }
56 58
57 - saveNodeAllData({ dataSourceJson: { ...dataSourceJson, chartOption: { ...values } } }) 59 + saveNodeAllData({ dataSourceJson: { ...dataSourceJson, attr: typeof value.attr == 'string' ? [value.attr] : value.attr, chartOption: { ...values } } })
58 createMessage.success('操作成功~') 60 createMessage.success('操作成功~')
59 savePageContent() 61 savePageContent()
60 } 62 }
@@ -3,31 +3,38 @@ import { isLightboxMode } from '@/utils/env' @@ -3,31 +3,38 @@ import { isLightboxMode } from '@/utils/env'
3 3
4 export const getDefaultOption = (): EChartsOption => { 4 export const getDefaultOption = (): EChartsOption => {
5 return { 5 return {
  6 +
6 tooltip: { 7 tooltip: {
7 - trigger: 'item',  
8 - confine: true, 8 + },
  9 + legend: {
  10 + top: '10%',
  11 + left: 'center',
  12 + data: [''],
9 }, 13 },
10 grid: { 14 grid: {
11 - left: '3%',  
12 - right: '4%',  
13 - bottom: '3%', 15 + top: '30%',
  16 + left: '6%',
  17 + right: '10%',
  18 + bottom: '8%',
14 containLabel: true, 19 containLabel: true,
15 }, 20 },
16 - dataset: {  
17 - source: isLightboxMode() 21 + xAxis: {
  22 + type: 'category',
  23 + },
  24 + yAxis: {
  25 + type: 'value',
  26 + boundaryGap: [0, '100%'],
  27 + },
  28 + series: [{
  29 + type: 'line',
  30 + name: '温度',
  31 + data: isLightboxMode()
18 ? [] 32 ? []
19 : [ 33 : [
20 - ['product', '2015', '2016', '2017'],  
21 - ['Matcha Latte', 43.3, 85.8, 93.7],  
22 - ['Milk Tea', 83.1, 73.4, 55.1],  
23 - ['Cheese Cocoa', 86.4, 65.2, 82.5],  
24 - ['Walnut Brownie', 72.4, 53.9, 39.1],  
25 - ],  
26 - },  
27 - xAxis: { type: 'category' },  
28 - yAxis: {},  
29 - series: [  
30 - { type: 'line' },  
31 - ], 34 + ['Matcha Latte', 43.3],
  35 + ['Milk Tea', 83.1],
  36 + ['Cheese Cocoa', 86.4],
  37 + ['Walnut Brownie', 72.4]],
  38 + }],
32 } 39 }
33 } 40 }
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 -import { computed, onMounted, onUnmounted, ref, unref } from 'vue'  
3 -import type { DatasetComponentOption, ECharts, EChartsOption } from 'echarts' 2 +import { computed, onMounted, onUnmounted, ref, toRaw, unref } from 'vue'
  3 +import type { ECharts, EChartsOption } from 'echarts'
4 import { init } from 'echarts' 4 import { init } from 'echarts'
5 import { getDefaultOption } from './index.config' 5 import { getDefaultOption } from './index.config'
6 import type { CreateComponentType, RenderComponentExposeType } from '@/core/Library/types' 6 import type { CreateComponentType, RenderComponentExposeType } from '@/core/Library/types'
@@ -9,8 +9,12 @@ import type { NodeDataDataSourceJsonType } from '@/api/node/model' @@ -9,8 +9,12 @@ import type { NodeDataDataSourceJsonType } from '@/api/node/model'
9 import type { SubscriptionData } from '@/core/websocket/type/message' 9 import type { SubscriptionData } from '@/core/websocket/type/message'
10 import { SocketSubscriberEnum } from '@/enums/datasource' 10 import { SocketSubscriberEnum } from '@/enums/datasource'
11 import { formatToDateTime } from '@/utils/dateUtil' 11 import { formatToDateTime } from '@/utils/dateUtil'
12 -import { useLatestMessageValue } from '@/core/Library/hook/useLatestMessageValue'  
13 import type { CommandSource } from '@/core/websocket/processor' 12 import type { CommandSource } from '@/core/websocket/processor'
  13 +import { useLatestMultipleMessageValue } from '@/core/Library/hook/useLatestMessageValue'
  14 +import { useProductsStore } from '@/store/modules/products'
  15 +interface IList {
  16 + time: string | number
  17 +}
14 18
15 const props = defineProps<{ 19 const props = defineProps<{
16 config: CreateComponentType 20 config: CreateComponentType
@@ -22,67 +26,131 @@ const chartElRef = ref<Nullable<HTMLDivElement>>() @@ -22,67 +26,131 @@ const chartElRef = ref<Nullable<HTMLDivElement>>()
22 26
23 const chartInstance = ref<Nullable<ECharts>>() 27 const chartInstance = ref<Nullable<ECharts>>()
24 28
  29 +const maxLength = ref<number>(20)
  30 +
  31 +const timeList = ref<number[] | string[] | any>([])// 存储XAxis的值
  32 +const seriesData = ref<any>([])// 存储series的值
  33 +const titleATTR = ref<string[] | any>([])
  34 +
  35 +const productsStore = useProductsStore()
  36 +
25 function initChartInstance() { 37 function initChartInstance() {
26 chartInstance.value = init(unref(chartElRef)) 38 chartInstance.value = init(unref(chartElRef))
27 chartInstance.value.setOption(getDefaultOption()) 39 chartInstance.value.setOption(getDefaultOption())
28 } 40 }
29 41
30 -const handleHistoryData = (message: SubscriptionData, attr: string) => {  
31 - const data = message[attr]  
32 - const xAxisData: string[] = []  
33 - const seriesData: number[] = [] 42 +const handleHistoryData = (commandSource: CommandSource, message: SubscriptionData, attr: string[] | string) => {
  43 + const { data } = commandSource || {}
  44 + const { deviceName } = data as NodeDataDataSourceJsonType
34 45
35 - for (const item of data) {  
36 - const [ts, value] = item  
37 - xAxisData.push(formatToDateTime(ts))  
38 - seriesData.push(value)  
39 - } 46 + (attr as string[]).forEach((item) => {
  47 + timeList.value.push(...message[item].map((item1) => {
  48 + const [ts] = item1
  49 + return ts
  50 + }))
  51 + })
  52 + timeList.value = Array.from(new Set(timeList.value))?.sort((a: any, b: any) => a - b)// 去重获取到的时间
  53 + seriesData.value.forEach((item: any) => {
  54 + item.data = unref(timeList).map((time: any) => ({ name: time, value: null }))
  55 + })
40 56
  57 + seriesData.value.forEach((item: any) => {
  58 + item.data.forEach((item1: any) => {
  59 + message[item.attribute].forEach((messageItem) => {
  60 + const [ts, value] = messageItem
  61 + if (item1.name === ts) {
  62 + item1.value = value || undefined
  63 + item1.name = formatToDateTime(item1.name)
  64 + }
  65 + })
  66 + })
  67 + })
41 unref(chartInstance)?.setOption({ 68 unref(chartInstance)?.setOption({
42 - xAxis: { data: xAxisData },  
43 - series: { data: seriesData }, 69 + title: {
  70 + text: `${deviceName || ''}`,
  71 + },
  72 + xAxis: {
  73 + data: toRaw(unref(timeList.value.map((item: string | number) => formatToDateTime(item)))),
  74 + },
  75 + legend: {
  76 + data: unref(titleATTR),
  77 + } as any,
  78 + series: toRaw(
  79 + unref(seriesData).map((item: { type: string; name: string; data: any }) => {
  80 + const { type, name, data } = item
  81 + return {
  82 + type,
  83 + name,
  84 + data,
  85 + }
  86 + }),
  87 + ),
44 } as EChartsOption) 88 } as EChartsOption)
45 } 89 }
46 90
47 -function sliceData(data: any[], maxLength = 20) {  
48 - if (data.length > maxLength)  
49 - return data.slice(1)  
50 - return data  
51 -} 91 +// const contentDataStore = useContentDataStoreWithOut()
52 92
53 -const handlerTimeSeriesData = (commandSource: CommandSource, message: SubscriptionData, attr: string) => { 93 +const handlerTimeSeriesData = (commandSource: CommandSource, message: SubscriptionData, attr: string[] | string) => {
54 const { data } = commandSource 94 const { data } = commandSource
55 - const { attrInfo, deviceInfo } = data as NodeDataDataSourceJsonType  
56 - const { deviceName } = deviceInfo || {}  
57 - const { ts, latestValue } = useLatestMessageValue(message, attr)  
58 - const option = unref(chartInstance)?.getOption() as EChartsOption  
59 - const oldDataset = (option.dataset as DatasetComponentOption[])?.[0].source as Recordable[]  
60 - oldDataset.push({  
61 - ts: formatToDateTime(ts),  
62 - [attr]: latestValue, 95 + const { deviceName } = data as NodeDataDataSourceJsonType
  96 + const list: IList | any = {}// 记录时间
  97 + useLatestMultipleMessageValue(message, attr as any, (attribute, timespan, value) => {
  98 + list.time = timespan || list.time
  99 + seriesData.value.forEach((item: any) => {
  100 + if (item.attribute === attribute) {
  101 + item.data.push({
  102 + name: formatToDateTime(list.time),
  103 + value: value || undefined,
  104 + })
  105 + }
  106 + if (item.data.length > unref(maxLength))
  107 + item.data.shift()
  108 + })
63 }) 109 })
  110 + list.time && timeList.value.push(formatToDateTime(list.time))
  111 + if (unref(timeList).length > unref(maxLength))
  112 + timeList.value.shift()
64 113
65 unref(chartInstance)?.setOption({ 114 unref(chartInstance)?.setOption({
66 title: { 115 title: {
67 - text: `${deviceName || ''}-${attrInfo.name || ''}`, 116 + text: `${deviceName || ''}`,
68 }, 117 },
69 - dataset: {  
70 - dimensions: ['ts', attr],  
71 - source: sliceData(oldDataset), 118 + xAxis: {
  119 + data: toRaw(unref(timeList)),
72 }, 120 },
  121 + legend: {
  122 + data: unref(titleATTR),
  123 + } as any,
  124 + series: toRaw(
  125 + unref(seriesData).map((item: { type: string; name: string; data: any }) => {
  126 + const { type, name, data } = item
  127 + return {
  128 + type,
  129 + name,
  130 + data,
  131 + }
  132 + }),
  133 + ),
73 } as EChartsOption) 134 } as EChartsOption)
74 } 135 }
75 136
76 const { onMessage } = useOnMessage({ 137 const { onMessage } = useOnMessage({
77 onReceiveDataSourceMessage(commandSource, message) { 138 onReceiveDataSourceMessage(commandSource, message) {
78 const { data } = commandSource 139 const { data } = commandSource
79 - const { chartOption, attr } = data as NodeDataDataSourceJsonType 140 + const { chartOption, attr, deviceProfileId } = data as NodeDataDataSourceJsonType
80 const { queryType } = chartOption || {} 141 const { queryType } = chartOption || {}
  142 + if (!seriesData.value.length) {
  143 + (attr as string[]).forEach((item: string) => {
  144 + titleATTR.value.push(productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, item)?.functionName)
  145 +
  146 + seriesData.value.push({ attribute: item, data: [], type: 'line', name: productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, item)?.functionName })
  147 + })
  148 + }
81 149
82 if (queryType === SocketSubscriberEnum.TS_SUB_CMDS) 150 if (queryType === SocketSubscriberEnum.TS_SUB_CMDS)
83 handlerTimeSeriesData(commandSource, message.data, attr) 151 handlerTimeSeriesData(commandSource, message.data, attr)
84 else if (queryType === SocketSubscriberEnum.HISTORY_CMDS) 152 else if (queryType === SocketSubscriberEnum.HISTORY_CMDS)
85 - handleHistoryData(message.data, attr) 153 + handleHistoryData(commandSource, message.data, attr)
86 }, 154 },
87 }) 155 })
88 156
@@ -34,7 +34,7 @@ const handleSubmit = async () => { @@ -34,7 +34,7 @@ const handleSubmit = async () => {
34 const formValues = unref(dataSourceFormRef)?.getFieldsValue() 34 const formValues = unref(dataSourceFormRef)?.getFieldsValue()
35 let dataSourceJson = formValues 35 let dataSourceJson = formValues
36 if (contentDataStore.getIsTemplate) 36 if (contentDataStore.getIsTemplate)
37 - dataSourceJson = { ...formValues, deviceProfileId: formValues?.deviceProfileTemplateId, deviceId: null } 37 + dataSourceJson = { ...formValues, deviceProfileId: formValues?.deviceProfileTemplateId, deviceId: null, deviceName: null }
38 38
39 await saveNodeAllData({ dataSourceJson: { ...dataSourceJson, rectFlowMeterOption: unref(colorConfig) } }) 39 await saveNodeAllData({ dataSourceJson: { ...dataSourceJson, rectFlowMeterOption: unref(colorConfig) } })
40 createMessage.success('保存成功') 40 createMessage.success('保存成功')
@@ -9,6 +9,7 @@ import { useOnMessage } from '@/core/Library/hook/useOnMessage' @@ -9,6 +9,7 @@ import { useOnMessage } from '@/core/Library/hook/useOnMessage'
9 import type { NodeDataDataSourceJsonType } from '@/api/node/model' 9 import type { NodeDataDataSourceJsonType } from '@/api/node/model'
10 import { useLatestMessageValue } from '@/core/Library/hook/useLatestMessageValue' 10 import { useLatestMessageValue } from '@/core/Library/hook/useLatestMessageValue'
11 import { useContentDataStore } from '@/store/modules/contentData' 11 import { useContentDataStore } from '@/store/modules/contentData'
  12 +import { useProductsStore } from '@/store/modules/products'
12 13
13 const props = defineProps<{ 14 const props = defineProps<{
14 config: CreateComponentType 15 config: CreateComponentType
@@ -22,6 +23,8 @@ const getCellBounds = computed(() => props.config.cellBounds || { width: 300, he @@ -22,6 +23,8 @@ const getCellBounds = computed(() => props.config.cellBounds || { width: 300, he
22 23
23 const chartElRef = ref<Nullable<HTMLDivElement>>() 24 const chartElRef = ref<Nullable<HTMLDivElement>>()
24 25
  26 +const productsStore = useProductsStore()
  27 +
25 const chartInstance = ref<Nullable<ECharts>>() 28 const chartInstance = ref<Nullable<ECharts>>()
26 29
27 function initChartInstance() { 30 function initChartInstance() {
@@ -34,13 +37,12 @@ function initChartInstance() { @@ -34,13 +37,12 @@ function initChartInstance() {
34 const { onMessage } = useOnMessage({ 37 const { onMessage } = useOnMessage({
35 onReceiveDataSourceMessage(commandSource, message) { 38 onReceiveDataSourceMessage(commandSource, message) {
36 const { data } = commandSource 39 const { data } = commandSource
37 - const { deviceInfo, attrInfo } = data || {}  
38 - const { deviceName } = deviceInfo || {}  
39 - const { attr } = data as NodeDataDataSourceJsonType  
40 - const { latestValue } = useLatestMessageValue(message.data, attr) 40 + const { attr, deviceName, deviceProfileId } = data as NodeDataDataSourceJsonType
  41 + const { functionName } = productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, (attr as string)) || {}
  42 + const { latestValue } = useLatestMessageValue(message.data, (attr as string))
41 unref(chartInstance)?.setOption({ 43 unref(chartInstance)?.setOption({
42 title: { 44 title: {
43 - text: `${deviceName || ''}-${attrInfo.name || ''}`, 45 + text: `${deviceName || ''} - ${functionName || ''}`,
44 }, 46 },
45 series: [{ 47 series: [{
46 data: getSetValue(Number(latestValue)), 48 data: getSetValue(Number(latestValue)),
@@ -7,6 +7,7 @@ import type { CreateComponentType, RenderComponentExposeType } from '@/core/Libr @@ -7,6 +7,7 @@ import type { CreateComponentType, RenderComponentExposeType } from '@/core/Libr
7 import type { NodeDataDataSourceJsonType } from '@/api/node/model' 7 import type { NodeDataDataSourceJsonType } from '@/api/node/model'
8 import { useLatestMessageValue } from '@/core/Library/hook/useLatestMessageValue' 8 import { useLatestMessageValue } from '@/core/Library/hook/useLatestMessageValue'
9 import { useOnMessage } from '@/core/Library/hook/useOnMessage' 9 import { useOnMessage } from '@/core/Library/hook/useOnMessage'
  10 +import { useProductsStore } from '@/store/modules/products'
10 11
11 const props = defineProps<{ 12 const props = defineProps<{
12 config: CreateComponentType 13 config: CreateComponentType
@@ -18,6 +19,8 @@ const chartElRef = ref<Nullable<HTMLDivElement>>() @@ -18,6 +19,8 @@ const chartElRef = ref<Nullable<HTMLDivElement>>()
18 19
19 const chartInstance = ref<Nullable<ECharts>>() 20 const chartInstance = ref<Nullable<ECharts>>()
20 21
  22 +const productsStore = useProductsStore()
  23 +
21 function initChartInstance() { 24 function initChartInstance() {
22 chartInstance.value = init(unref(chartElRef)) 25 chartInstance.value = init(unref(chartElRef))
23 chartInstance.value.setOption(getDefaultOption) 26 chartInstance.value.setOption(getDefaultOption)
@@ -26,13 +29,12 @@ function initChartInstance() { @@ -26,13 +29,12 @@ function initChartInstance() {
26 const { onMessage } = useOnMessage({ 29 const { onMessage } = useOnMessage({
27 onReceiveDataSourceMessage(commandSource, message) { 30 onReceiveDataSourceMessage(commandSource, message) {
28 const { data } = commandSource 31 const { data } = commandSource
29 - const { deviceInfo, attrInfo } = (data || {}) as NodeDataDataSourceJsonType  
30 - const { deviceName } = deviceInfo || {}  
31 - const { attr } = data as NodeDataDataSourceJsonType  
32 - const { latestValue } = useLatestMessageValue(message.data, attr) 32 + const { attr, deviceName, deviceProfileId } = data as NodeDataDataSourceJsonType
  33 + const { functionName } = productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, (attr as string)) || {}
  34 + const { latestValue } = useLatestMessageValue(message.data, (attr as string))
33 unref(chartInstance)?.setOption({ 35 unref(chartInstance)?.setOption({
34 title: { 36 title: {
35 - text: `${deviceName || ''}-${attrInfo.name || ''}`, 37 + text: `${deviceName || ''} - ${functionName || ''}`,
36 }, 38 },
37 series: [{ 39 series: [{
38 data: [{ 40 data: [{
  1 +import { unref } from 'vue'
  2 +import { getDeviceAttributes, getListByConfigurationId, getListByDeviceProfileIds } from '@/api/device'
  3 +import type { DeviceItemType, ThingsModelItemType } from '@/api/device/model'
  4 +import type { FormSchema } from '@/components/Form'
  5 +import { ComponentEnum } from '@/components/Form/src/enum'
  6 +import { ContentDataFieldsEnum, ContentDataFieldsNameEnum } from '@/enums/datasource'
  7 +import { useContentDataStoreWithOut } from '@/store/modules/contentData'
  8 +import type { ProductAndDevice } from '@/api/content/model'
  9 +import { ControlComponentEnum } from '@/core/Library/packages/Control'
  10 +import { useParseParams } from '@/core/LoadData'
  11 +import { DataTypeEnum } from '@/enums/objectModelEnum'
  12 +
  13 +const contentDataStore = useContentDataStoreWithOut()
  14 +export const formSchemas = (componentKey?: string): FormSchema[] => {
  15 + const isTemplate = contentDataStore.isTemplate // 判断是否是模板组态
  16 + const isTemplateLink = contentDataStore.getIsTemplateLink
  17 + const params = useParseParams()
  18 + const { configurationId } = params
  19 + return [
  20 + {
  21 + field: ContentDataFieldsEnum.DEVICE_PROFILE_ID,
  22 + label: ContentDataFieldsNameEnum.DEVICE_PROFILE_ID,
  23 + component: ComponentEnum.INPUT,
  24 + ifShow: false,
  25 + },
  26 + {
  27 + field: ContentDataFieldsEnum.DEVICE_PROFILE_TEMPLATE_ID, // 模板产品id
  28 + label: ContentDataFieldsNameEnum.DEVICE_PROFILE_ID,
  29 + component: ComponentEnum.API_SELECT,
  30 + ifShow: !!isTemplate,
  31 + required: !!isTemplate,
  32 + componentProps: ({ formModel }) => {
  33 + return {
  34 + options: (unref(contentDataStore.getProductAndDevice) || []).map((item: ProductAndDevice) => ({ label: item.profileName || item.name, value: item.profileId, transportType: item?.transportType, deviceType: item?.deviceType })),
  35 + placeholder: '请选择产品',
  36 + onSelect(value: string) {
  37 + formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] = value
  38 + formModel[ContentDataFieldsEnum.ATTR] = []
  39 + },
  40 + getPopupContainer: () => document.body,
  41 + }
  42 + },
  43 + },
  44 + {
  45 + field: ContentDataFieldsEnum.DEVICE_ID,
  46 + label: ContentDataFieldsNameEnum.DEVICE_ID,
  47 + component: ComponentEnum.API_SELECT,
  48 + ifShow: !isTemplate,
  49 + required: !isTemplate,
  50 + componentProps: ({ formModel }) => {
  51 + const organizationId = window.useParseParams().organizationId
  52 + if (!organizationId) return
  53 + return {
  54 + showSearch: true,
  55 + api: async (params: Recordable) => {
  56 + if (isTemplateLink) return await getListByConfigurationId(configurationId!)
  57 + return await getListByDeviceProfileIds(params)
  58 + },
  59 + params: {
  60 + deviceProfileIds: unref(contentDataStore.getProductIds),
  61 + organizationId,
  62 + },
  63 + aliasField: 'alias',
  64 + fieldNames: { label: 'name', value: 'tbDeviceId' },
  65 + onSelect(value: string, option: DeviceItemType) {
  66 + formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] = value ? option.deviceProfileId : null
  67 + formModel[ContentDataFieldsEnum.ATTR] = []
  68 + formModel[ContentDataFieldsEnum.DEVICE_NAME] = value ? option.alias || option.name : ''
  69 + },
  70 + filterOption: (inputValue: string, option: DeviceItemType) => {
  71 + return option.alias?.includes?.(inputValue) || option.name?.includes?.(inputValue)
  72 + },
  73 + }
  74 + },
  75 + },
  76 + {
  77 + field: ContentDataFieldsEnum.DEVICE_NAME,
  78 + label: ContentDataFieldsNameEnum.deviceName,
  79 + component: ComponentEnum.INPUT,
  80 + ifShow: false,
  81 + },
  82 + {
  83 + field: ContentDataFieldsEnum.ATTR,
  84 + label: ContentDataFieldsNameEnum.ATTR,
  85 + component: ComponentEnum.API_SELECT,
  86 + required: true,
  87 + componentProps: ({ formModel }) => {
  88 + const deviceProfileId = isTemplate ? formModel[ContentDataFieldsEnum.DEVICE_PROFILE_TEMPLATE_ID] : formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID]
  89 + return {
  90 + showSearch: true,
  91 + mode: 'multiple',
  92 + api: async (params: string) => {
  93 + if (!deviceProfileId) return []
  94 + const options = await getDeviceAttributes(params)
  95 + if (componentKey === ControlComponentEnum.SWITCH) { // 开关只返回bool
  96 + return options.filter((item) => {
  97 + return item.detail.dataType.type === DataTypeEnum.BOOL
  98 + })
  99 + }
  100 + return options
  101 + },
  102 + params: deviceProfileId,
  103 + fieldNames: { label: 'name', value: 'identifier' },
  104 + filterOption: (inputValue: string, option: ThingsModelItemType) => {
  105 + return option.name.includes(inputValue)
  106 + },
  107 + }
  108 + },
  109 + },
  110 + ]
  111 +}
  1 +export { default as DataSourceForm } from './index.vue'
  1 +<script setup lang="ts">
  2 +import { computed, unref } from 'vue'
  3 +import type { RuleError } from 'ant-design-vue/lib/form/interface'
  4 +import { formSchemas } from './config'
  5 +import { FormLayoutEnum } from '@/components/Form/src/enum'
  6 +import { BasicForm, useForm } from '@/components/Form'
  7 +
  8 +export interface ComponentExposeType {
  9 + getFieldsValue: () => any
  10 + setFieldsValue: (value: any) => void
  11 + validate: () => Promise<RuleError | any>
  12 +}
  13 +
  14 +const props = defineProps<{ componentKey?: string }>()
  15 +defineEmits(['fieldValueChange'])
  16 +
  17 +const getComponentKey = computed(() => {
  18 + return props.componentKey
  19 +})
  20 +
  21 +const [registerForm, formActionType] = useForm({
  22 + schemas: formSchemas(unref(getComponentKey)),
  23 + showActionButtonGroup: false,
  24 + layout: FormLayoutEnum.HORIZONTAL,
  25 + labelWidth: 70,
  26 +})
  27 +
  28 +const getFieldsValue = () => {
  29 + return formActionType.getFieldsValue()
  30 +}
  31 +
  32 +const validate = async () => {
  33 + return await formActionType.validate()
  34 +}
  35 +
  36 +const setFieldsValue = (value: Recordable) => {
  37 + formActionType.setFieldsValue(value)
  38 + formActionType.clearValidate()
  39 +}
  40 +
  41 +defineExpose<ComponentExposeType>({
  42 + getFieldsValue,
  43 + setFieldsValue,
  44 + validate,
  45 +})
  46 +</script>
  47 +
  48 +<template>
  49 + <BasicForm @register="registerForm" @field-value-change="$emit('fieldValueChange')" />
  50 +</template>
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { computed, ref, unref } from 'vue' 2 import { computed, ref, unref } from 'vue'
  3 +import { isNullOrUnDef } from '@wry-smile/utils-is'
3 import type { RenderComponentExposeType, RenderComponentProps } from '@/core/Library/types' 4 import type { RenderComponentExposeType, RenderComponentProps } from '@/core/Library/types'
4 import { useLatestMessageValue } from '@/core/Library/hook/useLatestMessageValue' 5 import { useLatestMessageValue } from '@/core/Library/hook/useLatestMessageValue'
5 import { NodeUtils } from '@/hooks/business/useNodeUtils' 6 import { NodeUtils } from '@/hooks/business/useNodeUtils'
@@ -25,6 +26,10 @@ const onReceiveDataSourceMessage = (_commandSource: CommandSource, message: Subs @@ -25,6 +26,10 @@ const onReceiveDataSourceMessage = (_commandSource: CommandSource, message: Subs
25 const cell = nodeUtils.getCellById(node) 26 const cell = nodeUtils.getCellById(node)
26 if (!cell) return 27 if (!cell) return
27 const { latestValue } = useLatestMessageValue(message.data, attr!) 28 const { latestValue } = useLatestMessageValue(message.data, attr!)
  29 +
  30 + if (isNullOrUnDef(latestValue))
  31 + return
  32 +
28 const flag = rangeList.find(item => item.statusValue?.toString() === latestValue?.toString()) 33 const flag = rangeList.find(item => item.statusValue?.toString() === latestValue?.toString())
29 34
30 if (flag) { 35 if (flag) {
@@ -8,6 +8,7 @@ import { useContentDataStoreWithOut } from '@/store/modules/contentData' @@ -8,6 +8,7 @@ import { useContentDataStoreWithOut } from '@/store/modules/contentData'
8 import { useUserStoreWithOut } from '@/store/modules/user' 8 import { useUserStoreWithOut } from '@/store/modules/user'
9 import { useMessage } from '@/hooks/web/useMessage' 9 import { useMessage } from '@/hooks/web/useMessage'
10 import { MessageEnum } from '@/enums/messageEnum' 10 import { MessageEnum } from '@/enums/messageEnum'
  11 +import { useProduct } from '@/core/Library/hook/useProduct'
11 12
12 export function useContentData() { 13 export function useContentData() {
13 const contentDataStore = useContentDataStoreWithOut() 14 const contentDataStore = useContentDataStoreWithOut()
@@ -46,6 +47,9 @@ export function useContentData() { @@ -46,6 +47,9 @@ export function useContentData() {
46 const result = mode === PageModeEnum.SHARE ? await shareModeBootstrap() : await getContent() 47 const result = mode === PageModeEnum.SHARE ? await shareModeBootstrap() : await getContent()
47 48
48 if (result) { 49 if (result) {
  50 + const { getAllDeviceProfileIds } = useProduct()
  51 + await getAllDeviceProfileIds(result)
  52 +
49 const { productAndDevice, nodelist, isTemplate, templateId } = result 53 const { productAndDevice, nodelist, isTemplate, templateId } = result
50 if (nodelist) contentDataStore.saveContentData(nodelist) 54 if (nodelist) contentDataStore.saveContentData(nodelist)
51 if (isTemplate) contentDataStore.setIsTemplate(isTemplate) 55 if (isTemplate) contentDataStore.setIsTemplate(isTemplate)
@@ -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) => {
@@ -2,6 +2,7 @@ import { isNull } from 'lodash-es' @@ -2,6 +2,7 @@ import { isNull } from 'lodash-es'
2 import type { App } from 'vue' 2 import type { App } from 'vue'
3 import type { SubscriptionUpdateMsg } from '../type/message' 3 import type { SubscriptionUpdateMsg } from '../type/message'
4 import { DataDynamicEffectHandler } from './dataDynamicEffectHandler' 4 import { DataDynamicEffectHandler } from './dataDynamicEffectHandler'
  5 +import { useObjectModelValue } from './useObjectModelValue'
5 import type { CommandSource, LightboxModeWebsocketService } from '.' 6 import type { CommandSource, LightboxModeWebsocketService } from '.'
6 import type { ActTypeEnum } from '@/enums/datasource' 7 import type { ActTypeEnum } from '@/enums/datasource'
7 import { DataSourceTypeEnum } from '@/enums/datasource' 8 import { DataSourceTypeEnum } from '@/enums/datasource'
@@ -52,10 +53,13 @@ export class MessageHandler { @@ -52,10 +53,13 @@ export class MessageHandler {
52 53
53 defaultHandler(commandSource: CommandSource, message: SubscriptionUpdateMsg) { 54 defaultHandler(commandSource: CommandSource, message: SubscriptionUpdateMsg) {
54 const { data, node } = commandSource 55 const { data, node } = commandSource
55 - const { attr } = data as NodeDataDataSourceJsonType  
56 - const { latestValue } = useLatestMessageValue(message.data, attr) 56 + const { attr, deviceProfileId } = data as NodeDataDataSourceJsonType
  57 +
  58 + let { latestValue } = useLatestMessageValue(message.data, attr as string)
57 if (isNull(latestValue)) return 59 if (isNull(latestValue)) return
  60 + latestValue = useObjectModelValue(deviceProfileId, attr as string, latestValue)
58 const cell = this.nodeUtils.getCellById(node) 61 const cell = this.nodeUtils.getCellById(node)
  62 +
59 const cellValue = cell.getValue() as Element 63 const cellValue = cell.getValue() as Element
60 cellValue.setAttribute('label', latestValue) 64 cellValue.setAttribute('label', latestValue)
61 this.nodeUtils.updateCellValue(cell, cellValue) 65 this.nodeUtils.updateCellValue(cell, cellValue)
  1 +import type { DataType, Specs, StructJSON } from '@/api/device/model'
  2 +import { TransportTypeEnum } from '@/enums/deviceEnum'
  3 +import { DataTypeEnum } from '@/enums/objectModelEnum'
  4 +import { useJsonParse } from '@/hooks/business/useJSONParse'
  5 +import { useProductsStoreWithOut } from '@/store/modules/products'
  6 +
  7 +function getBoolTypeValue(value: number, Specs: Specs) {
  8 + const { boolOpen, boolClose } = Specs
  9 +
  10 + return Number(value) ? boolOpen : boolClose
  11 +}
  12 +
  13 +function getEnumTypeValue(value: number, specsList: Specs[]) {
  14 + const res = specsList.find(item => item.value === Number(value))
  15 +
  16 + return res?.name
  17 +}
  18 +
  19 +function getStructTypeValue(value: string, specs: StructJSON[]): string {
  20 + const res = useJsonParse(value).value
  21 +
  22 + function generateStruct(specs: StructJSON[], value: Recordable) {
  23 + if (!value) return {}
  24 +
  25 + return specs.reduce((prev, next) => {
  26 + return {
  27 + ...prev,
  28 + [next.functionName]: getValueByType(next.dataType!.type, value[next.identifier], next.dataType!),
  29 + }
  30 + }, {})
  31 + }
  32 +
  33 + return JSON.stringify(generateStruct(specs, res))
  34 +}
  35 +
  36 +function getValueByType(type: string, value: any, dataType: DataType) {
  37 + switch (type) {
  38 + case DataTypeEnum.BOOL:
  39 + return getBoolTypeValue(value, dataType.specs as Specs)
  40 + case DataTypeEnum.STRUCT:
  41 + return getStructTypeValue(value, dataType.specs as StructJSON[])
  42 + case DataTypeEnum.ENUM:
  43 + return getEnumTypeValue(value, dataType.specsList as Specs[])
  44 + default:
  45 + return value
  46 + }
  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 +
  56 +export function useObjectModelValue(deviceProfileId: string, attr: string, value: any) {
  57 + const productsStore = useProductsStoreWithOut()
  58 +
  59 + const result = productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, attr)
  60 + if (!result)
  61 + return value
  62 +
  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)
  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 +}
@@ -7,3 +7,41 @@ export enum CommandWayNameEnum { @@ -7,3 +7,41 @@ export enum CommandWayNameEnum {
7 ONE_WAY = '单向', 7 ONE_WAY = '单向',
8 TWO_WAY = '双向', 8 TWO_WAY = '双向',
9 } 9 }
  10 +
  11 +export enum CommandCallWayEnum {
  12 + SYNC = 'SYNC',
  13 + ASYNC = 'ASYNC',
  14 +}
  15 +
  16 +export enum 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 +
  41 +export enum CommandMethodEnum {
  42 + THINGSKIT = 'methodThingskit',
  43 +}
  44 +
  45 +export enum ModbusCRCEnum {
  46 + CRC_16_LOWER = 'CRC_16_LOWER',
  47 +}
@@ -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 鼠标抬起
@@ -238,17 +194,9 @@ export enum ContentDataFieldsEnum { @@ -238,17 +194,9 @@ export enum ContentDataFieldsEnum {
238 DEVICE_ID = 'deviceId', 194 DEVICE_ID = 'deviceId',
239 ORG_ID = 'orgId', 195 ORG_ID = 'orgId',
240 ATTR = 'attr', 196 ATTR = 'attr',
241 - VIDEO_FILTER = 'videoFilter',  
242 - VIDEO_ID = 'id',  
243 - VIDEO_URL = 'videoUrl',  
244 - ACCESS_MODE = 'accessMode',  
245 - VIDEO_FLAG = 'videoComponentFlag',  
246 -  
247 - ATTR_INFO = 'attrInfo',  
248 - DEVICE_INFO = 'deviceInfo',  
249 - TRANSPORT_TYPE = 'transportType', 197 + DEVICE_NAME = 'deviceName',
  198 +
250 CODE_TYPE = 'codeType', 199 CODE_TYPE = 'codeType',
251 - DEVICE_ADDITIONAL_INFO = 'deviceAdditionalInfo',  
252 } 200 }
253 201
254 export enum ContentDataFieldsNameEnum { 202 export enum ContentDataFieldsNameEnum {
@@ -257,14 +205,7 @@ export enum ContentDataFieldsNameEnum { @@ -257,14 +205,7 @@ export enum ContentDataFieldsNameEnum {
257 ORG_ID = '组织', 205 ORG_ID = '组织',
258 DEVICE_ID = '设备', 206 DEVICE_ID = '设备',
259 ATTR = '属性', 207 ATTR = '属性',
260 - VIDEO_ID = '视频流',  
261 - ACCESS_MODE = 'ACCESS_MODE',  
262 - VIDEO_URL = '视频地址',  
263 -  
264 - ATTR_INFO = '物模型属性详情',  
265 - DEVICE_INFO = '设备详情',  
266 - TRANSPORT_TYPE = '传输协议',  
267 - DEVICE_ADDITIONAL_INFO = '设备额外信息', 208 + deviceName = '设备名称',
268 } 209 }
269 210
270 export enum VariableImageSourceEnum { 211 export enum VariableImageSourceEnum {
@@ -277,46 +218,6 @@ export enum VariableImageSourceNameEnum { @@ -277,46 +218,6 @@ export enum VariableImageSourceNameEnum {
277 GALLERY = '图库图形', 218 GALLERY = '图库图形',
278 } 219 }
279 220
280 -export enum CommandDeliveryWayEnum {  
281 - CUSTOM = 'CUSTOM',  
282 - SERVICE = 'SERVICE',  
283 - MODBUS = 'MODBUS',  
284 -}  
285 -  
286 -export enum CommandDeliveryWayNameEnum {  
287 - CUSTOM = '自定义命令',  
288 - SERVICE = '服务调用',  
289 - MODBUS = 'MODBUS',  
290 -}  
291 -  
292 -export enum FunctionType {  
293 - PROPERTIES = 'properties',  
294 - EVENTS = 'events',  
295 - SERVICE = 'services',  
296 -}  
297 -  
298 -export enum AssessMode {  
299 - READ = 'r',  
300 - WRITE = 'w',  
301 -}  
302 -  
303 -export enum CommandTypeEnum {  
304 - SERVICE = 1,  
305 - API = 'API',  
306 -}  
307 -  
308 -/**  
309 - * 新增参数 动态显示表单  
310 - */  
311 -export enum DataTypeEnum {  
312 - NUMBER_INT = 'INT',  
313 - NUMBER_DOUBLE = 'DOUBLE',  
314 - STRING = 'TEXT',  
315 - STRUCT = 'STRUCT',  
316 - BOOL = 'BOOL',  
317 - ENUM = 'ENUM',  
318 -}  
319 -  
320 export enum AggregateTypeEnum { 221 export enum AggregateTypeEnum {
321 MIN = 'MIN', 222 MIN = 'MIN',
322 MAX = 'MAX', 223 MAX = 'MAX',
@@ -339,24 +240,3 @@ export enum SocketSubscriberEnum { @@ -339,24 +240,3 @@ export enum SocketSubscriberEnum {
339 HISTORY_CMDS = 'historyCmds', 240 HISTORY_CMDS = 'historyCmds',
340 } 241 }
341 242
342 -export enum AlarmStatusEnum {  
343 - CLEARED_UN_ACK = 'CLEARED_UNACK',  
344 - ACTIVE_UN_ACK = 'ACTIVE_UNACK',  
345 - CLEARED_ACK = 'CLEARED_ACK',  
346 - ACTIVE_ACK = 'ACTIVE_ACK',  
347 -}  
348 -  
349 -export enum AlarmStatusColorEnum {  
350 - CLEARED_UNACK = 'red',  
351 - ACTIVE_UNACK = 'orange',  
352 - CLEARED_ACK = 'cyan',  
353 - ACTIVE_ACK = 'green',  
354 -}  
355 -  
356 -export enum AlarmStatusNameEnum {  
357 - CLEARED_UNACK = '清除未确认',  
358 - ACTIVE_UNACK = '激活未确认',  
359 - CLEARED_ACK = '清除已确认',  
360 - ACTIVE_ACK = '激活已确认',  
361 -}  
362 -  
  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 +export enum VideoAccessModeEnum {
  2 + ManuallyEnter = 0,
  3 + Streaming = 1,
  4 + GBT28181 = 2,
  5 +}
  1 +import { OriginalDataTypeEnum } from '@/enums/objectModelEnum'
  2 +import { useParseOriginalDataType } from '@/hooks/business/useParseOriginalDataType'
  3 +
  4 +export function getBoolValueByStatus(status: boolean) {
  5 + return status ? parseInt('FF00', 16) : parseInt('0000', 16)
  6 +}
  7 +
  8 +export function useBaseConversion() {
  9 + function DecTo32Float(number: number) {
  10 + const arr = new Uint8Array(4)
  11 + const view = new DataView(arr.buffer)
  12 + view.setFloat32(0, +number)
  13 + return arr
  14 + }
  15 +
  16 + function DecTo64Double(number: number) {
  17 + const arr = new Uint8Array(8)
  18 + const view = new DataView(arr.buffer)
  19 + view.setFloat64(0, +number)
  20 + return arr
  21 + }
  22 +
  23 + function arrToBase(toBase: number, arr: Uint8Array) {
  24 + let result = ''
  25 + for (let i = 0; i < arr.length; i++)
  26 + result += (256 + arr[i]).toString(toBase).substring(1).toUpperCase()
  27 +
  28 + return result
  29 + }
  30 +
  31 + function DecTo16Uint(number: number) {
  32 + const arr = new Uint8Array(2)
  33 + const view = new DataView(arr.buffer)
  34 + view.setUint16(0, +number)
  35 + return arr
  36 + }
  37 +
  38 + function DecTo16Int(number: number) {
  39 + const arr = new Uint8Array(2)
  40 + const view = new DataView(arr.buffer)
  41 + view.setInt16(0, +number)
  42 + return arr
  43 + }
  44 +
  45 + function DecTo32Uint(number: number) {
  46 + const arr = new Uint8Array(4)
  47 + const view = new DataView(arr.buffer)
  48 + view.setUint32(0, +number)
  49 + return arr
  50 + }
  51 +
  52 + function DecTo32Int(number: number) {
  53 + const arr = new Uint8Array(4)
  54 + const view = new DataView(arr.buffer)
  55 + view.setInt32(0, +number)
  56 + return arr
  57 + }
  58 +
  59 + function DecToBinaryByType(type: OriginalDataTypeEnum, number: number) {
  60 + switch (type) {
  61 + case OriginalDataTypeEnum.INT16_AB:
  62 + case OriginalDataTypeEnum.INT16_BA:
  63 + return arrToBase(2, DecTo16Int(number))
  64 +
  65 + case OriginalDataTypeEnum.UINT16_AB:
  66 + case OriginalDataTypeEnum.UINT16_BA:
  67 + return arrToBase(2, DecTo16Uint(number))
  68 +
  69 + case OriginalDataTypeEnum.INT32_AB_CD:
  70 + case OriginalDataTypeEnum.INT32_CD_AB:
  71 + case OriginalDataTypeEnum.INT32_BA_DC:
  72 + case OriginalDataTypeEnum.INT32_DC_BA:
  73 + return arrToBase(2, DecTo32Int(number))
  74 +
  75 + case OriginalDataTypeEnum.UINT32_AB_CD:
  76 + case OriginalDataTypeEnum.UINT32_CD_AB:
  77 + case OriginalDataTypeEnum.UINT32_BA_DC:
  78 + case OriginalDataTypeEnum.UINT32_DC_BA:
  79 + return arrToBase(2, DecTo32Uint(number))
  80 +
  81 + case OriginalDataTypeEnum.FLOAT_AB_CD:
  82 + case OriginalDataTypeEnum.FLOAT_CD_AB:
  83 + case OriginalDataTypeEnum.FLOAT_BA_DC:
  84 + case OriginalDataTypeEnum.FLOAT_DC_BA:
  85 + return arrToBase(2, DecTo32Float(number))
  86 +
  87 + case OriginalDataTypeEnum.DOUBLE:
  88 + return arrToBase(2, DecTo64Double(number))
  89 + }
  90 + }
  91 +
  92 + function SplitStringToGroupByItemLength(
  93 + value: string,
  94 + itemLength = 8,
  95 + ignoreLessThan = false,
  96 + ): string[] {
  97 + const reg = new RegExp(`.{${ignoreLessThan ? '' : '1,'}${itemLength}}`, 'g')
  98 + return value.match(reg) || []
  99 + }
  100 +
  101 + function ExchangeByteOrder(binary: string, order: string) {
  102 + const group = SplitStringToGroupByItemLength(binary)
  103 +
  104 + const BASE_ORDER = {
  105 + A: 0,
  106 + B: 1,
  107 + C: 2,
  108 + D: 3,
  109 + }
  110 +
  111 + const array: string[] = Array.from({ length: binary.length / 8 })
  112 +
  113 + order
  114 + .split('')
  115 + .forEach((bytePosition, index) => (array[index] = group[BASE_ORDER[bytePosition as keyof typeof BASE_ORDER] as number]))
  116 +
  117 + return array.join('')
  118 + }
  119 +
  120 + function ByteToHex(binary: string) {
  121 + const group = SplitStringToGroupByItemLength(binary, 16)
  122 + return group.map(byte => parseInt(byte, 2).toString(16))
  123 + }
  124 +
  125 + function ByteToDec(binary: string) {
  126 + const group = SplitStringToGroupByItemLength(binary, 16)
  127 + return group.map(byte => parseInt(byte, 2))
  128 + }
  129 +
  130 + function StringToHEXBuffer(string: string | number) {
  131 + return string
  132 + .toString()
  133 + .split('')
  134 + .map(string => string.charCodeAt(0).toString(16))
  135 + .reverse()
  136 + }
  137 +
  138 + function getRegisterValueByOriginalDataType(
  139 + value: number,
  140 + type: OriginalDataTypeEnum,
  141 + additional?: { bitMask?: number; registerNumber?: number },
  142 + ) {
  143 + const { exchangeSortFlag } = useParseOriginalDataType(type)
  144 + // eslint-disable-next-line no-console
  145 + console.groupCollapsed('Modbus Debug')
  146 +
  147 + let result: number[]
  148 +
  149 + if (type === OriginalDataTypeEnum.BOOLEAN) {
  150 + result = [getBoolValueByStatus(!!value)]
  151 + }
  152 + else if (type === OriginalDataTypeEnum.BITS) {
  153 + const { bitMask = 0 } = additional || {}
  154 + const binaryArray = Array.from({ length: 16 }, () => 0)
  155 + binaryArray[15 - bitMask] = value
  156 + result = [parseInt(binaryArray.join(''), 2)]
  157 + }
  158 + else if (type === OriginalDataTypeEnum.STRING) {
  159 + let buffer = StringToHEXBuffer(value)
  160 + const { registerNumber = 0 } = additional || {}
  161 +
  162 + if (buffer.length < registerNumber * 2) {
  163 + buffer = [
  164 + ...Array.from({ length: registerNumber * 2 - buffer.length }, () => '00'),
  165 + ...buffer,
  166 + ]
  167 + }
  168 +
  169 + result = SplitStringToGroupByItemLength(buffer.join(''), 4).map(hex => parseInt(hex, 16))
  170 + }
  171 + else {
  172 + // eslint-disable-next-line no-console
  173 + console.table({ input: value, sort: exchangeSortFlag, type })
  174 +
  175 + let binary = DecToBinaryByType(type, value)!
  176 +
  177 + // eslint-disable-next-line no-console
  178 + console.table({ beforeExchange: binary })
  179 +
  180 + if (exchangeSortFlag) binary = ExchangeByteOrder(binary, exchangeSortFlag)
  181 + result = ByteToDec(binary)
  182 +
  183 + // eslint-disable-next-line no-console
  184 + console.table({
  185 + afterEchange: binary,
  186 + dec: result.toString(),
  187 + hex: ByteToHex(binary).toString(),
  188 + })
  189 + }
  190 +
  191 + // eslint-disable-next-line no-console
  192 + console.groupEnd()
  193 +
  194 + return result
  195 + }
  196 +
  197 + return {
  198 + DecToBinaryByType,
  199 + ByteToDec,
  200 + ByteToHex,
  201 + ExchangeByteOrder,
  202 + SplitStringToGroupByItemLength,
  203 + getRegisterValueByOriginalDataType,
  204 + StringToHEXBuffer,
  205 + }
  206 +}
  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 + const message = '当前设备不在线'
  155 + createMessage.warning(message)
  156 + throw Error(message)
  157 + }
  158 + }
  159 + await sendApi({ way, deviceId: entityId, command: rpcCommand })
  160 + }
  161 + finally {
  162 + loading.value = false
  163 + }
  164 + }
  165 +
  166 + return {
  167 + loading,
  168 + doCommandDelivery,
  169 + }
  170 +}