From 14763d895151f3ddad09906f2233057b8b967881 Mon Sep 17 00:00:00 2001
From: huang <1532065656@qq.com>
Date: 星期五, 19 十二月 2025 17:06:18 +0800
Subject: [PATCH] 添加plc通讯协议工厂,支持后续多种plc协议
---
mes-web/src/views/device/DeviceEditDialog.vue | 307 +++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 282 insertions(+), 25 deletions(-)
diff --git a/mes-web/src/views/device/DeviceEditDialog.vue b/mes-web/src/views/device/DeviceEditDialog.vue
index 773dd73..ad6668c 100644
--- a/mes-web/src/views/device/DeviceEditDialog.vue
+++ b/mes-web/src/views/device/DeviceEditDialog.vue
@@ -40,21 +40,21 @@
</el-form-item>
<el-form-item label="璁惧绫诲瀷" prop="deviceType">
- <el-select v-model="deviceForm.deviceType" placeholder="閫夋嫨璁惧绫诲瀷" style="width: 100%;">
- <el-option label="涓婂ぇ杞�" value="涓婂ぇ杞�" />
- <el-option label="澶х悊鐗�" value="澶х悊鐗�" />
- <el-option label="鐜荤拑瀛樺偍" value="鐜荤拑瀛樺偍" />
+ <el-select v-model="deviceForm.deviceType" placeholder="閫夋嫨璁惧绫诲瀷" style="width: 100%;" :loading="deviceTypesLoading">
+ <el-option
+ v-for="type in deviceTypes"
+ :key="type"
+ :label="type"
+ :value="type"
+ />
</el-select>
</el-form-item>
- <el-form-item label="PLC绫诲瀷" prop="plcType">
- <el-select v-model="deviceForm.plcType" placeholder="閫夋嫨PLC绫诲瀷" style="width: 100%;" clearable>
+ <el-form-item label="閫氳绫诲瀷" prop="plcType">
+ <el-select v-model="deviceForm.plcType" placeholder="閫夋嫨閫氳绫诲瀷" style="width: 100%;" clearable>
<el-option label="瑗块棬瀛� S7-1200" value="S1200" />
<el-option label="瑗块棬瀛� S7-1500" value="S1500" />
- <el-option label="瑗块棬瀛� S7-400" value="S400" />
- <el-option label="瑗块棬瀛� S7-300" value="S300" />
- <el-option label="瑗块棬瀛� S7-200" value="S200" />
- <el-option label="瑗块棬瀛� S7-200 SMART" value="S200_SMART" />
+ <el-option label="Modbus TCP" value="MODBUS" />
</el-select>
</el-form-item>
@@ -105,15 +105,7 @@
/>
</el-form-item>
- <el-form-item label="閫氳鍗忚" prop="protocolType">
- <el-select v-model="deviceForm.protocolType" placeholder="閫夋嫨閫氳鍗忚" style="width: 100%;">
- <el-option label="Modbus TCP" value="Modbus TCP" />
- <el-option label="OPC UA" value="OPC UA" />
- <el-option label="EtherNet/IP" value="EtherNet/IP" />
- <el-option label="Profinet" value="Profinet" />
- <el-option label="鍏朵粬" value="鍏朵粬" />
- </el-select>
- </el-form-item>
+
<el-form-item label="瓒呮椂鏃堕棿(绉�)" prop="timeout">
<el-input-number
@@ -240,6 +232,29 @@
</div>
</el-card>
+ <!-- 璁惧閫昏緫閰嶇疆 -->
+ <el-card class="form-section" shadow="never" style="margin-top: 20px;" v-if="deviceForm.deviceType">
+ <template #header>
+ <span class="section-title">璁惧閫昏緫閰嶇疆</span>
+ <span class="form-tip">鏍规嵁璁惧绫诲瀷閰嶇疆鐗瑰畾鐨勪笟鍔¢�昏緫鍙傛暟</span>
+ </template>
+
+ <!-- 浣跨敤鍔ㄦ�佺粍浠跺姞杞藉搴旇澶囩被鍨嬬殑閰嶇疆缁勪欢 -->
+ <component
+ :is="deviceConfigComponent"
+ v-if="deviceConfigComponent"
+ v-model="deviceLogicParams"
+ />
+ <div v-else class="no-config-tip">
+ <el-alert
+ :title="`璁惧绫诲瀷銆�${deviceForm.deviceType}銆嶆殏鏃犻厤缃粍浠禶"
+ type="info"
+ :closable="false"
+ show-icon
+ />
+ </div>
+ </el-card>
+
<!-- 鎻忚堪淇℃伅 -->
<el-card class="form-section" shadow="never" style="margin-top: 20px;">
<template #header>
@@ -295,6 +310,7 @@
import { ref, reactive, watch, computed } from 'vue'
import { ElMessage } from 'element-plus'
import { deviceConfigApi } from '@/api/device/deviceManagement'
+import { getDeviceConfigComponent } from './components/DeviceLogicConfig'
// Props瀹氫箟
const props = defineProps({
@@ -317,6 +333,24 @@
const saving = ref(false)
const testing = ref(false)
const testResult = ref(null)
+
+// 璁惧绫诲瀷鍒楄〃
+const deviceTypes = ref([])
+const deviceTypesLoading = ref(false)
+
+// 璁惧閫昏緫鍙傛暟锛堟牴鎹澶囩被鍨嬪姩鎬佹樉绀猴級
+const deviceLogicParams = ref({})
+
+const S7_PLC_TYPES = ['S1200', 'S1500']
+const MODBUS_PLC_TYPES = ['MODBUS']
+
+// 璁$畻灞炴�э細鏍规嵁璁惧绫诲瀷鑾峰彇瀵瑰簲鐨勯厤缃粍浠�
+const deviceConfigComponent = computed(() => {
+ if (!deviceForm.deviceType) {
+ return null
+ }
+ return getDeviceConfigComponent(deviceForm.deviceType)
+})
// 璁惧琛ㄥ崟鏁版嵁
const getDefaultForm = () => ({
@@ -374,9 +408,7 @@
moduleName: [
{ required: true, message: '璇疯緭鍏ユā鍧楀悕绉�', trigger: 'blur' }
],
- protocolType: [
- { required: true, message: '璇烽�夋嫨閫氳鍗忚', trigger: 'change' }
- ],
+
timeout: [
{ required: true, message: '璇疯緭鍏ヨ秴鏃舵椂闂�', trigger: 'blur' },
{ type: 'number', min: 1, max: 300, message: '瓒呮椂鏃堕棿鍦� 1 鍒� 300 绉掍箣闂�', trigger: 'blur' }
@@ -404,6 +436,8 @@
watch(() => props.modelValue, (newVal) => {
dialogVisible.value = newVal
if (newVal) {
+ // 鍔犺浇璁惧绫诲瀷鍒楄〃
+ loadDeviceTypes()
if (isEdit.value && props.deviceData) {
loadDeviceData(props.deviceData)
} else {
@@ -420,7 +454,61 @@
emit('update:modelValue', newVal)
})
+// 鐩戝惉PLC绫诲瀷鍙樺寲锛岃嚜鍔ㄨ缃�氳鍗忚
+watch(() => deviceForm.plcType, (newPlcType) => {
+ if (!newPlcType) {
+ return
+ }
+
+ if (S7_PLC_TYPES.includes(newPlcType)) {
+ if (!deviceForm.protocolType || deviceForm.protocolType === '鍏朵粬' || deviceForm.protocolType === 'Modbus TCP') {
+ deviceForm.protocolType = 'S7 Communication'
+ }
+ return
+ }
+
+ if (MODBUS_PLC_TYPES.includes(newPlcType)) {
+ if (!deviceForm.protocolType || deviceForm.protocolType === '鍏朵粬' || deviceForm.protocolType === 'S7 Communication') {
+ deviceForm.protocolType = 'Modbus TCP'
+ }
+ }
+})
+
+
+
// 鏂规硶瀹氫箟
+// 鍔犺浇璁惧绫诲瀷鍒楄〃
+const loadDeviceTypes = async () => {
+ if (deviceTypes.value.length > 0) {
+ // 濡傛灉宸茬粡鍔犺浇杩囷紝涓嶅啀閲嶅鍔犺浇
+ return
+ }
+ // 鎵�鏈夋敮鎸佺殑璁惧绫诲瀷锛堢‘淇濆寘鍚墍鏈夋湁閰嶇疆缁勪欢鐨勭被鍨嬶級
+ const supportedTypes = ['澶ц溅璁惧', '澶х悊鐗囩', '鍗ц浆绔嬫壂鐮�', '鍗ц浆绔�']
+
+ try {
+ deviceTypesLoading.value = true
+ const res = await deviceConfigApi.getDeviceTypes()
+ if (res?.data && Array.isArray(res.data)) {
+ // 鍚堝苟鏁版嵁搴撲腑鐨勭被鍨嬪拰鏀寔鐨勭被鍨嬶紝鍘婚噸骞舵帓搴�
+ const dbTypes = res.data
+ const allTypes = [...new Set([...supportedTypes, ...dbTypes])].sort()
+ deviceTypes.value = allTypes
+ } else {
+ // 濡傛灉API杩斿洖澶辫触锛屼娇鐢ㄩ粯璁ょ被鍨�
+ deviceTypes.value = supportedTypes
+ console.warn('鑾峰彇璁惧绫诲瀷鍒楄〃澶辫触锛屼娇鐢ㄩ粯璁ょ被鍨�')
+ }
+ } catch (error) {
+ console.error('鍔犺浇璁惧绫诲瀷鍒楄〃澶辫触:', error)
+ // 澶辫触鏃朵娇鐢ㄩ粯璁ょ被鍨�
+ deviceTypes.value = supportedTypes
+ ElMessage.warning('鍔犺浇璁惧绫诲瀷鍒楄〃澶辫触锛屼娇鐢ㄩ粯璁ょ被鍨�')
+ } finally {
+ deviceTypesLoading.value = false
+ }
+}
+
const parseJsonSafe = (str, defaultValue = null) => {
if (!str) return defaultValue
try {
@@ -453,11 +541,132 @@
deviceForm.dbArea = plcConfig.dbArea || 'DB1'
deviceForm.beginIndex = plcConfig.beginIndex ?? 0
deviceForm.autoModeInterval = plcConfig.autoModeInterval ?? 5000
+
+ // 鍔犺浇閰嶇疆鍙傛暟锛堜粠 configJson锛�
+ // 鍏煎涓ょ鏍煎紡锛�
+ // 1. 鏁扮粍鏍煎紡锛歔{ paramKey, paramValue, description }]
+ // 2. 瀵硅薄鏍煎紡锛堟棫鏍煎紡锛夛細{ fieldName: offset } - 鑷姩杞崲涓烘暟缁勬牸寮�
+ loadConfigParams(data?.configJson)
+
+ // 鍔犺浇璁惧閫昏緫鍙傛暟
+ const deviceLogic = extraObj.deviceLogic || {}
+ loadDeviceLogicParams(deviceLogic, data?.deviceType)
}
+
+// 鍔犺浇閰嶇疆鍙傛暟锛堝吋瀹规棫鐨勫璞℃牸寮忥級
+const loadConfigParams = (configJson) => {
+ if (!configJson) {
+ deviceForm.configParams = []
+ return
+ }
+
+ try {
+ const parsed = typeof configJson === 'string' ? JSON.parse(configJson) : configJson
+
+ // 濡傛灉鏄暟缁勬牸寮忥紝鐩存帴浣跨敤
+ if (Array.isArray(parsed)) {
+ deviceForm.configParams = parsed
+ }
+ // 濡傛灉鏄璞℃牸寮忥紙瀛楁鍚� 鈫� 鍋忕Щ閲忥級锛岃浆鎹负鏁扮粍鏍煎紡
+ else if (typeof parsed === 'object' && parsed !== null) {
+ // 瀛楁鍚嶅埌涓枃鎻忚堪鐨勬槧灏�
+ const fieldDescriptionMap = {
+ 'plcRequest': 'PLC璇锋眰瀛�',
+ 'inPosition': '杩涚墖浣嶇疆',
+ 'plcGlassId1': '鐜荤拑id1',
+ 'plcGlassId2': '鐜荤拑id2',
+ 'plcGlassId3': '鐜荤拑id3',
+ 'plcGlassId4': '鐜荤拑id4',
+ 'plcGlassId5': '鐜荤拑id5',
+ 'plcGlassId6': '鐜荤拑id6',
+ 'plcGlassCount': '鐜荤拑鏁伴噺',
+ 'onlineState': '鑱旀満鐘舵��',
+ 'plcReport': 'PLC姹囨姤',
+ 'state1': '鐘舵��1',
+ 'state2': '鐘舵��2',
+ 'state3': '鐘舵��3',
+ 'state4': '鐘舵��4',
+ 'state5': '鐘舵��5',
+ 'state6': '鐘舵��6',
+ 'mesSend': 'MES鍙戦��',
+ 'mesConfirm': 'MES纭',
+ 'trainInfo': '鍒楄溅淇℃伅',
+ 'start1': '璧峰1',
+ 'start2': '璧峰2',
+ 'start3': '璧峰3',
+ 'start4': '璧峰4',
+ 'start5': '璧峰5',
+ 'start6': '璧峰6',
+ 'target1': '鐩爣1',
+ 'target2': '鐩爣2',
+ 'target3': '鐩爣3',
+ 'target4': '鐩爣4',
+ 'target5': '鐩爣5',
+ 'target6': '鐩爣6',
+ 'mesWidth1': 'MES瀹藉害1',
+ 'mesWidth2': 'MES瀹藉害2',
+ 'mesWidth3': 'MES瀹藉害3',
+ 'mesWidth4': 'MES瀹藉害4',
+ 'mesWidth5': 'MES瀹藉害5',
+ 'mesWidth6': 'MES瀹藉害6',
+ 'mesHeight1': 'MES楂樺害1',
+ 'mesHeight2': 'MES楂樺害2',
+ 'mesHeight3': 'MES楂樺害3',
+ 'mesHeight4': 'MES楂樺害4',
+ 'mesHeight5': 'MES楂樺害5',
+ 'mesHeight6': 'MES楂樺害6',
+ 'mesThickness1': 'MES鍘氬害1',
+ 'mesThickness2': 'MES鍘氬害2',
+ 'mesThickness3': 'MES鍘氬害3',
+ 'mesThickness4': 'MES鍘氬害4',
+ 'mesThickness5': 'MES鍘氬害5',
+ 'mesThickness6': 'MES鍘氬害6',
+ 'edgeDistance1': '杈圭紭璺濈1',
+ 'edgeDistance2': '杈圭紭璺濈2',
+ 'edgeDistance3': '杈圭紭璺濈3',
+ 'edgeDistance4': '杈圭紭璺濈4',
+ 'edgeDistance5': '杈圭紭璺濈5',
+ 'edgeDistance6': '杈圭紭璺濈6',
+ 'targetEdgeDistance1': '鐩爣杈圭紭璺濈1',
+ 'targetEdgeDistance2': '鐩爣杈圭紭璺濈2',
+ 'targetEdgeDistance3': '鐩爣杈圭紭璺濈3',
+ 'targetEdgeDistance4': '鐩爣杈圭紭璺濈4',
+ 'targetEdgeDistance5': '鐩爣杈圭紭璺濈5',
+ 'targetEdgeDistance6': '鐩爣杈圭紭璺濈6',
+ 'alarmInfo': '鎶ヨ淇℃伅'
+ }
+
+ // 杞崲涓烘暟缁勬牸寮�
+ deviceForm.configParams = Object.keys(parsed).map(fieldName => ({
+ paramKey: fieldName,
+ paramValue: String(parsed[fieldName]),
+ description: fieldDescriptionMap[fieldName] || fieldName
+ }))
+ } else {
+ deviceForm.configParams = []
+ }
+ } catch (error) {
+ console.warn('瑙f瀽configJson澶辫触', error)
+ deviceForm.configParams = []
+ }
+}
+
+// 鍔犺浇璁惧閫昏緫鍙傛暟
+const loadDeviceLogicParams = (deviceLogic) => {
+ if (deviceLogic && Object.keys(deviceLogic).length > 0) {
+ deviceLogicParams.value = { ...deviceLogic }
+ } else {
+ deviceLogicParams.value = {}
+ }
+}
+
const resetForm = () => {
Object.assign(deviceForm, getDefaultForm())
deviceFormRef.value?.clearValidate()
+
+ // 閲嶇疆璁惧閫昏緫鍙傛暟
+ deviceLogicParams.value = {}
}
const addConfigParam = () => {
@@ -531,6 +740,26 @@
plcType: deviceForm.plcType
}
+ // 淇濆瓨璁惧閫昏緫鍙傛暟锛堢洿鎺ヤ娇鐢╠eviceLogicParams锛岀敱鍚勪釜閰嶇疆缁勪欢绠$悊锛�
+ if (deviceLogicParams.value && Object.keys(deviceLogicParams.value).length > 0) {
+ extraObj.deviceLogic = { ...deviceLogicParams.value }
+ } else {
+ delete extraObj.deviceLogic
+ }
+
+ // 鏋勫缓 configJson锛氬皢 configParams 鏁扮粍杞崲涓� JSON 瀛楃涓�
+ // configParams 缁撴瀯: [{ paramKey: '', paramValue: '', description: '' }]
+ let configJsonValue = null
+ if (deviceForm.configParams && deviceForm.configParams.length > 0) {
+ // 杩囨护鎺夌┖鍙傛暟
+ const validParams = deviceForm.configParams.filter(
+ param => param.paramKey && param.paramKey.trim() !== ''
+ )
+ if (validParams.length > 0) {
+ configJsonValue = JSON.stringify(validParams)
+ }
+ }
+
const saveData = {
deviceName: deviceForm.deviceName,
deviceCode: deviceForm.deviceCode,
@@ -542,9 +771,7 @@
isPrimary: deviceForm.isPrimary,
enabled: deviceForm.enabled,
description: deviceForm.description,
- configJson: deviceForm.configParams.length > 0
- ? JSON.stringify(deviceForm.configParams)
- : null,
+ configJson: configJsonValue, // 淇濆瓨閰嶇疆鍙傛暟JSON
extraParams: JSON.stringify(extraObj)
}
@@ -562,6 +789,18 @@
handleClose()
} catch (error) {
console.error('淇濆瓨璁惧閰嶇疆澶辫触:', error)
+ // 濡傛灉鏄〃鍗曢獙璇侀敊璇紝鏄剧ず鏇磋缁嗙殑閿欒淇℃伅
+ if (error && typeof error === 'object' && !error.response) {
+ const errorFields = Object.keys(error)
+ if (errorFields.length > 0) {
+ const firstError = error[errorFields[0]]
+ const errorMessage = Array.isArray(firstError)
+ ? firstError[0]?.message || firstError[0]
+ : firstError?.message || firstError
+ ElMessage.error(`琛ㄥ崟楠岃瘉澶辫触: ${errorMessage}`)
+ return
+ }
+ }
ElMessage.error(isEdit.value ? '鏇存柊璁惧閰嶇疆澶辫触' : '鍒涘缓璁惧閰嶇疆澶辫触')
} finally {
saving.value = false
@@ -647,4 +886,22 @@
:deep(.el-card__body) {
padding: 20px;
}
+
+.position-mapping {
+ width: 100%;
+}
+
+.mapping-item {
+ display: flex;
+ align-items: center;
+ margin-bottom: 12px;
+ padding: 12px;
+ border: 1px solid #ebeef5;
+ border-radius: 6px;
+ background-color: #fafafa;
+}
+
+.no-config-tip {
+ padding: 20px;
+}
</style>
\ No newline at end of file
--
Gitblit v1.8.0