Showing
5 changed files
with
255 additions
and
17 deletions
| ... | ... | @@ -166,6 +166,7 @@ export interface DeviceRecord { |
| 166 | 166 | id: string; |
| 167 | 167 | creator: string; |
| 168 | 168 | createTime: string; |
| 169 | + codeType?: string; | |
| 169 | 170 | name: string; |
| 170 | 171 | tenantId: string; |
| 171 | 172 | transportType: string; |
| ... | ... | @@ -199,6 +200,8 @@ export interface DeviceModelOfMatterAttrs { |
| 199 | 200 | identifier: string; |
| 200 | 201 | accessMode: string; |
| 201 | 202 | detail: StructJSON; |
| 203 | + deviceDetail?: DeviceRecord; | |
| 204 | + extensionDesc?: any; | |
| 202 | 205 | } |
| 203 | 206 | |
| 204 | 207 | export interface DeviceStateLogModel { | ... | ... |
| ... | ... | @@ -238,7 +238,7 @@ |
| 238 | 238 | const handleSendCommandModal = (data: DataSource) => { |
| 239 | 239 | openSendCommandModal(true, { |
| 240 | 240 | mode: DataActionModeEnum.READ, |
| 241 | - record: { ...toRaw(data.detail) }, | |
| 241 | + record: { ...toRaw(data.detail), deviceDetail: props.deviceDetail as any }, | |
| 242 | 242 | } as ModalParamsType); |
| 243 | 243 | }; |
| 244 | 244 | ... | ... |
| 1 | +import { FormSchema } from '/@/components/Form'; | |
| 2 | + | |
| 3 | +const InsertString = (t, c, n) => { | |
| 4 | + const r: string | number[] = []; | |
| 5 | + | |
| 6 | + for (let i = 0; i * 2 < t.length; i++) { | |
| 7 | + r.push(t.substr(i * 2, n)); | |
| 8 | + } | |
| 9 | + return r.join(c); | |
| 10 | +}; | |
| 11 | +const FillString = (t, c, n, b) => { | |
| 12 | + if (t == '' || c.length != 1 || n <= t.length) { | |
| 13 | + return t; | |
| 14 | + } | |
| 15 | + const l = t.length; | |
| 16 | + | |
| 17 | + for (let i = 0; i < n - l; i++) { | |
| 18 | + if (b == true) { | |
| 19 | + t = c + t; | |
| 20 | + } else { | |
| 21 | + t += c; | |
| 22 | + } | |
| 23 | + } | |
| 24 | + return t; | |
| 25 | +}; | |
| 26 | +const SingleToHex = (t) => { | |
| 27 | + if (t == '') { | |
| 28 | + return ''; | |
| 29 | + } | |
| 30 | + t = parseFloat(t); | |
| 31 | + | |
| 32 | + if (isNaN(t) == true) { | |
| 33 | + return 'Error'; | |
| 34 | + } | |
| 35 | + if (t == 0) { | |
| 36 | + return '00000000'; | |
| 37 | + } | |
| 38 | + let s, e, m; | |
| 39 | + | |
| 40 | + if (t > 0) { | |
| 41 | + s = 0; | |
| 42 | + } else { | |
| 43 | + s = 1; | |
| 44 | + | |
| 45 | + t = 0 - t; | |
| 46 | + } | |
| 47 | + m = t.toString(2); | |
| 48 | + | |
| 49 | + if (m >= 1) { | |
| 50 | + if (m.indexOf('.') == -1) { | |
| 51 | + m = m + '.0'; | |
| 52 | + } | |
| 53 | + e = m.indexOf('.') - 1; | |
| 54 | + } else { | |
| 55 | + e = 1 - m.indexOf('1'); | |
| 56 | + } | |
| 57 | + if (e >= 0) { | |
| 58 | + m = m.replace('.', ''); | |
| 59 | + } else { | |
| 60 | + m = m.substring(m.indexOf('1')); | |
| 61 | + } | |
| 62 | + if (m.length > 24) { | |
| 63 | + m = m.substr(0, 24); | |
| 64 | + } else { | |
| 65 | + m = FillString(m, '0', 24, false); | |
| 66 | + } | |
| 67 | + m = m.substring(1); | |
| 68 | + | |
| 69 | + e = (e + 127).toString(2); | |
| 70 | + | |
| 71 | + e = FillString(e, '0', 8, true); | |
| 72 | + | |
| 73 | + let r = parseInt(s + e + m, 2).toString(16); | |
| 74 | + | |
| 75 | + r = FillString(r, '0', 8, true); | |
| 76 | + | |
| 77 | + return InsertString(r, ' ', 2).toUpperCase(); | |
| 78 | +}; | |
| 79 | + | |
| 80 | +const FormatHex = (t, n, ie) => { | |
| 81 | + const r: string[] = []; | |
| 82 | + | |
| 83 | + let s = ''; | |
| 84 | + | |
| 85 | + let c = 0; | |
| 86 | + | |
| 87 | + for (let i = 0; i < t.length; i++) { | |
| 88 | + if (t.charAt(i) != ' ') { | |
| 89 | + s += t.charAt(i); | |
| 90 | + | |
| 91 | + c += 1; | |
| 92 | + | |
| 93 | + if (c == n) { | |
| 94 | + r.push(s); | |
| 95 | + | |
| 96 | + s = ''; | |
| 97 | + | |
| 98 | + c = 0; | |
| 99 | + } | |
| 100 | + } | |
| 101 | + if (ie == false) { | |
| 102 | + if (i == t.length - 1 && s != '') { | |
| 103 | + r.push(s); | |
| 104 | + } | |
| 105 | + } | |
| 106 | + } | |
| 107 | + return r.join('\n'); | |
| 108 | +}; | |
| 109 | +const FormatHexBatch = (t, n, ie) => { | |
| 110 | + const a = t.split('\n'); | |
| 111 | + | |
| 112 | + const r: string[] = []; | |
| 113 | + | |
| 114 | + for (let i = 0; i < a.length; i++) { | |
| 115 | + r[i] = FormatHex(a[i], n, ie); | |
| 116 | + } | |
| 117 | + return r.join('\n'); | |
| 118 | +}; | |
| 119 | +const SingleToHexBatch = (t) => { | |
| 120 | + const a = t.split('\n'); | |
| 121 | + | |
| 122 | + const r: string[] = []; | |
| 123 | + | |
| 124 | + for (let i = 0; i < a.length; i++) { | |
| 125 | + r[i] = SingleToHex(a[i]); | |
| 126 | + } | |
| 127 | + return r.join('\r\n'); | |
| 128 | +}; | |
| 129 | + | |
| 130 | +const formSchemasConfig = (schemas): FormSchema[] => { | |
| 131 | + const { identifier, functionName } = schemas; | |
| 132 | + if (identifier == '06') { | |
| 133 | + return [ | |
| 134 | + { | |
| 135 | + field: identifier, | |
| 136 | + label: functionName, | |
| 137 | + component: 'InputNumber', | |
| 138 | + rules: [{ required: true, message: '请输入值' }], | |
| 139 | + componentProps: { | |
| 140 | + precision: 0, | |
| 141 | + placeholder: `请输入整数`, | |
| 142 | + }, | |
| 143 | + }, | |
| 144 | + ]; | |
| 145 | + } else if (identifier == '05') { | |
| 146 | + return [ | |
| 147 | + { | |
| 148 | + field: identifier, | |
| 149 | + label: functionName, | |
| 150 | + component: 'InputNumber', | |
| 151 | + rules: [{ required: true, message: '请输入值' }], | |
| 152 | + componentProps: { | |
| 153 | + min: 0, | |
| 154 | + max: 1, | |
| 155 | + precision: 0, | |
| 156 | + placeholder: `请输入0或1`, | |
| 157 | + }, | |
| 158 | + }, | |
| 159 | + ]; | |
| 160 | + } else { | |
| 161 | + return [ | |
| 162 | + { | |
| 163 | + field: identifier, | |
| 164 | + label: functionName, | |
| 165 | + component: 'InputNumber', | |
| 166 | + rules: [{ required: true, message: '请输入值' }], | |
| 167 | + componentProps: { | |
| 168 | + placeholder: `请输入值`, | |
| 169 | + formatter: (e) => | |
| 170 | + `${e}`.replace(/\B(?=(\d{3})+(?!\d))/g, '').replace(/^(-)*(\d+)\.(\d\d).*$/, '$1$2.$3'), | |
| 171 | + }, | |
| 172 | + }, | |
| 173 | + ]; | |
| 174 | + } | |
| 175 | +}; | |
| 176 | + | |
| 177 | +export { | |
| 178 | + InsertString, | |
| 179 | + FillString, | |
| 180 | + SingleToHex, | |
| 181 | + FormatHex, | |
| 182 | + FormatHexBatch, | |
| 183 | + SingleToHexBatch, | |
| 184 | + formSchemasConfig, | |
| 185 | +}; | ... | ... |
| ... | ... | @@ -10,6 +10,9 @@ |
| 10 | 10 | import { sendCommandOneway } from '/@/api/dataBoard'; |
| 11 | 11 | import { useMessage } from '/@/hooks/web/useMessage'; |
| 12 | 12 | import { unref } from 'vue'; |
| 13 | + import { genModbusCommand } from '/@/api/task'; | |
| 14 | + import { TaskTypeEnum } from '/@/views/task/center/config'; | |
| 15 | + import { SingleToHex, formSchemasConfig } from './config'; | |
| 13 | 16 | |
| 14 | 17 | defineEmits(['register']); |
| 15 | 18 | const props = defineProps<{ deviceId: string; deviceName: string }>(); |
| ... | ... | @@ -24,45 +27,93 @@ |
| 24 | 27 | |
| 25 | 28 | const keys = ref<string[]>([]); |
| 26 | 29 | |
| 27 | - const [register] = useModalInner((params: ModalParamsType<DeviceModelOfMatterAttrs>) => { | |
| 30 | + const modBUSForm = ref<any>({}); | |
| 31 | + const isShowModBUS = ref<Boolean>(false); | |
| 32 | + const formField = ref(''); | |
| 33 | + | |
| 34 | + const [register] = useModalInner(async (params: ModalParamsType<DeviceModelOfMatterAttrs>) => { | |
| 28 | 35 | const { record } = params; |
| 29 | - const { name, detail, identifier } = record; | |
| 36 | + const { name, detail, identifier, deviceDetail, extensionDesc } = record; | |
| 30 | 37 | const { dataType } = detail; |
| 31 | 38 | const { type } = dataType || {}; |
| 39 | + const { codeType } = deviceDetail || {}; | |
| 40 | + const { registerAddress, actionType } = extensionDesc || {}; | |
| 41 | + formField.value = identifier; | |
| 32 | 42 | |
| 33 | 43 | let schemas = [{ dataType: dataType, identifier, functionName: name } as StructJSON]; |
| 34 | 44 | |
| 35 | 45 | if (type === DataTypeEnum.IS_STRUCT) { |
| 36 | 46 | schemas = dataType?.specs as StructJSON[]; |
| 37 | 47 | } |
| 48 | + if (codeType == TaskTypeEnum.MODBUS) { | |
| 49 | + isShowModBUS.value = true; | |
| 50 | + modBUSForm.value = { | |
| 51 | + crc: 'CRC_16_LOWER', | |
| 52 | + deviceCode: '01', | |
| 53 | + method: actionType, | |
| 54 | + registerAddress, | |
| 55 | + registerNumber: 1, | |
| 56 | + registerValues: [], | |
| 57 | + }; | |
| 58 | + setProps({ schemas: formSchemasConfig(schemas[0]) }); | |
| 59 | + } else { | |
| 60 | + isShowModBUS.value = false; | |
| 61 | + const formSchemas = genForm(schemas); | |
| 62 | + keys.value = schemas.map((item) => { | |
| 63 | + return item.identifier!; | |
| 64 | + }); | |
| 65 | + setProps({ schemas: formSchemas }); | |
| 66 | + } | |
| 38 | 67 | |
| 39 | - const formSchemas = genForm(schemas); | |
| 40 | - | |
| 41 | - keys.value = schemas.map((item) => { | |
| 42 | - return item.identifier!; | |
| 43 | - }); | |
| 44 | - setProps({ schemas: formSchemas }); | |
| 45 | 68 | resetFields(); |
| 46 | 69 | }); |
| 47 | 70 | |
| 71 | + const getArray = (values) => { | |
| 72 | + const str = values.replace(/\s+/g, ''); | |
| 73 | + const array: any = []; | |
| 74 | + | |
| 75 | + for (let i = 0; i < str.length; i += 4) { | |
| 76 | + const chunk = parseInt(str.substring(i, i + 4), 16); | |
| 77 | + array.push(chunk); | |
| 78 | + } | |
| 79 | + return array; | |
| 80 | + }; | |
| 81 | + | |
| 48 | 82 | const { createMessage } = useMessage(); |
| 49 | 83 | const loading = ref(false); |
| 50 | 84 | const handleSend = async () => { |
| 51 | 85 | try { |
| 52 | 86 | loading.value = true; |
| 53 | 87 | if (!props.deviceId) return; |
| 54 | - const _value = getFieldsValue(); | |
| 55 | - const value = unref(keys).reduce((prev, next) => { | |
| 56 | - return { ...prev, [next]: _value[next] }; | |
| 57 | - }, {}); | |
| 88 | + const sendValue = ref({}); | |
| 89 | + if (unref(isShowModBUS)) { | |
| 90 | + //判断tcp类型 标识符是自定义还是ModBus | |
| 91 | + const oldValue = getFieldsValue()[unref(formField)]; | |
| 92 | + const newValue = getArray(SingleToHex(oldValue)); | |
| 93 | + | |
| 94 | + modBUSForm.value.registerValues = | |
| 95 | + unref(modBUSForm).method != '16' ? [newValue[0]] : newValue; | |
| 96 | + modBUSForm.value.registerNumber = unref(modBUSForm).method != '16' ? 1 : 2; | |
| 97 | + | |
| 98 | + sendValue.value = await genModbusCommand(unref(modBUSForm)); | |
| 99 | + } else { | |
| 100 | + const _value = getFieldsValue(); | |
| 101 | + | |
| 102 | + sendValue.value = unref(keys).reduce((prev, next) => { | |
| 103 | + return { ...prev, [next]: _value[next] }; | |
| 104 | + }, {}); | |
| 105 | + } | |
| 106 | + | |
| 58 | 107 | await sendCommandOneway({ |
| 59 | 108 | deviceId: props.deviceId, |
| 60 | 109 | value: { |
| 61 | 110 | persistent: true, |
| 62 | 111 | method: 'methodThingskit', |
| 63 | - params: { | |
| 64 | - ...value, | |
| 65 | - }, | |
| 112 | + params: unref(isShowModBUS) | |
| 113 | + ? unref(sendValue) | |
| 114 | + : { | |
| 115 | + ...unref(sendValue), | |
| 116 | + }, | |
| 66 | 117 | }, |
| 67 | 118 | }); |
| 68 | 119 | createMessage.success('属性下发成功~'); | ... | ... |