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