Commit 66a595da8ba91b1ee121c854c7aff0927aa62be7
Merge branch 'feat/object-model' into 'main_dev'
feat:支持标准modbus_rtu解析 See merge request yunteng/thingskit-front!1209
Showing
56 changed files
with
2131 additions
and
1276 deletions
Too many changes to show.
To preserve performance only 56 of 58 files are displayed.
@@ -29,6 +29,7 @@ | @@ -29,6 +29,7 @@ | ||
29 | "SNMP", | 29 | "SNMP", |
30 | "TSLV", | 30 | "TSLV", |
31 | "UNACK", | 31 | "UNACK", |
32 | + "UNINT", | ||
32 | "unref", | 33 | "unref", |
33 | "vben", | 34 | "vben", |
34 | "videojs", | 35 | "videojs", |
@@ -36,5 +37,5 @@ | @@ -36,5 +37,5 @@ | ||
36 | "vnode", | 37 | "vnode", |
37 | "vueuse", | 38 | "vueuse", |
38 | "windicss" | 39 | "windicss" |
39 | - ] | 40 | + ], |
40 | } | 41 | } |
1 | -import { DataType } from '../../device/model/modelOfMatterModel'; | ||
2 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 1 | +import { RpcCommandType } from '../../device/model/deviceConfigModel'; |
2 | +import { DataType, ExtensionDesc } from '../../device/model/modelOfMatterModel'; | ||
3 | +import { DataTypeEnum, ObjectModelAccessModeEnum } from '/@/enums/objectModelEnum'; | ||
3 | import { DataSource } from '/@/views/visual/palette/types'; | 4 | import { DataSource } from '/@/views/visual/palette/types'; |
4 | 5 | ||
5 | export interface AddDataBoardParams { | 6 | export interface AddDataBoardParams { |
@@ -154,12 +155,14 @@ export interface DeviceAttributeParams { | @@ -154,12 +155,14 @@ export interface DeviceAttributeParams { | ||
154 | export interface DeviceAttributeRecord { | 155 | export interface DeviceAttributeRecord { |
155 | name: string; | 156 | name: string; |
156 | identifier: string; | 157 | identifier: string; |
158 | + accessMode: ObjectModelAccessModeEnum; | ||
157 | detail: { dataType: DataType }; | 159 | detail: { dataType: DataType }; |
160 | + extensionDesc?: ExtensionDesc; | ||
158 | } | 161 | } |
159 | 162 | ||
160 | export interface SendCommandParams { | 163 | export interface SendCommandParams { |
161 | deviceId: string; | 164 | deviceId: string; |
162 | - value: any; | 165 | + value: RpcCommandType; |
163 | } | 166 | } |
164 | 167 | ||
165 | export interface GetMeetTheConditionsDeviceParams { | 168 | export interface GetMeetTheConditionsDeviceParams { |
1 | +import { ProfileData } from './deviceModel'; | ||
1 | import { BasicPageParams } from '/@/api/model/baseModel'; | 2 | import { BasicPageParams } from '/@/api/model/baseModel'; |
2 | import { CommandTypeEnum, RPCCommandMethodEnum } from '/@/enums/deviceEnum'; | 3 | import { CommandTypeEnum, RPCCommandMethodEnum } from '/@/enums/deviceEnum'; |
3 | 4 | ||
@@ -103,13 +104,6 @@ export interface ProvisionConfiguration { | @@ -103,13 +104,6 @@ export interface ProvisionConfiguration { | ||
103 | provisionDeviceSecret?: any; | 104 | provisionDeviceSecret?: any; |
104 | } | 105 | } |
105 | 106 | ||
106 | -export interface ProfileData { | ||
107 | - configuration: Configuration; | ||
108 | - transportConfiguration: TransportConfiguration; | ||
109 | - provisionConfiguration: ProvisionConfiguration; | ||
110 | - alarms: any; | ||
111 | -} | ||
112 | - | ||
113 | export interface ProfileRecord { | 107 | export interface ProfileRecord { |
114 | id: string; | 108 | id: string; |
115 | creator: string; | 109 | creator: string; |
@@ -168,22 +162,7 @@ export interface DeviceProfileDetail { | @@ -168,22 +162,7 @@ export interface DeviceProfileDetail { | ||
168 | type: string; | 162 | type: string; |
169 | deviceCount: number; | 163 | deviceCount: number; |
170 | default: boolean; | 164 | default: boolean; |
171 | -} | ||
172 | - | ||
173 | -export interface ProfileData { | ||
174 | - configuration: Configuration; | ||
175 | - transportConfiguration: TransportConfiguration; | ||
176 | - provisionConfiguration: ProvisionConfiguration; | ||
177 | - alarms: any; | ||
178 | - thingsModel: any; | ||
179 | -} | ||
180 | - | ||
181 | -export interface Configuration { | ||
182 | - type: string; | ||
183 | -} | ||
184 | - | ||
185 | -export interface TransportConfiguration { | ||
186 | - type: string; | 165 | + ifShowClass?: boolean; |
187 | } | 166 | } |
188 | 167 | ||
189 | export interface RpcCommandType { | 168 | export interface RpcCommandType { |
1 | -import { StructJSON } from './modelOfMatterModel'; | 1 | +import { DataType, ExtensionDesc, ModelOfMatterParams } from './modelOfMatterModel'; |
2 | import { BasicPageParams } from '/@/api/model/baseModel'; | 2 | import { BasicPageParams } from '/@/api/model/baseModel'; |
3 | import { AlarmStatus } from '/@/enums/alarmEnum'; | 3 | import { AlarmStatus } from '/@/enums/alarmEnum'; |
4 | +import { TCPProtocolTypeEnum } from '/@/enums/deviceEnum'; | ||
4 | import { DeviceStatusEnum } from '/@/views/rule/dataFlow/cpns/config'; | 5 | import { DeviceStatusEnum } from '/@/views/rule/dataFlow/cpns/config'; |
5 | export enum DeviceState { | 6 | export enum DeviceState { |
6 | INACTIVE = 'INACTIVE', | 7 | INACTIVE = 'INACTIVE', |
@@ -60,6 +61,7 @@ export interface DeviceProfileModel { | @@ -60,6 +61,7 @@ export interface DeviceProfileModel { | ||
60 | transportType: string; | 61 | transportType: string; |
61 | createTime: string; | 62 | createTime: string; |
62 | description: string; | 63 | description: string; |
64 | + profileData: ProfileData; | ||
63 | } | 65 | } |
64 | 66 | ||
65 | export type ChildDeviceParams = BasicPageParams & { | 67 | export type ChildDeviceParams = BasicPageParams & { |
@@ -148,6 +150,7 @@ export interface TransportConfiguration { | @@ -148,6 +150,7 @@ export interface TransportConfiguration { | ||
148 | 150 | ||
149 | // TCP | 151 | // TCP |
150 | scriptId: string; | 152 | scriptId: string; |
153 | + protocol?: TCPProtocolTypeEnum; | ||
151 | } | 154 | } |
152 | 155 | ||
153 | export interface ProvisionConfiguration { | 156 | export interface ProvisionConfiguration { |
@@ -160,6 +163,7 @@ export interface ProfileData { | @@ -160,6 +163,7 @@ export interface ProfileData { | ||
160 | transportConfiguration: TransportConfiguration; | 163 | transportConfiguration: TransportConfiguration; |
161 | provisionConfiguration: ProvisionConfiguration; | 164 | provisionConfiguration: ProvisionConfiguration; |
162 | alarms?: any; | 165 | alarms?: any; |
166 | + thingsModel?: ModelOfMatterParams[]; | ||
163 | } | 167 | } |
164 | 168 | ||
165 | export interface DeviceRecord { | 169 | export interface DeviceRecord { |
@@ -176,7 +180,6 @@ export interface DeviceRecord { | @@ -176,7 +180,6 @@ export interface DeviceRecord { | ||
176 | deviceCount: number; | 180 | deviceCount: number; |
177 | tbDeviceId: string; | 181 | tbDeviceId: string; |
178 | tbProfileId: string; | 182 | tbProfileId: string; |
179 | - profileData: ProfileData; | ||
180 | defaultQueueName: string; | 183 | defaultQueueName: string; |
181 | image: string; | 184 | image: string; |
182 | type: string; | 185 | type: string; |
@@ -192,6 +195,7 @@ export interface DeviceRecord { | @@ -192,6 +195,7 @@ export interface DeviceRecord { | ||
192 | default: boolean; | 195 | default: boolean; |
193 | name: string; | 196 | name: string; |
194 | transportType: string; | 197 | transportType: string; |
198 | + profileData: ProfileData; | ||
195 | }; | 199 | }; |
196 | customerAdditionalInfo?: { | 200 | customerAdditionalInfo?: { |
197 | isPublic?: boolean; | 201 | isPublic?: boolean; |
@@ -203,9 +207,10 @@ export interface DeviceModelOfMatterAttrs { | @@ -203,9 +207,10 @@ export interface DeviceModelOfMatterAttrs { | ||
203 | name: string; | 207 | name: string; |
204 | identifier: string; | 208 | identifier: string; |
205 | accessMode: string; | 209 | accessMode: string; |
206 | - detail: StructJSON; | ||
207 | - deviceDetail?: DeviceRecord; | ||
208 | - extensionDesc?: any; | 210 | + detail: { |
211 | + dataType: DataType; | ||
212 | + }; | ||
213 | + extensionDesc?: ExtensionDesc; | ||
209 | } | 214 | } |
210 | 215 | ||
211 | export interface DeviceStateLogModel { | 216 | export interface DeviceStateLogModel { |
1 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 1 | +import { |
2 | + DataTypeEnum, | ||
3 | + ExtendDescOperationTypeEnum, | ||
4 | + OriginalDataTypeEnum, | ||
5 | +} from '/@/enums/objectModelEnum'; | ||
2 | import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | 6 | import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; |
3 | 7 | ||
4 | export interface Specs { | 8 | export interface Specs { |
@@ -27,10 +31,14 @@ export interface DataType { | @@ -27,10 +31,14 @@ export interface DataType { | ||
27 | } | 31 | } |
28 | 32 | ||
29 | export interface ExtensionDesc { | 33 | export interface ExtensionDesc { |
30 | - zoomFactor?: number; | ||
31 | - actionType?: string; | ||
32 | - dataType: string; | ||
33 | - registerAddress: number; | 34 | + writeOnly?: boolean; |
35 | + bitMask?: number; | ||
36 | + operationType: ExtendDescOperationTypeEnum; | ||
37 | + originalDataType: OriginalDataTypeEnum; | ||
38 | + registerAddress: string; | ||
39 | + scaling?: number; | ||
40 | + valueRange?: Record<'min' | 'max', number>; | ||
41 | + registerCount?: number; | ||
34 | } | 42 | } |
35 | 43 | ||
36 | export interface StructJSON { | 44 | export interface StructJSON { |
@@ -64,7 +72,7 @@ export interface ModelOfMatterParams { | @@ -64,7 +72,7 @@ export interface ModelOfMatterParams { | ||
64 | } | 72 | } |
65 | 73 | ||
66 | export interface GetModelTslParams { | 74 | export interface GetModelTslParams { |
67 | - functionType: FunctionTypeEnum; | 75 | + functionType?: FunctionTypeEnum; |
68 | deviceProfileId: string; | 76 | deviceProfileId: string; |
69 | ifShowClass?: string | Boolean; | 77 | ifShowClass?: string | Boolean; |
70 | } | 78 | } |
@@ -108,7 +116,7 @@ export interface Tsl { | @@ -108,7 +116,7 @@ export interface Tsl { | ||
108 | specs?: { | 116 | specs?: { |
109 | dataType: DataType; | 117 | dataType: DataType; |
110 | }; | 118 | }; |
111 | - extensionDesc: ExtensionDesc; | 119 | + extensionDesc?: ExtensionDesc; |
112 | eventType?: string; | 120 | eventType?: string; |
113 | outputData?: StructJSON[]; | 121 | outputData?: StructJSON[]; |
114 | callType?: string; | 122 | callType?: string; |
@@ -5,6 +5,7 @@ import { | @@ -5,6 +5,7 @@ import { | ||
5 | ImportModelOfMatterType, | 5 | ImportModelOfMatterType, |
6 | ModelOfMatterItemRecordType, | 6 | ModelOfMatterItemRecordType, |
7 | ModelOfMatterParams, | 7 | ModelOfMatterParams, |
8 | + Tsl, | ||
8 | } from './model/modelOfMatterModel'; | 9 | } from './model/modelOfMatterModel'; |
9 | import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | 10 | import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; |
10 | import { defHttp } from '/@/utils/http/axios'; | 11 | import { defHttp } from '/@/utils/http/axios'; |
@@ -26,7 +27,7 @@ enum ModelOfMatter { | @@ -26,7 +27,7 @@ enum ModelOfMatter { | ||
26 | CATEGORY_EXPORT = '/things_model/categoryGetExport', | 27 | CATEGORY_EXPORT = '/things_model/categoryGetExport', |
27 | 28 | ||
28 | IMPORT_CSV = '/things_model/csvImport', | 29 | IMPORT_CSV = '/things_model/csvImport', |
29 | - EXCEL_EXPORT = '/things_model/downloadTemplate', | 30 | + EXCEL_EXPORT = '/things_model/download/template', |
30 | 31 | ||
31 | BATCH_GET_TSL_BY_DEVICE_PROFILES = '/things_model/batch/get_tsl', | 32 | BATCH_GET_TSL_BY_DEVICE_PROFILES = '/things_model/batch/get_tsl', |
32 | } | 33 | } |
@@ -47,8 +48,8 @@ export const getModelList = ( | @@ -47,8 +48,8 @@ export const getModelList = ( | ||
47 | }; | 48 | }; |
48 | 49 | ||
49 | export const getModelTsl = (params: GetModelTslParams) => { | 50 | export const getModelTsl = (params: GetModelTslParams) => { |
50 | - const { functionType, deviceProfileId } = params; | ||
51 | - return defHttp.get({ | 51 | + const { functionType = 'all', deviceProfileId } = params; |
52 | + return defHttp.get<Tsl[]>({ | ||
52 | url: `${ModelOfMatter.TSL}/${functionType}/${deviceProfileId}`, | 53 | url: `${ModelOfMatter.TSL}/${functionType}/${deviceProfileId}`, |
53 | }); | 54 | }); |
54 | }; | 55 | }; |
@@ -149,11 +150,12 @@ export const importCsvCategory = (params: { | @@ -149,11 +150,12 @@ export const importCsvCategory = (params: { | ||
149 | categoryId?: string; | 150 | categoryId?: string; |
150 | deviceProfileId?: string; | 151 | deviceProfileId?: string; |
151 | file: FormData; | 152 | file: FormData; |
153 | + type?: string; | ||
152 | }) => { | 154 | }) => { |
153 | - const { categoryId } = params || {}; | 155 | + const { categoryId, type } = params || {}; |
154 | 156 | ||
155 | return defHttp.post<any>({ | 157 | return defHttp.post<any>({ |
156 | - url: `${ModelOfMatter.IMPORT_CSV}?categoryId=${categoryId}`, | 158 | + url: `${ModelOfMatter.IMPORT_CSV}?categoryId=${categoryId}&type=${type}`, |
157 | params: params.file, | 159 | params: params.file, |
158 | }); | 160 | }); |
159 | }; | 161 | }; |
@@ -166,11 +168,12 @@ export const importCsvDeviceProfileId = (params: { | @@ -166,11 +168,12 @@ export const importCsvDeviceProfileId = (params: { | ||
166 | categoryId?: string; | 168 | categoryId?: string; |
167 | deviceProfileId?: string; | 169 | deviceProfileId?: string; |
168 | file: FormData; | 170 | file: FormData; |
171 | + type?: string; | ||
169 | }) => { | 172 | }) => { |
170 | - const { deviceProfileId } = params || {}; | 173 | + const { deviceProfileId, type } = params || {}; |
171 | 174 | ||
172 | return defHttp.post<any>({ | 175 | return defHttp.post<any>({ |
173 | - url: `${ModelOfMatter.IMPORT_CSV}?deviceProfileId=${deviceProfileId}`, | 176 | + url: `${ModelOfMatter.IMPORT_CSV}?deviceProfileId=${deviceProfileId}&type=${type}`, |
174 | params: params.file, | 177 | params: params.file, |
175 | }); | 178 | }); |
176 | }; | 179 | }; |
@@ -178,9 +181,10 @@ export const importCsvDeviceProfileId = (params: { | @@ -178,9 +181,10 @@ export const importCsvDeviceProfileId = (params: { | ||
178 | /** | 181 | /** |
179 | * 物模型excel导出模板 | 182 | * 物模型excel导出模板 |
180 | */ | 183 | */ |
181 | -export const excelExport = () => { | 184 | +export const excelExport = (type?: string) => { |
182 | return defHttp.get({ | 185 | return defHttp.get({ |
183 | url: `${ModelOfMatter.EXCEL_EXPORT}`, | 186 | url: `${ModelOfMatter.EXCEL_EXPORT}`, |
187 | + params: { type }, | ||
184 | responseType: 'blob', | 188 | responseType: 'blob', |
185 | }); | 189 | }); |
186 | }; | 190 | }; |
@@ -62,6 +62,7 @@ export interface GenModbusCommandType { | @@ -62,6 +62,7 @@ export interface GenModbusCommandType { | ||
62 | registerAddress: number; | 62 | registerAddress: number; |
63 | registerNumber?: number; | 63 | registerNumber?: number; |
64 | registerValues?: number[]; | 64 | registerValues?: number[]; |
65 | + hexByteOrderEnum?: string; | ||
65 | } | 66 | } |
66 | 67 | ||
67 | export interface ImmediateExecuteTaskType { | 68 | export interface ImmediateExecuteTaskType { |
@@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
2 | <div class="flex"> | 2 | <div class="flex"> |
3 | <InputNumber | 3 | <InputNumber |
4 | placeholder="最小值" | 4 | placeholder="最小值" |
5 | + v-bind="minInputProps" | ||
5 | :disabled="$props.disabled" | 6 | :disabled="$props.disabled" |
6 | :value="getValue.min!" | 7 | :value="getValue.min!" |
7 | @change="(value) => emitChange(value, 'min')" | 8 | @change="(value) => emitChange(value, 'min')" |
@@ -9,6 +10,7 @@ | @@ -9,6 +10,7 @@ | ||
9 | <div class="text-center flex-shrink-0 w-6">~</div> | 10 | <div class="text-center flex-shrink-0 w-6">~</div> |
10 | <InputNumber | 11 | <InputNumber |
11 | placeholder="最大值" | 12 | placeholder="最大值" |
13 | + v-bind="maxInputProps" | ||
12 | :disabled="$props.disabled" | 14 | :disabled="$props.disabled" |
13 | :value="getValue.max!" | 15 | :value="getValue.max!" |
14 | @change="(value) => emitChange(value, 'max')" | 16 | @change="(value) => emitChange(value, 'max')" |
@@ -22,7 +24,7 @@ | @@ -22,7 +24,7 @@ | ||
22 | </script> | 24 | </script> |
23 | <script lang="ts" setup> | 25 | <script lang="ts" setup> |
24 | import { computed } from 'vue'; | 26 | import { computed } from 'vue'; |
25 | - import { InputNumber } from 'ant-design-vue'; | 27 | + import { InputNumber, InputNumberProps } from 'ant-design-vue'; |
26 | 28 | ||
27 | const emit = defineEmits(['change', 'update:value']); | 29 | const emit = defineEmits(['change', 'update:value']); |
28 | const props = withDefaults( | 30 | const props = withDefaults( |
@@ -32,8 +34,12 @@ | @@ -32,8 +34,12 @@ | ||
32 | max: Nullable<number>; | 34 | max: Nullable<number>; |
33 | }; | 35 | }; |
34 | disabled: boolean; | 36 | disabled: boolean; |
37 | + maxInputProps?: InputNumberProps; | ||
38 | + minInputProps?: InputNumberProps; | ||
35 | }>(), | 39 | }>(), |
36 | { | 40 | { |
41 | + maxInputProps: () => ({}), | ||
42 | + minInputProps: () => ({}), | ||
37 | value: () => ({ min: null, max: null }), | 43 | value: () => ({ min: null, max: null }), |
38 | } | 44 | } |
39 | ); | 45 | ); |
@@ -128,6 +128,7 @@ export type ComponentType = | @@ -128,6 +128,7 @@ export type ComponentType = | ||
128 | | 'ProductPicker' | 128 | | 'ProductPicker' |
129 | | 'PollCommandInput' | 129 | | 'PollCommandInput' |
130 | | 'RegisterAddressInput' | 130 | | 'RegisterAddressInput' |
131 | + | 'HexInput' | ||
131 | | 'ControlGroup' | 132 | | 'ControlGroup' |
132 | | 'JSONEditor' | 133 | | 'JSONEditor' |
133 | | 'OrgTreeSelect' | 134 | | 'OrgTreeSelect' |
@@ -48,3 +48,13 @@ export enum CommandTypeNameEnum { | @@ -48,3 +48,13 @@ export enum CommandTypeNameEnum { | ||
48 | export enum RPCCommandMethodEnum { | 48 | export enum RPCCommandMethodEnum { |
49 | THINGSKIT = 'methodThingskit', | 49 | THINGSKIT = 'methodThingskit', |
50 | } | 50 | } |
51 | + | ||
52 | +export enum TCPProtocolTypeEnum { | ||
53 | + CUSTOM = 'CUSTOM', | ||
54 | + MODBUS_RTU = 'MODBUS_RTU', | ||
55 | +} | ||
56 | + | ||
57 | +export enum TCPProtocolTypeNameEnum { | ||
58 | + CUSTOM = '自定义', | ||
59 | + MODBUS_RTU = 'MODBUS_RTU', | ||
60 | +} |
@@ -31,31 +31,95 @@ export enum ObjectEventTypeNameEnum { | @@ -31,31 +31,95 @@ export enum ObjectEventTypeNameEnum { | ||
31 | ERROR = '故障', | 31 | ERROR = '故障', |
32 | } | 32 | } |
33 | 33 | ||
34 | -export enum RegisterDataTypeEnum { | ||
35 | - UN_SHORT = 'unshort', | 34 | +export enum ObjectModelAccessModeEnum { |
35 | + READ = 'r', | ||
36 | + READ_AND_WRITE = 'rw', | ||
36 | } | 37 | } |
37 | 38 | ||
38 | -export enum RegisterDataTypeNameEnum { | ||
39 | - UN_SHORT = '16位有符号', | 39 | +export enum ModbusCRCEnum { |
40 | + CRC_16_LOWER = 'CRC_16_LOWER', | ||
40 | } | 41 | } |
41 | 42 | ||
42 | -export enum RegisterActionTypeEnum { | ||
43 | - BOOL = '05', | ||
44 | - INT = '06', | ||
45 | - DOUBLE = '16', | 43 | +export enum BuiltInIdentifierEnum { |
44 | + SOURCE = 'source', | ||
46 | } | 45 | } |
47 | 46 | ||
48 | -export enum RegisterActionTypeNameEnum { | ||
49 | - BOOL = '05写入单个线圈寄存器', | ||
50 | - INT = '06写入单个保持寄存器', | ||
51 | - DOUBLE = '16写入多个保持寄存器', | 47 | +export enum ModbusMethodEnum { |
48 | + WRITE_10 = '10', | ||
52 | } | 49 | } |
53 | 50 | ||
54 | -export enum ObjectModelAccessModeEnum { | ||
55 | - READ = 'r', | ||
56 | - READ_AND_WRITE = 'rw', | 51 | +export enum OriginalDataTypeEnum { |
52 | + INT16_AB = 'INT16_AB', | ||
53 | + INT16_BA = 'INT16_BA', | ||
54 | + UINT16_AB = 'UINT16_AB', | ||
55 | + UINT16_BA = 'UINT16_BA', | ||
56 | + INT32_AB_CD = 'INT32_AB_CD', | ||
57 | + INT32_CD_AB = 'INT32_CD_AB', | ||
58 | + INT32_BA_DC = 'INT32_BA_DC', | ||
59 | + INT32_DC_BA = 'INT32_DC_BA', | ||
60 | + UINT32_AB_CD = 'UINT32_AB_CD', | ||
61 | + UINT32_CD_AB = 'UINT32_CD_AB', | ||
62 | + UINT32_BA_DC = 'UINT32_BA_DC', | ||
63 | + UINT32_DC_BA = 'UINT32_DC_BA', | ||
64 | + FLOAT_AB_CD = 'FLOAT_AB_CD', | ||
65 | + FLOAT_CD_AB = 'FLOAT_CD_AB', | ||
66 | + FLOAT_BA_DC = 'FLOAT_BA_DC', | ||
67 | + FLOAT_DC_BA = 'FLOAT_DC_BA', | ||
68 | + DOUBLE = 'DOUBLE', | ||
69 | + STRING = 'STRING', | ||
70 | + BOOLEAN = 'BOOLEAN', | ||
71 | + BITS = 'BITS', | ||
57 | } | 72 | } |
58 | 73 | ||
59 | -export enum ModbusCRCEnum { | ||
60 | - CRC_16_LOWER = 'CRC_16_LOWER', | 74 | +export enum OriginalDataTypeNameEnum { |
75 | + INT16_AB = '16位有符号整数AB', | ||
76 | + INT16_BA = '16位有符号整数BA', | ||
77 | + UINT16_AB = '16位无符号整数AB', | ||
78 | + UINT16_BA = '16位无符号整数BA', | ||
79 | + INT32_AB_CD = '32位有符号整数AB_CD', | ||
80 | + INT32_CD_AB = '32位有符号整数CD_AB', | ||
81 | + INT32_BA_DC = '32位有符号整数BA_DC', | ||
82 | + INT32_DC_BA = '32位有符号整数DC_BA', | ||
83 | + UINT32_AB_CD = '32位无符号整数AB_CD', | ||
84 | + UINT32_CD_AB = '32位无符号整数CD_AB', | ||
85 | + UINT32_BA_DC = '32位无符号整数BA_DC', | ||
86 | + UINT32_DC_BA = '32位无符号整数DC_BA', | ||
87 | + FLOAT_AB_CD = '单精度浮点型AB_CD', | ||
88 | + FLOAT_CD_AB = '单精度浮点型CD_AB', | ||
89 | + FLOAT_BA_DC = '单精度浮点型BA_DC', | ||
90 | + FLOAT_DC_BA = '单精度浮点型DC_BA', | ||
91 | + DOUBLE = '双精度浮点型', | ||
92 | + STRING = '字符串', | ||
93 | + BOOLEAN = '布尔型', | ||
94 | + BITS = '位', | ||
95 | +} | ||
96 | + | ||
97 | +export enum ExtendDescOperationTypeEnum { | ||
98 | + INPUT_STATUS_R_02 = 'inputStatus_r_02', | ||
99 | + COIL_STATUS_R_01 = 'coilStatus_r_01', | ||
100 | + COIL_STATUS_RW_01_05 = 'coilStatus_rw_01_05', | ||
101 | + COIL_STATUS_RW_01_0F = 'coilStatus_rw_01_0F', | ||
102 | + COIL_STATUS_W_05 = 'coilStatus_w_05', | ||
103 | + COIL_STATUS_W_0F = 'coilStatus_w_0F', | ||
104 | + HOLDING_REGISTER_R_03 = 'holdingRegister_r_03', | ||
105 | + HOLDING_REGISTER_RW_03_06 = 'holdingRegister_rw_03_06', | ||
106 | + HOLDING_REGISTER_RW_03_10 = 'holdingRegister_rw_03_10', | ||
107 | + HOLDING_REGISTER_W_06 = 'holdingRegister_w_06', | ||
108 | + HOLDING_REGISTER_W_10 = 'holdingRegister_w_10', | ||
109 | + INPUT_REGISTER_R_04 = 'inputRegister_r_04', | ||
110 | +} | ||
111 | + | ||
112 | +export enum ExtendDescOperationTypeNameEnum { | ||
113 | + INPUT_STATUS_R_02 = '离散量输入(只读,0x02)', | ||
114 | + COIL_STATUS_R_01 = '线圈状态(只读,0x01)', | ||
115 | + COIL_STATUS_RW_01_05 = '线圈状态(读写,读取使用0x01,写入使用0x05)', | ||
116 | + COIL_STATUS_RW_01_0F = '线圈状态(读写,读取使用0x01,写入使用0x0F)', | ||
117 | + COIL_STATUS_W_05 = '线圈状态(只写,0x05)', | ||
118 | + COIL_STATUS_W_0F = '线圈状态(只写,0x0F)', | ||
119 | + HOLDING_REGISTER_R_03 = '保持寄存器(只读,0x03)', | ||
120 | + HOLDING_REGISTER_RW_03_06 = '保持寄存器(读写,读取使用0x03,写入使用0x06)', | ||
121 | + HOLDING_REGISTER_RW_03_10 = '保持寄存器(读写,读取使用0x03,写入使用0x10)', | ||
122 | + HOLDING_REGISTER_W_06 = '保持寄存器(只写,0x06)', | ||
123 | + HOLDING_REGISTER_W_10 = '保持寄存器(只写,0x10)', | ||
124 | + INPUT_REGISTER_R_04 = '输入寄存器(只读,0x04)', | ||
61 | } | 125 | } |
src/hooks/business/useBaseConversion.ts
0 → 100644
1 | +import { useParseOriginalDataType } from './useParseOriginalDataType'; | ||
2 | +import { OriginalDataTypeEnum } from '/@/enums/objectModelEnum'; | ||
3 | + | ||
4 | +export function useBaseConversion() { | ||
5 | + function DecTo32Float(number: number) { | ||
6 | + const arr = new Uint8Array(4); | ||
7 | + const view = new DataView(arr.buffer); | ||
8 | + view.setFloat32(0, +number); | ||
9 | + return arr; | ||
10 | + } | ||
11 | + | ||
12 | + function DecTo64Double(number: number) { | ||
13 | + const arr = new Uint8Array(8); | ||
14 | + const view = new DataView(arr.buffer); | ||
15 | + view.setFloat64(0, +number); | ||
16 | + return arr; | ||
17 | + } | ||
18 | + | ||
19 | + function arrToBase(toBase: number, arr: Uint8Array) { | ||
20 | + let result = ''; | ||
21 | + for (let i = 0; i < arr.length; i++) { | ||
22 | + result += (256 + arr[i]).toString(toBase).substring(1).toUpperCase(); | ||
23 | + } | ||
24 | + | ||
25 | + return result; | ||
26 | + } | ||
27 | + | ||
28 | + function DecTo16Uint(number: number) { | ||
29 | + const arr = new Uint8Array(2); | ||
30 | + const view = new DataView(arr.buffer); | ||
31 | + view.setUint16(0, +number); | ||
32 | + return arr; | ||
33 | + } | ||
34 | + | ||
35 | + function DecTo16Int(number: number) { | ||
36 | + const arr = new Uint8Array(2); | ||
37 | + const view = new DataView(arr.buffer); | ||
38 | + view.setInt16(0, +number); | ||
39 | + return arr; | ||
40 | + } | ||
41 | + | ||
42 | + function DecTo32Uint(number: number) { | ||
43 | + const arr = new Uint8Array(4); | ||
44 | + const view = new DataView(arr.buffer); | ||
45 | + view.setUint32(0, +number); | ||
46 | + return arr; | ||
47 | + } | ||
48 | + | ||
49 | + function DecTo32Int(number: number) { | ||
50 | + const arr = new Uint8Array(4); | ||
51 | + const view = new DataView(arr.buffer); | ||
52 | + view.setInt32(0, +number); | ||
53 | + return arr; | ||
54 | + } | ||
55 | + | ||
56 | + function DecToBinaryByType(type: OriginalDataTypeEnum, number: number) { | ||
57 | + switch (type) { | ||
58 | + case OriginalDataTypeEnum.INT16_AB: | ||
59 | + case OriginalDataTypeEnum.INT16_BA: | ||
60 | + return arrToBase(2, DecTo16Int(number)); | ||
61 | + | ||
62 | + case OriginalDataTypeEnum.UINT16_AB: | ||
63 | + case OriginalDataTypeEnum.UINT16_BA: | ||
64 | + return arrToBase(2, DecTo16Uint(number)); | ||
65 | + | ||
66 | + case OriginalDataTypeEnum.INT32_AB_CD: | ||
67 | + case OriginalDataTypeEnum.INT32_CD_AB: | ||
68 | + case OriginalDataTypeEnum.INT32_BA_DC: | ||
69 | + case OriginalDataTypeEnum.INT32_DC_BA: | ||
70 | + return arrToBase(2, DecTo32Int(number)); | ||
71 | + | ||
72 | + case OriginalDataTypeEnum.UINT32_AB_CD: | ||
73 | + case OriginalDataTypeEnum.UINT32_CD_AB: | ||
74 | + case OriginalDataTypeEnum.UINT32_BA_DC: | ||
75 | + case OriginalDataTypeEnum.UINT32_DC_BA: | ||
76 | + return arrToBase(2, DecTo32Uint(number)); | ||
77 | + | ||
78 | + case OriginalDataTypeEnum.FLOAT_AB_CD: | ||
79 | + case OriginalDataTypeEnum.FLOAT_CD_AB: | ||
80 | + case OriginalDataTypeEnum.FLOAT_BA_DC: | ||
81 | + case OriginalDataTypeEnum.FLOAT_DC_BA: | ||
82 | + return arrToBase(2, DecTo32Float(number)); | ||
83 | + | ||
84 | + case OriginalDataTypeEnum.DOUBLE: | ||
85 | + return arrToBase(2, DecTo64Double(number)); | ||
86 | + } | ||
87 | + } | ||
88 | + | ||
89 | + function SplitStringToGroupByItemLength( | ||
90 | + value: string, | ||
91 | + itemLength = 8, | ||
92 | + ignoreLessThan = false | ||
93 | + ): string[] { | ||
94 | + const reg = new RegExp(`.{${ignoreLessThan ? '' : '1,'}${itemLength}}`, 'g'); | ||
95 | + return value.match(reg) || []; | ||
96 | + } | ||
97 | + | ||
98 | + function ExchangeByteOrder(binary: string, order: string) { | ||
99 | + const group = SplitStringToGroupByItemLength(binary); | ||
100 | + | ||
101 | + const BASE_ORDER = { | ||
102 | + A: 0, | ||
103 | + B: 1, | ||
104 | + C: 2, | ||
105 | + D: 3, | ||
106 | + }; | ||
107 | + | ||
108 | + const array: string[] = Array.from({ length: binary.length / 8 }); | ||
109 | + | ||
110 | + order | ||
111 | + .split('') | ||
112 | + .forEach((bytePosition, index) => (array[index] = group[BASE_ORDER[bytePosition]])); | ||
113 | + | ||
114 | + return array.join(''); | ||
115 | + } | ||
116 | + | ||
117 | + function ByteToHex(binary: string) { | ||
118 | + const group = SplitStringToGroupByItemLength(binary, 16); | ||
119 | + return group.map((byte) => parseInt(byte, 2).toString(16)); | ||
120 | + } | ||
121 | + | ||
122 | + function ByteToDec(binary: string) { | ||
123 | + const group = SplitStringToGroupByItemLength(binary, 16); | ||
124 | + return group.map((byte) => parseInt(byte, 2)); | ||
125 | + } | ||
126 | + | ||
127 | + function StringToHEXBuffer(string: string | number) { | ||
128 | + return string | ||
129 | + .toString() | ||
130 | + .split('') | ||
131 | + .map((string) => string.charCodeAt(0).toString(16)) | ||
132 | + .reverse(); | ||
133 | + } | ||
134 | + | ||
135 | + function getRegisterValueByOriginalDataType( | ||
136 | + value: number, | ||
137 | + type: OriginalDataTypeEnum, | ||
138 | + additional?: { bitMask?: number; registerNumber?: number } | ||
139 | + ) { | ||
140 | + const { exchangeSortFlag } = useParseOriginalDataType(type); | ||
141 | + // eslint-disable-next-line no-console | ||
142 | + console.groupCollapsed('Modbus Debug'); | ||
143 | + // eslint-disable-next-line no-console | ||
144 | + console.table({ input: value, sort: exchangeSortFlag, type }); | ||
145 | + | ||
146 | + let result: number[]; | ||
147 | + | ||
148 | + if (type === OriginalDataTypeEnum.BOOLEAN) { | ||
149 | + result = [value]; | ||
150 | + } else if (type === OriginalDataTypeEnum.STRING) { | ||
151 | + let buffer = StringToHEXBuffer(value); | ||
152 | + const { registerNumber = 0 } = additional || {}; | ||
153 | + | ||
154 | + if (buffer.length < registerNumber * 2) { | ||
155 | + buffer = [ | ||
156 | + ...Array.from({ length: registerNumber * 2 - buffer.length }, () => '00'), | ||
157 | + ...buffer, | ||
158 | + ]; | ||
159 | + } | ||
160 | + | ||
161 | + result = SplitStringToGroupByItemLength(buffer.join(''), 4).map((hex) => parseInt(hex, 16)); | ||
162 | + } else { | ||
163 | + let binary = DecToBinaryByType(type, value)!; | ||
164 | + // eslint-disable-next-line no-console | ||
165 | + console.table({ beforeExchange: binary }); | ||
166 | + | ||
167 | + if (exchangeSortFlag) binary = ExchangeByteOrder(binary, exchangeSortFlag); | ||
168 | + result = ByteToDec(binary); | ||
169 | + | ||
170 | + // eslint-disable-next-line no-console | ||
171 | + console.table({ | ||
172 | + afterEchange: binary, | ||
173 | + dec: result.toString(), | ||
174 | + hex: ByteToHex(binary).toString(), | ||
175 | + }); | ||
176 | + } | ||
177 | + | ||
178 | + // eslint-disable-next-line no-console | ||
179 | + console.groupEnd(); | ||
180 | + | ||
181 | + return result; | ||
182 | + } | ||
183 | + | ||
184 | + return { | ||
185 | + DecToBinaryByType, | ||
186 | + ByteToDec, | ||
187 | + ByteToHex, | ||
188 | + ExchangeByteOrder, | ||
189 | + SplitStringToGroupByItemLength, | ||
190 | + getRegisterValueByOriginalDataType, | ||
191 | + StringToHEXBuffer, | ||
192 | + }; | ||
193 | +} |
src/hooks/business/useCommandDelivery.ts
0 → 100644
1 | +import { ref } from 'vue'; | ||
2 | +import { useCoverModbusCommand } from './useCoverModbusCommand'; | ||
3 | +import { commandIssuanceApi, getDeviceDetail } from '/@/api/device/deviceManager'; | ||
4 | +import { RpcCommandType } from '/@/api/device/model/deviceConfigModel'; | ||
5 | +import { DeviceProfileModel, DeviceRecord } from '/@/api/device/model/deviceModel'; | ||
6 | +import { Tsl } from '/@/api/device/model/modelOfMatterModel'; | ||
7 | +import { getModelTsl } from '/@/api/device/modelOfMatter'; | ||
8 | +import { | ||
9 | + CommandDeliveryWayEnum, | ||
10 | + CommandTypeEnum, | ||
11 | + RPCCommandMethodEnum, | ||
12 | + ServiceCallTypeEnum, | ||
13 | + TCPProtocolTypeEnum, | ||
14 | + TransportTypeEnum, | ||
15 | +} from '/@/enums/deviceEnum'; | ||
16 | +import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | ||
17 | +import { isFunction } from '/@/utils/is'; | ||
18 | +import { getDeviceActiveTime } from '/@/api/alarm/position'; | ||
19 | +import { useMessage } from '../web/useMessage'; | ||
20 | + | ||
21 | +interface SetupType { | ||
22 | + entityId: string; | ||
23 | + transportType: TransportTypeEnum; | ||
24 | + isTCPModbus: boolean; | ||
25 | + deviceCode: string | undefined; | ||
26 | + deviceDetail: DeviceRecord; | ||
27 | + objectModel: Tsl | undefined; | ||
28 | + identifier: string; | ||
29 | +} | ||
30 | + | ||
31 | +interface DoCommandDeliverParamsType { | ||
32 | + value: any; | ||
33 | + deviceDetail?: DeviceRecord; | ||
34 | + deviceId?: string; | ||
35 | + identifier?: string; | ||
36 | + objectModel?: Tsl; | ||
37 | + deviceProfileId?: string; | ||
38 | + deviceProfileDetail?: DeviceProfileModel; | ||
39 | + way?: CommandDeliveryWayEnum; | ||
40 | + cmdType?: CommandTypeEnum; | ||
41 | + transportType?: TransportTypeEnum; | ||
42 | + beforeFetch?: ( | ||
43 | + rpcCommand: RpcCommandType, | ||
44 | + setup: SetupType | ||
45 | + ) => | ||
46 | + | { rpcCommand: RpcCommandType; way?: CommandDeliveryWayEnum } | ||
47 | + | Promise<{ rpcCommand: RpcCommandType; way?: CommandDeliveryWayEnum }>; | ||
48 | +} | ||
49 | + | ||
50 | +export function useCommandDelivery() { | ||
51 | + const loading = ref(false); | ||
52 | + async function doSetup(params: DoCommandDeliverParamsType) { | ||
53 | + let { deviceDetail, identifier, deviceProfileId, objectModel, transportType } = params; | ||
54 | + const { deviceId, deviceProfileDetail } = params; | ||
55 | + | ||
56 | + const entityId = deviceId || deviceDetail?.tbDeviceId; | ||
57 | + if (!entityId) { | ||
58 | + throw new Error('not found entityId'); | ||
59 | + } | ||
60 | + | ||
61 | + identifier = identifier || objectModel?.identifier; | ||
62 | + | ||
63 | + if (!identifier) { | ||
64 | + throw new Error('not found identifier'); | ||
65 | + } | ||
66 | + | ||
67 | + transportType = transportType || (deviceDetail?.transportType as TransportTypeEnum); | ||
68 | + | ||
69 | + if ( | ||
70 | + !transportType || | ||
71 | + (transportType === TransportTypeEnum.TCP && !deviceDetail) || | ||
72 | + !deviceDetail?.deviceProfile | ||
73 | + ) { | ||
74 | + deviceDetail = await getDeviceDetail(entityId); | ||
75 | + transportType = deviceDetail.transportType as TransportTypeEnum; | ||
76 | + } | ||
77 | + | ||
78 | + const isTCPModbus = | ||
79 | + transportType === TransportTypeEnum.TCP && | ||
80 | + deviceDetail?.deviceProfile?.profileData?.transportConfiguration?.protocol === | ||
81 | + TCPProtocolTypeEnum.MODBUS_RTU; | ||
82 | + | ||
83 | + if (isTCPModbus && !objectModel?.extensionDesc) { | ||
84 | + deviceProfileId = deviceDetail.deviceProfileId || deviceProfileId || deviceProfileDetail?.id; | ||
85 | + if (!deviceProfileId) { | ||
86 | + throw new Error('not found deviceProfile'); | ||
87 | + } | ||
88 | + | ||
89 | + const objectModels = await getModelTsl({ deviceProfileId }); | ||
90 | + objectModel = objectModels.find((item) => item.identifier === identifier); | ||
91 | + } | ||
92 | + | ||
93 | + const deviceCode = deviceDetail.code; | ||
94 | + | ||
95 | + return { | ||
96 | + entityId, | ||
97 | + transportType, | ||
98 | + isTCPModbus, | ||
99 | + deviceCode, | ||
100 | + deviceDetail, | ||
101 | + objectModel, | ||
102 | + identifier, | ||
103 | + }; | ||
104 | + } | ||
105 | + | ||
106 | + async function doCommandDelivery(params: DoCommandDeliverParamsType) { | ||
107 | + try { | ||
108 | + loading.value = true; | ||
109 | + | ||
110 | + const setupResult = await doSetup(params); | ||
111 | + | ||
112 | + const { entityId, transportType, isTCPModbus, deviceCode, objectModel, identifier } = | ||
113 | + setupResult; | ||
114 | + | ||
115 | + let command = params.value; | ||
116 | + | ||
117 | + if (transportType === TransportTypeEnum.TCP) { | ||
118 | + command = params.value; | ||
119 | + if (isTCPModbus) { | ||
120 | + const { doCoverCommand } = useCoverModbusCommand(); | ||
121 | + command = await doCoverCommand(params.value, objectModel!, deviceCode, entityId); | ||
122 | + } | ||
123 | + } else { | ||
124 | + command = { | ||
125 | + [identifier]: command, | ||
126 | + }; | ||
127 | + } | ||
128 | + | ||
129 | + let rpcCommand: RpcCommandType = { | ||
130 | + persistent: true, | ||
131 | + method: RPCCommandMethodEnum.THINGSKIT, | ||
132 | + additionalInfo: { | ||
133 | + cmdType: params.cmdType ?? CommandTypeEnum.API, | ||
134 | + }, | ||
135 | + params: command, | ||
136 | + }; | ||
137 | + | ||
138 | + let way = params.way ?? CommandDeliveryWayEnum.ONE_WAY; | ||
139 | + | ||
140 | + if (objectModel?.functionType === FunctionTypeEnum.SERVICE) { | ||
141 | + rpcCommand.additionalInfo.cmdType = CommandTypeEnum.SERVICE; | ||
142 | + way = | ||
143 | + objectModel.callType === ServiceCallTypeEnum.ASYNC | ||
144 | + ? CommandDeliveryWayEnum.ONE_WAY | ||
145 | + : CommandDeliveryWayEnum.TWO_WAY; | ||
146 | + } | ||
147 | + | ||
148 | + const sendApi = commandIssuanceApi; | ||
149 | + | ||
150 | + if (params.beforeFetch && isFunction(params.beforeFetch)) { | ||
151 | + const { rpcCommand: _rpcCommand, way: _way } = await params.beforeFetch( | ||
152 | + rpcCommand, | ||
153 | + setupResult | ||
154 | + ); | ||
155 | + rpcCommand = _rpcCommand; | ||
156 | + if (_way) way = _way; | ||
157 | + } | ||
158 | + | ||
159 | + if (way === CommandDeliveryWayEnum.TWO_WAY) { | ||
160 | + const result = await getDeviceActiveTime(entityId); | ||
161 | + const [firsetItem] = result || []; | ||
162 | + | ||
163 | + if (!firsetItem.value) { | ||
164 | + const { createMessage } = useMessage(); | ||
165 | + const message = '当前设备不在线'; | ||
166 | + createMessage.warning(message); | ||
167 | + throw Error(message); | ||
168 | + } | ||
169 | + } | ||
170 | + await sendApi(way, entityId, rpcCommand); | ||
171 | + } finally { | ||
172 | + loading.value = false; | ||
173 | + } | ||
174 | + } | ||
175 | + | ||
176 | + return { | ||
177 | + loading, | ||
178 | + doCommandDelivery, | ||
179 | + }; | ||
180 | +} |
src/hooks/business/useCoverModbusCommand.ts
0 → 100644
1 | +import { useParseOriginalDataType } from './useParseOriginalDataType'; | ||
2 | +import { getDeviceHistoryInfo } from '/@/api/alarm/position'; | ||
3 | +import { getDeviceDetail } from '/@/api/device/deviceManager'; | ||
4 | +import { ExtensionDesc, Specs, Tsl } from '/@/api/device/model/modelOfMatterModel'; | ||
5 | +import { genModbusCommand } from '/@/api/task'; | ||
6 | +import { GenModbusCommandType } from '/@/api/task/model'; | ||
7 | +import { ModbusCRCEnum, OriginalDataTypeEnum } from '/@/enums/objectModelEnum'; | ||
8 | +import { useBaseConversion } from '/@/hooks/business/useBaseConversion'; | ||
9 | +import { useMessage } from '/@/hooks/web/useMessage'; | ||
10 | +import { isNullOrUnDef } from '/@/utils/is'; | ||
11 | +import { | ||
12 | + isFloatType, | ||
13 | + isNumberType, | ||
14 | + useParseOperationType, | ||
15 | +} from '/@/views/device/profiles/components/ObjectModelForm/ExtendDesc/useParseOperationType'; | ||
16 | + | ||
17 | +const getFloatPart = (number: string | number) => { | ||
18 | + const isLessZero = Number(number) < 0; | ||
19 | + number = number.toString(); | ||
20 | + const floatPartStartIndex = number.indexOf('.'); | ||
21 | + const value = ~floatPartStartIndex | ||
22 | + ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}` | ||
23 | + : '0'; | ||
24 | + return Number(value); | ||
25 | +}; | ||
26 | + | ||
27 | +function getValueFromValueRange(value: number, valueRange?: Record<'min' | 'max', number>) { | ||
28 | + const { min, max } = valueRange || {}; | ||
29 | + if (!isNullOrUnDef(min) && value < min) return min; | ||
30 | + if (!isNullOrUnDef(max) && value > max) return max; | ||
31 | + return value; | ||
32 | +} | ||
33 | + | ||
34 | +async function getCurrentBitCommand(entityId: string, objectModel: Tsl, value: number) { | ||
35 | + const deviceDetail = await getDeviceDetail(entityId); | ||
36 | + const thingsModels = deviceDetail.deviceProfile.profileData.thingsModel; | ||
37 | + | ||
38 | + const { registerAddress } = objectModel.extensionDesc || {}; | ||
39 | + | ||
40 | + const bitsModel = thingsModels?.filter( | ||
41 | + (item) => | ||
42 | + item.extensionDesc?.originalDataType === OriginalDataTypeEnum.BITS && | ||
43 | + item.extensionDesc.registerAddress === registerAddress | ||
44 | + ); | ||
45 | + | ||
46 | + const valuePositionMap = | ||
47 | + bitsModel?.reduce((prev, next) => { | ||
48 | + return { ...prev, [next.identifier]: next.extensionDesc?.bitMask }; | ||
49 | + }, {} as Record<string, number>) || {}; | ||
50 | + | ||
51 | + const attrKeys = Object.keys(valuePositionMap); | ||
52 | + | ||
53 | + const latestBitsValues = await getDeviceHistoryInfo({ entityId, keys: attrKeys.join(',') }); | ||
54 | + | ||
55 | + const binaryArr = Array.from({ length: 16 }, () => 0); | ||
56 | + | ||
57 | + for (const key of attrKeys) { | ||
58 | + const index = valuePositionMap[key]; | ||
59 | + | ||
60 | + if (!isNullOrUnDef(index)) { | ||
61 | + const [latest] = latestBitsValues[key]; | ||
62 | + const { value } = latest; | ||
63 | + binaryArr[index] = Number(value); | ||
64 | + } | ||
65 | + } | ||
66 | + | ||
67 | + if (objectModel.extensionDesc?.bitMask) { | ||
68 | + binaryArr[objectModel.extensionDesc.bitMask] = value; | ||
69 | + } | ||
70 | + | ||
71 | + return [parseInt(binaryArr.reverse().join(''), 2)]; | ||
72 | +} | ||
73 | + | ||
74 | +export function useCoverModbusCommand() { | ||
75 | + const { createMessage } = useMessage(); | ||
76 | + | ||
77 | + const doCoverCommand = async ( | ||
78 | + value: number, | ||
79 | + objectModel: Tsl, | ||
80 | + deviceAddressCode?: string, | ||
81 | + entityId?: string | ||
82 | + ) => { | ||
83 | + if (!deviceAddressCode) { | ||
84 | + const message = '当前设备未绑定设备地址码'; | ||
85 | + createMessage.warning(message); | ||
86 | + throw new Error(message); | ||
87 | + } | ||
88 | + | ||
89 | + const { | ||
90 | + registerAddress, | ||
91 | + operationType, | ||
92 | + scaling, | ||
93 | + originalDataType, | ||
94 | + bitMask, | ||
95 | + registerCount: registerNumber, | ||
96 | + } = objectModel.extensionDesc as Required<ExtensionDesc>; | ||
97 | + | ||
98 | + const { writeRegisterAddress } = useParseOperationType(operationType); | ||
99 | + const { unsigned, exchangeSortFlag, registerCount } = | ||
100 | + useParseOriginalDataType(originalDataType); | ||
101 | + | ||
102 | + const params: GenModbusCommandType = { | ||
103 | + crc: ModbusCRCEnum.CRC_16_LOWER, | ||
104 | + registerNumber: registerCount || registerNumber, | ||
105 | + deviceCode: deviceAddressCode, | ||
106 | + registerAddress: parseInt(registerAddress, 16), | ||
107 | + method: writeRegisterAddress!, | ||
108 | + registerValues: [value], | ||
109 | + }; | ||
110 | + | ||
111 | + if (exchangeSortFlag) params.hexByteOrderEnum = exchangeSortFlag; | ||
112 | + | ||
113 | + const { getRegisterValueByOriginalDataType } = useBaseConversion(); | ||
114 | + | ||
115 | + if (isNumberType(originalDataType)) { | ||
116 | + let newValue = Math.trunc(value) * scaling + getFloatPart(value) * scaling; | ||
117 | + | ||
118 | + newValue = unsigned ? newValue : Math.abs(newValue); | ||
119 | + | ||
120 | + newValue = getValueFromValueRange( | ||
121 | + newValue, | ||
122 | + (objectModel.specs?.dataType.specs as Specs).valueRange | ||
123 | + ); | ||
124 | + | ||
125 | + if (!isFloatType(originalDataType) && newValue % 1 !== 0) { | ||
126 | + const message = `属性下发类型必须是整数,缩放因子为${scaling}`; | ||
127 | + createMessage.warning(message); | ||
128 | + throw Error(message); | ||
129 | + } | ||
130 | + | ||
131 | + value = newValue; | ||
132 | + } | ||
133 | + | ||
134 | + params.registerValues = | ||
135 | + originalDataType === OriginalDataTypeEnum.BITS | ||
136 | + ? await getCurrentBitCommand(entityId!, objectModel, value) | ||
137 | + : getRegisterValueByOriginalDataType(value, originalDataType, { | ||
138 | + bitMask, | ||
139 | + registerNumber, | ||
140 | + }); | ||
141 | + | ||
142 | + if (!params.method) { | ||
143 | + const message = '物模型操作类型无法进行写入'; | ||
144 | + createMessage.warning(message); | ||
145 | + throw Error(message); | ||
146 | + } | ||
147 | + | ||
148 | + return await genModbusCommand(params); | ||
149 | + }; | ||
150 | + | ||
151 | + return { | ||
152 | + doCoverCommand, | ||
153 | + }; | ||
154 | +} |
1 | +import { OriginalDataTypeEnum } from '/@/enums/objectModelEnum'; | ||
2 | + | ||
3 | +export type OriginalDataTypePrefixType<S = `${OriginalDataTypeEnum}`> = S extends string | ||
4 | + ? S extends `${infer D}_${string}` | ||
5 | + ? D | ||
6 | + : S | ||
7 | + : ''; | ||
8 | + | ||
9 | +function getRegisterCount(originalDataType: OriginalDataTypeEnum) { | ||
10 | + switch (originalDataType) { | ||
11 | + case OriginalDataTypeEnum.INT16_AB: | ||
12 | + case OriginalDataTypeEnum.INT16_BA: | ||
13 | + case OriginalDataTypeEnum.UINT16_AB: | ||
14 | + case OriginalDataTypeEnum.UINT16_BA: | ||
15 | + case OriginalDataTypeEnum.BITS: | ||
16 | + case OriginalDataTypeEnum.BOOLEAN: | ||
17 | + return 1; | ||
18 | + | ||
19 | + case OriginalDataTypeEnum.INT32_AB_CD: | ||
20 | + case OriginalDataTypeEnum.INT32_BA_DC: | ||
21 | + case OriginalDataTypeEnum.INT32_CD_AB: | ||
22 | + case OriginalDataTypeEnum.INT32_DC_BA: | ||
23 | + case OriginalDataTypeEnum.UINT32_AB_CD: | ||
24 | + case OriginalDataTypeEnum.UINT32_BA_DC: | ||
25 | + case OriginalDataTypeEnum.UINT32_CD_AB: | ||
26 | + case OriginalDataTypeEnum.UINT32_DC_BA: | ||
27 | + return 2; | ||
28 | + | ||
29 | + case OriginalDataTypeEnum.FLOAT_AB_CD: | ||
30 | + case OriginalDataTypeEnum.FLOAT_BA_DC: | ||
31 | + case OriginalDataTypeEnum.FLOAT_CD_AB: | ||
32 | + case OriginalDataTypeEnum.FLOAT_DC_BA: | ||
33 | + return 2; | ||
34 | + | ||
35 | + case OriginalDataTypeEnum.DOUBLE: | ||
36 | + return 4; | ||
37 | + } | ||
38 | +} | ||
39 | + | ||
40 | +export function useParseOriginalDataType(originalDataType: OriginalDataTypeEnum) { | ||
41 | + const signedMatchRef = /^UN/; | ||
42 | + | ||
43 | + const splitArray = originalDataType.split('_') as [OriginalDataTypePrefixType]; | ||
44 | + | ||
45 | + const [dataType] = splitArray; | ||
46 | + | ||
47 | + const exchangeSortFlag = splitArray.slice(1).join('_'); | ||
48 | + | ||
49 | + return { | ||
50 | + registerCount: getRegisterCount(originalDataType), | ||
51 | + unsigned: !signedMatchRef.test(originalDataType), | ||
52 | + dataType, | ||
53 | + exchangeSortFlag: exchangeSortFlag || null, | ||
54 | + }; | ||
55 | +} |
@@ -5,17 +5,19 @@ import { JSONEditor } from '/@/components/CodeEditor'; | @@ -5,17 +5,19 @@ import { JSONEditor } from '/@/components/CodeEditor'; | ||
5 | import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | 5 | import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; |
6 | import { h } from 'vue'; | 6 | import { h } from 'vue'; |
7 | import { TaskTypeEnum } from '/@/views/task/center/config'; | 7 | import { TaskTypeEnum } from '/@/views/task/center/config'; |
8 | -import { AddressTypeEnum } from '/@/views/task/center/components/PollCommandInput'; | ||
9 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | 8 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; |
10 | import { createImgPreview } from '/@/components/Preview'; | 9 | import { createImgPreview } from '/@/components/Preview'; |
11 | import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter'; | 10 | import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter'; |
12 | import LockControlGroup from '/@/components/Form/src/components/LockControlGroup.vue'; | 11 | import LockControlGroup from '/@/components/Form/src/components/LockControlGroup.vue'; |
13 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; | 12 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; |
14 | -import { TransportTypeEnum } from '/@/enums/deviceEnum'; | 13 | +import { TCPProtocolTypeEnum, TransportTypeEnum } from '/@/enums/deviceEnum'; |
14 | +import { HexInput, InputTypeEnum } from '../../profiles/components/ObjectModelForm/HexInput'; | ||
15 | +import { DeviceProfileDetail } from '/@/api/device/model/deviceConfigModel'; | ||
15 | 16 | ||
16 | useComponentRegister('JSONEditor', JSONEditor); | 17 | useComponentRegister('JSONEditor', JSONEditor); |
17 | useComponentRegister('LockControlGroup', LockControlGroup); | 18 | useComponentRegister('LockControlGroup', LockControlGroup); |
18 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); | 19 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); |
20 | +useComponentRegister('HexInput', HexInput); | ||
19 | 21 | ||
20 | export enum TypeEnum { | 22 | export enum TypeEnum { |
21 | IS_GATEWAY = 'GATEWAY', | 23 | IS_GATEWAY = 'GATEWAY', |
@@ -108,6 +110,12 @@ export const step1Schemas: FormSchema[] = [ | @@ -108,6 +110,12 @@ export const step1Schemas: FormSchema[] = [ | ||
108 | show: false, | 110 | show: false, |
109 | }, | 111 | }, |
110 | { | 112 | { |
113 | + field: 'tcpDeviceProtocol', | ||
114 | + label: 'TCP设备协议类型', | ||
115 | + component: 'Input', | ||
116 | + show: false, | ||
117 | + }, | ||
118 | + { | ||
111 | field: 'profileId', | 119 | field: 'profileId', |
112 | label: '所属产品', | 120 | label: '所属产品', |
113 | required: true, | 121 | required: true, |
@@ -127,30 +135,35 @@ export const step1Schemas: FormSchema[] = [ | @@ -127,30 +135,35 @@ export const step1Schemas: FormSchema[] = [ | ||
127 | const options = await queryDeviceProfileBy({ | 135 | const options = await queryDeviceProfileBy({ |
128 | deviceType: formModel?.isUpdate ? formModel?.deviceType : null, | 136 | deviceType: formModel?.isUpdate ? formModel?.deviceType : null, |
129 | }); | 137 | }); |
130 | - const { profileId } = formModel; | ||
131 | - if (profileId) { | ||
132 | - const selectRecord = options.find((item) => item.tbProfileId === profileId); | ||
133 | - selectRecord && setFieldsValue({ transportType: selectRecord!.transportType }); | ||
134 | - } | 138 | + |
135 | return options; | 139 | return options; |
136 | }, | 140 | }, |
137 | labelField: 'name', | 141 | labelField: 'name', |
138 | valueField: 'tbProfileId', | 142 | valueField: 'tbProfileId', |
139 | - onChange( | ||
140 | - _value: string, | ||
141 | - option: { deviceType: string; transportType: string; id: string } | ||
142 | - ) { | 143 | + onChange(_value: string, option: DeviceProfileDetail) { |
143 | const { deviceType, transportType, id } = option; | 144 | const { deviceType, transportType, id } = option; |
144 | setFieldsValue({ | 145 | setFieldsValue({ |
145 | deviceType: deviceType, | 146 | deviceType: deviceType, |
146 | transportType, | 147 | transportType, |
147 | deviceProfileId: id, | 148 | deviceProfileId: id, |
148 | gatewayId: null, | 149 | gatewayId: null, |
149 | - codeType: transportType === TransportTypeEnum.TCP ? TaskTypeEnum.MODBUS_RTU : null, | ||
150 | code: null, | 150 | code: null, |
151 | addressCode: null, | 151 | addressCode: null, |
152 | + tcpDeviceProtocol: option?.profileData?.transportConfiguration?.protocol, | ||
152 | }); | 153 | }); |
153 | }, | 154 | }, |
155 | + onOptionsChange(options: (DeviceProfileDetail & Record<'value', string>)[]) { | ||
156 | + const { profileId } = formModel; | ||
157 | + if (profileId) { | ||
158 | + const selectRecord = options.find((item) => item.value === profileId); | ||
159 | + | ||
160 | + selectRecord && | ||
161 | + setFieldsValue({ | ||
162 | + transportType: selectRecord!.transportType, | ||
163 | + tcpDeviceProtocol: selectRecord?.profileData?.transportConfiguration?.protocol, | ||
164 | + }); | ||
165 | + } | ||
166 | + }, | ||
154 | showSearch: true, | 167 | showSearch: true, |
155 | placeholder: '请选择产品', | 168 | placeholder: '请选择产品', |
156 | filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => | 169 | filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => |
@@ -177,35 +190,6 @@ export const step1Schemas: FormSchema[] = [ | @@ -177,35 +190,6 @@ export const step1Schemas: FormSchema[] = [ | ||
177 | }, | 190 | }, |
178 | }, | 191 | }, |
179 | { | 192 | { |
180 | - field: 'codeType', | ||
181 | - label: '标识符类型', | ||
182 | - component: 'RadioGroup', | ||
183 | - dynamicRules({ values }) { | ||
184 | - return [ | ||
185 | - { | ||
186 | - required: values?.transportType === TransportTypeEnum.TCP, | ||
187 | - message: '请输入设备标识符', | ||
188 | - }, | ||
189 | - ]; | ||
190 | - }, | ||
191 | - // ifShow: ({ values }) => | ||
192 | - // values?.transportType === TransportTypeEnum.TCP && | ||
193 | - // (values.deviceType === DeviceTypeEnum.SENSOR || values.deviceType === DeviceTypeEnum.GATEWAY), | ||
194 | - ifShow: ({ values }) => values?.transportType === TransportTypeEnum.TCP, | ||
195 | - componentProps: ({ formActionType }) => { | ||
196 | - const { setFieldsValue } = formActionType; | ||
197 | - return { | ||
198 | - options: [ | ||
199 | - { label: '自定义', value: TaskTypeEnum.CUSTOM }, | ||
200 | - { label: 'ModBus', value: TaskTypeEnum.MODBUS_RTU }, | ||
201 | - ], | ||
202 | - onChange() { | ||
203 | - setFieldsValue({ addressCode: null }); | ||
204 | - }, | ||
205 | - }; | ||
206 | - }, | ||
207 | - }, | ||
208 | - { | ||
209 | field: 'addressCode', | 193 | field: 'addressCode', |
210 | label: '地址码', | 194 | label: '地址码', |
211 | dynamicRules({ values }) { | 195 | dynamicRules({ values }) { |
@@ -213,32 +197,25 @@ export const step1Schemas: FormSchema[] = [ | @@ -213,32 +197,25 @@ export const step1Schemas: FormSchema[] = [ | ||
213 | { | 197 | { |
214 | required: | 198 | required: |
215 | values?.transportType === TransportTypeEnum.TCP && | 199 | values?.transportType === TransportTypeEnum.TCP && |
216 | - values?.deviceType === DeviceTypeEnum.SENSOR, | ||
217 | - message: '请输入设备地址码', | 200 | + values?.tcpDeviceProtocol === TCPProtocolTypeEnum.MODBUS_RTU, |
201 | + message: '地址码范围为00~FF', | ||
202 | + pattern: /^[0-9A-Fa-f]{2}$/, | ||
218 | }, | 203 | }, |
219 | ]; | 204 | ]; |
220 | }, | 205 | }, |
221 | - component: 'RegisterAddressInput', | 206 | + helpMessage: ['地址码范围为00~FF'], |
207 | + component: 'HexInput', | ||
222 | changeEvent: 'update:value', | 208 | changeEvent: 'update:value', |
223 | valueField: 'value', | 209 | valueField: 'value', |
224 | componentProps: { | 210 | componentProps: { |
225 | - type: AddressTypeEnum.DEC, | ||
226 | - maxValue: 247, | ||
227 | - minValue: 0, | ||
228 | - disabledSwitch: true, | ||
229 | - }, | ||
230 | - // ifShow: ({ values }) => { | ||
231 | - // return ( | ||
232 | - // values?.transportType === TransportTypeEnum.TCP && | ||
233 | - // (values.deviceType === DeviceTypeEnum.SENSOR || | ||
234 | - // values.deviceType === DeviceTypeEnum.GATEWAY) && | ||
235 | - // values?.codeType === TaskTypeEnum.MODBUS_RTU | ||
236 | - // ); | ||
237 | - // }, | 211 | + type: InputTypeEnum.HEX, |
212 | + maxValue: parseInt('FF', 16), | ||
213 | + placeholder: '请输入寄存器地址', | ||
214 | + }, | ||
238 | ifShow: ({ values }) => { | 215 | ifShow: ({ values }) => { |
239 | return ( | 216 | return ( |
240 | values?.transportType === TransportTypeEnum.TCP && | 217 | values?.transportType === TransportTypeEnum.TCP && |
241 | - values?.codeType === TaskTypeEnum.MODBUS_RTU | 218 | + values?.tcpDeviceProtocol === TCPProtocolTypeEnum.MODBUS_RTU |
242 | ); | 219 | ); |
243 | }, | 220 | }, |
244 | }, | 221 | }, |
@@ -264,7 +241,8 @@ export const step1Schemas: FormSchema[] = [ | @@ -264,7 +241,8 @@ export const step1Schemas: FormSchema[] = [ | ||
264 | }, | 241 | }, |
265 | ifShow: ({ values }) => { | 242 | ifShow: ({ values }) => { |
266 | return ( | 243 | return ( |
267 | - values?.transportType === TransportTypeEnum.TCP && values?.codeType === TaskTypeEnum.CUSTOM | 244 | + values?.transportType === TransportTypeEnum.TCP && |
245 | + values?.tcpDeviceProtocol === TaskTypeEnum.CUSTOM | ||
268 | ); | 246 | ); |
269 | }, | 247 | }, |
270 | }, | 248 | }, |
@@ -88,12 +88,12 @@ | @@ -88,12 +88,12 @@ | ||
88 | import { validatorLongitude, validatorLatitude } from '/@/utils/rules'; | 88 | import { validatorLongitude, validatorLatitude } from '/@/utils/rules'; |
89 | import { getOrganizationList } from '/@/api/system/system'; | 89 | import { getOrganizationList } from '/@/api/system/system'; |
90 | import { copyTransFun } from '/@/utils/fnUtils'; | 90 | import { copyTransFun } from '/@/utils/fnUtils'; |
91 | - import { TaskTypeEnum } from '/@/views/task/center/config'; | ||
92 | import { toRaw } from 'vue'; | 91 | import { toRaw } from 'vue'; |
93 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | 92 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; |
94 | import { buildUUID } from '/@/utils/uuid'; | 93 | import { buildUUID } from '/@/utils/uuid'; |
95 | import { useMessage } from '/@/hooks/web/useMessage'; | 94 | import { useMessage } from '/@/hooks/web/useMessage'; |
96 | import { computed } from 'vue'; | 95 | import { computed } from 'vue'; |
96 | + import { TCPProtocolTypeEnum } from '/@/enums/deviceEnum'; | ||
97 | 97 | ||
98 | export default defineComponent({ | 98 | export default defineComponent({ |
99 | components: { | 99 | components: { |
@@ -405,10 +405,11 @@ | @@ -405,10 +405,11 @@ | ||
405 | icon: [{ uid: buildUUID(), name: 'name', url: deviceInfo.avatar } as FileItem], | 405 | icon: [{ uid: buildUUID(), name: 'name', url: deviceInfo.avatar } as FileItem], |
406 | }); | 406 | }); |
407 | } | 407 | } |
408 | + | ||
408 | setFieldsValue({ | 409 | setFieldsValue({ |
409 | ...data, | 410 | ...data, |
410 | code: data?.code, | 411 | code: data?.code, |
411 | - addressCode: parseInt(data?.code || '', 16), | 412 | + addressCode: data?.code, |
412 | isUpdate: unref(isUpdate1), | 413 | isUpdate: unref(isUpdate1), |
413 | }); | 414 | }); |
414 | } | 415 | } |
@@ -424,9 +425,9 @@ | @@ -424,9 +425,9 @@ | ||
424 | ...(value?.code || value?.addressCode | 425 | ...(value?.code || value?.addressCode |
425 | ? { | 426 | ? { |
426 | code: | 427 | code: |
427 | - value?.codeType === TaskTypeEnum.CUSTOM | 428 | + value?.tcpDeviceProtocol === TCPProtocolTypeEnum.CUSTOM |
428 | ? value?.code | 429 | ? value?.code |
429 | - : (value?.addressCode || '').toString(16).padStart(2, '0').toUpperCase(), | 430 | + : value?.addressCode || '', |
430 | } | 431 | } |
431 | : {}), | 432 | : {}), |
432 | }; | 433 | }; |
@@ -10,7 +10,9 @@ import { | @@ -10,7 +10,9 @@ import { | ||
10 | ServiceCallTypeEnum, | 10 | ServiceCallTypeEnum, |
11 | CommandDeliveryWayEnum, | 11 | CommandDeliveryWayEnum, |
12 | CommandDeliveryWayNameEnum, | 12 | CommandDeliveryWayNameEnum, |
13 | + TCPProtocolTypeEnum, | ||
13 | } from '/@/enums/deviceEnum'; | 14 | } from '/@/enums/deviceEnum'; |
15 | +import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | ||
14 | 16 | ||
15 | export interface CommandDeliveryFormFieldType { | 17 | export interface CommandDeliveryFormFieldType { |
16 | [CommandFieldsEnum.COMMAND_TYPE]: CommandTypeEnum; | 18 | [CommandFieldsEnum.COMMAND_TYPE]: CommandTypeEnum; |
@@ -36,10 +38,16 @@ export enum CommandFieldsEnum { | @@ -36,10 +38,16 @@ export enum CommandFieldsEnum { | ||
36 | 38 | ||
37 | useComponentRegister('JSONEditor', JSONEditor); | 39 | useComponentRegister('JSONEditor', JSONEditor); |
38 | 40 | ||
39 | -export const CommandSchemas = ( | ||
40 | - transportType: TransportTypeEnum, | ||
41 | - deviceProfileId: string | ||
42 | -): FormSchema[] => { | 41 | +export const CommandSchemas = (deviceRecord: DeviceRecord): FormSchema[] => { |
42 | + const { transportType, deviceProfileId, deviceType } = deviceRecord; | ||
43 | + | ||
44 | + const isTCPTransport = transportType === TransportTypeEnum.TCP; | ||
45 | + | ||
46 | + const isTCPModbus = | ||
47 | + isTCPTransport && | ||
48 | + deviceRecord.deviceProfile?.profileData?.transportConfiguration?.protocol === | ||
49 | + TCPProtocolTypeEnum.MODBUS_RTU; | ||
50 | + | ||
43 | return [ | 51 | return [ |
44 | { | 52 | { |
45 | field: CommandFieldsEnum.COMMAND_TYPE, | 53 | field: CommandFieldsEnum.COMMAND_TYPE, |
@@ -49,11 +57,19 @@ export const CommandSchemas = ( | @@ -49,11 +57,19 @@ export const CommandSchemas = ( | ||
49 | required: true, | 57 | required: true, |
50 | componentProps: ({ formActionType }) => { | 58 | componentProps: ({ formActionType }) => { |
51 | const { setFieldsValue } = formActionType; | 59 | const { setFieldsValue } = formActionType; |
60 | + | ||
61 | + const getOptions = () => { | ||
62 | + const options = [{ label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM }]; | ||
63 | + | ||
64 | + if (isTCPModbus || (isTCPTransport && deviceType === DeviceTypeEnum.SENSOR)) | ||
65 | + return options; | ||
66 | + | ||
67 | + options.push({ label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE }); | ||
68 | + | ||
69 | + return options; | ||
70 | + }; | ||
52 | return { | 71 | return { |
53 | - options: [ | ||
54 | - { label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM }, | ||
55 | - { label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE }, | ||
56 | - ], | 72 | + options: getOptions(), |
57 | onChange() { | 73 | onChange() { |
58 | setFieldsValue({ | 74 | setFieldsValue({ |
59 | [CommandFieldsEnum.SERVICE]: null, | 75 | [CommandFieldsEnum.SERVICE]: null, |
@@ -37,14 +37,15 @@ | @@ -37,14 +37,15 @@ | ||
37 | defineEmits(['register']); | 37 | defineEmits(['register']); |
38 | 38 | ||
39 | const thingsModelFormRef = ref<InstanceType<typeof ThingsModelForm>>(); | 39 | const thingsModelFormRef = ref<InstanceType<typeof ThingsModelForm>>(); |
40 | - const deviceDetail = ref<DeviceRecord>(); | 40 | + const props = defineProps<{ |
41 | + deviceDetail: DeviceRecord; | ||
42 | + }>(); | ||
41 | 43 | ||
42 | const [registerModal, { setModalProps }] = useModalInner( | 44 | const [registerModal, { setModalProps }] = useModalInner( |
43 | (params: ModalParamsType<DeviceRecord>) => { | 45 | (params: ModalParamsType<DeviceRecord>) => { |
44 | const { record } = params; | 46 | const { record } = params; |
45 | - deviceDetail.value = record; | ||
46 | setProps({ | 47 | setProps({ |
47 | - schemas: CommandSchemas(record.transportType as TransportTypeEnum, record.deviceProfileId), | 48 | + schemas: CommandSchemas(record), |
48 | }); | 49 | }); |
49 | } | 50 | } |
50 | ); | 51 | ); |
@@ -72,7 +73,7 @@ | @@ -72,7 +73,7 @@ | ||
72 | }; | 73 | }; |
73 | 74 | ||
74 | const handleValidateDeviceActive = async (): Promise<boolean> => { | 75 | const handleValidateDeviceActive = async (): Promise<boolean> => { |
75 | - const result = await getDeviceActiveTime(unref(deviceDetail)!.tbDeviceId); | 76 | + const result = await getDeviceActiveTime(unref(props.deviceDetail)!.tbDeviceId); |
76 | const [firstItem] = result; | 77 | const [firstItem] = result; |
77 | return !!firstItem.value; | 78 | return !!firstItem.value; |
78 | }; | 79 | }; |
@@ -83,7 +84,7 @@ | @@ -83,7 +84,7 @@ | ||
83 | ) => { | 84 | ) => { |
84 | const { commandType, service } = values; | 85 | const { commandType, service } = values; |
85 | 86 | ||
86 | - const isTcpDevice = unref(deviceDetail)?.transportType === TransportTypeEnum.TCP; | 87 | + const isTcpDevice = unref(props.deviceDetail)?.transportType === TransportTypeEnum.TCP; |
87 | if (commandType === CommandTypeEnum.CUSTOM) { | 88 | if (commandType === CommandTypeEnum.CUSTOM) { |
88 | if (isTcpDevice) { | 89 | if (isTcpDevice) { |
89 | return values.tcpCommandValue; | 90 | return values.tcpCommandValue; |
@@ -124,7 +125,7 @@ | @@ -124,7 +125,7 @@ | ||
124 | params: handleCommandParams(values, serviceCommand), | 125 | params: handleCommandParams(values, serviceCommand), |
125 | }; | 126 | }; |
126 | 127 | ||
127 | - await commandIssuanceApi(callType, unref(deviceDetail)!.tbDeviceId, rpcCommands); | 128 | + await commandIssuanceApi(callType, unref(props.deviceDetail)!.tbDeviceId, rpcCommands); |
128 | 129 | ||
129 | createMessage.success('命令下发成功'); | 130 | createMessage.success('命令下发成功'); |
130 | } finally { | 131 | } finally { |
1 | -import { DeviceModelOfMatterAttrs } from '/@/api/device/model/deviceModel'; | 1 | +import { DeviceModelOfMatterAttrs, DeviceRecord } from '/@/api/device/model/deviceModel'; |
2 | import { DataType, Specs } from '/@/api/device/model/modelOfMatterModel'; | 2 | import { DataType, Specs } from '/@/api/device/model/modelOfMatterModel'; |
3 | +import { TCPProtocolTypeEnum, TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
3 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 4 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
4 | import { isArray } from '/@/utils/is'; | 5 | import { isArray } from '/@/utils/is'; |
5 | 6 | ||
@@ -31,18 +32,26 @@ export interface SocketInfoDataSourceItemType extends BaseAdditionalInfo { | @@ -31,18 +32,26 @@ export interface SocketInfoDataSourceItemType extends BaseAdditionalInfo { | ||
31 | } | 32 | } |
32 | 33 | ||
33 | export function buildTableDataSourceByObjectModel( | 34 | export function buildTableDataSourceByObjectModel( |
34 | - models: DeviceModelOfMatterAttrs[] | 35 | + models: DeviceModelOfMatterAttrs[], |
36 | + deviceDetail: DeviceRecord | ||
35 | ): SocketInfoDataSourceItemType[] { | 37 | ): SocketInfoDataSourceItemType[] { |
38 | + const isTCPTransportType = deviceDetail.transportType === TransportTypeEnum.TCP; | ||
39 | + | ||
40 | + const isModbusDevice = | ||
41 | + isTCPTransportType && | ||
42 | + deviceDetail?.deviceProfile?.profileData?.transportConfiguration?.protocol === | ||
43 | + TCPProtocolTypeEnum.MODBUS_RTU; | ||
44 | + | ||
36 | function getAdditionalInfoByDataType(dataType?: DataType) { | 45 | function getAdditionalInfoByDataType(dataType?: DataType) { |
37 | const { specs, specsList, type } = dataType || {}; | 46 | const { specs, specsList, type } = dataType || {}; |
38 | if (isArray(specs)) return {}; | 47 | if (isArray(specs)) return {}; |
39 | const { unit, boolClose, boolOpen, unitName } = (specs as Partial<Specs>) || {}; | 48 | const { unit, boolClose, boolOpen, unitName } = (specs as Partial<Specs>) || {}; |
40 | const result = { unit, boolClose, boolOpen, unitName }; | 49 | const result = { unit, boolClose, boolOpen, unitName }; |
41 | - if (type == DataTypeEnum.ENUM && specsList && specsList.length) { | 50 | + if ((type == DataTypeEnum.ENUM && specsList && specsList.length) || isModbusDevice) { |
42 | Reflect.set( | 51 | Reflect.set( |
43 | result, | 52 | result, |
44 | 'enum', | 53 | 'enum', |
45 | - specsList.reduce((prev, next) => ({ ...prev, [next.value!]: next.name }), {}) | 54 | + (specsList || []).reduce((prev, next) => ({ ...prev, [next.value!]: next.name }), {}) |
46 | ); | 55 | ); |
47 | } | 56 | } |
48 | 57 |
@@ -19,7 +19,7 @@ | @@ -19,7 +19,7 @@ | ||
19 | import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget'; | 19 | import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget'; |
20 | import { toRaw } from 'vue'; | 20 | import { toRaw } from 'vue'; |
21 | import { DataActionModeEnum } from '/@/enums/toolEnum'; | 21 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
22 | - import { ReadAndWriteEnum } from '/@/enums/deviceEnum'; | 22 | + import { ReadAndWriteEnum, TCPProtocolTypeEnum, TransportTypeEnum } from '/@/enums/deviceEnum'; |
23 | import { ObjectModelCommandDeliveryModal } from './ObjectModelCommandDeliveryModal'; | 23 | import { ObjectModelCommandDeliveryModal } from './ObjectModelCommandDeliveryModal'; |
24 | import { ModalParamsType } from '/#/utils'; | 24 | import { ModalParamsType } from '/#/utils'; |
25 | import { AreaChartOutlined } from '@ant-design/icons-vue'; | 25 | import { AreaChartOutlined } from '@ant-design/icons-vue'; |
@@ -275,17 +275,30 @@ | @@ -275,17 +275,30 @@ | ||
275 | openModal(true); | 275 | openModal(true); |
276 | }; | 276 | }; |
277 | 277 | ||
278 | + const getIsModbusDevice = computed( | ||
279 | + () => | ||
280 | + props.deviceDetail.transportType === TransportTypeEnum.TCP && | ||
281 | + props.deviceDetail?.deviceProfile?.profileData?.transportConfiguration?.protocol === | ||
282 | + TCPProtocolTypeEnum.MODBUS_RTU | ||
283 | + ); | ||
284 | + | ||
278 | onMounted(async () => { | 285 | onMounted(async () => { |
279 | const { deviceProfileId } = props.deviceDetail; | 286 | const { deviceProfileId } = props.deviceDetail; |
280 | const value = await getDeviceAttrs({ deviceProfileId }); | 287 | const value = await getDeviceAttrs({ deviceProfileId }); |
281 | socketInfo.attrKeys = isArray(value) ? value.map((item) => item.identifier) : []; | 288 | socketInfo.attrKeys = isArray(value) ? value.map((item) => item.identifier) : []; |
282 | - socketInfo.rawDataSource = buildTableDataSourceByObjectModel(value); | 289 | + socketInfo.rawDataSource = buildTableDataSourceByObjectModel(value, props.deviceDetail); |
283 | setDataSource(); | 290 | setDataSource(); |
284 | open(); | 291 | open(); |
285 | }); | 292 | }); |
286 | 293 | ||
287 | const formatValue = (item: SocketInfoDataSourceItemType) => { | 294 | const formatValue = (item: SocketInfoDataSourceItemType) => { |
288 | - if (isNullOrUnDef(item)) return '--'; | 295 | + if (isNullOrUnDef(item) || isNullOrUnDef(item.value)) return '--'; |
296 | + | ||
297 | + if (unref(getIsModbusDevice) && item.type === DataTypeEnum.BOOL) { | ||
298 | + const _result = Reflect.get(item.enum || {}, item.value as string); | ||
299 | + return isNullOrUnDef(_result) ? item.value : _result; | ||
300 | + } | ||
301 | + | ||
289 | switch (item.type) { | 302 | switch (item.type) { |
290 | case DataTypeEnum.BOOL: | 303 | case DataTypeEnum.BOOL: |
291 | return !!Number(item.value) ? item.boolOpen : item.boolClose; | 304 | return !!Number(item.value) ? item.boolOpen : item.boolClose; |
@@ -300,7 +313,7 @@ | @@ -300,7 +313,7 @@ | ||
300 | const handleSendCommandModal = (data: SocketInfoDataSourceItemType) => { | 313 | const handleSendCommandModal = (data: SocketInfoDataSourceItemType) => { |
301 | openSendCommandModal(true, { | 314 | openSendCommandModal(true, { |
302 | mode: DataActionModeEnum.READ, | 315 | mode: DataActionModeEnum.READ, |
303 | - record: { ...toRaw(data.detail), deviceDetail: props.deviceDetail as any }, | 316 | + record: { objectModel: toRaw(unref(data.detail)), deviceDetail: props.deviceDetail as any }, |
304 | } as ModalParamsType); | 317 | } as ModalParamsType); |
305 | }; | 318 | }; |
306 | 319 |
src/views/device/list/cpns/tabs/ObjectModelCommandDeliveryModal/config.ts
deleted
100644 → 0
1 | -import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
2 | -import { FormSchema } from '/@/components/Form'; | ||
3 | -import { validateTCPCustomCommand } from '/@/components/Form/src/components/ThingsModelForm'; | ||
4 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
5 | - | ||
6 | -const InsertString = (t, c, n) => { | ||
7 | - const r: string | number[] = []; | ||
8 | - | ||
9 | - for (let i = 0; i * 2 < t.length; i++) { | ||
10 | - r.push(t.substr(i * 2, n)); | ||
11 | - } | ||
12 | - return r.join(c); | ||
13 | -}; | ||
14 | -const FillString = (t, c, n, b) => { | ||
15 | - if (t == '' || c.length != 1 || n <= t.length) { | ||
16 | - return t; | ||
17 | - } | ||
18 | - const l = t.length; | ||
19 | - | ||
20 | - for (let i = 0; i < n - l; i++) { | ||
21 | - if (b == true) { | ||
22 | - t = c + t; | ||
23 | - } else { | ||
24 | - t += c; | ||
25 | - } | ||
26 | - } | ||
27 | - return t; | ||
28 | -}; | ||
29 | -const SingleToHex = (t) => { | ||
30 | - if (t == '') { | ||
31 | - return ''; | ||
32 | - } | ||
33 | - t = parseFloat(t); | ||
34 | - | ||
35 | - if (isNaN(t) == true) { | ||
36 | - return 'Error'; | ||
37 | - } | ||
38 | - if (t == 0) { | ||
39 | - return '00000000'; | ||
40 | - } | ||
41 | - let s, e, m; | ||
42 | - | ||
43 | - if (t > 0) { | ||
44 | - s = 0; | ||
45 | - } else { | ||
46 | - s = 1; | ||
47 | - | ||
48 | - t = 0 - t; | ||
49 | - } | ||
50 | - m = t.toString(2); | ||
51 | - | ||
52 | - if (m >= 1) { | ||
53 | - if (m.indexOf('.') == -1) { | ||
54 | - m = m + '.0'; | ||
55 | - } | ||
56 | - e = m.indexOf('.') - 1; | ||
57 | - } else { | ||
58 | - e = 1 - m.indexOf('1'); | ||
59 | - } | ||
60 | - if (e >= 0) { | ||
61 | - m = m.replace('.', ''); | ||
62 | - } else { | ||
63 | - m = m.substring(m.indexOf('1')); | ||
64 | - } | ||
65 | - if (m.length > 24) { | ||
66 | - m = m.substr(0, 24); | ||
67 | - } else { | ||
68 | - m = FillString(m, '0', 24, false); | ||
69 | - } | ||
70 | - m = m.substring(1); | ||
71 | - | ||
72 | - e = (e + 127).toString(2); | ||
73 | - | ||
74 | - e = FillString(e, '0', 8, true); | ||
75 | - | ||
76 | - let r = parseInt(s + e + m, 2).toString(16); | ||
77 | - | ||
78 | - r = FillString(r, '0', 8, true); | ||
79 | - | ||
80 | - return InsertString(r, ' ', 2).toUpperCase(); | ||
81 | -}; | ||
82 | - | ||
83 | -const FormatHex = (t, n, ie) => { | ||
84 | - const r: string[] = []; | ||
85 | - | ||
86 | - let s = ''; | ||
87 | - | ||
88 | - let c = 0; | ||
89 | - | ||
90 | - for (let i = 0; i < t.length; i++) { | ||
91 | - if (t.charAt(i) != ' ') { | ||
92 | - s += t.charAt(i); | ||
93 | - | ||
94 | - c += 1; | ||
95 | - | ||
96 | - if (c == n) { | ||
97 | - r.push(s); | ||
98 | - | ||
99 | - s = ''; | ||
100 | - | ||
101 | - c = 0; | ||
102 | - } | ||
103 | - } | ||
104 | - if (ie == false) { | ||
105 | - if (i == t.length - 1 && s != '') { | ||
106 | - r.push(s); | ||
107 | - } | ||
108 | - } | ||
109 | - } | ||
110 | - return r.join('\n'); | ||
111 | -}; | ||
112 | -const FormatHexBatch = (t, n, ie) => { | ||
113 | - const a = t.split('\n'); | ||
114 | - | ||
115 | - const r: string[] = []; | ||
116 | - | ||
117 | - for (let i = 0; i < a.length; i++) { | ||
118 | - r[i] = FormatHex(a[i], n, ie); | ||
119 | - } | ||
120 | - return r.join('\n'); | ||
121 | -}; | ||
122 | -const SingleToHexBatch = (t) => { | ||
123 | - const a = t.split('\n'); | ||
124 | - | ||
125 | - const r: string[] = []; | ||
126 | - | ||
127 | - for (let i = 0; i < a.length; i++) { | ||
128 | - r[i] = SingleToHex(a[i]); | ||
129 | - } | ||
130 | - return r.join('\r\n'); | ||
131 | -}; | ||
132 | - | ||
133 | -const formSchemasConfig = (schemas: StructJSON, actionType: string): FormSchema[] => { | ||
134 | - const { identifier, functionName, dataType } = schemas; | ||
135 | - | ||
136 | - if (dataType?.type === DataTypeEnum.STRING) { | ||
137 | - return [ | ||
138 | - { | ||
139 | - field: identifier, | ||
140 | - label: functionName!, | ||
141 | - component: 'Input', | ||
142 | - rules: [{ required: true, validator: validateTCPCustomCommand }], | ||
143 | - componentProps: { | ||
144 | - placeholder: `请输入${functionName}`, | ||
145 | - }, | ||
146 | - }, | ||
147 | - ]; | ||
148 | - } | ||
149 | - | ||
150 | - if (actionType == '06') { | ||
151 | - return [ | ||
152 | - { | ||
153 | - field: identifier, | ||
154 | - label: functionName!, | ||
155 | - component: 'InputNumber', | ||
156 | - rules: [{ required: true, message: '请输入正数' }], | ||
157 | - componentProps: { | ||
158 | - min: 0, | ||
159 | - precision: 2, | ||
160 | - placeholder: `请输入正数`, | ||
161 | - }, | ||
162 | - }, | ||
163 | - ]; | ||
164 | - } else if (actionType == '05') { | ||
165 | - return [ | ||
166 | - { | ||
167 | - field: identifier, | ||
168 | - label: functionName!, | ||
169 | - component: 'InputNumber', | ||
170 | - rules: [{ required: true, message: '请输入值' }], | ||
171 | - componentProps: { | ||
172 | - min: 0, | ||
173 | - max: 1, | ||
174 | - precision: 0, | ||
175 | - placeholder: `请输入0或1`, | ||
176 | - }, | ||
177 | - }, | ||
178 | - ]; | ||
179 | - } else { | ||
180 | - return [ | ||
181 | - { | ||
182 | - field: identifier, | ||
183 | - label: functionName!, | ||
184 | - component: 'InputNumber', | ||
185 | - rules: [{ required: true, message: '请输入值' }], | ||
186 | - componentProps: { | ||
187 | - placeholder: `请输入数字`, | ||
188 | - precision: 2, | ||
189 | - }, | ||
190 | - }, | ||
191 | - ]; | ||
192 | - } | ||
193 | -}; | ||
194 | - | ||
195 | -export { | ||
196 | - InsertString, | ||
197 | - FillString, | ||
198 | - SingleToHex, | ||
199 | - FormatHex, | ||
200 | - FormatHexBatch, | ||
201 | - SingleToHexBatch, | ||
202 | - formSchemasConfig, | ||
203 | -}; |
1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
2 | - import { ref } from 'vue'; | ||
3 | - import { useGenDynamicForm } from './useGenDynamicForm'; | 2 | + import { ref, unref } from 'vue'; |
3 | + import { useGenerateFormSchemasByObjectModel } from './useGenerateFormSchemasByObjectModel'; | ||
4 | import { ModalParamsType } from '/#/utils'; | 4 | import { ModalParamsType } from '/#/utils'; |
5 | - import { DeviceModelOfMatterAttrs } from '/@/api/device/model/deviceModel'; | ||
6 | - import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | 5 | + import { DeviceModelOfMatterAttrs, DeviceRecord } from '/@/api/device/model/deviceModel'; |
7 | import { BasicForm, useForm } from '/@/components/Form'; | 6 | import { BasicForm, useForm } from '/@/components/Form'; |
8 | import { BasicModal, useModalInner } from '/@/components/Modal'; | 7 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
9 | - import { sendCommandOneway } from '/@/api/dataBoard'; | 8 | + import { CommandTypeEnum } from '/@/enums/deviceEnum'; |
9 | + import { DataTypeEnum, FunctionTypeEnum } from '/@/enums/objectModelEnum'; | ||
10 | + import { useCommandDelivery } from '/@/hooks/business/useCommandDelivery'; | ||
10 | import { useMessage } from '/@/hooks/web/useMessage'; | 11 | import { useMessage } from '/@/hooks/web/useMessage'; |
11 | - import { unref } from 'vue'; | ||
12 | - import { genModbusCommand } from '/@/api/task'; | ||
13 | - import { TaskTypeEnum } from '/@/views/task/center/config'; | ||
14 | - import { SingleToHex, formSchemasConfig } from './config'; | ||
15 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
16 | - import { TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
17 | 12 | ||
18 | defineEmits(['register']); | 13 | defineEmits(['register']); |
19 | const props = defineProps<{ deviceId: string; deviceName: string }>(); | 14 | const props = defineProps<{ deviceId: string; deviceName: string }>(); |
@@ -24,110 +19,25 @@ | @@ -24,110 +19,25 @@ | ||
24 | layout: 'vertical', | 19 | layout: 'vertical', |
25 | }); | 20 | }); |
26 | 21 | ||
27 | - const { genForm, transformValue } = useGenDynamicForm(); | ||
28 | - | ||
29 | - const keys = ref<string[]>([]); | ||
30 | - | ||
31 | - const modBUSForm = ref<any>({}); | ||
32 | - const isShowModBUS = ref<Boolean>(false); //用于判断标识符类型是否时自定义还是modBUS | ||
33 | - const isShowActionType = ref<Boolean>(true); //判断设备属性标识符为modBus时没有填写扩展描述 | ||
34 | - const formField = ref(''); //存一个表单取值的field | ||
35 | - const zoomFactorValue = ref<number>(1); //缩放因子 | ||
36 | - const isShowMultiply = ref<Boolean>(false); // 只有tcp --> int和double类型才相乘缩放因子 | ||
37 | - const deviceTransportType = ref<string>(); | ||
38 | - const objectDataType = ref<DataTypeEnum>(); | ||
39 | - | ||
40 | - const [register] = useModalInner(async (params: ModalParamsType<DeviceModelOfMatterAttrs>) => { | ||
41 | - const { record } = params; | ||
42 | - const { name, detail, identifier, deviceDetail, extensionDesc } = record; | ||
43 | - const { dataType } = detail; | ||
44 | - const { type } = dataType || {}; | ||
45 | - const { codeType, deviceProfile, code } = deviceDetail || {}; | ||
46 | - const { transportType } = deviceProfile || {}; | ||
47 | - const { registerAddress, actionType, zoomFactor } = extensionDesc || {}; //获取扩展描述内容 | ||
48 | - formField.value = identifier; | ||
49 | - zoomFactorValue.value = zoomFactor ? Number(zoomFactor) : 1; | ||
50 | - isShowMultiply.value = type == 'INT' || type == 'DOUBLE' ? true : false; | ||
51 | - deviceTransportType.value = transportType; | ||
52 | - objectDataType.value = type; | ||
53 | - | ||
54 | - let schemas = [{ dataType: dataType, identifier, functionName: name } as StructJSON]; | ||
55 | - | ||
56 | - if (type === DataTypeEnum.STRUCT) { | ||
57 | - schemas = dataType?.specs as StructJSON[]; | ||
58 | - } | ||
59 | - | ||
60 | - keys.value = schemas.map((item) => { | ||
61 | - return item.identifier!; | ||
62 | - }); | ||
63 | - isShowActionType.value = actionType ? true : false; //判断modBUS类型时 物模型是否填写扩展描述 | ||
64 | - | ||
65 | - //是modBUS类型的就用另外的表单 | ||
66 | - //判断是否是TCP ==> modBus的下发命令 | ||
67 | - if (codeType === TaskTypeEnum.MODBUS_RTU && transportType == TransportTypeEnum.TCP) { | ||
68 | - isShowModBUS.value = true; | ||
69 | - modBUSForm.value = { | ||
70 | - crc: 'CRC_16_LOWER', | ||
71 | - deviceCode: code, | ||
72 | - method: actionType == '16' ? '10' : actionType, | ||
73 | - registerAddress, | ||
74 | - registerNumber: 1, | ||
75 | - registerValues: [], | ||
76 | - }; | ||
77 | - setProps({ schemas: formSchemasConfig(schemas[0], actionType) }); | ||
78 | - } else { | ||
79 | - isShowModBUS.value = false; | ||
80 | - if (transportType === TransportTypeEnum.TCP) { | ||
81 | - setProps({ | ||
82 | - schemas: [ | ||
83 | - { | ||
84 | - field: 'command', | ||
85 | - label: name, | ||
86 | - component: 'Input', | ||
87 | - required: true, | ||
88 | - rules: [ | ||
89 | - { | ||
90 | - pattern: /^[\s0-9a-fA-F]+$/, | ||
91 | - required: true, | ||
92 | - message: '请输入ASCII或HEX服务命令(0~9/A~F)', | ||
93 | - }, | ||
94 | - ], | ||
95 | - componentProps: { | ||
96 | - placeholder: `请输入${name}`, | ||
97 | - }, | ||
98 | - }, | ||
99 | - ], | ||
100 | - }); | ||
101 | - } else { | ||
102 | - const formSchemas = genForm(schemas); | ||
103 | - setProps({ schemas: formSchemas }); | ||
104 | - } | ||
105 | - } | ||
106 | - | ||
107 | - resetFields(); | ||
108 | - }); | ||
109 | - | ||
110 | - const getArray = (values) => { | ||
111 | - const str = values.replace(/\s+/g, ''); | ||
112 | - const array: any = []; | ||
113 | - | ||
114 | - for (let i = 0; i < str.length; i += 4) { | ||
115 | - const chunk = parseInt(str.substring(i, i + 4), 16); | ||
116 | - array.push(chunk); | 22 | + const { getFormByObjectModel } = useGenerateFormSchemasByObjectModel(); |
23 | + | ||
24 | + const currentParams = ref<{ | ||
25 | + deviceDetail: DeviceRecord; | ||
26 | + objectModel: DeviceModelOfMatterAttrs; | ||
27 | + }>(); | ||
28 | + | ||
29 | + const [register] = useModalInner( | ||
30 | + async ( | ||
31 | + params: ModalParamsType<{ deviceDetail: DeviceRecord; objectModel: DeviceModelOfMatterAttrs }> | ||
32 | + ) => { | ||
33 | + const { record } = params; | ||
34 | + currentParams.value = record; | ||
35 | + const { objectModel, deviceDetail } = record; | ||
36 | + const schemas = getFormByObjectModel(objectModel, deviceDetail); | ||
37 | + setProps({ schemas }); | ||
38 | + resetFields(); | ||
117 | } | 39 | } |
118 | - return array; | ||
119 | - }; | ||
120 | - | ||
121 | - // 获取小数 | ||
122 | - const getFloatPart = (number: string | number) => { | ||
123 | - const isLessZero = Number(number) < 0; | ||
124 | - number = number.toString(); | ||
125 | - const floatPartStartIndex = number.indexOf('.'); | ||
126 | - const value = ~floatPartStartIndex | ||
127 | - ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}` | ||
128 | - : '0'; | ||
129 | - return Number(value); | ||
130 | - }; | 40 | + ); |
131 | 41 | ||
132 | const { createMessage } = useMessage(); | 42 | const { createMessage } = useMessage(); |
133 | const loading = ref(false); | 43 | const loading = ref(false); |
@@ -136,82 +46,29 @@ | @@ -136,82 +46,29 @@ | ||
136 | loading.value = true; | 46 | loading.value = true; |
137 | if (!props.deviceId) return; | 47 | if (!props.deviceId) return; |
138 | 48 | ||
139 | - const sendValue = ref({}); | ||
140 | - //判断tcp类型 标识符是自定义还是ModBus | ||
141 | - if (unref(objectDataType) === DataTypeEnum.STRING) { | ||
142 | - const flag = await validate(); | ||
143 | - if (!flag) return; | ||
144 | - const value = getFieldsValue()[unref(formField)]; | ||
145 | - sendValue.value = value; | ||
146 | - } else if (unref(isShowModBUS)) { | ||
147 | - if (!unref(isShowActionType)) { | ||
148 | - createMessage.warning('当前物模型扩展描述没有填写'); | ||
149 | - return; | ||
150 | - } | ||
151 | - const flag = await validate(); | ||
152 | - if (!flag) return; | ||
153 | - | ||
154 | - const oldValue = getFieldsValue()[unref(formField)]; | ||
155 | - modBUSForm.value.registerNumber = 1; | ||
156 | - modBUSForm.value.registerValues = [oldValue]; | ||
157 | - | ||
158 | - if (unref(isShowMultiply) && unref(modBUSForm).method == '06') { | ||
159 | - const newValue = | ||
160 | - Math.trunc(oldValue) * unref(zoomFactorValue) + | ||
161 | - getFloatPart(oldValue) * unref(zoomFactorValue); | ||
162 | - if (newValue % 1 != 0) { | ||
163 | - createMessage.warning(`属性下发类型必须是整数,缩放因子为${unref(zoomFactorValue)}`); | ||
164 | - return; | ||
165 | - } | ||
166 | - | ||
167 | - if (oldValue * unref(zoomFactorValue) > 65535) { | ||
168 | - createMessage.warning(`属性下发值不能超过65535,缩放因子是${unref(zoomFactorValue)}`); | ||
169 | - return; | ||
170 | - } | ||
171 | - //bool类型的就不用去乘缩放因子了 | ||
172 | - modBUSForm.value.registerValues = [newValue]; | ||
173 | - } | 49 | + await validate(); |
50 | + const { deviceDetail, objectModel } = unref(currentParams) || {}; | ||
174 | 51 | ||
175 | - if (unref(modBUSForm).method == '16' || unref(modBUSForm).method == '10') { | ||
176 | - const regex = /^-?\d+(\.\d{0,2})?$/; | ||
177 | - const values = | ||
178 | - Math.trunc(oldValue) * unref(zoomFactorValue) + | ||
179 | - getFloatPart(oldValue) * unref(zoomFactorValue); | ||
180 | - | ||
181 | - if (!regex.test(values as any)) { | ||
182 | - createMessage.warning(`属性下发值精确到两位小数,缩放因子是${unref(zoomFactorValue)}`); | ||
183 | - return; | ||
184 | - } | ||
185 | - | ||
186 | - const newValue = | ||
187 | - values == 0 ? [0, 0] : getArray(SingleToHex(unref(isShowMultiply) ? values : oldValue)); | ||
188 | - modBUSForm.value.registerValues = newValue; | ||
189 | - modBUSForm.value.registerNumber = 2; | ||
190 | - modBUSForm.value.method = '10'; | ||
191 | - } | ||
192 | - | ||
193 | - sendValue.value = await genModbusCommand(unref(modBUSForm)); | ||
194 | - } else { | ||
195 | - await validate(); | ||
196 | - const _value = transformValue(getFieldsValue()); | ||
197 | - sendValue.value = unref(keys).reduce((prev, next) => { | ||
198 | - return { ...prev, [next]: _value[next] }; | ||
199 | - }, {}); | ||
200 | - | ||
201 | - // tcp 设备下发字符串 | ||
202 | - if (unref(deviceTransportType) === TransportTypeEnum.TCP) { | ||
203 | - sendValue.value = Object.values(_value).join('').replaceAll(/\s/g, ''); | ||
204 | - } | 52 | + let value = getFieldsValue(); |
53 | + if (objectModel?.detail.dataType.type !== DataTypeEnum.STRUCT) { | ||
54 | + value = value[objectModel!.identifier]; | ||
205 | } | 55 | } |
206 | 56 | ||
207 | - await sendCommandOneway({ | ||
208 | - deviceId: props.deviceId, | ||
209 | - value: { | ||
210 | - persistent: true, | ||
211 | - method: 'methodThingskit', | ||
212 | - params: unref(sendValue), | 57 | + const { doCommandDelivery } = useCommandDelivery(); |
58 | + | ||
59 | + await doCommandDelivery({ | ||
60 | + deviceDetail, | ||
61 | + objectModel: { | ||
62 | + ...(objectModel || {}), | ||
63 | + functionName: objectModel!.name, | ||
64 | + identifier: objectModel!.identifier, | ||
65 | + functionType: FunctionTypeEnum.PROPERTIES, | ||
66 | + specs: objectModel?.detail, | ||
213 | }, | 67 | }, |
68 | + cmdType: CommandTypeEnum.ATTRIBUTE, | ||
69 | + value, | ||
214 | }); | 70 | }); |
71 | + | ||
215 | createMessage.success('属性下发成功'); | 72 | createMessage.success('属性下发成功'); |
216 | } catch (error) { | 73 | } catch (error) { |
217 | throw error; | 74 | throw error; |
src/views/device/list/cpns/tabs/ObjectModelCommandDeliveryModal/useGenerateFormSchemasByObjectModel.ts
renamed from
src/views/device/list/cpns/tabs/ObjectModelCommandDeliveryModal/useGenDynamicForm.ts
1 | +import { unref } from 'vue'; | ||
2 | +import { DeviceModelOfMatterAttrs, DeviceRecord } from '/@/api/device/model/deviceModel'; | ||
1 | import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | 3 | import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
2 | -import { JSONEditor } from '/@/components/CodeEditor'; | ||
3 | -import { FormSchema, useComponentRegister } from '/@/components/Form'; | ||
4 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
5 | -import { useJsonParse } from '/@/hooks/business/useJsonParse'; | 4 | +import { FormSchema } from '/@/components/Form'; |
5 | +import { DataTypeEnum, OriginalDataTypeEnum } from '/@/enums/objectModelEnum'; | ||
6 | +import { TCPProtocolTypeEnum } from '/@/enums/deviceEnum'; | ||
6 | 7 | ||
7 | export interface BasicCreateFormParams { | 8 | export interface BasicCreateFormParams { |
8 | identifier: string; | 9 | identifier: string; |
@@ -10,11 +11,9 @@ export interface BasicCreateFormParams { | @@ -10,11 +11,9 @@ export interface BasicCreateFormParams { | ||
10 | dataType: DataType; | 11 | dataType: DataType; |
11 | } | 12 | } |
12 | 13 | ||
13 | -useComponentRegister('JSONEditor', JSONEditor); | ||
14 | - | ||
15 | const validateDouble = (value: number, min?: number | string, max?: number | string) => { | 14 | const validateDouble = (value: number, min?: number | string, max?: number | string) => { |
16 | - min = Number(min) || Number.MIN_SAFE_INTEGER; | ||
17 | - max = Number(max) || Number.MAX_SAFE_INTEGER; | 15 | + min = Number(min) ?? Number.MIN_SAFE_INTEGER; |
16 | + max = Number(max) ?? Number.MAX_SAFE_INTEGER; | ||
18 | 17 | ||
19 | return { | 18 | return { |
20 | flag: value < min || value > max, | 19 | flag: value < min || value > max, |
@@ -22,7 +21,7 @@ const validateDouble = (value: number, min?: number | string, max?: number | str | @@ -22,7 +21,7 @@ const validateDouble = (value: number, min?: number | string, max?: number | str | ||
22 | }; | 21 | }; |
23 | }; | 22 | }; |
24 | 23 | ||
25 | -export const useGenDynamicForm = () => { | 24 | +export const useGenerateFormSchemasByObjectModel = () => { |
26 | const createInputNumber = ({ | 25 | const createInputNumber = ({ |
27 | identifier, | 26 | identifier, |
28 | functionName, | 27 | functionName, |
@@ -128,24 +127,39 @@ export const useGenDynamicForm = () => { | @@ -128,24 +127,39 @@ export const useGenDynamicForm = () => { | ||
128 | }; | 127 | }; |
129 | }; | 128 | }; |
130 | 129 | ||
131 | - const createInputJson = ({ identifier, functionName }: BasicCreateFormParams): FormSchema => { | 130 | + const createModbusValueInput = (objectModel: DeviceModelOfMatterAttrs): FormSchema => { |
131 | + const { identifier, name, detail, extensionDesc } = objectModel; | ||
132 | + | ||
133 | + const { dataType } = detail || {}; | ||
134 | + const { specs } = dataType || {}; | ||
135 | + const { valueRange } = specs as Specs; | ||
136 | + const { max, min } = valueRange || {}; | ||
137 | + | ||
138 | + const isStringType = extensionDesc?.originalDataType === OriginalDataTypeEnum.STRING; | ||
132 | return { | 139 | return { |
133 | field: identifier, | 140 | field: identifier, |
134 | - label: functionName, | ||
135 | - component: 'JSONEditor', | ||
136 | - valueField: 'value', | ||
137 | - changeEvent: 'update:value', | ||
138 | - rules: [ | ||
139 | - { | ||
140 | - validator: (_rule, value: any) => { | ||
141 | - if (value) { | ||
142 | - const { flag } = useJsonParse(value); | ||
143 | - if (!flag) return Promise.reject(`${functionName} 不是一个有效的JSON对象`); | ||
144 | - } | ||
145 | - return Promise.resolve(); | ||
146 | - }, | ||
147 | - }, | ||
148 | - ], | 141 | + label: name, |
142 | + component: isStringType ? 'Input' : 'InputNumber', | ||
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(`${name}${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: `请输入${name}`, | ||
161 | + // precision: floatType.includes(extensionDesc!.originalDataType) ? 2 : 0, | ||
162 | + }, | ||
149 | }; | 163 | }; |
150 | }; | 164 | }; |
151 | 165 | ||
@@ -154,48 +168,38 @@ export const useGenDynamicForm = () => { | @@ -154,48 +168,38 @@ export const useGenDynamicForm = () => { | ||
154 | [DataTypeEnum.NUMBER_DOUBLE]: createInputNumber, | 168 | [DataTypeEnum.NUMBER_DOUBLE]: createInputNumber, |
155 | [DataTypeEnum.NUMBER_INT]: createInputNumber, | 169 | [DataTypeEnum.NUMBER_INT]: createInputNumber, |
156 | [DataTypeEnum.STRING]: createInput, | 170 | [DataTypeEnum.STRING]: createInput, |
157 | - [DataTypeEnum.STRUCT]: createInputJson, | ||
158 | [DataTypeEnum.ENUM]: createEnumSelect, | 171 | [DataTypeEnum.ENUM]: createEnumSelect, |
159 | }; | 172 | }; |
160 | 173 | ||
161 | - const fieldTypeMap = new Map<string, DataTypeEnum>(); | ||
162 | - const genForm = (schemas: StructJSON[]) => { | ||
163 | - fieldTypeMap.clear(); | ||
164 | - const formSchema = schemas.map((item) => { | ||
165 | - const { functionName, identifier, dataType } = item; | 174 | + const getFormByObjectModel = ( |
175 | + objectModel: DeviceModelOfMatterAttrs, | ||
176 | + deviceDetail: DeviceRecord | ||
177 | + ): FormSchema[] => { | ||
178 | + const { name, identifier, detail } = objectModel; | ||
166 | 179 | ||
167 | - const { type } = dataType || {}; | 180 | + const isTCPModbusProduct = |
181 | + unref(deviceDetail).deviceProfile?.profileData?.transportConfiguration?.protocol === | ||
182 | + TCPProtocolTypeEnum.MODBUS_RTU; | ||
168 | 183 | ||
169 | - fieldTypeMap.set(identifier!, dataType!.type); | ||
170 | - const method = schemaMethod[type!]; | 184 | + const { dataType } = detail; |
185 | + const { type } = dataType || {}; | ||
171 | 186 | ||
172 | - const formSchema = method?.({ | ||
173 | - identifier: identifier!, | ||
174 | - functionName: functionName!, | ||
175 | - dataType: dataType!, | ||
176 | - }); | 187 | + if (isTCPModbusProduct) { |
188 | + return [createModbusValueInput(objectModel)]; | ||
189 | + } | ||
177 | 190 | ||
178 | - return formSchema; | ||
179 | - }); | 191 | + if (type === DataTypeEnum.STRUCT) { |
192 | + return (dataType?.specs as StructJSON[]).map((item) => { | ||
193 | + const { functionName, identifier, dataType } = item; | ||
194 | + const { type } = dataType || {}; | ||
180 | 195 | ||
181 | - return formSchema.filter(Boolean); | ||
182 | - }; | 196 | + return schemaMethod[type!]?.({ identifier, functionName, dataType }); |
197 | + }); | ||
198 | + } | ||
183 | 199 | ||
184 | - const transformValue = (value: Recordable) => { | ||
185 | - return Object.keys(value || {}).reduce((prev, next) => { | ||
186 | - const dataType = fieldTypeMap.get(next)!; | ||
187 | - | ||
188 | - let itemValue = value[next]; | ||
189 | - if (dataType === DataTypeEnum.STRUCT) { | ||
190 | - const { value } = useJsonParse(itemValue); | ||
191 | - itemValue = value; | ||
192 | - } | ||
193 | - return { | ||
194 | - ...prev, | ||
195 | - [next]: itemValue, | ||
196 | - }; | ||
197 | - }, {} as Recordable); | 200 | + const result = schemaMethod[type!]?.({ identifier, functionName: name, dataType: dataType! }); |
201 | + return result ? [result] : []; | ||
198 | }; | 202 | }; |
199 | 203 | ||
200 | - return { genForm, transformValue }; | 204 | + return { getFormByObjectModel }; |
201 | }; | 205 | }; |
@@ -39,45 +39,37 @@ export const validateValueRange: ValidatorRule['validator'] = ( | @@ -39,45 +39,37 @@ export const validateValueRange: ValidatorRule['validator'] = ( | ||
39 | return Promise.resolve(); | 39 | return Promise.resolve(); |
40 | }; | 40 | }; |
41 | 41 | ||
42 | -export const validateFunctionName: ValidatorRule['validator'] = (_rule, value: any) => { | ||
43 | - if (/^[a-zA-Z0-9_\-\u4e00-\u9fa5]+$/.test(value)) return Promise.resolve(); | ||
44 | - return Promise.reject('支持中文、大小写字母、数字、短划线、下划线.'); | ||
45 | -}; | ||
46 | - | ||
47 | -export const validateIdentifier: ValidatorRule['validator'] = (_rule, value: any) => { | ||
48 | - if (/^[a-zA-Z0-9_]+$/.test(value)) { | ||
49 | - return Promise.resolve(); | ||
50 | - } | ||
51 | - return Promise.reject('支持大小写字母、数字和下划线.'); | ||
52 | -}; | ||
53 | - | ||
54 | export const createFunctionNameFormItem = (params: Partial<FormSchema> = {}): FormSchema => { | 42 | export const createFunctionNameFormItem = (params: Partial<FormSchema> = {}): FormSchema => { |
43 | + const helpMessage = '支持中文、大小写字母、数字、短划线、下划线。'; | ||
55 | return { | 44 | return { |
56 | field: FormFieldsEnum.FUNCTION_NAME, | 45 | field: FormFieldsEnum.FUNCTION_NAME, |
57 | label: FormFieldsNameEnum.FUNCTION_NAME, | 46 | label: FormFieldsNameEnum.FUNCTION_NAME, |
58 | component: 'Input', | 47 | component: 'Input', |
59 | required: true, | 48 | required: true, |
60 | - helpMessage: '支持中文、大小写字母、数字、短划线、下划线。', | 49 | + helpMessage, |
61 | rules: [ | 50 | rules: [ |
62 | { | 51 | { |
63 | required: true, | 52 | required: true, |
64 | - validator: validateFunctionName, | 53 | + message: helpMessage, |
54 | + pattern: /^[a-zA-Z0-9_\-\u4e00-\u9fa5\(\)]+$/, | ||
65 | }, | 55 | }, |
66 | ], | 56 | ], |
67 | componentProps: { | 57 | componentProps: { |
68 | placeholder: `请输入${FormFieldsNameEnum.FUNCTION_NAME}`, | 58 | placeholder: `请输入${FormFieldsNameEnum.FUNCTION_NAME}`, |
59 | + maxlength: 32, | ||
69 | }, | 60 | }, |
70 | ...params, | 61 | ...params, |
71 | }; | 62 | }; |
72 | }; | 63 | }; |
73 | 64 | ||
74 | export const createIdentifierFormItem = (params: Partial<FormSchema> = {}): FormSchema => { | 65 | export const createIdentifierFormItem = (params: Partial<FormSchema> = {}): FormSchema => { |
66 | + const helpMessage = '支持大小写字母、数字和下划线.'; | ||
75 | return { | 67 | return { |
76 | field: FormFieldsEnum.IDENTIFIER, | 68 | field: FormFieldsEnum.IDENTIFIER, |
77 | label: FormFieldsNameEnum.IDENTIFIER, | 69 | label: FormFieldsNameEnum.IDENTIFIER, |
78 | required: true, | 70 | required: true, |
79 | component: 'Input', | 71 | component: 'Input', |
80 | - helpMessage: '支持大小写字母、数字和下划线.', | 72 | + helpMessage, |
81 | componentProps: { | 73 | componentProps: { |
82 | maxLength: 128, | 74 | maxLength: 128, |
83 | placeholder: '请输入标识符', | 75 | placeholder: '请输入标识符', |
@@ -85,7 +77,8 @@ export const createIdentifierFormItem = (params: Partial<FormSchema> = {}): Form | @@ -85,7 +77,8 @@ export const createIdentifierFormItem = (params: Partial<FormSchema> = {}): Form | ||
85 | rules: [ | 77 | rules: [ |
86 | { | 78 | { |
87 | required: true, | 79 | required: true, |
88 | - validator: validateIdentifier, | 80 | + message: helpMessage, |
81 | + pattern: /^[a-zA-Z0-9_\-\u4e00-\u9fa5\(\)]+$/, | ||
89 | }, | 82 | }, |
90 | ], | 83 | ], |
91 | ...params, | 84 | ...params, |
@@ -330,7 +323,6 @@ export const getFormSchemas = (dataType: DataTypeEnum[], showRemark: boolean): F | @@ -330,7 +323,6 @@ export const getFormSchemas = (dataType: DataTypeEnum[], showRemark: boolean): F | ||
330 | model[FormFieldsEnum.FUNCTION_TYPE] | 323 | model[FormFieldsEnum.FUNCTION_TYPE] |
331 | ), | 324 | ), |
332 | }, | 325 | }, |
333 | - | ||
334 | { | 326 | { |
335 | field: FormFieldsEnum.REMARK, | 327 | field: FormFieldsEnum.REMARK, |
336 | label: FormFieldsNameEnum.REMARK, | 328 | label: FormFieldsNameEnum.REMARK, |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | } | 20 | } |
21 | ); | 21 | ); |
22 | 22 | ||
23 | - defineEmits<{ | 23 | + const emits = defineEmits<{ |
24 | (event: 'field-value-change', field: string, value: any): void; | 24 | (event: 'field-value-change', field: string, value: any): void; |
25 | }>(); | 25 | }>(); |
26 | 26 | ||
@@ -37,6 +37,9 @@ | @@ -37,6 +37,9 @@ | ||
37 | enumListRef, | 37 | enumListRef, |
38 | }); | 38 | }); |
39 | 39 | ||
40 | + const handleFieldValueChang = (field: string, value: any) => | ||
41 | + emits('field-value-change', field, value); | ||
42 | + | ||
40 | defineExpose({ getFieldsValue, setFieldsValue, validate, resetFieldsValue }); | 43 | defineExpose({ getFieldsValue, setFieldsValue, validate, resetFieldsValue }); |
41 | </script> | 44 | </script> |
42 | 45 | ||
@@ -45,7 +48,7 @@ | @@ -45,7 +48,7 @@ | ||
45 | @register="register" | 48 | @register="register" |
46 | class="data-type-form" | 49 | class="data-type-form" |
47 | :disabled="mode === DataActionModeEnum.READ" | 50 | :disabled="mode === DataActionModeEnum.READ" |
48 | - @field-value-change="(field, value) => $emit('field-value-change', field, value)" | 51 | + @field-value-change="handleFieldValueChang" |
49 | > | 52 | > |
50 | <template #enumsData="{ model, field }"> | 53 | <template #enumsData="{ model, field }"> |
51 | <EnumList | 54 | <EnumList |
@@ -77,4 +80,3 @@ | @@ -77,4 +80,3 @@ | ||
77 | } | 80 | } |
78 | } | 81 | } |
79 | </style> | 82 | </style> |
80 | -./config |
1 | import { Ref, unref } from 'vue'; | 1 | import { Ref, unref } from 'vue'; |
2 | import { FormActionType } from '/@/components/Form'; | 2 | import { FormActionType } from '/@/components/Form'; |
3 | -import EnumList from './EnumList.vue'; | 3 | +import { EnumList } from '../EnumList'; |
4 | import { DefineComponentsBasicExpose } from '/#/utils'; | 4 | import { DefineComponentsBasicExpose } from '/#/utils'; |
5 | import { DataTypeFormGetFieldsValueType } from './config'; | 5 | import { DataTypeFormGetFieldsValueType } from './config'; |
6 | import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | 6 | import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
@@ -6,15 +6,21 @@ export enum FormFieldsEnum { | @@ -6,15 +6,21 @@ export enum FormFieldsEnum { | ||
6 | DATA_TYPE = 'dataType', | 6 | DATA_TYPE = 'dataType', |
7 | } | 7 | } |
8 | 8 | ||
9 | -export const getFormSchemas = (): FormSchema[] => { | 9 | +export const getFormSchemas = (required: boolean): FormSchema[] => { |
10 | return [ | 10 | return [ |
11 | { | 11 | { |
12 | field: FormFieldsEnum.VALUE, | 12 | field: FormFieldsEnum.VALUE, |
13 | label: '', | 13 | label: '', |
14 | component: 'InputNumber', | 14 | component: 'InputNumber', |
15 | - rules: [ | ||
16 | - { required: true, message: `支持整型,取值范围:-2147483648 ~ 2147483647`, type: 'number' }, | ||
17 | - ], | 15 | + dynamicRules: ({ model }) => { |
16 | + return [ | ||
17 | + { | ||
18 | + required: required || model[FormFieldsEnum.NAME], | ||
19 | + message: `支持整型,取值范围:-2147483648 ~ 2147483647`, | ||
20 | + type: 'number', | ||
21 | + }, | ||
22 | + ]; | ||
23 | + }, | ||
18 | componentProps: () => { | 24 | componentProps: () => { |
19 | return { | 25 | return { |
20 | placeholder: '编号如"0"', | 26 | placeholder: '编号如"0"', |
@@ -41,7 +47,15 @@ export const getFormSchemas = (): FormSchema[] => { | @@ -41,7 +47,15 @@ export const getFormSchemas = (): FormSchema[] => { | ||
41 | field: FormFieldsEnum.NAME, | 47 | field: FormFieldsEnum.NAME, |
42 | label: '', | 48 | label: '', |
43 | component: 'Input', | 49 | component: 'Input', |
44 | - rules: [{ required: true, message: `参数描述不能为空`, type: 'string' }], | 50 | + dynamicRules: ({ model }) => { |
51 | + return [ | ||
52 | + { | ||
53 | + required: required || model[FormFieldsEnum.VALUE], | ||
54 | + message: `参数描述不能为空`, | ||
55 | + type: 'string', | ||
56 | + }, | ||
57 | + ]; | ||
58 | + }, | ||
45 | componentProps: () => { | 59 | componentProps: () => { |
46 | return { | 60 | return { |
47 | placeholder: '对该枚举项的描述', | 61 | placeholder: '对该枚举项的描述', |
@@ -9,7 +9,18 @@ | @@ -9,7 +9,18 @@ | ||
9 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 9 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
10 | import { isNullOrUnDef } from '/@/utils/is'; | 10 | import { isNullOrUnDef } from '/@/utils/is'; |
11 | 11 | ||
12 | - const props = defineProps<{ disabled?: boolean; value?: Specs[] }>(); | 12 | + const props = withDefaults( |
13 | + defineProps<{ | ||
14 | + disabled?: boolean; | ||
15 | + value?: Specs[]; | ||
16 | + addButtonName?: string; | ||
17 | + required?: boolean; | ||
18 | + }>(), | ||
19 | + { | ||
20 | + addButtonName: '+添加枚举项', | ||
21 | + required: true, | ||
22 | + } | ||
23 | + ); | ||
13 | 24 | ||
14 | interface EnumElItemType { | 25 | interface EnumElItemType { |
15 | uuid: string; | 26 | uuid: string; |
@@ -18,7 +29,7 @@ | @@ -18,7 +29,7 @@ | ||
18 | } | 29 | } |
19 | 30 | ||
20 | const [registerForm] = useForm({ | 31 | const [registerForm] = useForm({ |
21 | - schemas: getFormSchemas(), | 32 | + schemas: getFormSchemas(props.required), |
22 | showActionButtonGroup: false, | 33 | showActionButtonGroup: false, |
23 | layout: 'inline', | 34 | layout: 'inline', |
24 | }); | 35 | }); |
@@ -75,6 +86,8 @@ | @@ -75,6 +86,8 @@ | ||
75 | const index = unref(enumsListElRef).findIndex((temp) => item.uuid === temp.uuid); | 86 | const index = unref(enumsListElRef).findIndex((temp) => item.uuid === temp.uuid); |
76 | 87 | ||
77 | ~index && enumsListElRef.value.splice(index, 1); | 88 | ~index && enumsListElRef.value.splice(index, 1); |
89 | + | ||
90 | + validateSameEnum(); | ||
78 | }; | 91 | }; |
79 | 92 | ||
80 | const handleAddEnums = () => { | 93 | const handleAddEnums = () => { |
@@ -102,14 +115,14 @@ | @@ -102,14 +115,14 @@ | ||
102 | <section class="w-full"> | 115 | <section class="w-full"> |
103 | <header class="flex h-8 items-center"> | 116 | <header class="flex h-8 items-center"> |
104 | <div class="w-1/2"> | 117 | <div class="w-1/2"> |
105 | - <span class="mr-1 text-red-400">*</span> | 118 | + <span v-if="required" class="mr-1 text-red-400">*</span> |
106 | <span> 参考值 </span> | 119 | <span> 参考值 </span> |
107 | <Tooltip title="支持整型,取值范围:-2147483648 ~ 2147483647"> | 120 | <Tooltip title="支持整型,取值范围:-2147483648 ~ 2147483647"> |
108 | <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> | 121 | <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> |
109 | </Tooltip> | 122 | </Tooltip> |
110 | </div> | 123 | </div> |
111 | <div class="w-1/2"> | 124 | <div class="w-1/2"> |
112 | - <span class="mr-1 text-red-400">*</span> | 125 | + <span v-if="required" class="mr-1 text-red-400">*</span> |
113 | <span> 参考描述 </span> | 126 | <span> 参考描述 </span> |
114 | <Tooltip | 127 | <Tooltip |
115 | title="支持中文、英文大小写、日文、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符" | 128 | title="支持中文、英文大小写、日文、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符" |
@@ -144,7 +157,7 @@ | @@ -144,7 +157,7 @@ | ||
144 | <div v-if="hasSameEnum" class="text-red-400">枚举项中存在相同的参数值或参数描述</div> | 157 | <div v-if="hasSameEnum" class="text-red-400">枚举项中存在相同的参数值或参数描述</div> |
145 | <Tooltip title="枚举项最多创建 100 个"> | 158 | <Tooltip title="枚举项最多创建 100 个"> |
146 | <Button type="link" @click="handleAddEnums" :disabled="disabled || getEnumsLimit"> | 159 | <Button type="link" @click="handleAddEnums" :disabled="disabled || getEnumsLimit"> |
147 | - +添加枚举项 | 160 | + {{ addButtonName }} |
148 | </Button> | 161 | </Button> |
149 | </Tooltip> | 162 | </Tooltip> |
150 | </section> | 163 | </section> |
1 | -import { unref } from 'vue'; | ||
2 | -import { useObjectModelFormContext } from '../useObjectModelFormContext'; | ||
3 | -import { findDictItemByCode } from '/@/api/system/dict'; | ||
4 | import { FormSchema } from '/@/components/Table'; | 1 | import { FormSchema } from '/@/components/Table'; |
5 | -import { DictEnum } from '/@/enums/dictEnum'; | ||
6 | import { | 2 | import { |
7 | - DataTypeEnum, | ||
8 | - RegisterActionTypeEnum, | ||
9 | - RegisterActionTypeNameEnum, | ||
10 | - RegisterDataTypeEnum, | 3 | + ExtendDescOperationTypeEnum, |
4 | + ExtendDescOperationTypeNameEnum, | ||
5 | + OriginalDataTypeEnum, | ||
6 | + OriginalDataTypeNameEnum, | ||
11 | } from '/@/enums/objectModelEnum'; | 7 | } from '/@/enums/objectModelEnum'; |
8 | +import { validateValueRange } from '../DataTypeForm/config'; | ||
9 | +import { useComponentRegister } from '/@/components/Form'; | ||
10 | +import { HexInput, InputTypeEnum } from '../HexInput'; | ||
11 | +import { isFloatType } from './useParseOperationType'; | ||
12 | +import { useObjectModelFormContext } from '../useObjectModelFormContext'; | ||
13 | +import { unref } from 'vue'; | ||
14 | +import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
15 | + | ||
16 | +useComponentRegister('HexInput', HexInput); | ||
12 | 17 | ||
13 | export enum FormFieldsEnum { | 18 | export enum FormFieldsEnum { |
14 | - REGISTER_ADDRESS = 'registerAddress', | ||
15 | - DATA_TYPE = 'dataType', | ||
16 | ACTION_TYPE = 'actionType', | 19 | ACTION_TYPE = 'actionType', |
17 | ZOOM_FACTOR = 'zoomFactor', | 20 | ZOOM_FACTOR = 'zoomFactor', |
18 | 21 | ||
19 | OBJECT_MODEL_TYPE = 'objectModelType', | 22 | OBJECT_MODEL_TYPE = 'objectModelType', |
23 | + | ||
24 | + ORIGINAL_DATA_TYPE = 'originalDataType', | ||
25 | + REGISTER_ADDRESS = 'registerAddress', | ||
26 | + OPERATION_TYPE = 'operationType', | ||
27 | + VALUE_RANGE = 'valueRange', | ||
28 | + SCALING = 'scaling', | ||
29 | + BIT_MASK = 'bitMask', | ||
30 | + | ||
31 | + REGISTER_COUNT = 'registerCount', | ||
32 | + | ||
33 | + VALUE_MAPPING = 'valueMapping', | ||
20 | } | 34 | } |
21 | 35 | ||
22 | -function getActionTypeByObjectModelType(dataType: DataTypeEnum) { | ||
23 | - const list: Record<'label' | 'value', string>[] = []; | ||
24 | - if (dataType === DataTypeEnum.BOOL) { | ||
25 | - list.push({ label: RegisterActionTypeNameEnum.BOOL, value: RegisterActionTypeEnum.BOOL }); | ||
26 | - } else if (dataType === DataTypeEnum.NUMBER_INT) { | ||
27 | - list.push({ label: RegisterActionTypeNameEnum.INT, value: RegisterActionTypeEnum.INT }); | ||
28 | - } else if (dataType === DataTypeEnum.NUMBER_DOUBLE) { | ||
29 | - list.push({ label: RegisterActionTypeNameEnum.DOUBLE, value: RegisterActionTypeEnum.DOUBLE }); | ||
30 | - } else { | ||
31 | - list.push( | ||
32 | - ...Object.keys(RegisterActionTypeEnum).map((key) => ({ | ||
33 | - label: RegisterActionTypeNameEnum[key], | ||
34 | - value: RegisterActionTypeEnum[key], | ||
35 | - })) | ||
36 | - ); | 36 | +export enum FormFieldsNameEnum { |
37 | + ORIGINAL_DATA_TYPE = '数据类型', | ||
38 | + REGISTER_ADDRESS = '寄存器地址', | ||
39 | + OPERATION_TYPE = '操作类型', | ||
40 | + VALUE_RANGE = '取值范围', | ||
41 | + SCALING = '缩放因子', | ||
42 | + BIT_MASK = '比特位置', | ||
43 | + | ||
44 | + REGISTER_COUNT = '寄存器个数', | ||
45 | + VALUE_MAPPING = '值映射', | ||
46 | +} | ||
47 | + | ||
48 | +export const BOOL_DEFAULT_VALUE_RANGE = { min: 0, max: 1 }; | ||
49 | + | ||
50 | +export const INT16_VALUE_RANGE = { | ||
51 | + min: -(2 ** 15), | ||
52 | + max: 2 ** 15 - 1, | ||
53 | +}; | ||
54 | + | ||
55 | +export const UINT16_VALUE_RANGE = { | ||
56 | + min: 0, | ||
57 | + max: 2 ** 16 - 1, | ||
58 | +}; | ||
59 | + | ||
60 | +export const INT32_VALUE_RANGE = { | ||
61 | + min: -(2 ** 31), | ||
62 | + max: 2 ** 31 - 1, | ||
63 | +}; | ||
64 | + | ||
65 | +export const UINT32_VALUE_RANGE = { | ||
66 | + min: 0, | ||
67 | + max: 2 ** 32 - 1, | ||
68 | +}; | ||
69 | + | ||
70 | +function getValueRangeFromOriginDataType( | ||
71 | + originalDataType?: OriginalDataTypeEnum | ||
72 | +): Record<'min' | 'max', number> { | ||
73 | + switch (originalDataType) { | ||
74 | + case OriginalDataTypeEnum.BOOLEAN: | ||
75 | + case OriginalDataTypeEnum.BITS: | ||
76 | + return BOOL_DEFAULT_VALUE_RANGE; | ||
77 | + | ||
78 | + case OriginalDataTypeEnum.INT16_AB: | ||
79 | + case OriginalDataTypeEnum.INT16_BA: | ||
80 | + return INT16_VALUE_RANGE; | ||
81 | + | ||
82 | + case OriginalDataTypeEnum.UINT16_AB: | ||
83 | + case OriginalDataTypeEnum.UINT16_BA: | ||
84 | + return UINT16_VALUE_RANGE; | ||
85 | + | ||
86 | + case OriginalDataTypeEnum.INT32_AB_CD: | ||
87 | + case OriginalDataTypeEnum.INT32_BA_DC: | ||
88 | + case OriginalDataTypeEnum.INT32_CD_AB: | ||
89 | + case OriginalDataTypeEnum.INT32_DC_BA: | ||
90 | + return INT32_VALUE_RANGE; | ||
91 | + | ||
92 | + case OriginalDataTypeEnum.UINT32_AB_CD: | ||
93 | + case OriginalDataTypeEnum.UINT32_BA_DC: | ||
94 | + case OriginalDataTypeEnum.UINT32_CD_AB: | ||
95 | + case OriginalDataTypeEnum.UINT32_DC_BA: | ||
96 | + return UINT32_VALUE_RANGE; | ||
97 | + | ||
98 | + default: | ||
99 | + return INT32_VALUE_RANGE; | ||
37 | } | 100 | } |
101 | +} | ||
38 | 102 | ||
39 | - return list; | 103 | +const BOOL_OPERATION_TYPE = [ |
104 | + ExtendDescOperationTypeEnum.INPUT_STATUS_R_02, | ||
105 | + ExtendDescOperationTypeEnum.COIL_STATUS_R_01, | ||
106 | + ExtendDescOperationTypeEnum.COIL_STATUS_RW_01_05, | ||
107 | + ExtendDescOperationTypeEnum.COIL_STATUS_RW_01_0F, | ||
108 | + ExtendDescOperationTypeEnum.COIL_STATUS_W_05, | ||
109 | + ExtendDescOperationTypeEnum.COIL_STATUS_W_0F, | ||
110 | +]; | ||
111 | + | ||
112 | +function getOriginalDataTypeByOperationType( | ||
113 | + operationType: ExtendDescOperationTypeEnum | ||
114 | +): Record<'label' | 'value', string>[] { | ||
115 | + switch (operationType) { | ||
116 | + case ExtendDescOperationTypeEnum.INPUT_STATUS_R_02: | ||
117 | + case ExtendDescOperationTypeEnum.COIL_STATUS_R_01: | ||
118 | + case ExtendDescOperationTypeEnum.COIL_STATUS_RW_01_05: | ||
119 | + case ExtendDescOperationTypeEnum.COIL_STATUS_RW_01_0F: | ||
120 | + case ExtendDescOperationTypeEnum.COIL_STATUS_W_05: | ||
121 | + case ExtendDescOperationTypeEnum.COIL_STATUS_W_0F: | ||
122 | + return [{ label: OriginalDataTypeNameEnum.BOOLEAN, value: OriginalDataTypeEnum.BOOLEAN }]; | ||
123 | + | ||
124 | + case ExtendDescOperationTypeEnum.HOLDING_REGISTER_RW_03_06: | ||
125 | + case ExtendDescOperationTypeEnum.HOLDING_REGISTER_W_06: | ||
126 | + return [ | ||
127 | + { label: OriginalDataTypeNameEnum.INT16_AB, value: OriginalDataTypeEnum.INT16_AB }, | ||
128 | + { label: OriginalDataTypeNameEnum.INT16_BA, value: OriginalDataTypeEnum.INT16_BA }, | ||
129 | + { label: OriginalDataTypeNameEnum.UINT16_AB, value: OriginalDataTypeEnum.UINT16_AB }, | ||
130 | + { label: OriginalDataTypeNameEnum.UINT16_BA, value: OriginalDataTypeEnum.UINT16_BA }, | ||
131 | + { label: OriginalDataTypeNameEnum.BITS, value: OriginalDataTypeEnum.BITS }, | ||
132 | + { label: OriginalDataTypeNameEnum.BOOLEAN, value: OriginalDataTypeEnum.BOOLEAN }, | ||
133 | + ]; | ||
134 | + | ||
135 | + case ExtendDescOperationTypeEnum.HOLDING_REGISTER_RW_03_10: | ||
136 | + case ExtendDescOperationTypeEnum.HOLDING_REGISTER_W_10: | ||
137 | + case ExtendDescOperationTypeEnum.HOLDING_REGISTER_R_03: | ||
138 | + case ExtendDescOperationTypeEnum.INPUT_REGISTER_R_04: | ||
139 | + return Object.keys(OriginalDataTypeEnum).map((key) => ({ | ||
140 | + label: OriginalDataTypeNameEnum[key], | ||
141 | + value: OriginalDataTypeEnum[key], | ||
142 | + })); | ||
143 | + } | ||
40 | } | 144 | } |
41 | 145 | ||
42 | -export const getFormSchemas = (): FormSchema[] => { | ||
43 | - const { getDataType } = useObjectModelFormContext(); | 146 | +export const getExtendDescFormSchemas = (): FormSchema[] => { |
147 | + const DEFAULT_OPERATION_TYPE = ExtendDescOperationTypeEnum.INPUT_STATUS_R_02; | ||
148 | + const DEFAULT_ORIGINAL_TYPE = OriginalDataTypeEnum.BOOLEAN; | ||
44 | return [ | 149 | return [ |
45 | { | 150 | { |
151 | + field: FormFieldsEnum.OPERATION_TYPE, | ||
152 | + label: FormFieldsNameEnum.OPERATION_TYPE, | ||
153 | + component: 'Select', | ||
154 | + required: true, | ||
155 | + defaultValue: DEFAULT_OPERATION_TYPE, | ||
156 | + componentProps: ({ formActionType }) => { | ||
157 | + return { | ||
158 | + options: Object.keys(ExtendDescOperationTypeEnum).map((key) => ({ | ||
159 | + label: ExtendDescOperationTypeNameEnum[key], | ||
160 | + value: ExtendDescOperationTypeEnum[key], | ||
161 | + })), | ||
162 | + labelField: 'itemText', | ||
163 | + valueField: 'itemValue', | ||
164 | + getPopupContainer: () => document.body, | ||
165 | + placeholder: '请选择操作类型', | ||
166 | + allowClear: false, | ||
167 | + onChange(value: ExtendDescOperationTypeEnum) { | ||
168 | + const isBoolTypeOperationType = BOOL_OPERATION_TYPE.includes(value); | ||
169 | + const originalDataType = isBoolTypeOperationType | ||
170 | + ? OriginalDataTypeEnum.BOOLEAN | ||
171 | + : OriginalDataTypeEnum.INT16_AB; | ||
172 | + | ||
173 | + formActionType.setFieldsValue({ | ||
174 | + [FormFieldsEnum.ORIGINAL_DATA_TYPE]: originalDataType, | ||
175 | + [FormFieldsEnum.VALUE_RANGE]: isBoolTypeOperationType | ||
176 | + ? BOOL_DEFAULT_VALUE_RANGE | ||
177 | + : INT16_VALUE_RANGE, | ||
178 | + }); | ||
179 | + }, | ||
180 | + }; | ||
181 | + }, | ||
182 | + }, | ||
183 | + { | ||
46 | field: FormFieldsEnum.REGISTER_ADDRESS, | 184 | field: FormFieldsEnum.REGISTER_ADDRESS, |
47 | - component: 'RegisterAddressInput', | ||
48 | - label: '寄存器地址', | 185 | + label: FormFieldsNameEnum.REGISTER_ADDRESS, |
186 | + component: 'HexInput', | ||
49 | changeEvent: 'update:value', | 187 | changeEvent: 'update:value', |
50 | valueField: 'value', | 188 | valueField: 'value', |
51 | - rules: [{ message: '请输入寄存器地址', required: true, type: 'number' }], | 189 | + helpMessage: ['范围在 0x0-0xFFFF'], |
190 | + rules: [{ message: '请输入寄存器地址', required: true }], | ||
52 | componentProps: { | 191 | componentProps: { |
53 | placeholder: '请输入寄存器地址', | 192 | placeholder: '请输入寄存器地址', |
193 | + type: InputTypeEnum.HEX, | ||
194 | + maxValue: parseInt('0xFFFF', 16), | ||
54 | }, | 195 | }, |
55 | }, | 196 | }, |
56 | { | 197 | { |
57 | - field: FormFieldsEnum.DATA_TYPE, | ||
58 | - component: 'ApiSelect', | ||
59 | - label: '数据格式', | ||
60 | - rules: [{ message: '请选择数据格式', required: true }], | ||
61 | - defaultValue: RegisterDataTypeEnum.UN_SHORT, | ||
62 | - componentProps: { | ||
63 | - api: findDictItemByCode, | ||
64 | - params: { | ||
65 | - dictCode: DictEnum.REGISTER_DATA_FORMAT, | ||
66 | - }, | ||
67 | - labelField: 'itemText', | ||
68 | - valueField: 'itemValue', | ||
69 | - placeholder: '请选择数据格式', | ||
70 | - getPopupContainer: () => document.body, | 198 | + field: FormFieldsEnum.ORIGINAL_DATA_TYPE, |
199 | + label: FormFieldsNameEnum.ORIGINAL_DATA_TYPE, | ||
200 | + component: 'Select', | ||
201 | + required: true, | ||
202 | + defaultValue: DEFAULT_ORIGINAL_TYPE, | ||
203 | + dynamicDisabled: ({ model }) => | ||
204 | + BOOL_OPERATION_TYPE.includes(model[FormFieldsEnum.OPERATION_TYPE]) || | ||
205 | + unref(useObjectModelFormContext().getModalMode) === DataActionModeEnum.READ, | ||
206 | + componentProps: ({ formActionType, formModel }) => { | ||
207 | + const operationType = formModel[FormFieldsEnum.OPERATION_TYPE]; | ||
208 | + | ||
209 | + return { | ||
210 | + options: getOriginalDataTypeByOperationType(operationType), | ||
211 | + labelField: 'itemText', | ||
212 | + valueField: 'itemValue', | ||
213 | + placeholder: '请选择数据类型', | ||
214 | + getPopupContainer: () => document.body, | ||
215 | + onChange(value: OriginalDataTypeEnum) { | ||
216 | + const { setFieldsValue } = formActionType; | ||
217 | + setFieldsValue({ | ||
218 | + [FormFieldsEnum.VALUE_RANGE]: getValueRangeFromOriginDataType( | ||
219 | + value as OriginalDataTypeEnum | ||
220 | + ), | ||
221 | + }); | ||
222 | + }, | ||
223 | + }; | ||
71 | }, | 224 | }, |
72 | }, | 225 | }, |
73 | { | 226 | { |
74 | - field: FormFieldsEnum.ACTION_TYPE, | 227 | + field: FormFieldsEnum.BIT_MASK, |
228 | + label: FormFieldsNameEnum.BIT_MASK, | ||
75 | component: 'Select', | 229 | component: 'Select', |
76 | - label: '操作类型', | ||
77 | - rules: [{ message: '请选择操作类型', required: true }], | 230 | + ifShow: ({ model }) => model[FormFieldsEnum.ORIGINAL_DATA_TYPE] === OriginalDataTypeEnum.BITS, |
231 | + required: true, | ||
232 | + defaultValue: 7, | ||
78 | componentProps: () => { | 233 | componentProps: () => { |
79 | return { | 234 | return { |
80 | - options: getActionTypeByObjectModelType(unref(getDataType)), | ||
81 | - placeholder: '请选择操作类型', | 235 | + options: Array.from({ length: 16 }, (_, index) => ({ |
236 | + label: `${index}`, | ||
237 | + value: index, | ||
238 | + })), | ||
239 | + allowClear: false, | ||
82 | getPopupContainer: () => document.body, | 240 | getPopupContainer: () => document.body, |
83 | }; | 241 | }; |
84 | }, | 242 | }, |
85 | }, | 243 | }, |
86 | { | 244 | { |
87 | - field: FormFieldsEnum.ZOOM_FACTOR, | 245 | + field: FormFieldsEnum.VALUE_RANGE, |
246 | + label: FormFieldsNameEnum.VALUE_RANGE, | ||
247 | + component: 'CustomMinMaxInput', | ||
248 | + rules: [{ required: true, validator: validateValueRange }], | ||
249 | + changeEvent: 'update:value', | ||
250 | + valueField: 'value', | ||
251 | + defaultValue: BOOL_DEFAULT_VALUE_RANGE, | ||
252 | + helpMessage: [ | ||
253 | + '取值范围指的是原始数据经过缩放因子处理之后的取值范围,超出取值范围的数据会被丢弃。', | ||
254 | + ], | ||
255 | + ifShow: ({ model }) => | ||
256 | + model[FormFieldsEnum.ORIGINAL_DATA_TYPE] !== OriginalDataTypeEnum.STRING, | ||
257 | + componentProps: ({ formModel }) => { | ||
258 | + const originalDataType = formModel[FormFieldsEnum.ORIGINAL_DATA_TYPE]; | ||
259 | + | ||
260 | + const { min, max } = getValueRangeFromOriginDataType( | ||
261 | + originalDataType as OriginalDataTypeEnum | ||
262 | + ); | ||
263 | + | ||
264 | + const flag = isFloatType(originalDataType); | ||
265 | + | ||
266 | + return { | ||
267 | + minInputProps: { | ||
268 | + precision: 0, | ||
269 | + min: flag ? undefined : min, | ||
270 | + max: flag ? undefined : max, | ||
271 | + }, | ||
272 | + maxInputProps: { | ||
273 | + precision: 0, | ||
274 | + min: flag ? undefined : min, | ||
275 | + max: flag ? undefined : max, | ||
276 | + }, | ||
277 | + }; | ||
278 | + }, | ||
279 | + }, | ||
280 | + | ||
281 | + { | ||
282 | + field: FormFieldsEnum.SCALING, | ||
283 | + label: FormFieldsNameEnum.SCALING, | ||
88 | component: 'InputNumber', | 284 | component: 'InputNumber', |
89 | - label: '缩放因子', | ||
90 | - helpMessage: ['缩放因子不能为0,默认为1'], | 285 | + required: true, |
91 | defaultValue: 1, | 286 | defaultValue: 1, |
287 | + helpMessage: ['缩放因子,不能为 0,默认为 1'], | ||
92 | ifShow: ({ model }) => | 288 | ifShow: ({ model }) => |
93 | - ![DataTypeEnum.BOOL, DataTypeEnum.STRING].includes(model[FormFieldsEnum.OBJECT_MODEL_TYPE]), | ||
94 | - componentProps: { | ||
95 | - min: 1, | ||
96 | - placeholder: '请输入缩放因子', | 289 | + ![OriginalDataTypeEnum.STRING, OriginalDataTypeEnum.BOOLEAN].includes( |
290 | + model[FormFieldsEnum.ORIGINAL_DATA_TYPE] | ||
291 | + ), | ||
292 | + rules: [ | ||
293 | + { | ||
294 | + validator: (_rule, value: any) => { | ||
295 | + if (Number(value) > 0) return Promise.resolve(); | ||
296 | + return Promise.reject(Error('缩放因子不能为0')); | ||
297 | + }, | ||
298 | + }, | ||
299 | + ], | ||
300 | + componentProps: () => {}, | ||
301 | + }, | ||
302 | + { | ||
303 | + field: FormFieldsEnum.REGISTER_COUNT, | ||
304 | + label: FormFieldsNameEnum.REGISTER_COUNT, | ||
305 | + component: 'InputNumber', | ||
306 | + helpMessage: ['操作类型为保持寄存器/输入寄存器时限制为1~125'], | ||
307 | + ifShow: ({ model }) => | ||
308 | + model[FormFieldsEnum.ORIGINAL_DATA_TYPE] === OriginalDataTypeEnum.STRING, | ||
309 | + required: true, | ||
310 | + componentProps: () => { | ||
311 | + return { | ||
312 | + placeholder: '请输入寄存器个数', | ||
313 | + min: 1, | ||
314 | + max: 125, | ||
315 | + precision: 0, | ||
316 | + }; | ||
97 | }, | 317 | }, |
98 | }, | 318 | }, |
319 | + { | ||
320 | + field: FormFieldsEnum.VALUE_MAPPING, | ||
321 | + component: 'Input', | ||
322 | + label: FormFieldsNameEnum.VALUE_MAPPING, | ||
323 | + slot: FormFieldsEnum.VALUE_MAPPING, | ||
324 | + defaultValue: [ | ||
325 | + { value: 0, name: '关' }, | ||
326 | + { value: 1, name: '开' }, | ||
327 | + ], | ||
328 | + ifShow: ({ model }) => | ||
329 | + [OriginalDataTypeEnum.BOOLEAN, OriginalDataTypeEnum.BITS].includes( | ||
330 | + model[FormFieldsEnum.ORIGINAL_DATA_TYPE] | ||
331 | + ), | ||
332 | + }, | ||
99 | ]; | 333 | ]; |
100 | }; | 334 | }; |
1 | export { default as ExtendDesc } from './index.vue'; | 1 | export { default as ExtendDesc } from './index.vue'; |
2 | + | ||
3 | +import { FormFieldsEnum } from './config'; | ||
4 | +import { Specs } from '/@/api/device/model/modelOfMatterModel'; | ||
5 | +import { ExtendDescOperationTypeEnum, OriginalDataTypeEnum } from '/@/enums/objectModelEnum'; | ||
6 | + | ||
7 | +export interface ExtendDescFormFieldsValueType { | ||
8 | + [FormFieldsEnum.BIT_MASK]?: number; | ||
9 | + [FormFieldsEnum.OPERATION_TYPE]: ExtendDescOperationTypeEnum; | ||
10 | + [FormFieldsEnum.ORIGINAL_DATA_TYPE]: OriginalDataTypeEnum; | ||
11 | + [FormFieldsEnum.REGISTER_ADDRESS]: string; | ||
12 | + [FormFieldsEnum.SCALING]?: number; | ||
13 | + [FormFieldsEnum.VALUE_RANGE]?: Record<'min' | 'max', number>; | ||
14 | + [FormFieldsEnum.REGISTER_COUNT]?: number; | ||
15 | + [FormFieldsEnum.VALUE_MAPPING]?: Specs[]; | ||
16 | +} |
1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
2 | - import { Button } from 'ant-design-vue'; | ||
3 | - import { nextTick, ref, watch } from 'vue'; | 2 | + import { Button, Divider } from 'ant-design-vue'; |
3 | + import { nextTick, ref, unref, watch } from 'vue'; | ||
4 | import { BasicForm, useForm } from '/@/components/Form'; | 4 | import { BasicForm, useForm } from '/@/components/Form'; |
5 | - import { BasicModal } from '/@/components/Modal'; | ||
6 | import { PlusCircleOutlined } from '@ant-design/icons-vue'; | 5 | import { PlusCircleOutlined } from '@ant-design/icons-vue'; |
7 | - import { getFormSchemas } from './config'; | ||
8 | - import { ExtensionDesc } from '/@/api/device/model/modelOfMatterModel'; | 6 | + import { getExtendDescFormSchemas } from './config'; |
7 | + import { ExtendDescFormFieldsValueType } from '.'; | ||
8 | + import { BasicModal } from '/@/components/Modal'; | ||
9 | + import { PlusSquareOutlined, MinusSquareOutlined } from '@ant-design/icons-vue'; | ||
10 | + import { | ||
11 | + ExtendDescOperationTypeEnum, | ||
12 | + ExtendDescOperationTypeNameEnum, | ||
13 | + OriginalDataTypeNameEnum, | ||
14 | + } from '/@/enums/objectModelEnum'; | ||
15 | + import { EnumList } from '../EnumList'; | ||
9 | 16 | ||
10 | const show = ref(false); | 17 | const show = ref(false); |
11 | 18 | ||
12 | const props = withDefaults( | 19 | const props = withDefaults( |
13 | defineProps<{ | 20 | defineProps<{ |
14 | - value?: ExtensionDesc; | 21 | + value?: ExtendDescFormFieldsValueType; |
15 | disabled?: boolean; | 22 | disabled?: boolean; |
16 | }>(), | 23 | }>(), |
17 | { | 24 | { |
18 | - value: () => ({} as ExtensionDesc), | 25 | + value: () => ({} as ExtendDescFormFieldsValueType), |
19 | } | 26 | } |
20 | ); | 27 | ); |
21 | 28 | ||
22 | - const emit = defineEmits(['update:value']); | 29 | + const emit = defineEmits(['update:value', 'change']); |
23 | 30 | ||
24 | const [registerForm, { setFieldsValue, getFieldsValue, setProps, validate, resetFields }] = | 31 | const [registerForm, { setFieldsValue, getFieldsValue, setProps, validate, resetFields }] = |
25 | useForm({ | 32 | useForm({ |
26 | - schemas: getFormSchemas(), | 33 | + schemas: getExtendDescFormSchemas(), |
27 | showActionButtonGroup: false, | 34 | showActionButtonGroup: false, |
28 | }); | 35 | }); |
29 | 36 | ||
37 | + const infoExpandFlag = ref(false); | ||
38 | + | ||
39 | + const enumListRef = ref<InstanceType<typeof EnumList>>(); | ||
40 | + | ||
30 | const handleClick = async () => { | 41 | const handleClick = async () => { |
31 | show.value = true; | 42 | show.value = true; |
32 | await nextTick(); | 43 | await nextTick(); |
@@ -37,24 +48,111 @@ | @@ -37,24 +48,111 @@ | ||
37 | 48 | ||
38 | const handleSubmit = async () => { | 49 | const handleSubmit = async () => { |
39 | await validate(); | 50 | await validate(); |
40 | - const value = getFieldsValue(); | 51 | + await unref(enumListRef)?.validate(); |
52 | + const value = getFieldsValue() as ExtendDescFormFieldsValueType; | ||
53 | + const valueMapping = unref(enumListRef)?.getFieldsValue(); | ||
54 | + value.valueMapping = valueMapping; | ||
41 | emit('update:value', value); | 55 | emit('update:value', value); |
56 | + emit('change', value); | ||
42 | show.value = false; | 57 | show.value = false; |
43 | }; | 58 | }; |
44 | 59 | ||
60 | + const handleDelete = () => { | ||
61 | + emit('update:value', {}); | ||
62 | + emit('change', {}); | ||
63 | + }; | ||
64 | + | ||
45 | watch(show, async (value) => { | 65 | watch(show, async (value) => { |
46 | if (value) { | 66 | if (value) { |
47 | await nextTick(); | 67 | await nextTick(); |
48 | setFieldsValue({ ...props.value }); | 68 | setFieldsValue({ ...props.value }); |
49 | } | 69 | } |
50 | }); | 70 | }); |
71 | + | ||
72 | + const getOperationTypeName = (operationType: ExtendDescOperationTypeEnum) => { | ||
73 | + const key = Object.keys(ExtendDescOperationTypeEnum).find( | ||
74 | + (key) => ExtendDescOperationTypeEnum[key] === operationType | ||
75 | + ); | ||
76 | + | ||
77 | + return key ? ExtendDescOperationTypeNameEnum[key] : key; | ||
78 | + }; | ||
51 | </script> | 79 | </script> |
52 | 80 | ||
53 | <template> | 81 | <template> |
54 | <section> | 82 | <section> |
55 | - <Button type="link" @click="handleClick"><PlusCircleOutlined />新增扩展描述</Button> | ||
56 | - <BasicModal title="扩展描述" v-model:visible="show" @ok="handleSubmit" :show-ok-btn="!disabled"> | ||
57 | - <BasicForm class="extension-form" @register="registerForm" /> | 83 | + <Button v-if="!value.operationType" type="link" @click="handleClick"> |
84 | + <PlusCircleOutlined /> | ||
85 | + 新增扩展描述 | ||
86 | + </Button> | ||
87 | + <main v-if="value.operationType" class="flex"> | ||
88 | + <div class="flex-auto flex text-gray-400 bg-blue-50 text-xs p-2 border gap-2 border-gray-100"> | ||
89 | + <div text-base> | ||
90 | + <PlusSquareOutlined | ||
91 | + v-if="!infoExpandFlag" | ||
92 | + class="cursor-pointer" | ||
93 | + @click="infoExpandFlag = !infoExpandFlag" | ||
94 | + /> | ||
95 | + <MinusSquareOutlined | ||
96 | + v-else | ||
97 | + class="cursor-pointer" | ||
98 | + @click="infoExpandFlag = !infoExpandFlag" | ||
99 | + /> | ||
100 | + </div> | ||
101 | + <div> | ||
102 | + <div class="mb-2"> | ||
103 | + <span class="text-gray-400">操作类型</span> | ||
104 | + <span class="ml-2">{{ getOperationTypeName(value.operationType) }}</span> | ||
105 | + </div> | ||
106 | + <section | ||
107 | + class="flex flex-col gap-2 overflow-hidden transition" | ||
108 | + :class="infoExpandFlag ? '' : 'h-0'" | ||
109 | + > | ||
110 | + <div> | ||
111 | + <span>寄存器地址:</span> | ||
112 | + <span class="ml-2">{{ value.registerAddress }}</span> | ||
113 | + </div> | ||
114 | + <div> | ||
115 | + <span>原始数据类型:</span> | ||
116 | + <span class="ml-2">{{ OriginalDataTypeNameEnum[value.originalDataType] }}</span> | ||
117 | + </div> | ||
118 | + <div v-if="value.scaling"> | ||
119 | + <span>缩放因子:</span> | ||
120 | + <span class="ml-2">{{ value.scaling }}</span> | ||
121 | + </div> | ||
122 | + <div v-if="value.valueRange"> | ||
123 | + <span>取值范围:</span> | ||
124 | + <span class="ml-2">{{ value.valueRange?.min }} ~ {{ value.valueRange?.max }}</span> | ||
125 | + </div> | ||
126 | + </section> | ||
127 | + </div> | ||
128 | + </div> | ||
129 | + <div class="ml-2"> | ||
130 | + <Button class="!px-0" @click="handleClick" type="link"> | ||
131 | + {{ disabled ? '查看' : '编辑' }} | ||
132 | + </Button> | ||
133 | + <Divider v-if="!disabled" type="vertical" /> | ||
134 | + <Button class="!px-0" @click="handleDelete" type="link" v-if="!disabled"> 删除 </Button> | ||
135 | + </div> | ||
136 | + </main> | ||
137 | + | ||
138 | + <BasicModal | ||
139 | + title="扩展描述" | ||
140 | + v-model:visible="show" | ||
141 | + @ok="handleSubmit" | ||
142 | + :show-ok-btn="!disabled" | ||
143 | + :width="480" | ||
144 | + > | ||
145 | + <BasicForm class="extension-form" @register="registerForm"> | ||
146 | + <template #valueMapping="{ model, field }"> | ||
147 | + <EnumList | ||
148 | + ref="enumListRef" | ||
149 | + v-model:value="model[field]" | ||
150 | + :disabled="disabled" | ||
151 | + :required="false" | ||
152 | + add-button-name="+添加值映射" | ||
153 | + /> | ||
154 | + </template> | ||
155 | + </BasicForm> | ||
58 | </BasicModal> | 156 | </BasicModal> |
59 | </section> | 157 | </section> |
60 | </template> | 158 | </template> |
1 | +import { | ||
2 | + DataTypeEnum, | ||
3 | + ExtendDescOperationTypeEnum, | ||
4 | + ObjectModelAccessModeEnum, | ||
5 | + OriginalDataTypeEnum, | ||
6 | +} from '/@/enums/objectModelEnum'; | ||
7 | + | ||
8 | +export function isNumberType(originalDataType: OriginalDataTypeEnum) { | ||
9 | + return ![ | ||
10 | + OriginalDataTypeEnum.BITS, | ||
11 | + OriginalDataTypeEnum.BOOLEAN, | ||
12 | + OriginalDataTypeEnum.STRING, | ||
13 | + ].includes(originalDataType); | ||
14 | +} | ||
15 | + | ||
16 | +export function isFloatType(originalDataType: OriginalDataTypeEnum) { | ||
17 | + return [ | ||
18 | + OriginalDataTypeEnum.FLOAT_AB_CD, | ||
19 | + OriginalDataTypeEnum.FLOAT_BA_DC, | ||
20 | + OriginalDataTypeEnum.FLOAT_CD_AB, | ||
21 | + OriginalDataTypeEnum.FLOAT_DC_BA, | ||
22 | + OriginalDataTypeEnum.DOUBLE, | ||
23 | + ].includes(originalDataType); | ||
24 | +} | ||
25 | + | ||
26 | +export function getDataTypeByOriginalDataType(originalDataType: OriginalDataTypeEnum) { | ||
27 | + if (originalDataType === OriginalDataTypeEnum.STRING) return DataTypeEnum.STRING; | ||
28 | + else if (originalDataType === OriginalDataTypeEnum.BOOLEAN) return DataTypeEnum.BOOL; | ||
29 | + else if (isFloatType(originalDataType)) return DataTypeEnum.NUMBER_DOUBLE; | ||
30 | + else return DataTypeEnum.NUMBER_INT; | ||
31 | +} | ||
32 | + | ||
33 | +export function useParseOperationType(actionType: ExtendDescOperationTypeEnum) { | ||
34 | + const [_, accessMode, inputOrOutput, output = undefined] = (actionType || '').split('_'); | ||
35 | + | ||
36 | + const _accessMode = | ||
37 | + accessMode === ObjectModelAccessModeEnum.READ | ||
38 | + ? ObjectModelAccessModeEnum.READ | ||
39 | + : ObjectModelAccessModeEnum.READ_AND_WRITE; | ||
40 | + | ||
41 | + const writeOnly = | ||
42 | + _accessMode === ObjectModelAccessModeEnum.READ_AND_WRITE && | ||
43 | + accessMode !== ObjectModelAccessModeEnum.READ_AND_WRITE; | ||
44 | + | ||
45 | + return { | ||
46 | + accessMode: _accessMode, | ||
47 | + originAccessMode: accessMode, | ||
48 | + writeOnly, | ||
49 | + readRegisterAddress: writeOnly ? undefined : inputOrOutput, | ||
50 | + writeRegisterAddress: writeOnly ? inputOrOutput : output, | ||
51 | + }; | ||
52 | +} |
1 | +<script setup lang="ts"> | ||
2 | + import { InputGroup, Select, Input } from 'ant-design-vue'; | ||
3 | + import { computed, ref, unref } from 'vue'; | ||
4 | + import { InputTypeEnum } from './config'; | ||
5 | + import { isNullOrUnDef } from '/@/utils/is'; | ||
6 | + | ||
7 | + const props = withDefaults( | ||
8 | + defineProps<{ | ||
9 | + type?: InputTypeEnum; | ||
10 | + value?: number | string; | ||
11 | + hexWithPrefix?: boolean; | ||
12 | + hexUpperCase?: boolean; | ||
13 | + disabled?: boolean; | ||
14 | + maxValue?: number; | ||
15 | + showScaleSelect?: boolean; | ||
16 | + }>(), | ||
17 | + { | ||
18 | + type: InputTypeEnum.HEX, | ||
19 | + hexWithPrefix: false, | ||
20 | + hexUpperCase: true, | ||
21 | + maxValue: Number.MAX_SAFE_INTEGER, | ||
22 | + showScaleSelect: true, | ||
23 | + } | ||
24 | + ); | ||
25 | + | ||
26 | + const emits = defineEmits(['update:value', 'change']); | ||
27 | + | ||
28 | + const typOptions = Object.values(InputTypeEnum).map((value) => ({ label: value, value })); | ||
29 | + | ||
30 | + const inputType = ref(props.type); | ||
31 | + | ||
32 | + const validateDECReg = /^[\d]+$/; | ||
33 | + | ||
34 | + const validateHEXReg = /^(0x)?[\da-fA-F]+$/; | ||
35 | + | ||
36 | + const getMaxValue = computed(() => { | ||
37 | + const { maxValue } = props; | ||
38 | + return maxValue; | ||
39 | + }); | ||
40 | + | ||
41 | + const getDecToHex = (value: number | string) => { | ||
42 | + const { hexUpperCase, hexWithPrefix } = props; | ||
43 | + let hex = Number(value).toString(16); | ||
44 | + hex = hexUpperCase ? hex.toUpperCase() : hex; | ||
45 | + return `${hexWithPrefix ? '0x' : ''}${hex}`; | ||
46 | + }; | ||
47 | + | ||
48 | + const getHexToDec = (value: number | string) => { | ||
49 | + return parseInt(value, 16); | ||
50 | + }; | ||
51 | + | ||
52 | + const getDECValue = (value: number | string) => { | ||
53 | + return parseInt(value, unref(inputType) === InputTypeEnum.HEX ? 16 : 10); | ||
54 | + }; | ||
55 | + | ||
56 | + const getFormatValue = (value: number | string) => { | ||
57 | + if (unref(inputType) === InputTypeEnum.HEX) { | ||
58 | + const { hexUpperCase, hexWithPrefix } = props; | ||
59 | + if (hexUpperCase) value = value.toString().toUpperCase(); | ||
60 | + if (hexWithPrefix) value = `0x${value}`; | ||
61 | + return value; | ||
62 | + } | ||
63 | + | ||
64 | + return value; | ||
65 | + }; | ||
66 | + | ||
67 | + const getInputValue = computed({ | ||
68 | + get() { | ||
69 | + const { type, value } = props; | ||
70 | + | ||
71 | + if (isNullOrUnDef(value)) return; | ||
72 | + | ||
73 | + if (type === unref(inputType)) return value; | ||
74 | + | ||
75 | + if (type === InputTypeEnum.HEX && unref(inputType) === InputTypeEnum.DEC) { | ||
76 | + return getHexToDec(value); | ||
77 | + } else { | ||
78 | + return getDecToHex(value); | ||
79 | + } | ||
80 | + }, | ||
81 | + set(value: string) { | ||
82 | + const { type } = props; | ||
83 | + | ||
84 | + if (unref(inputType) === InputTypeEnum.HEX) value = value.replace('0x', ''); | ||
85 | + | ||
86 | + if (!value) { | ||
87 | + emits('update:value', undefined); | ||
88 | + return; | ||
89 | + } | ||
90 | + | ||
91 | + if (unref(inputType) === InputTypeEnum.HEX && !validateHEXReg.test(value)) return; | ||
92 | + | ||
93 | + if (unref(inputType) === InputTypeEnum.DEC && !validateDECReg.test(value)) return; | ||
94 | + | ||
95 | + if (getDECValue(value) > unref(getMaxValue)) return; | ||
96 | + | ||
97 | + if (type === unref(inputType)) { | ||
98 | + emits('update:value', getFormatValue(value)); | ||
99 | + return; | ||
100 | + } | ||
101 | + | ||
102 | + if (type === InputTypeEnum.HEX && unref(inputType) === InputTypeEnum.DEC) { | ||
103 | + emits('update:value', getDecToHex(value)); | ||
104 | + } else { | ||
105 | + emits('update:value', getHexToDec(value)); | ||
106 | + } | ||
107 | + }, | ||
108 | + }); | ||
109 | + | ||
110 | + const getNegationValue = computed(() => { | ||
111 | + const { hexWithPrefix } = props; | ||
112 | + if (isNullOrUnDef(unref(getInputValue))) return 0; | ||
113 | + return unref(inputType) === InputTypeEnum.HEX | ||
114 | + ? getHexToDec(unref(getInputValue)!) | ||
115 | + : `${hexWithPrefix ? '0x' : ''}${Number(unref(getInputValue)).toString(16).toUpperCase()}`; | ||
116 | + }); | ||
117 | +</script> | ||
118 | + | ||
119 | +<template> | ||
120 | + <InputGroup class="!flex w-full h-full justify-center items-center" compact> | ||
121 | + <Select | ||
122 | + v-if="showScaleSelect" | ||
123 | + v-model:value="inputType" | ||
124 | + class="min-w-20" | ||
125 | + :options="typOptions" | ||
126 | + :disabled="disabled" | ||
127 | + /> | ||
128 | + <Input v-bind="$attrs" v-model:value="getInputValue" class="!w-full" :disabled="disabled" /> | ||
129 | + <div class="min-w-14 h-full px-2 flex-grow flex-shrink-0 text-center leading-8 bg-gray-200"> | ||
130 | + {{ getNegationValue }} | ||
131 | + </div> | ||
132 | + </InputGroup> | ||
133 | +</template> |
1 | -import { FormFieldsEnum, FormFieldsNameEnum } from '../config'; | ||
2 | -import { findDictItemByCode } from '/@/api/system/dict'; | ||
3 | -import { FormSchema } from '/@/components/Form'; | ||
4 | -import { DictEnum } from '/@/enums/dictEnum'; | ||
5 | -import { DataTypeEnum, FunctionTypeEnum, FunctionTypeNameEnum } from '/@/enums/objectModelEnum'; | ||
6 | -import { isNullOrUnDef } from '/@/utils/is'; | ||
7 | - | ||
8 | -export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => { | ||
9 | - value = value || {}; | ||
10 | - const { min, max } = value; | ||
11 | - if (min > max) { | ||
12 | - return Promise.reject('最大值小于最小值'); | ||
13 | - } | ||
14 | - return Promise.resolve(); | ||
15 | -}; | ||
16 | - | ||
17 | -export const getFormSchemas = (): FormSchema[] => { | ||
18 | - return [ | ||
19 | - { | ||
20 | - field: FormFieldsEnum.FUNCTION_TYPE, | ||
21 | - label: FormFieldsNameEnum.FUNCTION_TYPE, | ||
22 | - component: 'Segmented', | ||
23 | - defaultValue: FunctionTypeEnum.PROPERTIES, | ||
24 | - required: true, | ||
25 | - componentProps: ({ formActionType }) => { | ||
26 | - return { | ||
27 | - options: Object.keys(FunctionTypeEnum).map((key) => ({ | ||
28 | - title: FunctionTypeNameEnum[key], | ||
29 | - value: FunctionTypeEnum[key], | ||
30 | - })), | ||
31 | - onChange() { | ||
32 | - const { setFieldsValue, clearValidate } = formActionType; | ||
33 | - setFieldsValue({ | ||
34 | - [FormFieldsEnum.INPUT_DATA]: [], | ||
35 | - [FormFieldsEnum.OUTPUT_DATA]: [], | ||
36 | - [FormFieldsEnum.STRUCT_DATA]: [], | ||
37 | - [FormFieldsEnum.SERVICE_COMMAND]: null, | ||
38 | - }); | ||
39 | - clearValidate(); | ||
40 | - }, | ||
41 | - }; | ||
42 | - }, | ||
43 | - }, | ||
44 | - { | ||
45 | - field: FormFieldsEnum.FUNCTION_NAME, | ||
46 | - label: FormFieldsNameEnum.FUNCTION_NAME, | ||
47 | - component: 'Input', | ||
48 | - required: true, | ||
49 | - dynamicRules: ({ values }) => { | ||
50 | - return [ | ||
51 | - { required: true, message: '请输入功能名称' }, | ||
52 | - { | ||
53 | - validator: () => { | ||
54 | - const reg = /[,,]+/; | ||
55 | - if (reg.test(values?.[FormFieldsEnum.FUNCTION_NAME])) { | ||
56 | - return Promise.reject(`${FormFieldsNameEnum.FUNCTION_NAME}不能包含逗号`); | ||
57 | - } | ||
58 | - return Promise.resolve(); | ||
59 | - }, | ||
60 | - }, | ||
61 | - ]; | ||
62 | - }, | ||
63 | - componentProps: { | ||
64 | - placeholder: `请输入${FormFieldsNameEnum.FUNCTION_NAME}`, | ||
65 | - }, | ||
66 | - }, | ||
67 | - { | ||
68 | - field: FormFieldsEnum.IDENTIFIER, | ||
69 | - label: FormFieldsNameEnum.IDENTIFIER, | ||
70 | - required: true, | ||
71 | - component: 'Input', | ||
72 | - componentProps: { | ||
73 | - maxLength: 128, | ||
74 | - placeholder: '请输入标识符', | ||
75 | - }, | ||
76 | - dynamicRules: ({ values }) => { | ||
77 | - return [ | ||
78 | - { required: true, message: '请输入标识符' }, | ||
79 | - { | ||
80 | - validator: () => { | ||
81 | - const reg = /[,,]+/; | ||
82 | - if (reg.test(values?.[FormFieldsEnum.IDENTIFIER])) { | ||
83 | - return Promise.reject(`${FormFieldsNameEnum.IDENTIFIER}不能包含逗号`); | ||
84 | - } | ||
85 | - return Promise.resolve(); | ||
86 | - }, | ||
87 | - }, | ||
88 | - ]; | ||
89 | - }, | ||
90 | - }, | ||
91 | - { | ||
92 | - field: FormFieldsEnum.DATA_TYPE, | ||
93 | - label: FormFieldsNameEnum.DATA_TYPE, | ||
94 | - required: true, | ||
95 | - component: 'ApiSelect', | ||
96 | - defaultValue: DataTypeEnum.NUMBER_INT, | ||
97 | - ifShow: ({ model }) => | ||
98 | - ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
99 | - model[FormFieldsEnum.FUNCTION_TYPE] | ||
100 | - ), | ||
101 | - componentProps: () => { | ||
102 | - return { | ||
103 | - placeholder: '请选择数据类型', | ||
104 | - api: async (params: Recordable) => { | ||
105 | - const result = await findDictItemByCode(params); | ||
106 | - return result; | ||
107 | - }, | ||
108 | - params: { | ||
109 | - dictCode: DictEnum.DATA_TYPE, | ||
110 | - }, | ||
111 | - labelField: 'itemText', | ||
112 | - valueField: 'itemValue', | ||
113 | - getPopupContainer: () => document.body, | ||
114 | - }; | ||
115 | - }, | ||
116 | - }, | ||
117 | - { | ||
118 | - field: FormFieldsEnum.VALUE_RANGE, | ||
119 | - label: FormFieldsNameEnum.VALUE_RANGE, | ||
120 | - component: 'CustomMinMaxInput', | ||
121 | - valueField: 'value', | ||
122 | - changeEvent: 'update:value', | ||
123 | - ifShow: ({ model }) => | ||
124 | - ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
125 | - model[FormFieldsEnum.FUNCTION_TYPE] | ||
126 | - ) && | ||
127 | - (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
128 | - model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
129 | - rules: [{ validator: validateValueRange }], | ||
130 | - }, | ||
131 | - { | ||
132 | - field: FormFieldsEnum.STEP, | ||
133 | - label: FormFieldsNameEnum.STEP, | ||
134 | - component: 'InputNumber', | ||
135 | - componentProps: { | ||
136 | - maxLength: 255, | ||
137 | - placeholder: '请输入步长', | ||
138 | - min: 1, | ||
139 | - formatter: (value: number | string) => { | ||
140 | - return value ? Math.floor(Number(value)) : value; | ||
141 | - }, | ||
142 | - }, | ||
143 | - ifShow: ({ model }) => | ||
144 | - ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
145 | - model[FormFieldsEnum.FUNCTION_TYPE] | ||
146 | - ) && | ||
147 | - (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
148 | - model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
149 | - dynamicRules: ({ model }) => { | ||
150 | - const valueRange = model[FormFieldsEnum.VALUE_RANGE] || {}; | ||
151 | - const { min, max } = valueRange; | ||
152 | - const step = model[FormFieldsEnum.STEP]; | ||
153 | - return [ | ||
154 | - { | ||
155 | - validator: () => { | ||
156 | - if ([min, max].every(isNullOrUnDef)) return Promise.resolve(); | ||
157 | - if (step > max - min) { | ||
158 | - return Promise.reject('步长不能大于取值范围的差值'); | ||
159 | - } | ||
160 | - return Promise.resolve(); | ||
161 | - }, | ||
162 | - }, | ||
163 | - ]; | ||
164 | - }, | ||
165 | - }, | ||
166 | - { | ||
167 | - field: FormFieldsEnum.UNIT_NAME, | ||
168 | - label: FormFieldsNameEnum.UNIT_NAME, | ||
169 | - component: 'Input', | ||
170 | - show: false, | ||
171 | - }, | ||
172 | - { | ||
173 | - field: FormFieldsEnum.UNIT, | ||
174 | - label: FormFieldsNameEnum.UNIT, | ||
175 | - component: 'ApiSelect', | ||
176 | - componentProps: ({ formActionType }) => { | ||
177 | - const { setFieldsValue } = formActionType; | ||
178 | - return { | ||
179 | - placeholder: '请选择单位', | ||
180 | - api: async (params: Recordable) => { | ||
181 | - const list = await findDictItemByCode(params); | ||
182 | - list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`)); | ||
183 | - return list; | ||
184 | - }, | ||
185 | - params: { | ||
186 | - dictCode: DictEnum.ATTRIBUTE_UNIT, | ||
187 | - }, | ||
188 | - labelInValue: true, | ||
189 | - labelField: 'itemText', | ||
190 | - valueField: 'itemValue', | ||
191 | - onChange(_, record: Record<'label' | 'value', string>) { | ||
192 | - if (record) { | ||
193 | - const { label } = record; | ||
194 | - setFieldsValue({ [FormFieldsEnum.UNIT_NAME]: label }); | ||
195 | - } | ||
196 | - }, | ||
197 | - getPopupContainer: () => document.body, | ||
198 | - showSearch: true, | ||
199 | - filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | ||
200 | - let { label, value } = option; | ||
201 | - label = label.toLowerCase(); | ||
202 | - value = value.toLowerCase(); | ||
203 | - inputValue = inputValue.toLowerCase(); | ||
204 | - return label.includes(inputValue) || value.includes(inputValue); | ||
205 | - }, | ||
206 | - }; | ||
207 | - }, | ||
208 | - ifShow: ({ model }) => | ||
209 | - ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
210 | - model[FormFieldsEnum.FUNCTION_TYPE] | ||
211 | - ) && | ||
212 | - (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
213 | - model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
214 | - }, | ||
215 | - { | ||
216 | - field: FormFieldsEnum.BOOL_CLOSE, | ||
217 | - component: 'Input', | ||
218 | - required: true, | ||
219 | - label: FormFieldsNameEnum.BOOL_CLOSE, | ||
220 | - componentProps: { | ||
221 | - placeholder: '如:关', | ||
222 | - }, | ||
223 | - defaultValue: '关', | ||
224 | - ifShow: ({ model }) => | ||
225 | - ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
226 | - model[FormFieldsEnum.FUNCTION_TYPE] | ||
227 | - ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | ||
228 | - dynamicRules: ({ model }) => { | ||
229 | - const close = model[FormFieldsEnum.BOOL_CLOSE]; | ||
230 | - const open = model[FormFieldsEnum.BOOL_OPEN]; | ||
231 | - return [ | ||
232 | - { | ||
233 | - required: true, | ||
234 | - message: `布尔值不能为空`, | ||
235 | - }, | ||
236 | - { | ||
237 | - validator() { | ||
238 | - if (open === close) return Promise.reject('布尔值不能相同'); | ||
239 | - return Promise.resolve(); | ||
240 | - }, | ||
241 | - }, | ||
242 | - ]; | ||
243 | - }, | ||
244 | - }, | ||
245 | - { | ||
246 | - field: FormFieldsEnum.BOOL_OPEN, | ||
247 | - component: 'Input', | ||
248 | - required: true, | ||
249 | - label: FormFieldsNameEnum.BOOL_OPEN, | ||
250 | - componentProps: { | ||
251 | - placeholder: '如:开', | ||
252 | - }, | ||
253 | - defaultValue: '开', | ||
254 | - ifShow: ({ model }) => | ||
255 | - ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
256 | - model[FormFieldsEnum.FUNCTION_TYPE] | ||
257 | - ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | ||
258 | - dynamicRules: ({ model }) => { | ||
259 | - const close = model[FormFieldsEnum.BOOL_CLOSE]; | ||
260 | - const open = model[FormFieldsEnum.BOOL_OPEN]; | ||
261 | - return [ | ||
262 | - { | ||
263 | - required: true, | ||
264 | - message: `布尔值不能为空`, | ||
265 | - }, | ||
266 | - { | ||
267 | - validator() { | ||
268 | - if (open === close) return Promise.reject('布尔值不能相同'); | ||
269 | - return Promise.resolve(); | ||
270 | - }, | ||
271 | - }, | ||
272 | - ]; | ||
273 | - }, | ||
274 | - }, | ||
275 | - { | ||
276 | - field: FormFieldsEnum.LENGTH, | ||
277 | - component: 'Input', | ||
278 | - required: true, | ||
279 | - label: FormFieldsNameEnum.LENGTH, | ||
280 | - defaultValue: '10240', | ||
281 | - colProps: { | ||
282 | - span: 8, | ||
283 | - }, | ||
284 | - componentProps: { | ||
285 | - placeholder: '请输入数据长度', | ||
286 | - }, | ||
287 | - renderComponentContent: () => { | ||
288 | - return { | ||
289 | - suffix: () => '字节', | ||
290 | - }; | ||
291 | - }, | ||
292 | - ifShow: ({ model }) => | ||
293 | - ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
294 | - model[FormFieldsEnum.FUNCTION_TYPE] | ||
295 | - ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRING, | ||
296 | - }, | ||
297 | - { | ||
298 | - field: FormFieldsEnum.ENUMS_DATA, | ||
299 | - label: FormFieldsNameEnum.ENUMS_DATA, | ||
300 | - component: 'Input', | ||
301 | - slot: FormFieldsEnum.ENUMS_DATA, | ||
302 | - changeEvent: 'update:value', | ||
303 | - valueField: 'value', | ||
304 | - ifShow: ({ model }) => | ||
305 | - ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
306 | - model[FormFieldsEnum.FUNCTION_TYPE] | ||
307 | - ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.ENUM, | ||
308 | - }, | ||
309 | - { | ||
310 | - field: FormFieldsEnum.STRUCT_DATA, | ||
311 | - label: FormFieldsNameEnum.STRUCT_DATA, | ||
312 | - component: 'Input', | ||
313 | - slot: FormFieldsEnum.STRUCT_DATA, | ||
314 | - changeEvent: 'update:value', | ||
315 | - valueField: 'value', | ||
316 | - required: true, | ||
317 | - ifShow: ({ model }) => | ||
318 | - model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRUCT && | ||
319 | - ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
320 | - model[FormFieldsEnum.FUNCTION_TYPE] | ||
321 | - ), | ||
322 | - }, | ||
323 | - ]; | ||
324 | -}; |
@@ -7,6 +7,7 @@ import { FormSchema } from '/@/components/Form'; | @@ -7,6 +7,7 @@ import { FormSchema } from '/@/components/Form'; | ||
7 | import { | 7 | import { |
8 | ServiceCallTypeEnum, | 8 | ServiceCallTypeEnum, |
9 | ServiceCallTypeNameEnum, | 9 | ServiceCallTypeNameEnum, |
10 | + TCPProtocolTypeEnum, | ||
10 | TransportTypeEnum, | 11 | TransportTypeEnum, |
11 | } from '/@/enums/deviceEnum'; | 12 | } from '/@/enums/deviceEnum'; |
12 | import { DictEnum } from '/@/enums/dictEnum'; | 13 | import { DictEnum } from '/@/enums/dictEnum'; |
@@ -15,8 +16,11 @@ import { | @@ -15,8 +16,11 @@ import { | ||
15 | FunctionTypeEnum, | 16 | FunctionTypeEnum, |
16 | FunctionTypeNameEnum, | 17 | FunctionTypeNameEnum, |
17 | ObjectEventTypeEnum, | 18 | ObjectEventTypeEnum, |
19 | + BuiltInIdentifierEnum, | ||
18 | } from '/@/enums/objectModelEnum'; | 20 | } from '/@/enums/objectModelEnum'; |
19 | import { DataActionModeEnum } from '/@/enums/toolEnum'; | 21 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
22 | +import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | ||
23 | +import { DeviceProfileDetail } from '/@/api/device/model/deviceConfigModel'; | ||
20 | 24 | ||
21 | export enum FormFieldsEnum { | 25 | export enum FormFieldsEnum { |
22 | FUNCTION_TYPE = 'functionType', | 26 | FUNCTION_TYPE = 'functionType', |
@@ -74,10 +78,18 @@ export enum FormFieldsNameEnum { | @@ -74,10 +78,18 @@ export enum FormFieldsNameEnum { | ||
74 | 78 | ||
75 | export const getFormSchemas = ({ | 79 | export const getFormSchemas = ({ |
76 | transportType, | 80 | transportType, |
81 | + deviceRecord, | ||
77 | }: { | 82 | }: { |
78 | transportType?: string; | 83 | transportType?: string; |
79 | mode?: DataActionModeEnum; | 84 | mode?: DataActionModeEnum; |
85 | + deviceRecord?: DeviceProfileDetail; | ||
80 | }): FormSchema[] => { | 86 | }): FormSchema[] => { |
87 | + const isTCPTransport = deviceRecord?.transportType === TransportTypeEnum.TCP; | ||
88 | + | ||
89 | + const isTCPModbusProduct = | ||
90 | + isTCPTransport && | ||
91 | + deviceRecord?.profileData.transportConfiguration.protocol === TCPProtocolTypeEnum.MODBUS_RTU; | ||
92 | + | ||
81 | return [ | 93 | return [ |
82 | { | 94 | { |
83 | field: FormFieldsEnum.FUNCTION_TYPE, | 95 | field: FormFieldsEnum.FUNCTION_TYPE, |
@@ -85,16 +97,29 @@ export const getFormSchemas = ({ | @@ -85,16 +97,29 @@ export const getFormSchemas = ({ | ||
85 | component: 'Segmented', | 97 | component: 'Segmented', |
86 | defaultValue: FunctionTypeEnum.PROPERTIES, | 98 | defaultValue: FunctionTypeEnum.PROPERTIES, |
87 | required: true, | 99 | required: true, |
100 | + helpMessage: [ | ||
101 | + '属性一般是指设备的运行状态,如当前温度等;服务是指设备可被调用的方法,支持定义参数,如执行某项任务;事件则是指设备上报的通知,如告警,需要被及时处理。', | ||
102 | + ], | ||
88 | dynamicDisabled: () => { | 103 | dynamicDisabled: () => { |
89 | const { getModalMode } = useObjectModelFormContext(); | 104 | const { getModalMode } = useObjectModelFormContext(); |
90 | return unref(getModalMode) !== DataActionModeEnum.CREATE; | 105 | return unref(getModalMode) !== DataActionModeEnum.CREATE; |
91 | }, | 106 | }, |
92 | componentProps: ({ formActionType }) => { | 107 | componentProps: ({ formActionType }) => { |
108 | + const isSensor = deviceRecord?.deviceType === DeviceTypeEnum.SENSOR; | ||
93 | return { | 109 | return { |
94 | - options: Object.keys(FunctionTypeEnum).map((key) => ({ | ||
95 | - title: FunctionTypeNameEnum[key], | ||
96 | - value: FunctionTypeEnum[key], | ||
97 | - })), | 110 | + options: [ |
111 | + { title: FunctionTypeNameEnum.PROPERTIES, value: FunctionTypeEnum.PROPERTIES }, | ||
112 | + { | ||
113 | + title: FunctionTypeNameEnum.SERVICE, | ||
114 | + value: FunctionTypeEnum.SERVICE, | ||
115 | + disabled: (isTCPTransport && isSensor) || isTCPModbusProduct, | ||
116 | + }, | ||
117 | + { | ||
118 | + title: FunctionTypeNameEnum.EVENTS, | ||
119 | + value: FunctionTypeEnum.EVENTS, | ||
120 | + disabled: isTCPTransport, | ||
121 | + }, | ||
122 | + ], | ||
98 | onChange() { | 123 | onChange() { |
99 | const { setFieldsValue, clearValidate } = formActionType; | 124 | const { setFieldsValue, clearValidate } = formActionType; |
100 | setFieldsValue({ | 125 | setFieldsValue({ |
@@ -112,26 +137,26 @@ export const getFormSchemas = ({ | @@ -112,26 +137,26 @@ export const getFormSchemas = ({ | ||
112 | field: FormFieldsEnum.DATA_TYPE_FORM, | 137 | field: FormFieldsEnum.DATA_TYPE_FORM, |
113 | component: 'Input', | 138 | component: 'Input', |
114 | label: '', | 139 | label: '', |
115 | - ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.PROPERTIES, | 140 | + ifShow: ({ model }) => |
141 | + model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.PROPERTIES && !isTCPModbusProduct, | ||
116 | colSlot: FormFieldsEnum.DATA_TYPE_FORM, | 142 | colSlot: FormFieldsEnum.DATA_TYPE_FORM, |
117 | }, | 143 | }, |
118 | createFunctionNameFormItem({ | 144 | createFunctionNameFormItem({ |
119 | - ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] !== FunctionTypeEnum.PROPERTIES, | 145 | + ifShow: ({ model }) => |
146 | + model[FormFieldsEnum.FUNCTION_TYPE] !== FunctionTypeEnum.PROPERTIES || isTCPModbusProduct, | ||
120 | }), | 147 | }), |
121 | createIdentifierFormItem({ | 148 | createIdentifierFormItem({ |
122 | - ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] !== FunctionTypeEnum.PROPERTIES, | ||
123 | - }), | ||
124 | - { | ||
125 | - field: FormFieldsEnum.EXTENSION_DESC, | ||
126 | - component: 'Input', | ||
127 | - label: FormFieldsNameEnum.EXTENSION_DESC, | ||
128 | - slot: FormFieldsEnum.EXTENSION_DESC, | 149 | + helpMessage: ['支持大小写字母、数字和下划线', '如要支持原始数据留存,请使用标识符 "source"'], |
129 | ifShow: ({ model }) => | 150 | ifShow: ({ model }) => |
130 | - transportType === TransportTypeEnum.TCP && | ||
131 | - ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
132 | - model[FormFieldsEnum.FUNCTION_TYPE] | ||
133 | - ), | ||
134 | - }, | 151 | + model[FormFieldsEnum.FUNCTION_TYPE] !== FunctionTypeEnum.PROPERTIES || isTCPModbusProduct, |
152 | + component: 'AutoComplete', | ||
153 | + defaultValue: '', | ||
154 | + componentProps: { | ||
155 | + options: isTCPModbusProduct ? [{ value: BuiltInIdentifierEnum.SOURCE }] : [], | ||
156 | + placeholder: '请输入功能标识符', | ||
157 | + autocomplete: false, | ||
158 | + }, | ||
159 | + }), | ||
135 | { | 160 | { |
136 | field: FormFieldsEnum.ACCESS_MODE, | 161 | field: FormFieldsEnum.ACCESS_MODE, |
137 | component: 'ApiRadioGroup', | 162 | component: 'ApiRadioGroup', |
@@ -140,7 +165,7 @@ export const getFormSchemas = ({ | @@ -140,7 +165,7 @@ export const getFormSchemas = ({ | ||
140 | ifShow: ({ model }) => | 165 | ifShow: ({ model }) => |
141 | ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | 166 | ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( |
142 | model[FormFieldsEnum.FUNCTION_TYPE] | 167 | model[FormFieldsEnum.FUNCTION_TYPE] |
143 | - ), | 168 | + ) && !isTCPModbusProduct, |
144 | defaultValue: ObjectModelAccessModeEnum.READ_AND_WRITE, | 169 | defaultValue: ObjectModelAccessModeEnum.READ_AND_WRITE, |
145 | componentProps: { | 170 | componentProps: { |
146 | placeholder: '请选择读写类型', | 171 | placeholder: '请选择读写类型', |
@@ -232,6 +257,70 @@ export const getFormSchemas = ({ | @@ -232,6 +257,70 @@ export const getFormSchemas = ({ | ||
232 | ), | 257 | ), |
233 | }, | 258 | }, |
234 | 259 | ||
260 | + // TCP Modbus | ||
261 | + { | ||
262 | + field: FormFieldsEnum.UNIT_NAME, | ||
263 | + label: FormFieldsNameEnum.UNIT_NAME, | ||
264 | + component: 'Input', | ||
265 | + show: false, | ||
266 | + }, | ||
267 | + { | ||
268 | + field: FormFieldsEnum.UNIT, | ||
269 | + label: FormFieldsNameEnum.UNIT, | ||
270 | + component: 'ApiSelect', | ||
271 | + ifShow: () => isTCPModbusProduct, | ||
272 | + componentProps: ({ formActionType }) => { | ||
273 | + const { setFieldsValue } = formActionType; | ||
274 | + return { | ||
275 | + placeholder: '请选择单位', | ||
276 | + api: async (params: Recordable) => { | ||
277 | + const list = await findDictItemByCode(params); | ||
278 | + list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`)); | ||
279 | + return list; | ||
280 | + }, | ||
281 | + params: { | ||
282 | + dictCode: DictEnum.ATTRIBUTE_UNIT, | ||
283 | + }, | ||
284 | + labelField: 'itemText', | ||
285 | + valueField: 'itemValue', | ||
286 | + onChange(_, record: Record<'label' | 'value', string>) { | ||
287 | + if (record) { | ||
288 | + const { label } = record; | ||
289 | + setFieldsValue({ [FormFieldsEnum.UNIT_NAME]: label }); | ||
290 | + } | ||
291 | + }, | ||
292 | + getPopupContainer: () => document.body, | ||
293 | + showSearch: true, | ||
294 | + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | ||
295 | + let { label, value } = option; | ||
296 | + label = label.toLowerCase(); | ||
297 | + value = value.toLowerCase(); | ||
298 | + inputValue = inputValue.toLowerCase(); | ||
299 | + return label.includes(inputValue) || value.includes(inputValue); | ||
300 | + }, | ||
301 | + }; | ||
302 | + }, | ||
303 | + }, | ||
304 | + { | ||
305 | + field: FormFieldsEnum.EXTENSION_DESC, | ||
306 | + component: 'Input', | ||
307 | + label: FormFieldsNameEnum.EXTENSION_DESC, | ||
308 | + slot: FormFieldsEnum.EXTENSION_DESC, | ||
309 | + helpMessage: ['扩展描述用于配置设备接入网关协议和物模型定义之间的映射关系。'], | ||
310 | + rules: [ | ||
311 | + { | ||
312 | + required: true, | ||
313 | + validator(_rule, value: any) { | ||
314 | + return Object.values(value || {}).length | ||
315 | + ? Promise.resolve() | ||
316 | + : Promise.reject('请填写拓展描述符'); | ||
317 | + }, | ||
318 | + }, | ||
319 | + ], | ||
320 | + ifShow: ({ model }) => | ||
321 | + isTCPModbusProduct && model[FormFieldsEnum.IDENTIFIER] !== BuiltInIdentifierEnum.SOURCE, | ||
322 | + }, | ||
323 | + | ||
235 | { | 324 | { |
236 | field: FormFieldsEnum.REMARK, | 325 | field: FormFieldsEnum.REMARK, |
237 | label: FormFieldsNameEnum.REMARK, | 326 | label: FormFieldsNameEnum.REMARK, |
@@ -3,18 +3,14 @@ | @@ -3,18 +3,14 @@ | ||
3 | import { getFormSchemas, FormFieldsEnum } from './config'; | 3 | import { getFormSchemas, FormFieldsEnum } from './config'; |
4 | import { StructFormItem } from './StructFormItem'; | 4 | import { StructFormItem } from './StructFormItem'; |
5 | import { useObjectFormData } from './useObjectFormData'; | 5 | import { useObjectFormData } from './useObjectFormData'; |
6 | - import { computed, ref, unref } from 'vue'; | ||
7 | - import { TransportTypeEnum } from '/@/enums/deviceEnum'; | 6 | + import { computed, ref } from 'vue'; |
8 | import { DataTypeForm } from './DataTypeForm'; | 7 | import { DataTypeForm } from './DataTypeForm'; |
9 | import { DataActionModeEnum } from '/@/enums/toolEnum'; | 8 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
10 | import { ExtendDesc } from './ExtendDesc'; | 9 | import { ExtendDesc } from './ExtendDesc'; |
11 | import { createObjectModelFormContext } from './useObjectModelFormContext'; | 10 | import { createObjectModelFormContext } from './useObjectModelFormContext'; |
12 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 11 | + import { ObjectModelFormPropsType } from './types'; |
13 | 12 | ||
14 | - const props = defineProps<{ | ||
15 | - transportType?: TransportTypeEnum; | ||
16 | - mode?: DataActionModeEnum; | ||
17 | - }>(); | 13 | + const props = defineProps<ObjectModelFormPropsType>(); |
18 | 14 | ||
19 | const dataTypeFormRef = ref<InstanceType<typeof DataTypeForm>>(); | 15 | const dataTypeFormRef = ref<InstanceType<typeof DataTypeForm>>(); |
20 | 16 | ||
@@ -23,27 +19,20 @@ | @@ -23,27 +19,20 @@ | ||
23 | layout: 'vertical', | 19 | layout: 'vertical', |
24 | showActionButtonGroup: false, | 20 | showActionButtonGroup: false, |
25 | }); | 21 | }); |
26 | - | ||
27 | const { getFieldsValue, setFieldsValue, validate, resetFieldsValue } = useObjectFormData({ | 22 | const { getFieldsValue, setFieldsValue, validate, resetFieldsValue } = useObjectFormData({ |
28 | formActionType, | 23 | formActionType, |
29 | dataTypeFormRef, | 24 | dataTypeFormRef, |
30 | - transportType: props.transportType!, | 25 | + propsRef: props, |
31 | }); | 26 | }); |
32 | 27 | ||
33 | - const objectModelType = ref(DataTypeEnum.NUMBER_INT); | ||
34 | - | ||
35 | createObjectModelFormContext({ | 28 | createObjectModelFormContext({ |
36 | getTransportType: computed(() => props.transportType), | 29 | getTransportType: computed(() => props.transportType), |
37 | - getDataType: computed(() => unref(objectModelType)), | ||
38 | getModalMode: computed(() => props.mode), | 30 | getModalMode: computed(() => props.mode), |
39 | }); | 31 | }); |
40 | 32 | ||
41 | - const handleDataTypeFormChange = (field: string, value: any) => { | ||
42 | - if (field === FormFieldsEnum.DATA_TYPE) { | ||
43 | - objectModelType.value = value; | ||
44 | - } | 33 | + const handleValidateExtendDesc = () => { |
34 | + formActionType.clearValidate(FormFieldsEnum.EXTENSION_DESC); | ||
45 | }; | 35 | }; |
46 | - | ||
47 | defineExpose({ | 36 | defineExpose({ |
48 | getFieldsValue, | 37 | getFieldsValue, |
49 | setFieldsValue, | 38 | setFieldsValue, |
@@ -55,11 +44,7 @@ | @@ -55,11 +44,7 @@ | ||
55 | <template> | 44 | <template> |
56 | <BasicForm @register="register" :disabled="mode === DataActionModeEnum.READ"> | 45 | <BasicForm @register="register" :disabled="mode === DataActionModeEnum.READ"> |
57 | <template #dataTypeForm> | 46 | <template #dataTypeForm> |
58 | - <DataTypeForm | ||
59 | - ref="dataTypeFormRef" | ||
60 | - :mode="mode" | ||
61 | - @field-value-change="handleDataTypeFormChange" | ||
62 | - /> | 47 | + <DataTypeForm ref="dataTypeFormRef" :mode="mode" /> |
63 | </template> | 48 | </template> |
64 | <template #inputData="{ model, field }"> | 49 | <template #inputData="{ model, field }"> |
65 | <StructFormItem v-model:value="model[field]" buttonName="+ 增加参数" :mode="mode" /> | 50 | <StructFormItem v-model:value="model[field]" buttonName="+ 增加参数" :mode="mode" /> |
@@ -68,7 +53,11 @@ | @@ -68,7 +53,11 @@ | ||
68 | <StructFormItem v-model:value="model[field]" buttonName="+ 增加参数" :mode="mode" /> | 53 | <StructFormItem v-model:value="model[field]" buttonName="+ 增加参数" :mode="mode" /> |
69 | </template> | 54 | </template> |
70 | <template #extensionDesc="{ model, field }"> | 55 | <template #extensionDesc="{ model, field }"> |
71 | - <ExtendDesc v-model:value="model[field]" :disabled="mode === DataActionModeEnum.READ" /> | 56 | + <ExtendDesc |
57 | + v-model:value="model[field]" | ||
58 | + :disabled="mode === DataActionModeEnum.READ" | ||
59 | + @change="handleValidateExtendDesc" | ||
60 | + /> | ||
72 | </template> | 61 | </template> |
73 | </BasicForm> | 62 | </BasicForm> |
74 | </template> | 63 | </template> |
1 | +import { ExtendDescFormFieldsValueType } from './ExtendDesc'; | ||
1 | import { FormFieldsEnum } from './config'; | 2 | import { FormFieldsEnum } from './config'; |
2 | -import { ExtensionDesc, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
3 | -import { ServiceCallTypeEnum } from '/@/enums/deviceEnum'; | 3 | +import { DeviceProfileDetail } from '/@/api/device/model/deviceConfigModel'; |
4 | +import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
5 | +import { ServiceCallTypeEnum, TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
4 | import { | 6 | import { |
5 | FunctionTypeEnum, | 7 | FunctionTypeEnum, |
6 | ObjectEventTypeEnum, | 8 | ObjectEventTypeEnum, |
7 | ObjectModelAccessModeEnum, | 9 | ObjectModelAccessModeEnum, |
8 | } from '/@/enums/objectModelEnum'; | 10 | } from '/@/enums/objectModelEnum'; |
11 | +import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
12 | + | ||
13 | +export interface ObjectModelFormPropsType { | ||
14 | + transportType?: TransportTypeEnum; | ||
15 | + mode?: DataActionModeEnum; | ||
16 | + deviceRecord?: DeviceProfileDetail; | ||
17 | +} | ||
9 | 18 | ||
10 | export interface ObjectModelFormGetFieldsValueType { | 19 | export interface ObjectModelFormGetFieldsValueType { |
11 | [FormFieldsEnum.FUNCTION_TYPE]: FunctionTypeEnum; | 20 | [FormFieldsEnum.FUNCTION_TYPE]: FunctionTypeEnum; |
@@ -14,16 +23,13 @@ export interface ObjectModelFormGetFieldsValueType { | @@ -14,16 +23,13 @@ export interface ObjectModelFormGetFieldsValueType { | ||
14 | [FormFieldsEnum.FUNCTION_NAME]: string; | 23 | [FormFieldsEnum.FUNCTION_NAME]: string; |
15 | [FormFieldsEnum.IDENTIFIER]: string; | 24 | [FormFieldsEnum.IDENTIFIER]: string; |
16 | [FormFieldsEnum.EVENT_TYPE]?: ObjectEventTypeEnum; | 25 | [FormFieldsEnum.EVENT_TYPE]?: ObjectEventTypeEnum; |
26 | + [FormFieldsEnum.UNIT]?: string; | ||
27 | + [FormFieldsEnum.UNIT_NAME]: string; | ||
17 | 28 | ||
18 | [FormFieldsEnum.CALL_TYPE]?: ServiceCallTypeEnum; | 29 | [FormFieldsEnum.CALL_TYPE]?: ServiceCallTypeEnum; |
19 | [FormFieldsEnum.INPUT_DATA]?: StructJSON[]; | 30 | [FormFieldsEnum.INPUT_DATA]?: StructJSON[]; |
20 | [FormFieldsEnum.OUTPUT_DATA]?: StructJSON[]; | 31 | [FormFieldsEnum.OUTPUT_DATA]?: StructJSON[]; |
21 | 32 | ||
22 | [FormFieldsEnum.SERVICE_COMMAND]?: string; | 33 | [FormFieldsEnum.SERVICE_COMMAND]?: string; |
23 | - [FormFieldsEnum.EXTENSION_DESC]?: ExtensionDesc; | ||
24 | - | ||
25 | - // EXTENSION_DESC = 'extensionDesc', | ||
26 | - // STRUCT_DATA = 'structData', | ||
27 | - // INPUT_DATA = 'inputData', | ||
28 | - // OUTPUT_DATA = 'outputData', | 34 | + [FormFieldsEnum.EXTENSION_DESC]?: ExtendDescFormFieldsValueType; |
29 | } | 35 | } |
1 | import { DataTypeForm } from './DataTypeForm'; | 1 | import { DataTypeForm } from './DataTypeForm'; |
2 | +import { ExtendDescFormFieldsValueType } from './ExtendDesc'; | ||
3 | +import { isFloatType, useParseOperationType } from './ExtendDesc/useParseOperationType'; | ||
2 | import { FormFieldsEnum } from './config'; | 4 | import { FormFieldsEnum } from './config'; |
3 | -import { ObjectModelFormGetFieldsValueType } from './types'; | 5 | +import { ObjectModelFormGetFieldsValueType, ObjectModelFormPropsType } from './types'; |
4 | import { DefineComponentsBasicExpose } from '/#/utils'; | 6 | import { DefineComponentsBasicExpose } from '/#/utils'; |
5 | -import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | 7 | +import { ModelOfMatterParams, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
6 | import { FormActionType } from '/@/components/Form'; | 8 | import { FormActionType } from '/@/components/Form'; |
7 | -import { TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
8 | -import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | ||
9 | -import { Ref, toRaw, unref } from 'vue'; | 9 | +import { TCPProtocolTypeEnum, TransportTypeEnum } from '/@/enums/deviceEnum'; |
10 | +import { | ||
11 | + BuiltInIdentifierEnum, | ||
12 | + DataTypeEnum, | ||
13 | + FunctionTypeEnum, | ||
14 | + ObjectModelAccessModeEnum, | ||
15 | + OriginalDataTypeEnum, | ||
16 | +} from '/@/enums/objectModelEnum'; | ||
17 | +import { Ref, computed, toRaw, unref } from 'vue'; | ||
10 | 18 | ||
11 | interface UseObjectFormDataParamsType { | 19 | interface UseObjectFormDataParamsType { |
12 | formActionType: FormActionType; | 20 | formActionType: FormActionType; |
13 | dataTypeFormRef: Ref<InstanceType<typeof DataTypeForm> | undefined>; | 21 | dataTypeFormRef: Ref<InstanceType<typeof DataTypeForm> | undefined>; |
14 | - transportType: TransportTypeEnum; | 22 | + propsRef?: ObjectModelFormPropsType; |
23 | +} | ||
24 | + | ||
25 | +export function getDataTypeByOriginalDataType( | ||
26 | + originalDataType: OriginalDataTypeEnum, | ||
27 | + identifier: string | ||
28 | +) { | ||
29 | + if (identifier === BuiltInIdentifierEnum.SOURCE) return DataTypeEnum.STRING; | ||
30 | + | ||
31 | + if (isFloatType(originalDataType)) return DataTypeEnum.NUMBER_DOUBLE; | ||
32 | + else if (originalDataType === OriginalDataTypeEnum.STRING) return DataTypeEnum.STRING; | ||
33 | + else if ([OriginalDataTypeEnum.BOOLEAN, OriginalDataTypeEnum.BITS].includes(originalDataType)) | ||
34 | + return DataTypeEnum.BOOL; | ||
35 | + else return DataTypeEnum.NUMBER_INT; | ||
36 | +} | ||
37 | + | ||
38 | +function handleTCPModbusProductPropertiesType( | ||
39 | + value: ObjectModelFormGetFieldsValueType | ||
40 | +): ModelOfMatterParams { | ||
41 | + const { functionName, identifier, functionType, remark, extensionDesc, unit, unitName } = value; | ||
42 | + | ||
43 | + const { | ||
44 | + operationType, | ||
45 | + originalDataType, | ||
46 | + bitMask, | ||
47 | + valueRange, | ||
48 | + registerAddress, | ||
49 | + scaling, | ||
50 | + registerCount, | ||
51 | + valueMapping, | ||
52 | + } = extensionDesc! || {}; | ||
53 | + | ||
54 | + const { writeOnly, accessMode } = useParseOperationType(operationType); | ||
55 | + | ||
56 | + const dataType = getDataTypeByOriginalDataType(originalDataType, identifier); | ||
57 | + | ||
58 | + return { | ||
59 | + functionType, | ||
60 | + functionName, | ||
61 | + identifier, | ||
62 | + functionJson: { | ||
63 | + dataType: { | ||
64 | + type: dataType, | ||
65 | + specs: { | ||
66 | + unit, | ||
67 | + unitName, | ||
68 | + valueRange, | ||
69 | + }, | ||
70 | + specsList: valueMapping || [], | ||
71 | + }, | ||
72 | + }, | ||
73 | + accessMode: | ||
74 | + identifier === BuiltInIdentifierEnum.SOURCE ? ObjectModelAccessModeEnum.READ : accessMode, | ||
75 | + remark, | ||
76 | + extensionDesc: { | ||
77 | + writeOnly, | ||
78 | + bitMask, | ||
79 | + operationType, | ||
80 | + originalDataType, | ||
81 | + registerAddress, | ||
82 | + scaling, | ||
83 | + registerCount, | ||
84 | + }, | ||
85 | + }; | ||
15 | } | 86 | } |
16 | 87 | ||
17 | function handlePropertiesType( | 88 | function handlePropertiesType( |
18 | value: ObjectModelFormGetFieldsValueType, | 89 | value: ObjectModelFormGetFieldsValueType, |
19 | - dataTypeFormValue: StructJSON, | ||
20 | - transportType?: TransportTypeEnum | 90 | + dataTypeFormValue: StructJSON |
21 | ): ModelOfMatterParams { | 91 | ): ModelOfMatterParams { |
22 | - const { functionType, accessMode, remark, extensionDesc } = value; | ||
23 | - const { functionName, identifier, dataType } = dataTypeFormValue; | 92 | + const { functionType, accessMode, remark } = value; |
93 | + const { functionName, identifier, dataType } = dataTypeFormValue || {}; | ||
24 | 94 | ||
25 | const result: ModelOfMatterParams = { | 95 | const result: ModelOfMatterParams = { |
26 | functionType, | 96 | functionType, |
@@ -33,15 +103,12 @@ function handlePropertiesType( | @@ -33,15 +103,12 @@ function handlePropertiesType( | ||
33 | remark, | 103 | remark, |
34 | }; | 104 | }; |
35 | 105 | ||
36 | - if (transportType === TransportTypeEnum.TCP && extensionDesc) | ||
37 | - result.extensionDesc = extensionDesc; | ||
38 | - | ||
39 | return result; | 106 | return result; |
40 | } | 107 | } |
41 | 108 | ||
42 | function handleServiceType( | 109 | function handleServiceType( |
43 | value: ObjectModelFormGetFieldsValueType, | 110 | value: ObjectModelFormGetFieldsValueType, |
44 | - transportType: TransportTypeEnum | 111 | + transportType?: TransportTypeEnum |
45 | ): ModelOfMatterParams { | 112 | ): ModelOfMatterParams { |
46 | const { | 113 | const { |
47 | functionName, | 114 | functionName, |
@@ -51,9 +118,11 @@ function handleServiceType( | @@ -51,9 +118,11 @@ function handleServiceType( | ||
51 | inputData = [], | 118 | inputData = [], |
52 | outputData = [], | 119 | outputData = [], |
53 | serviceCommand, | 120 | serviceCommand, |
121 | + callType, | ||
54 | } = value; | 122 | } = value; |
55 | 123 | ||
56 | return { | 124 | return { |
125 | + callType, | ||
57 | functionType, | 126 | functionType, |
58 | functionName, | 127 | functionName, |
59 | identifier, | 128 | identifier, |
@@ -86,26 +155,37 @@ function handleEventType(value: ObjectModelFormGetFieldsValueType): ModelOfMatte | @@ -86,26 +155,37 @@ function handleEventType(value: ObjectModelFormGetFieldsValueType): ModelOfMatte | ||
86 | export const useObjectFormData = ({ | 155 | export const useObjectFormData = ({ |
87 | formActionType, | 156 | formActionType, |
88 | dataTypeFormRef, | 157 | dataTypeFormRef, |
89 | - transportType, | 158 | + propsRef, |
90 | }: UseObjectFormDataParamsType): DefineComponentsBasicExpose<ModelOfMatterParams> => { | 159 | }: UseObjectFormDataParamsType): DefineComponentsBasicExpose<ModelOfMatterParams> => { |
160 | + const getTCPModbusProductFlag = computed( | ||
161 | + () => | ||
162 | + propsRef?.deviceRecord?.profileData.transportConfiguration.protocol === | ||
163 | + TCPProtocolTypeEnum.MODBUS_RTU | ||
164 | + ); | ||
165 | + | ||
166 | + const getTransportType = computed( | ||
167 | + () => propsRef?.deviceRecord?.transportType as TransportTypeEnum | ||
168 | + ); | ||
169 | + | ||
91 | const getFieldsValue = (): ModelOfMatterParams => { | 170 | const getFieldsValue = (): ModelOfMatterParams => { |
92 | const value = formActionType.getFieldsValue() as ObjectModelFormGetFieldsValueType; | 171 | const value = formActionType.getFieldsValue() as ObjectModelFormGetFieldsValueType; |
93 | const { functionType } = value; | 172 | const { functionType } = value; |
94 | - | ||
95 | if (functionType === FunctionTypeEnum.PROPERTIES) { | 173 | if (functionType === FunctionTypeEnum.PROPERTIES) { |
96 | const dataTypeFormValue = unref(dataTypeFormRef)?.getFieldsValue?.(); | 174 | const dataTypeFormValue = unref(dataTypeFormRef)?.getFieldsValue?.(); |
97 | - return handlePropertiesType(value, dataTypeFormValue!, transportType); | 175 | + if (unref(getTCPModbusProductFlag)) return handleTCPModbusProductPropertiesType(value); |
176 | + return handlePropertiesType(value, dataTypeFormValue!); | ||
98 | } | 177 | } |
99 | 178 | ||
100 | if (functionType === FunctionTypeEnum.EVENTS) { | 179 | if (functionType === FunctionTypeEnum.EVENTS) { |
101 | return handleEventType(value); | 180 | return handleEventType(value); |
102 | - } else { | ||
103 | - return handleServiceType(value, transportType); | ||
104 | } | 181 | } |
182 | + | ||
183 | + return handleServiceType(value, unref(getTransportType)); | ||
105 | }; | 184 | }; |
106 | 185 | ||
107 | const setFieldsValue = (values: ModelOfMatterParams) => { | 186 | const setFieldsValue = (values: ModelOfMatterParams) => { |
108 | values = toRaw(unref(values)); | 187 | values = toRaw(unref(values)); |
188 | + const transportType = unref(getTransportType); | ||
109 | const { | 189 | const { |
110 | functionName, | 190 | functionName, |
111 | identifier, | 191 | identifier, |
@@ -115,10 +195,11 @@ export const useObjectFormData = ({ | @@ -115,10 +195,11 @@ export const useObjectFormData = ({ | ||
115 | callType, | 195 | callType, |
116 | remark, | 196 | remark, |
117 | extensionDesc, | 197 | extensionDesc, |
198 | + eventType, | ||
118 | } = values; | 199 | } = values; |
119 | const { dataType, inputData = [], outputData = [] } = functionJson || {}; | 200 | const { dataType, inputData = [], outputData = [] } = functionJson || {}; |
120 | 201 | ||
121 | - formActionType.setFieldsValue({ | 202 | + const setValue = { |
122 | identifier, | 203 | identifier, |
123 | functionName, | 204 | functionName, |
124 | functionType, | 205 | functionType, |
@@ -127,9 +208,23 @@ export const useObjectFormData = ({ | @@ -127,9 +208,23 @@ export const useObjectFormData = ({ | ||
127 | inputData, | 208 | inputData, |
128 | outputData, | 209 | outputData, |
129 | remark, | 210 | remark, |
211 | + eventType, | ||
130 | serviceCommand: transportType === TransportTypeEnum.TCP ? inputData?.[0]?.serviceCommand : '', | 212 | serviceCommand: transportType === TransportTypeEnum.TCP ? inputData?.[0]?.serviceCommand : '', |
131 | extensionDesc, | 213 | extensionDesc, |
132 | - } as ObjectModelFormGetFieldsValueType); | 214 | + } as ObjectModelFormGetFieldsValueType; |
215 | + | ||
216 | + if (unref(getTCPModbusProductFlag)) { | ||
217 | + const { specs, type, specsList } = dataType || {}; | ||
218 | + const { unit, unitName, valueRange } = (specs as Specs) || {}; | ||
219 | + Object.assign(setValue, { unit, unitName } as ObjectModelFormGetFieldsValueType); | ||
220 | + Object.assign(setValue.extensionDesc || {}, { | ||
221 | + valueRange, | ||
222 | + dataType: type, | ||
223 | + valueMapping: specsList, | ||
224 | + } as Partial<ExtendDescFormFieldsValueType>); | ||
225 | + } | ||
226 | + | ||
227 | + formActionType.setFieldsValue(setValue); | ||
133 | 228 | ||
134 | if (functionType === FunctionTypeEnum.PROPERTIES) { | 229 | if (functionType === FunctionTypeEnum.PROPERTIES) { |
135 | unref(dataTypeFormRef)?.setFieldsValue({ | 230 | unref(dataTypeFormRef)?.setFieldsValue({ |
@@ -145,7 +240,7 @@ export const useObjectFormData = ({ | @@ -145,7 +240,7 @@ export const useObjectFormData = ({ | ||
145 | await unref(dataTypeFormRef)?.validate?.(); | 240 | await unref(dataTypeFormRef)?.validate?.(); |
146 | }; | 241 | }; |
147 | 242 | ||
148 | - const resetFieldsValue = () => { | 243 | + const resetFieldsValue = async () => { |
149 | formActionType.resetFields(); | 244 | formActionType.resetFields(); |
150 | unref(dataTypeFormRef)?.resetFieldsValue?.(); | 245 | unref(dataTypeFormRef)?.resetFieldsValue?.(); |
151 | }; | 246 | }; |
1 | import type { InjectionKey, ComputedRef } from 'vue'; | 1 | import type { InjectionKey, ComputedRef } from 'vue'; |
2 | import { createContext, useContext } from '/@/hooks/core/useContext'; | 2 | import { createContext, useContext } from '/@/hooks/core/useContext'; |
3 | import { TransportTypeEnum } from '/@/enums/deviceEnum'; | 3 | import { TransportTypeEnum } from '/@/enums/deviceEnum'; |
4 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
5 | import { DataActionModeEnum } from '/@/enums/toolEnum'; | 4 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
6 | 5 | ||
7 | export interface ObjectModelFormContextProps { | 6 | export interface ObjectModelFormContextProps { |
8 | getTransportType: ComputedRef<TransportTypeEnum | undefined>; | 7 | getTransportType: ComputedRef<TransportTypeEnum | undefined>; |
9 | - getDataType: ComputedRef<DataTypeEnum>; | ||
10 | getModalMode: ComputedRef<DataActionModeEnum | undefined>; | 8 | getModalMode: ComputedRef<DataActionModeEnum | undefined>; |
11 | } | 9 | } |
12 | 10 |
@@ -28,10 +28,9 @@ | @@ -28,10 +28,9 @@ | ||
28 | </BasicModal> | 28 | </BasicModal> |
29 | </template> | 29 | </template> |
30 | <script lang="ts" setup> | 30 | <script lang="ts" setup> |
31 | - import { ref, unref } from 'vue'; | 31 | + import { computed, ref, unref } from 'vue'; |
32 | import { Button, Radio, RadioGroup } from 'ant-design-vue'; | 32 | import { Button, Radio, RadioGroup } from 'ant-design-vue'; |
33 | import { BasicModal, useModalInner } from '/@/components/Modal'; | 33 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
34 | - import { DeviceRecord } from '/@/api/device/model/deviceModel'; | ||
35 | import { useMessage } from '/@/hooks/web/useMessage'; | 34 | import { useMessage } from '/@/hooks/web/useMessage'; |
36 | import { isObject, isString } from '/@/utils/is'; | 35 | import { isObject, isString } from '/@/utils/is'; |
37 | import { | 36 | import { |
@@ -45,11 +44,13 @@ | @@ -45,11 +44,13 @@ | ||
45 | import { BasicForm, useForm } from '/@/components/Form'; | 44 | import { BasicForm, useForm } from '/@/components/Form'; |
46 | import { Authority } from '/@/components/Authority'; | 45 | import { Authority } from '/@/components/Authority'; |
47 | import { usePermission } from '/@/hooks/web/usePermission'; | 46 | import { usePermission } from '/@/hooks/web/usePermission'; |
47 | + import { TCPProtocolTypeEnum, TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
48 | + import { DeviceProfileDetail } from '/@/api/device/model/deviceConfigModel'; | ||
48 | 49 | ||
49 | const emits = defineEmits(['register', 'handleImportCSV', 'handleReload']); | 50 | const emits = defineEmits(['register', 'handleImportCSV', 'handleReload']); |
50 | 51 | ||
51 | - defineProps<{ | ||
52 | - record: DeviceRecord; | 52 | + const props = defineProps<{ |
53 | + record: DeviceProfileDetail; | ||
53 | }>(); | 54 | }>(); |
54 | 55 | ||
55 | const { createMessage } = useMessage(); | 56 | const { createMessage } = useMessage(); |
@@ -196,10 +197,14 @@ | @@ -196,10 +197,14 @@ | ||
196 | const formData = new FormData(); | 197 | const formData = new FormData(); |
197 | formData.set('file', file); | 198 | formData.set('file', file); |
198 | const flag = isCateGory | 199 | const flag = isCateGory |
199 | - ? await importCsvCategory({ categoryId: id, deviceProfileId: undefined, file: formData }) | 200 | + ? await importCsvCategory({ |
201 | + categoryId: id, | ||
202 | + type: unref(getIsModbusDeviceFlag) ? 'modbus' : '', | ||
203 | + file: formData, | ||
204 | + }) | ||
200 | : await importCsvDeviceProfileId({ | 205 | : await importCsvDeviceProfileId({ |
201 | - categoryId: undefined, | ||
202 | deviceProfileId: id, | 206 | deviceProfileId: id, |
207 | + type: unref(getIsModbusDeviceFlag) ? 'modbus' : '', | ||
203 | file: formData, | 208 | file: formData, |
204 | }); | 209 | }); |
205 | flag && createMessage.info(flag?.message); | 210 | flag && createMessage.info(flag?.message); |
@@ -224,10 +229,18 @@ | @@ -224,10 +229,18 @@ | ||
224 | URL.revokeObjectURL(objectURL); | 229 | URL.revokeObjectURL(objectURL); |
225 | }; | 230 | }; |
226 | 231 | ||
232 | + const getIsModbusDeviceFlag = computed(() => { | ||
233 | + const isTCPProtocol = props.record.transportType === TransportTypeEnum.TCP; | ||
234 | + return ( | ||
235 | + isTCPProtocol && | ||
236 | + props.record?.profileData?.transportConfiguration?.protocol === TCPProtocolTypeEnum.MODBUS_RTU | ||
237 | + ); | ||
238 | + }); | ||
239 | + | ||
227 | // 模板下载 | 240 | // 模板下载 |
228 | const handleTemplateDownload = async () => { | 241 | const handleTemplateDownload = async () => { |
229 | - const res = await excelExport(); | ||
230 | - downloadFile(res, '物模型属性导入模板', 'xls'); | 242 | + const res = await excelExport(unref(getIsModbusDeviceFlag) ? 'modbus' : ''); |
243 | + downloadFile(res, `${unref(getIsModbusDeviceFlag) ? 'Modbus' : ''}物模型属性导入模板`, 'xls'); | ||
231 | }; | 244 | }; |
232 | 245 | ||
233 | const handleCancel = () => { | 246 | const handleCancel = () => { |
@@ -140,7 +140,6 @@ | @@ -140,7 +140,6 @@ | ||
140 | import PhysicalModelTsl from './cpns/physical/PhysicalModelTsl.vue'; | 140 | import PhysicalModelTsl from './cpns/physical/PhysicalModelTsl.vue'; |
141 | import { Popconfirm, Button, Alert } from 'ant-design-vue'; | 141 | import { Popconfirm, Button, Alert } from 'ant-design-vue'; |
142 | import { useMessage } from '/@/hooks/web/useMessage'; | 142 | import { useMessage } from '/@/hooks/web/useMessage'; |
143 | - import { DeviceRecord } from '/@/api/device/model/deviceModel'; | ||
144 | import { | 143 | import { |
145 | deleteModel, | 144 | deleteModel, |
146 | deleteModelCategory, | 145 | deleteModelCategory, |
@@ -158,12 +157,13 @@ | @@ -158,12 +157,13 @@ | ||
158 | import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | 157 | import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; |
159 | import SelectImport from '../components/SelectImport.vue'; | 158 | import SelectImport from '../components/SelectImport.vue'; |
160 | import { DataActionModeEnum } from '/@/enums/toolEnum'; | 159 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
160 | + import { DeviceProfileDetail } from '/@/api/device/model/deviceConfigModel'; | ||
161 | 161 | ||
162 | const { isPlatformAdmin, isSysadmin } = useRole(); | 162 | const { isPlatformAdmin, isSysadmin } = useRole(); |
163 | defineEmits(['register']); | 163 | defineEmits(['register']); |
164 | 164 | ||
165 | const props = defineProps<{ | 165 | const props = defineProps<{ |
166 | - record: DeviceRecord; | 166 | + record: DeviceProfileDetail; |
167 | }>(); | 167 | }>(); |
168 | 168 | ||
169 | const { createMessage } = useMessage(); | 169 | const { createMessage } = useMessage(); |
@@ -49,7 +49,7 @@ | @@ -49,7 +49,7 @@ | ||
49 | </div> | 49 | </div> |
50 | </template> | 50 | </template> |
51 | <script lang="ts" setup> | 51 | <script lang="ts" setup> |
52 | - import { reactive, ref, onUnmounted, nextTick } from 'vue'; | 52 | + import { reactive, ref, onUnmounted, nextTick, unref } from 'vue'; |
53 | import { BasicForm, useForm } from '/@/components/Form'; | 53 | import { BasicForm, useForm } from '/@/components/Form'; |
54 | import { step2Schemas } from '../device.profile.data'; | 54 | import { step2Schemas } from '../device.profile.data'; |
55 | import { Button } from '/@/components/Button'; | 55 | import { Button } from '/@/components/Button'; |
@@ -183,6 +183,7 @@ | @@ -183,6 +183,7 @@ | ||
183 | }, | 183 | }, |
184 | }, | 184 | }, |
185 | }); | 185 | }); |
186 | + unref(tcpRef)?.setProtocolStatus(status); | ||
186 | }; | 187 | }; |
187 | const updateDisabled = async () => { | 188 | const updateDisabled = async () => { |
188 | await nextTick(); | 189 | await nextTick(); |
@@ -8,6 +8,7 @@ | @@ -8,6 +8,7 @@ | ||
8 | <ObjectModelForm | 8 | <ObjectModelForm |
9 | ref="objectModelElRef" | 9 | ref="objectModelElRef" |
10 | :mode="openModalMode" | 10 | :mode="openModalMode" |
11 | + :device-record="record" | ||
11 | :transport-type="(record.transportType as TransportTypeEnum)" | 12 | :transport-type="(record.transportType as TransportTypeEnum)" |
12 | /> | 13 | /> |
13 | </BasicModal> | 14 | </BasicModal> |
@@ -23,12 +24,12 @@ | @@ -23,12 +24,12 @@ | ||
23 | createModelCategory, | 24 | createModelCategory, |
24 | updateModelCategory, | 25 | updateModelCategory, |
25 | } from '/@/api/device/modelOfMatter'; | 26 | } from '/@/api/device/modelOfMatter'; |
26 | - import { DeviceRecord } from '/@/api/device/model/deviceModel'; | ||
27 | import { useMessage } from '/@/hooks/web/useMessage'; | 27 | import { useMessage } from '/@/hooks/web/useMessage'; |
28 | import { useRole } from '/@/hooks/business/useRole'; | 28 | import { useRole } from '/@/hooks/business/useRole'; |
29 | import { ObjectModelForm } from '../../../components/ObjectModelForm'; | 29 | import { ObjectModelForm } from '../../../components/ObjectModelForm'; |
30 | import { TransportTypeEnum } from '/@/enums/deviceEnum'; | 30 | import { TransportTypeEnum } from '/@/enums/deviceEnum'; |
31 | import { DataActionModeEnum, DataActionModeNameEnum } from '/@/enums/toolEnum'; | 31 | import { DataActionModeEnum, DataActionModeNameEnum } from '/@/enums/toolEnum'; |
32 | + import { DeviceProfileDetail } from '/@/api/device/model/deviceConfigModel'; | ||
32 | 33 | ||
33 | const { isPlatformAdmin, isSysadmin } = useRole(); | 34 | const { isPlatformAdmin, isSysadmin } = useRole(); |
34 | 35 | ||
@@ -37,7 +38,7 @@ | @@ -37,7 +38,7 @@ | ||
37 | const objectModelElRef = ref<InstanceType<typeof ObjectModelForm>>(); | 38 | const objectModelElRef = ref<InstanceType<typeof ObjectModelForm>>(); |
38 | 39 | ||
39 | const props = defineProps<{ | 40 | const props = defineProps<{ |
40 | - record: DeviceRecord; | 41 | + record: DeviceProfileDetail; |
41 | }>(); | 42 | }>(); |
42 | 43 | ||
43 | const blockContent = `属性一般是指设备的运行状态,如当前温度等;服务是指设备可被调用的方法,支持定义参数,如执行某项任务;事件则是指设备上报的通知,如告警,需要被及时处理。`; | 44 | const blockContent = `属性一般是指设备的运行状态,如当前温度等;服务是指设备可被调用的方法,支持定义参数,如执行某项任务;事件则是指设备上报的通知,如告警,需要被及时处理。`; |
@@ -55,13 +56,14 @@ | @@ -55,13 +56,14 @@ | ||
55 | if (record) { | 56 | if (record) { |
56 | await nextTick(); | 57 | await nextTick(); |
57 | } | 58 | } |
58 | - unref(objectModelElRef)?.resetFieldsValue?.(); | 59 | + |
59 | const title = `${DataActionModeNameEnum[mode]}物模型`; | 60 | const title = `${DataActionModeNameEnum[mode]}物模型`; |
60 | setModalProps({ | 61 | setModalProps({ |
61 | showOkBtn: mode !== DataActionModeEnum.READ, | 62 | showOkBtn: mode !== DataActionModeEnum.READ, |
62 | title, | 63 | title, |
63 | }); | 64 | }); |
64 | 65 | ||
66 | + unref(objectModelElRef)?.resetFieldsValue?.(); | ||
65 | if (mode !== DataActionModeEnum.CREATE && record) { | 67 | if (mode !== DataActionModeEnum.CREATE && record) { |
66 | currentActionModel.value = record; | 68 | currentActionModel.value = record; |
67 | unref(objectModelElRef)?.setFieldsValue(record || {}); | 69 | unref(objectModelElRef)?.setFieldsValue(record || {}); |
1 | -<template> | ||
2 | - <div> | ||
3 | - <a-form | ||
4 | - ref="formRef" | ||
5 | - :model="scriptForm" | ||
6 | - name="basic" | ||
7 | - :label-col="{ span: 4 }" | ||
8 | - :wrapper-col="{ span: 16 }" | ||
9 | - autocomplete="off" | ||
10 | - style="margin-left: 2.4rem" | ||
11 | - > | ||
12 | - <a-form-item | ||
13 | - v-if="deviceTypeStr !== TypeEnum.SENSOR" | ||
14 | - label="鉴权脚本" | ||
15 | - name="authScriptId" | ||
16 | - :rules="[{ required: true, message: '请选择鉴权脚本' }]" | ||
17 | - > | ||
18 | - <ScriptSelectItem | ||
19 | - ref="scriptSelectItemAuthRef" | ||
20 | - v-model:value="scriptForm.authScriptId" | ||
21 | - :scriptType="ScriptTypeEnum.TRANSPORT_TCP_AUTH" | ||
22 | - /> | ||
23 | - </a-form-item> | ||
24 | - <a-form-item | ||
25 | - label="上行脚本" | ||
26 | - name="upScriptId" | ||
27 | - :rules="[{ required: true, message: '请选择上行脚本' }]" | ||
28 | - > | ||
29 | - <ScriptSelectItem | ||
30 | - ref="scriptSelectItemUpRef" | ||
31 | - v-model:value="scriptForm.upScriptId" | ||
32 | - :scriptType="ScriptTypeEnum.TRANSPORT_TCP_UP" | ||
33 | - /> | ||
34 | - </a-form-item> | ||
35 | - </a-form> | ||
36 | - </div> | ||
37 | -</template> | ||
38 | -<script lang="ts" setup name="index"> | ||
39 | - import { reactive, ref } from 'vue'; | ||
40 | - import { useMessage } from '/@/hooks/web/useMessage'; | ||
41 | - import { ScriptSelectItem } from './components'; | ||
42 | - import { ScriptTypeEnum } from '/@/views/rule/script/TcpConversionScript/config'; | ||
43 | - import { TypeEnum } from '/@/views/device/list/config/data'; | ||
44 | - | ||
45 | - const props = defineProps({ | ||
46 | - deviceTypeStr: { type: String, default: '' }, | ||
47 | - }); | ||
48 | - | ||
49 | - const scriptForm = reactive({ | ||
50 | - authScriptId: '', | ||
51 | - upScriptId: '', | ||
52 | - }); | ||
53 | - | ||
54 | - const { createMessage } = useMessage(); | ||
55 | - | ||
56 | - const scriptSelectItemAuthRef = ref<InstanceType<typeof ScriptSelectItem>>(); | ||
57 | - | ||
58 | - const scriptSelectItemUpRef = ref<InstanceType<typeof ScriptSelectItem>>(); | ||
59 | - | ||
60 | - const getFormData = async () => { | ||
61 | - scriptForm.authScriptId = scriptSelectItemAuthRef.value?.getValue(); | ||
62 | - scriptForm.upScriptId = scriptSelectItemUpRef.value?.getValue(); | ||
63 | - //业务 网关子设备没有鉴权脚本 | ||
64 | - if (props.deviceTypeStr === TypeEnum.SENSOR) Reflect.deleteProperty(scriptForm, 'authScriptId'); | ||
65 | - if (Object.values(scriptForm).some((item) => !item)) { | ||
66 | - createMessage.error('请先选择对应脚本'); | ||
67 | - throw new Error('请先选择对应脚本'); | ||
68 | - } | ||
69 | - return { | ||
70 | - ...scriptForm, | ||
71 | - type: 'TCP', | ||
72 | - }; | ||
73 | - }; | ||
74 | - | ||
75 | - const resetFormData = () => {}; | ||
76 | - | ||
77 | - const setFormData = (data) => { | ||
78 | - scriptForm.authScriptId = data?.authScriptId; | ||
79 | - scriptForm.upScriptId = data?.upScriptId; | ||
80 | - scriptSelectItemAuthRef.value?.setValue(data?.authScriptId); | ||
81 | - scriptSelectItemUpRef.value?.setValue(data?.upScriptId); | ||
82 | - }; | ||
83 | - defineExpose({ | ||
84 | - getFormData, | ||
85 | - resetFormData, | ||
86 | - setFormData, | ||
87 | - }); | ||
88 | -</script> | ||
89 | -<style lang="less" scoped></style> | 1 | +<template> |
2 | + <div> | ||
3 | + <Form | ||
4 | + ref="formRef" | ||
5 | + :model="scriptForm" | ||
6 | + name="basic" | ||
7 | + :label-col="{ span: 4 }" | ||
8 | + :wrapper-col="{ span: 16 }" | ||
9 | + autocomplete="off" | ||
10 | + style="margin-left: 2.4rem" | ||
11 | + > | ||
12 | + <Form.Item | ||
13 | + label="类型" | ||
14 | + :rules="[{ required: true, message: '请选择鉴权脚本' }]" | ||
15 | + :wrapper-col="{ span: 10 }" | ||
16 | + > | ||
17 | + <Select | ||
18 | + v-model:value="scriptForm.protocol" | ||
19 | + :options="typeOptions" | ||
20 | + :disabled="protocolStatus" | ||
21 | + /> | ||
22 | + </Form.Item> | ||
23 | + <Form.Item | ||
24 | + v-if=" | ||
25 | + scriptForm.protocol === TCPProtocolTypeEnum.CUSTOM && deviceTypeStr !== TypeEnum.SENSOR | ||
26 | + " | ||
27 | + label="鉴权脚本" | ||
28 | + name="authScriptId" | ||
29 | + :rules="[{ required: true, message: '请选择鉴权脚本' }]" | ||
30 | + > | ||
31 | + <ScriptSelectItem | ||
32 | + ref="scriptSelectItemAuthRef" | ||
33 | + v-model:value="scriptForm.authScriptId" | ||
34 | + :scriptType="ScriptTypeEnum.TRANSPORT_TCP_AUTH" | ||
35 | + /> | ||
36 | + </Form.Item> | ||
37 | + <Form.Item | ||
38 | + v-if="scriptForm.protocol === TCPProtocolTypeEnum.CUSTOM" | ||
39 | + label="上行脚本" | ||
40 | + name="upScriptId" | ||
41 | + :rules="[{ required: true, message: '请选择上行脚本' }]" | ||
42 | + > | ||
43 | + <ScriptSelectItem | ||
44 | + ref="scriptSelectItemUpRef" | ||
45 | + v-model:value="scriptForm.upScriptId" | ||
46 | + :scriptType="ScriptTypeEnum.TRANSPORT_TCP_UP" | ||
47 | + /> | ||
48 | + </Form.Item> | ||
49 | + </Form> | ||
50 | + </div> | ||
51 | +</template> | ||
52 | +<script lang="ts" setup name="index"> | ||
53 | + import { reactive, ref } from 'vue'; | ||
54 | + import { Form, Select } from 'ant-design-vue'; | ||
55 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
56 | + import { ScriptSelectItem } from './components'; | ||
57 | + import { ScriptTypeEnum } from '/@/views/rule/script/TcpConversionScript/config'; | ||
58 | + import { TypeEnum } from '/@/views/device/list/config/data'; | ||
59 | + import { TCPProtocolTypeEnum, TCPProtocolTypeNameEnum } from '/@/enums/deviceEnum'; | ||
60 | + | ||
61 | + const props = defineProps({ | ||
62 | + deviceTypeStr: { type: String, default: '' }, | ||
63 | + }); | ||
64 | + | ||
65 | + const typeOptions = Object.keys(TCPProtocolTypeEnum).map((key) => ({ | ||
66 | + label: TCPProtocolTypeNameEnum[key], | ||
67 | + value: TCPProtocolTypeEnum[key], | ||
68 | + })); | ||
69 | + | ||
70 | + const scriptForm = reactive({ | ||
71 | + authScriptId: '', | ||
72 | + upScriptId: '', | ||
73 | + protocol: TCPProtocolTypeEnum.MODBUS_RTU, | ||
74 | + }); | ||
75 | + | ||
76 | + const { createMessage } = useMessage(); | ||
77 | + | ||
78 | + const scriptSelectItemAuthRef = ref<InstanceType<typeof ScriptSelectItem>>(); | ||
79 | + | ||
80 | + const scriptSelectItemUpRef = ref<InstanceType<typeof ScriptSelectItem>>(); | ||
81 | + | ||
82 | + const getFormData = async () => { | ||
83 | + scriptForm.authScriptId = scriptSelectItemAuthRef.value?.getValue(); | ||
84 | + scriptForm.upScriptId = scriptSelectItemUpRef.value?.getValue(); | ||
85 | + //业务 网关子设备没有鉴权脚本 | ||
86 | + if (scriptForm.protocol === TCPProtocolTypeEnum.CUSTOM) { | ||
87 | + if (props.deviceTypeStr === TypeEnum.SENSOR) | ||
88 | + Reflect.deleteProperty(scriptForm, 'authScriptId'); | ||
89 | + if (Object.values(scriptForm).some((item) => !item)) { | ||
90 | + createMessage.error('请先选择对应脚本'); | ||
91 | + throw new Error('请先选择对应脚本'); | ||
92 | + } | ||
93 | + } | ||
94 | + | ||
95 | + return { | ||
96 | + ...scriptForm, | ||
97 | + type: 'TCP', | ||
98 | + }; | ||
99 | + }; | ||
100 | + | ||
101 | + const resetFormData = () => {}; | ||
102 | + | ||
103 | + const setFormData = (data) => { | ||
104 | + scriptForm.protocol = data?.protocol; | ||
105 | + scriptForm.authScriptId = data?.authScriptId; | ||
106 | + scriptForm.upScriptId = data?.upScriptId; | ||
107 | + scriptSelectItemAuthRef.value?.setValue(data?.authScriptId); | ||
108 | + scriptSelectItemUpRef.value?.setValue(data?.upScriptId); | ||
109 | + }; | ||
110 | + | ||
111 | + const protocolStatus = ref(false); | ||
112 | + | ||
113 | + const setProtocolStatus = (flag: boolean) => { | ||
114 | + protocolStatus.value = flag; | ||
115 | + }; | ||
116 | + | ||
117 | + defineExpose({ | ||
118 | + getFormData, | ||
119 | + resetFormData, | ||
120 | + setFormData, | ||
121 | + setProtocolStatus, | ||
122 | + }); | ||
123 | +</script> | ||
124 | +<style lang="less" scoped></style> |
@@ -5,7 +5,7 @@ import { | @@ -5,7 +5,7 @@ import { | ||
5 | TriggerEntityTypeEnum, | 5 | TriggerEntityTypeEnum, |
6 | TriggerEntityTypeNameEnum, | 6 | TriggerEntityTypeNameEnum, |
7 | } from '/@/enums/linkedgeEnum'; | 7 | } from '/@/enums/linkedgeEnum'; |
8 | -import { CommandTypeEnum, CommandTypeNameEnum } from '/@/enums/deviceEnum'; | 8 | +import { CommandTypeEnum, CommandTypeNameEnum, TCPProtocolTypeEnum } from '/@/enums/deviceEnum'; |
9 | import AlarmProfileSelect from './AlarmProfileSelect.vue'; | 9 | import AlarmProfileSelect from './AlarmProfileSelect.vue'; |
10 | import { | 10 | import { |
11 | byOrganizationIdGetMasterDevice, | 11 | byOrganizationIdGetMasterDevice, |
@@ -50,6 +50,8 @@ export enum FormFieldsEnum { | @@ -50,6 +50,8 @@ export enum FormFieldsEnum { | ||
50 | TRANSPORT_TYPE = 'transportType', | 50 | TRANSPORT_TYPE = 'transportType', |
51 | ENABLE_CLEAR_RULE = 'enableClearRule', | 51 | ENABLE_CLEAR_RULE = 'enableClearRule', |
52 | CLEAR_RULE = 'clearRule', | 52 | CLEAR_RULE = 'clearRule', |
53 | + | ||
54 | + IS_TCP_MODBUS_PRODUCT = 'isTCPModbusProduct', | ||
53 | } | 55 | } |
54 | 56 | ||
55 | export enum FormFieldsNameEnum { | 57 | export enum FormFieldsNameEnum { |
@@ -199,6 +201,12 @@ export const getFormSchemas = (hasAlarmNotify: Ref<boolean>): FormSchema[] => { | @@ -199,6 +201,12 @@ export const getFormSchemas = (hasAlarmNotify: Ref<boolean>): FormSchema[] => { | ||
199 | ifShow: false, | 201 | ifShow: false, |
200 | }, | 202 | }, |
201 | { | 203 | { |
204 | + field: FormFieldsEnum.IS_TCP_MODBUS_PRODUCT, | ||
205 | + label: 'TCPModbus产品', | ||
206 | + component: 'Input', | ||
207 | + ifShow: false, | ||
208 | + }, | ||
209 | + { | ||
202 | field: FormFieldsEnum.DEVICE_PROFILE_ID, | 210 | field: FormFieldsEnum.DEVICE_PROFILE_ID, |
203 | label: '', | 211 | label: '', |
204 | component: 'ApiSelect', | 212 | component: 'ApiSelect', |
@@ -216,22 +224,33 @@ export const getFormSchemas = (hasAlarmNotify: Ref<boolean>): FormSchema[] => { | @@ -216,22 +224,33 @@ export const getFormSchemas = (hasAlarmNotify: Ref<boolean>): FormSchema[] => { | ||
216 | placeholder: `请选择${FormFieldsNameEnum.DEVICE_PROFILE_ID}`, | 224 | placeholder: `请选择${FormFieldsNameEnum.DEVICE_PROFILE_ID}`, |
217 | ...createPickerSearch(), | 225 | ...createPickerSearch(), |
218 | onChange(_value: string, option: DeviceProfileModel) { | 226 | onChange(_value: string, option: DeviceProfileModel) { |
227 | + const transportType = option?.transportType; | ||
219 | setFieldsValue({ | 228 | setFieldsValue({ |
220 | [FormFieldsEnum.ENTITY_ID]: [], | 229 | [FormFieldsEnum.ENTITY_ID]: [], |
221 | - [FormFieldsEnum.TRANSPORT_TYPE]: option?.transportType, | 230 | + [FormFieldsEnum.TRANSPORT_TYPE]: transportType, |
222 | [FormFieldsEnum.SERVICE_ID]: null, | 231 | [FormFieldsEnum.SERVICE_ID]: null, |
232 | + [FormFieldsEnum.COMMAND_TYPE]: CommandTypeEnum.CUSTOM, | ||
223 | [FormFieldsEnum.CALL_SERVICE]: null, | 233 | [FormFieldsEnum.CALL_SERVICE]: null, |
224 | [FormFieldsEnum.CALL_SERVICE_IDENTIFIER]: null, | 234 | [FormFieldsEnum.CALL_SERVICE_IDENTIFIER]: null, |
225 | [FormFieldsEnum.SERVICE_COMMAND]: null, | 235 | [FormFieldsEnum.SERVICE_COMMAND]: null, |
236 | + [FormFieldsEnum.IS_TCP_MODBUS_PRODUCT]: | ||
237 | + transportType === TransportTypeEnum.TCP && | ||
238 | + option?.profileData?.transportConfiguration?.protocol === | ||
239 | + TCPProtocolTypeEnum.MODBUS_RTU, | ||
226 | }); | 240 | }); |
227 | }, | 241 | }, |
228 | onOptionsChange(options: (DeviceProfileModel & Record<'label' | 'value', string>)[]) { | 242 | onOptionsChange(options: (DeviceProfileModel & Record<'label' | 'value', string>)[]) { |
229 | const deviceProfileId = formModel[FormFieldsEnum.DEVICE_PROFILE_ID]; | 243 | const deviceProfileId = formModel[FormFieldsEnum.DEVICE_PROFILE_ID]; |
230 | const res = options.find((item) => item.value === deviceProfileId); | 244 | const res = options.find((item) => item.value === deviceProfileId); |
231 | - res && | ||
232 | - setFieldsValue({ | ||
233 | - [FormFieldsEnum.TRANSPORT_TYPE]: res.transportType, | ||
234 | - }); | 245 | + if (!res) return; |
246 | + const transportType = res.transportType; | ||
247 | + setFieldsValue({ | ||
248 | + [FormFieldsEnum.TRANSPORT_TYPE]: transportType, | ||
249 | + [FormFieldsEnum.IS_TCP_MODBUS_PRODUCT]: | ||
250 | + transportType === TransportTypeEnum.TCP && | ||
251 | + res?.profileData?.transportConfiguration?.protocol === | ||
252 | + TCPProtocolTypeEnum.MODBUS_RTU, | ||
253 | + }); | ||
235 | }, | 254 | }, |
236 | }; | 255 | }; |
237 | }, | 256 | }, |
@@ -295,12 +314,23 @@ export const getFormSchemas = (hasAlarmNotify: Ref<boolean>): FormSchema[] => { | @@ -295,12 +314,23 @@ export const getFormSchemas = (hasAlarmNotify: Ref<boolean>): FormSchema[] => { | ||
295 | ], | 314 | ], |
296 | defaultValue: CommandTypeEnum.CUSTOM, | 315 | defaultValue: CommandTypeEnum.CUSTOM, |
297 | ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT, | 316 | ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT, |
298 | - componentProps: ({ formActionType }) => { | 317 | + componentProps: ({ formModel, formActionType }) => { |
318 | + const getOptions = () => { | ||
319 | + const options = [{ label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM }]; | ||
320 | + | ||
321 | + const isTCPModbusProduct = formModel[FormFieldsEnum.IS_TCP_MODBUS_PRODUCT]; | ||
322 | + const isTCP = formModel[FormFieldsEnum.TRANSPORT_TYPE] === TransportTypeEnum.TCP; | ||
323 | + const deviceType = formModel[FormFieldsEnum.DEVICE_TYPE]; | ||
324 | + | ||
325 | + // TCPModbus设备 TCP自定义网关子设备 | ||
326 | + if (isTCPModbusProduct || (isTCP && deviceType === DeviceTypeEnum.SENSOR)) return options; | ||
327 | + | ||
328 | + options.push({ label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE }); | ||
329 | + | ||
330 | + return options; | ||
331 | + }; | ||
299 | return { | 332 | return { |
300 | - options: [ | ||
301 | - { label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM }, | ||
302 | - { label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE }, | ||
303 | - ], | 333 | + options: getOptions(), |
304 | placeholder: `请选择${FormFieldsNameEnum.COMMAND_TYPE}`, | 334 | placeholder: `请选择${FormFieldsNameEnum.COMMAND_TYPE}`, |
305 | onChange() { | 335 | onChange() { |
306 | const { setFieldsValue } = formActionType; | 336 | const { setFieldsValue } = formActionType; |
@@ -144,6 +144,7 @@ | @@ -144,6 +144,7 @@ | ||
144 | [oldCategory, category].some((item) => item === PackagesCategoryEnum.CONTROL) && | 144 | [oldCategory, category].some((item) => item === PackagesCategoryEnum.CONTROL) && |
145 | oldCategory !== category && | 145 | oldCategory !== category && |
146 | firstEnter; | 146 | firstEnter; |
147 | + | ||
147 | dataSource.value = unref(dataSource).map((item) => ({ | 148 | dataSource.value = unref(dataSource).map((item) => ({ |
148 | ...item, | 149 | ...item, |
149 | ...(needReset ? { attribute: null } : {}), | 150 | ...(needReset ? { attribute: null } : {}), |
@@ -9,8 +9,8 @@ | @@ -9,8 +9,8 @@ | ||
9 | import { useDataFetch } from '../../../hook/socket/useSocket'; | 9 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
10 | import { useModal } from '/@/components/Modal'; | 10 | import { useModal } from '/@/components/Modal'; |
11 | import PasswordModal from '../component/PasswordModal.vue'; | 11 | import PasswordModal from '../component/PasswordModal.vue'; |
12 | - import { useCommandDelivery } from '../../../hook/useCommandDelivery'; | ||
13 | import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | 12 | import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; |
13 | + import { useControlComand } from '../../../hook/useControlCommand'; | ||
14 | 14 | ||
15 | const props = defineProps<{ | 15 | const props = defineProps<{ |
16 | config: ComponentPropsConfigType<typeof option>; | 16 | config: ComponentPropsConfigType<typeof option>; |
@@ -38,12 +38,12 @@ | @@ -38,12 +38,12 @@ | ||
38 | }; | 38 | }; |
39 | }); | 39 | }); |
40 | 40 | ||
41 | - const { doCommandDeliver, loading } = useCommandDelivery(); | 41 | + const { loading, doControlSendCommand } = useControlComand(); |
42 | 42 | ||
43 | const handleSendCommand = async () => { | 43 | const handleSendCommand = async () => { |
44 | if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; | 44 | if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; |
45 | const { option } = props.config || {}; | 45 | const { option } = props.config || {}; |
46 | - const result = await doCommandDeliver(option, Number(!unref(currentValue))); | 46 | + const result = await doControlSendCommand(option, Number(!unref(currentValue))); |
47 | currentValue.value = result ? !unref(currentValue) : unref(currentValue); | 47 | currentValue.value = result ? !unref(currentValue) : unref(currentValue); |
48 | }; | 48 | }; |
49 | 49 |
@@ -11,8 +11,8 @@ | @@ -11,8 +11,8 @@ | ||
11 | import { useDataFetch } from '../../../hook/socket/useSocket'; | 11 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
12 | import { useModal } from '/@/components/Modal'; | 12 | import { useModal } from '/@/components/Modal'; |
13 | import PasswordModal from '../component/PasswordModal.vue'; | 13 | import PasswordModal from '../component/PasswordModal.vue'; |
14 | - import { useCommandDelivery } from '../../../hook/useCommandDelivery'; | ||
15 | import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | 14 | import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; |
15 | + import { useControlComand } from '../../../hook/useControlCommand'; | ||
16 | 16 | ||
17 | const props = defineProps<{ | 17 | const props = defineProps<{ |
18 | config: ComponentPropsConfigType<typeof option>; | 18 | config: ComponentPropsConfigType<typeof option>; |
@@ -44,7 +44,7 @@ | @@ -44,7 +44,7 @@ | ||
44 | }; | 44 | }; |
45 | }); | 45 | }); |
46 | 46 | ||
47 | - const { loading, doCommandDeliver } = useCommandDelivery(); | 47 | + const { loading, doControlSendCommand } = useControlComand(); |
48 | 48 | ||
49 | const handleChange = async () => { | 49 | const handleChange = async () => { |
50 | if (unref(getDesign).password) { | 50 | if (unref(getDesign).password) { |
@@ -58,7 +58,7 @@ | @@ -58,7 +58,7 @@ | ||
58 | const handleSendCommand = async () => { | 58 | const handleSendCommand = async () => { |
59 | if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; | 59 | if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; |
60 | const { option } = props.config || {}; | 60 | const { option } = props.config || {}; |
61 | - const result = await doCommandDeliver(option, Number(unref(checked))); | 61 | + const result = await doControlSendCommand(option, Number(unref(checked))); |
62 | if (!result) checked.value = !unref(checked); | 62 | if (!result) checked.value = !unref(checked); |
63 | }; | 63 | }; |
64 | 64 |
@@ -9,8 +9,8 @@ | @@ -9,8 +9,8 @@ | ||
9 | import { useDataFetch } from '../../../hook/socket/useSocket'; | 9 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
10 | import PasswordModal from '../component/PasswordModal.vue'; | 10 | import PasswordModal from '../component/PasswordModal.vue'; |
11 | import { useModal } from '/@/components/Modal'; | 11 | import { useModal } from '/@/components/Modal'; |
12 | - import { useCommandDelivery } from '../../../hook/useCommandDelivery'; | ||
13 | import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | 12 | import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; |
13 | + import { useControlComand } from '../../../hook/useControlCommand'; | ||
14 | 14 | ||
15 | const props = defineProps<{ | 15 | const props = defineProps<{ |
16 | config: ComponentPropsConfigType<typeof option>; | 16 | config: ComponentPropsConfigType<typeof option>; |
@@ -37,12 +37,13 @@ | @@ -37,12 +37,13 @@ | ||
37 | fontSize: fontSize || persetFontSize || 14, | 37 | fontSize: fontSize || persetFontSize || 14, |
38 | }; | 38 | }; |
39 | }); | 39 | }); |
40 | - const { doCommandDeliver, loading } = useCommandDelivery(); | 40 | + |
41 | + const { loading, doControlSendCommand } = useControlComand(); | ||
41 | 42 | ||
42 | const handleSendCommand = async () => { | 43 | const handleSendCommand = async () => { |
43 | if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; | 44 | if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; |
44 | const { option } = props.config || {}; | 45 | const { option } = props.config || {}; |
45 | - const result = await doCommandDeliver(option, Number(!unref(currentValue))); | 46 | + const result = await doControlSendCommand(option, Number(!unref(currentValue))); |
46 | currentValue.value = result ? !unref(currentValue) : unref(currentValue); | 47 | currentValue.value = result ? !unref(currentValue) : unref(currentValue); |
47 | }; | 48 | }; |
48 | const handleChange = async (event: Event) => { | 49 | const handleChange = async (event: Event) => { |
@@ -10,8 +10,8 @@ | @@ -10,8 +10,8 @@ | ||
10 | import { useDataFetch } from '../../../hook/socket/useSocket'; | 10 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
11 | import PasswordModal from '../component/PasswordModal.vue'; | 11 | import PasswordModal from '../component/PasswordModal.vue'; |
12 | import { useModal } from '/@/components/Modal'; | 12 | import { useModal } from '/@/components/Modal'; |
13 | - import { useCommandDelivery } from '../../../hook/useCommandDelivery'; | ||
14 | import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | 13 | import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; |
14 | + import { useControlComand } from '../../../hook/useControlCommand'; | ||
15 | 15 | ||
16 | const props = defineProps<{ | 16 | const props = defineProps<{ |
17 | config: ComponentPropsConfigType<typeof option>; | 17 | config: ComponentPropsConfigType<typeof option>; |
@@ -67,7 +67,7 @@ | @@ -67,7 +67,7 @@ | ||
67 | sendValue.value = value; | 67 | sendValue.value = value; |
68 | }; | 68 | }; |
69 | 69 | ||
70 | - const { loading, doCommandDeliver } = useCommandDelivery(); | 70 | + const { loading, doControlSendCommand } = useControlComand(); |
71 | 71 | ||
72 | const handleAfterChange = () => { | 72 | const handleAfterChange = () => { |
73 | if (unref(getDesign).password) { | 73 | if (unref(getDesign).password) { |
@@ -82,7 +82,7 @@ | @@ -82,7 +82,7 @@ | ||
82 | if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; | 82 | if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; |
83 | const value = unref(sendValue); | 83 | const value = unref(sendValue); |
84 | const { option } = props.config || {}; | 84 | const { option } = props.config || {}; |
85 | - const result = await doCommandDeliver(option, value); | 85 | + const result = await doControlSendCommand(option, value); |
86 | unref(sliderElRef)?.blur(); | 86 | unref(sliderElRef)?.blur(); |
87 | sliderValue.value = result ? value : unref(sliderValue); | 87 | sliderValue.value = result ? value : unref(sliderValue); |
88 | }; | 88 | }; |
@@ -13,11 +13,11 @@ | @@ -13,11 +13,11 @@ | ||
13 | import { useReceiveValue } from '../../../hook/useReceiveValue'; | 13 | import { useReceiveValue } from '../../../hook/useReceiveValue'; |
14 | import { useModal } from '/@/components/Modal'; | 14 | import { useModal } from '/@/components/Modal'; |
15 | import PasswordModal from '../component/PasswordModal.vue'; | 15 | import PasswordModal from '../component/PasswordModal.vue'; |
16 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | ||
16 | import { | 17 | import { |
17 | DoCommandDeliverDataSourceType, | 18 | DoCommandDeliverDataSourceType, |
18 | - useCommandDelivery, | ||
19 | - } from '../../../hook/useCommandDelivery'; | ||
20 | - import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | 19 | + useControlComand, |
20 | + } from '../../../hook/useControlCommand'; | ||
21 | 21 | ||
22 | const props = defineProps<{ | 22 | const props = defineProps<{ |
23 | config: ComponentPropsConfigType<typeof option>; | 23 | config: ComponentPropsConfigType<typeof option>; |
@@ -96,10 +96,10 @@ | @@ -96,10 +96,10 @@ | ||
96 | props.config.option.dataSource ? unref(getDesign).dataSource : DEFAULT_VALUE | 96 | props.config.option.dataSource ? unref(getDesign).dataSource : DEFAULT_VALUE |
97 | ); | 97 | ); |
98 | 98 | ||
99 | - const { loading, doCommandDeliver } = useCommandDelivery(); | 99 | + const { loading, doControlSendCommand } = useControlComand(); |
100 | const handleSendCommand = async (modalData: SwitchItemType) => { | 100 | const handleSendCommand = async (modalData: SwitchItemType) => { |
101 | if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; | 101 | if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; |
102 | - await doCommandDeliver( | 102 | + await doControlSendCommand( |
103 | toRaw(unref(modalData)) as DoCommandDeliverDataSourceType, | 103 | toRaw(unref(modalData)) as DoCommandDeliverDataSourceType, |
104 | Number(unref(modalData).checked) | 104 | Number(unref(modalData).checked) |
105 | ); | 105 | ); |
@@ -9,12 +9,11 @@ import { findDictItemByCode } from '/@/api/system/dict'; | @@ -9,12 +9,11 @@ import { findDictItemByCode } from '/@/api/system/dict'; | ||
9 | import { ApiCascader, FormSchema, useComponentRegister } from '/@/components/Form'; | 9 | import { ApiCascader, FormSchema, useComponentRegister } from '/@/components/Form'; |
10 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; | 10 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; |
11 | import { DataActionModeEnum } from '/@/enums/toolEnum'; | 11 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
12 | -import { TaskTypeEnum } from '/@/views/task/center/config'; | ||
13 | import { createPickerSearch } from '/@/utils/pickerSearch'; | 12 | import { createPickerSearch } from '/@/utils/pickerSearch'; |
14 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
15 | -import { CommandTypeEnum, CommandTypeNameEnum } from '/@/enums/deviceEnum'; | 13 | +import { DataTypeEnum, ObjectModelAccessModeEnum } from '/@/enums/objectModelEnum'; |
14 | +import { CommandTypeEnum, CommandTypeNameEnum, TCPProtocolTypeEnum } from '/@/enums/deviceEnum'; | ||
16 | import { TransportTypeEnum } from '/@/enums/deviceEnum'; | 15 | import { TransportTypeEnum } from '/@/enums/deviceEnum'; |
17 | -import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | 16 | +import { DeviceProfileModel, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; |
18 | import { ControlComponentEnum } from '../components/Control'; | 17 | import { ControlComponentEnum } from '../components/Control'; |
19 | import { isNullOrUnDef } from '/@/utils/is'; | 18 | import { isNullOrUnDef } from '/@/utils/is'; |
20 | import { validateTCPCustomCommand } from '/@/components/Form/src/components/ThingsModelForm'; | 19 | import { validateTCPCustomCommand } from '/@/components/Form/src/components/ThingsModelForm'; |
@@ -35,20 +34,16 @@ export interface CommonDataSourceBindValueType extends Record<DataSourceField, s | @@ -35,20 +34,16 @@ export interface CommonDataSourceBindValueType extends Record<DataSourceField, s | ||
35 | } | 34 | } |
36 | 35 | ||
37 | export enum DataSourceField { | 36 | export enum DataSourceField { |
38 | - // IS_GATEWAY_DEVICE = 'gatewayDevice', | ||
39 | DEVICE_TYPE = 'deviceType', | 37 | DEVICE_TYPE = 'deviceType', |
40 | TRANSPORT_TYPE = 'transportType', | 38 | TRANSPORT_TYPE = 'transportType', |
41 | ORIGINATION_ID = 'organizationId', | 39 | ORIGINATION_ID = 'organizationId', |
42 | DEVICE_ID = 'deviceId', | 40 | DEVICE_ID = 'deviceId', |
43 | - // DEVICE_CODE = 'deviceCode', //设备地址码 | ||
44 | DEVICE_PROFILE_ID = 'deviceProfileId', | 41 | DEVICE_PROFILE_ID = 'deviceProfileId', |
45 | ATTRIBUTE = 'attribute', | 42 | ATTRIBUTE = 'attribute', |
46 | - // ATTRIBUTE_NAME = 'attributeName', | ||
47 | ATTRIBUTE_RENAME = 'attributeRename', | 43 | ATTRIBUTE_RENAME = 'attributeRename', |
48 | DEVICE_NAME = 'deviceName', | 44 | DEVICE_NAME = 'deviceName', |
49 | DEVICE_RENAME = 'deviceRename', | 45 | DEVICE_RENAME = 'deviceRename', |
50 | 46 | ||
51 | - CODE_TYPE = 'codeType', | ||
52 | // COMMAND = 'command', | 47 | // COMMAND = 'command', |
53 | 48 | ||
54 | OPEN_COMMAND = 'openCommand', | 49 | OPEN_COMMAND = 'openCommand', |
@@ -78,8 +73,7 @@ export enum DataSourceField { | @@ -78,8 +73,7 @@ export enum DataSourceField { | ||
78 | */ | 73 | */ |
79 | LATITUDE_IDENTIFIER = 'latitudeIdentifier', | 74 | LATITUDE_IDENTIFIER = 'latitudeIdentifier', |
80 | 75 | ||
81 | - // EXTENSION_DESC = 'extensionDesc', | ||
82 | - // CALL_TYPE = 'callType', | 76 | + IS_TCP_MODBUS_PROFILE = 'isTcpModbusProfile', |
83 | } | 77 | } |
84 | 78 | ||
85 | const isTcpProfile = (transportType: string) => transportType === TransportTypeEnum.TCP; | 79 | const isTcpProfile = (transportType: string) => transportType === TransportTypeEnum.TCP; |
@@ -131,6 +125,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | @@ -131,6 +125,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | ||
131 | [DataSourceField.DEVICE_ID]: null, | 125 | [DataSourceField.DEVICE_ID]: null, |
132 | [DataSourceField.ATTRIBUTE]: null, | 126 | [DataSourceField.ATTRIBUTE]: null, |
133 | [DataSourceField.TRANSPORT_TYPE]: null, | 127 | [DataSourceField.TRANSPORT_TYPE]: null, |
128 | + [DataSourceField.COMMAND_TYPE]: null, | ||
134 | }); | 129 | }); |
135 | }, | 130 | }, |
136 | getPopupContainer: () => document.body, | 131 | getPopupContainer: () => document.body, |
@@ -144,6 +139,12 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | @@ -144,6 +139,12 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | ||
144 | ifShow: false, | 139 | ifShow: false, |
145 | }, | 140 | }, |
146 | { | 141 | { |
142 | + field: DataSourceField.IS_TCP_MODBUS_PROFILE, | ||
143 | + component: 'Input', | ||
144 | + label: 'TCP Modbus产品', | ||
145 | + ifShow: false, | ||
146 | + }, | ||
147 | + { | ||
147 | field: DataSourceField.DEVICE_PROFILE_ID, | 148 | field: DataSourceField.DEVICE_PROFILE_ID, |
148 | component: 'ApiSelect', | 149 | component: 'ApiSelect', |
149 | label: '产品', | 150 | label: '产品', |
@@ -166,14 +167,35 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | @@ -166,14 +167,35 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | ||
166 | labelField: 'name', | 167 | labelField: 'name', |
167 | valueField: 'id', | 168 | valueField: 'id', |
168 | placeholder: '请选择产品', | 169 | placeholder: '请选择产品', |
169 | - onChange: (_, option: Record<'transportType', string>) => { | 170 | + onChange: (_, option: DeviceProfileModel) => { |
171 | + const transportType = option?.[DataSourceField.TRANSPORT_TYPE]; | ||
170 | setFieldsValue({ | 172 | setFieldsValue({ |
171 | [DataSourceField.DEVICE_ID]: null, | 173 | [DataSourceField.DEVICE_ID]: null, |
172 | [DataSourceField.ATTRIBUTE]: null, | 174 | [DataSourceField.ATTRIBUTE]: null, |
173 | - [DataSourceField.TRANSPORT_TYPE]: option?.[DataSourceField.TRANSPORT_TYPE], | ||
174 | [DataSourceField.COMMAND_TYPE]: null, | 175 | [DataSourceField.COMMAND_TYPE]: null, |
176 | + [DataSourceField.TRANSPORT_TYPE]: transportType, | ||
177 | + [DataSourceField.IS_TCP_MODBUS_PROFILE]: | ||
178 | + transportType === TransportTypeEnum.TCP && | ||
179 | + option?.profileData?.transportConfiguration?.protocol === | ||
180 | + TCPProtocolTypeEnum.MODBUS_RTU, | ||
175 | }); | 181 | }); |
176 | }, | 182 | }, |
183 | + onOptionsChange(options: (DeviceProfileModel & Record<'value' | 'label', string>)[]) { | ||
184 | + const currentItem = options.find( | ||
185 | + (item) => item.value === formModel[DataSourceField.DEVICE_PROFILE_ID] | ||
186 | + ); | ||
187 | + | ||
188 | + if (currentItem) { | ||
189 | + const transportType = currentItem?.[DataSourceField.TRANSPORT_TYPE]; | ||
190 | + setFieldsValue({ | ||
191 | + [DataSourceField.TRANSPORT_TYPE]: transportType, | ||
192 | + [DataSourceField.IS_TCP_MODBUS_PROFILE]: | ||
193 | + transportType === TransportTypeEnum.TCP && | ||
194 | + currentItem?.profileData?.transportConfiguration?.protocol === | ||
195 | + TCPProtocolTypeEnum.MODBUS_RTU, | ||
196 | + }); | ||
197 | + } | ||
198 | + }, | ||
177 | getPopupContainer: () => document.body, | 199 | getPopupContainer: () => document.body, |
178 | }; | 200 | }; |
179 | }, | 201 | }, |
@@ -236,13 +258,13 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | @@ -236,13 +258,13 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | ||
236 | deviceType: item.deviceType, | 258 | deviceType: item.deviceType, |
237 | })); | 259 | })); |
238 | 260 | ||
239 | - if ( | ||
240 | - unref(selectWidgetKeys).componentKey === | ||
241 | - ControlComponentEnum.LATERAL_NUMERICAL_CONTROL && | ||
242 | - isTcpProfile(formModel[DataSourceField.TRANSPORT_TYPE]) | ||
243 | - ) { | ||
244 | - return result.filter((item) => item.codeType === TaskTypeEnum.MODBUS_RTU); | ||
245 | - } | 261 | + // if ( |
262 | + // unref(selectWidgetKeys).componentKey === | ||
263 | + // ControlComponentEnum.LATERAL_NUMERICAL_CONTROL && | ||
264 | + // isTcpProfile(formModel[DataSourceField.TRANSPORT_TYPE]) | ||
265 | + // ) { | ||
266 | + // return result.filter((item) => item.codeType === TaskTypeEnum.MODBUS_RTU); | ||
267 | + // } | ||
246 | 268 | ||
247 | return result; | 269 | return result; |
248 | } | 270 | } |
@@ -254,7 +276,6 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | @@ -254,7 +276,6 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | ||
254 | setFieldsValue({ | 276 | setFieldsValue({ |
255 | [DataSourceField.COMMAND_TYPE]: null, | 277 | [DataSourceField.COMMAND_TYPE]: null, |
256 | [DataSourceField.DEVICE_NAME]: options?.label, | 278 | [DataSourceField.DEVICE_NAME]: options?.label, |
257 | - [DataSourceField.CODE_TYPE]: options?.codeType, | ||
258 | }); | 279 | }); |
259 | }, | 280 | }, |
260 | placeholder: '请选择设备', | 281 | placeholder: '请选择设备', |
@@ -263,26 +284,19 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | @@ -263,26 +284,19 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | ||
263 | }, | 284 | }, |
264 | }, | 285 | }, |
265 | { | 286 | { |
266 | - field: DataSourceField.CODE_TYPE, | ||
267 | - label: '设备标识符类型', | ||
268 | - component: 'Input', | ||
269 | - ifShow: false, | ||
270 | - }, | ||
271 | - { | ||
272 | field: DataSourceField.ATTRIBUTE, | 287 | field: DataSourceField.ATTRIBUTE, |
273 | component: 'ApiSelect', | 288 | component: 'ApiSelect', |
274 | label: '属性', | 289 | label: '属性', |
275 | colProps: { span: 8 }, | 290 | colProps: { span: 8 }, |
276 | rules: [{ required: true, message: '请选择属性' }], | 291 | rules: [{ required: true, message: '请选择属性' }], |
277 | ifShow: () => category !== CategoryEnum.MAP, | 292 | ifShow: () => category !== CategoryEnum.MAP, |
278 | - componentProps({ formModel, formActionType }) { | ||
279 | - const { setFieldsValue } = formActionType; | 293 | + componentProps({ formModel }) { |
280 | const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | 294 | const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; |
281 | return { | 295 | return { |
282 | api: async () => { | 296 | api: async () => { |
283 | try { | 297 | try { |
284 | if (deviceProfileId) { | 298 | if (deviceProfileId) { |
285 | - const option = await getDeviceAttribute({ | 299 | + let option = await getDeviceAttribute({ |
286 | deviceProfileId, | 300 | deviceProfileId, |
287 | dataType: | 301 | dataType: |
288 | (isControlComponent(category!) && | 302 | (isControlComponent(category!) && |
@@ -292,21 +306,26 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | @@ -292,21 +306,26 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | ||
292 | ? DataTypeEnum.BOOL | 306 | ? DataTypeEnum.BOOL |
293 | : undefined, | 307 | : undefined, |
294 | }); | 308 | }); |
309 | + // 过滤只读属性 | ||
310 | + option = option.filter( | ||
311 | + (item) => item.accessMode !== ObjectModelAccessModeEnum.READ | ||
312 | + ); | ||
295 | 313 | ||
296 | // 选择控制组件4的时候只能选择属性且是(int double类型) | 314 | // 选择控制组件4的时候只能选择属性且是(int double类型) |
297 | if ( | 315 | if ( |
298 | unref(selectWidgetKeys).componentKey === | 316 | unref(selectWidgetKeys).componentKey === |
299 | ControlComponentEnum.LATERAL_NUMERICAL_CONTROL | 317 | ControlComponentEnum.LATERAL_NUMERICAL_CONTROL |
300 | ) { | 318 | ) { |
301 | - setFieldsValue({ | ||
302 | - [DataSourceField.COMMAND_TYPE]: CommandTypeEnum.ATTRIBUTE.toString(), | ||
303 | - }); | 319 | + // setFieldsValue({ |
320 | + // [DataSourceField.COMMAND_TYPE]: CommandTypeEnum.ATTRIBUTE.toString(), | ||
321 | + // }); | ||
304 | return option.filter( | 322 | return option.filter( |
305 | (item) => | 323 | (item) => |
306 | item.detail.dataType.type === DataTypeEnum.NUMBER_INT || | 324 | item.detail.dataType.type === DataTypeEnum.NUMBER_INT || |
307 | item.detail.dataType.type == DataTypeEnum.NUMBER_DOUBLE | 325 | item.detail.dataType.type == DataTypeEnum.NUMBER_DOUBLE |
308 | ); | 326 | ); |
309 | } | 327 | } |
328 | + | ||
310 | return option; | 329 | return option; |
311 | } | 330 | } |
312 | } catch (error) {} | 331 | } catch (error) {} |
@@ -412,18 +431,19 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | @@ -412,18 +431,19 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | ||
412 | isControlComponent(category!) && | 431 | isControlComponent(category!) && |
413 | unref(selectWidgetKeys).componentKey !== ControlComponentEnum.LATERAL_NUMERICAL_CONTROL && | 432 | unref(selectWidgetKeys).componentKey !== ControlComponentEnum.LATERAL_NUMERICAL_CONTROL && |
414 | isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && | 433 | isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && |
415 | - model[DataSourceField.CODE_TYPE] === TaskTypeEnum.CUSTOM | 434 | + !model[DataSourceField.IS_TCP_MODBUS_PROFILE] |
416 | ); | 435 | ); |
417 | }, | 436 | }, |
418 | componentProps: ({ formActionType, formModel }) => { | 437 | componentProps: ({ formActionType, formModel }) => { |
419 | const { setFieldsValue } = formActionType; | 438 | const { setFieldsValue } = formActionType; |
420 | const deviceType = formModel[DataSourceField.DEVICE_TYPE]; | 439 | const deviceType = formModel[DataSourceField.DEVICE_TYPE]; |
440 | + const isTCPModbusProfile = formModel[DataSourceField.IS_TCP_MODBUS_PROFILE]; | ||
421 | const options: Record<'label' | 'value', string | number>[] = [ | 441 | const options: Record<'label' | 'value', string | number>[] = [ |
422 | { label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM }, | 442 | { label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM }, |
423 | ]; | 443 | ]; |
424 | 444 | ||
425 | - // 网关子设备无服务 | ||
426 | - if (deviceType !== DeviceTypeEnum.SENSOR) | 445 | + // TCP网关子设备无服务 TCP Modbus类型设备无服务 |
446 | + if (deviceType !== DeviceTypeEnum.SENSOR && !isTCPModbusProfile) | ||
427 | options.push({ label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE }); | 447 | options.push({ label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE }); |
428 | 448 | ||
429 | return { | 449 | return { |
@@ -448,9 +468,8 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | @@ -448,9 +468,8 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | ||
448 | colProps: { span: 8 }, | 468 | colProps: { span: 8 }, |
449 | rules: [{ required: true, message: '请选择开服务' }], | 469 | rules: [{ required: true, message: '请选择开服务' }], |
450 | ifShow: ({ model }) => | 470 | ifShow: ({ model }) => |
451 | - isControlComponent(category!) && | ||
452 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE && | ||
453 | - isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), | 471 | + isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && |
472 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE, | ||
454 | componentProps({ formModel }) { | 473 | componentProps({ formModel }) { |
455 | const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | 474 | const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; |
456 | return { | 475 | return { |
@@ -472,9 +491,8 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | @@ -472,9 +491,8 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | ||
472 | colProps: { span: 8 }, | 491 | colProps: { span: 8 }, |
473 | rules: [{ required: true, message: '请选择关服务' }], | 492 | rules: [{ required: true, message: '请选择关服务' }], |
474 | ifShow: ({ model }) => | 493 | ifShow: ({ model }) => |
475 | - isControlComponent(category!) && | ||
476 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE && | ||
477 | - isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), | 494 | + isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && |
495 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE, | ||
478 | componentProps({ formModel }) { | 496 | componentProps({ formModel }) { |
479 | const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | 497 | const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; |
480 | return { | 498 | return { |
@@ -497,10 +515,8 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | @@ -497,10 +515,8 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | ||
497 | rules: [{ validator: validateTCPCustomCommand }], | 515 | rules: [{ validator: validateTCPCustomCommand }], |
498 | // 是控制组件 && 自定义命令 && 传输协议为TCP | 516 | // 是控制组件 && 自定义命令 && 传输协议为TCP |
499 | ifShow: ({ model }) => | 517 | ifShow: ({ model }) => |
500 | - isControlComponent(category!) && | ||
501 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM && | ||
502 | - model[DataSourceField.TRANSPORT_TYPE] && | ||
503 | - isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), | 518 | + isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && |
519 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM, | ||
504 | componentProps: { | 520 | componentProps: { |
505 | placeholder: '请输入开下发命令', | 521 | placeholder: '请输入开下发命令', |
506 | }, | 522 | }, |
@@ -513,10 +529,8 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | @@ -513,10 +529,8 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | ||
513 | rules: [{ validator: validateTCPCustomCommand }], | 529 | rules: [{ validator: validateTCPCustomCommand }], |
514 | // 是控制组件 && 自定义命令 && 传输协议为TCP | 530 | // 是控制组件 && 自定义命令 && 传输协议为TCP |
515 | ifShow: ({ model }) => | 531 | ifShow: ({ model }) => |
516 | - isControlComponent(category!) && | ||
517 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM && | ||
518 | - model[DataSourceField.TRANSPORT_TYPE] && | ||
519 | - isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), | 532 | + isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && |
533 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM, | ||
520 | componentProps: { | 534 | componentProps: { |
521 | placeholder: '请输入关下发命令', | 535 | placeholder: '请输入关下发命令', |
522 | }, | 536 | }, |