Commit 66a595da8ba91b1ee121c854c7aff0927aa62be7

Authored by xp.Huang
2 parents 9fc75900 3804a6bf

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 }
  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 +}
  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 +}
  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
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 +export enum InputTypeEnum {
  2 + /**
  3 + * @description 16进制
  4 + */
  5 + HEX = 'HEX',
  6 +
  7 + /**
  8 + * @description 10进制
  9 + */
  10 + DEC = 'DEC',
  11 +}
  1 +export { default as HexInput } from './index.vue';
  2 +export { InputTypeEnum } from './config';
  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 -};  
@@ -98,4 +98,3 @@ @@ -98,4 +98,3 @@
98 /> 98 />
99 </section> 99 </section>
100 </template> 100 </template>
101 -./index.config  
@@ -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 },