Files
39-ZhuzhouCRRC-ZZBZ-Pad/src/views/HMIView/index.vue
2026-06-08 17:09:27 +08:00

664 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="main">
<div class="top">
<div class="left">
<span>当前RGV通道</span>
<span class="text">{{ store.user.opDescription || store.user.opName }}</span>
</div>
<div class="right">
<el-button type="success" @mousedown="WritePlc('启动',1)" @mouseup="WritePlc('启动',0)">启动</el-button>
<el-button type="warning" @mousedown="WritePlc('复位',1)" @mouseup="WritePlc('复位',0)">复位</el-button>
</div>
</div>
<div class="center">
<fieldset class="left">
<legend class="title">主界面</legend>
<div class="main">
<div class="carBattery">
当前电量
<div class="battery">
<div :style="{ height: data.batteryLevel + '%' }" class="battery-bar"></div>
<div style="position: relative;top: 40%">{{ data.batteryLevel }} %</div>
</div>
</div>
<div class="carPosList">
<div class="item">
<div class="unit">{{ data.XCWZ }} mm</div>
<div class="text">小车位置</div>
</div>
<div class="item">
<div class="unit">{{ data.SJWZ }} mm</div>
<div class="text">升降位置</div>
</div>
</div>
<div class="carStatus">
<div class="item">
<div :style="{ backgroundColor: data.carStatus1 ? '#1afa29' : '#8a8a8a' }" class="status"></div>
<el-button type="primary" @mousedown="WritePlc('点动',1)" @mouseup="WritePlc('点动',0)">点动</el-button>
</div>
<div class="item">
<div :style="{ backgroundColor: data.carStatus2 ? '#1afa29' : '#8a8a8a' }" class="status"></div>
<el-button type="primary" @mousedown="WritePlc('半自动',1)" @mouseup="WritePlc('半自动',0)">半自动</el-button>
</div>
<div class="item">
<div :style="{ backgroundColor: data.carStatus3 ? '#1afa29' : '#8a8a8a' }" class="status"></div>
<el-button type="primary" @mousedown="WritePlc('上料自动',1)" @mouseup="WritePlc('上料自动',0)">上料自动</el-button>
</div>
<div class="item">
<div :style="{ backgroundColor: data.carStatus4 ? '#1afa29' : '#8a8a8a' }" class="status"></div>
<el-button type="primary" @mousedown="WritePlc('下料自动',1)" @mouseup="WritePlc('下料自动',0)">下料自动</el-button>
</div>
</div>
</div>
</fieldset>
<fieldset class="right">
<legend class="title">手动操作</legend>
<div class="itemList">
<div class="item"></div>
<div class="item">
<div class="ellipseTest"
:style="{ backgroundColor: data.SJHYWC ? 'rgba(26, 250, 41,1)' : 'rgba(26, 250, 41,0)' }">升降回原完成</div>
</div>
<div class="item">
<el-button type="primary" @mousedown="WritePlc('小车前进',1)" @mouseup="WritePlc('小车前进',0)">小车前进</el-button>
</div>
<div class="item">
<el-button type="primary" @mousedown="WritePlc('升降回原位',1)" @mouseup="WritePlc('升降回原位',0)">升降回原位</el-button>
</div>
<div class="item">
<el-button type="primary" @mousedown="WritePlc('小车后退',1)" @mouseup="WritePlc('小车后退',0)">小车后退</el-button>
</div>
<div class="item">
<el-button type="primary" @mousedown="WritePlc('上升定位',1)" @mouseup="WritePlc('上升定位',0)">上升定位</el-button>
</div>
<div class="item"></div>
<div class="item">
<el-button type="primary" @mousedown="WritePlc('下降定位',1)" @mouseup="WritePlc('下降定位',0)">下降定位</el-button>
</div>
</div>
</fieldset>
</div>
<div class="bottom">
<fieldset class="left">
<legend class="title">充电桩</legend>
<div class="itemList">
<div class="item">
<div class="ellipseTest"
:style="{ backgroundColor: data.ZZCD ? 'rgba(26, 250, 41,1)' : 'rgba(26, 250, 41,0)' }">正在充电</div>
</div>
<div class="item">
<div class="unit">{{ data.CDDL }}</div>
<div class="text">充电电流</div>
</div>
<div class="item">
<div class="unit">{{ data.CDDY }}</div>
<div class="text">充电电压</div>
</div>
<div class="item">
<div class="ellipseTest"
:style="{ backgroundColor: data.DSSCDW ? 'rgba(26, 250, 41,1)' : 'rgba(26, 250, 41,0)' }">电刷伸出到位</div>
</div>
<div class="item">
<div class="unit">{{ data.CDMS }}</div>
<div class="text">充电模式</div>
</div>
<div class="item">
<div class="unit">{{ data.XT }}</div>
<div class="text">心跳</div>
</div>
</div>
<div style="display: flex">
<div class="item">
<el-button type="primary" @mousedown="WritePlc('开始充电',1)" @mouseup="WritePlc('开始充电',0)">开始充电</el-button>
</div>
<div class="item">
<el-button type="primary" @mousedown="WritePlc('停止充电',1)" @mouseup="WritePlc('停止充电',0)">停止充电</el-button>
</div>
</div>
</fieldset>
<fieldset class="right">
<legend class="title">工位操作</legend>
<div class="itemList">
<div class="item">上料位</div>
<div class="item">充电位</div>
<div class="item">位置1</div>
<div class="item">位置2</div>
<div class="item">位置3</div>
<div class="item">位置4</div>
<div class="item">位置5</div>
<div class="item" :style="{ color: getYdColor(data.SLDD) }">{{ data.SLDD ? '已到' : '未到' }}</div>
<div class="item" :style="{ color: getYdColor(data.CDWDD) }">{{ data.CDWDD ? '已到' : '未到' }}</div>
<div class="item" :style="{ color: getYdColor(data.WZ1DD) }">{{ data.WZ1DD ? '已到' : '未到' }}</div>
<div class="item" :style="{ color: getYdColor(data.WZ2DD) }">{{ data.WZ2DD ? '已到' : '未到' }}</div>
<div class="item" :style="{ color: getYdColor(data.WZ3DD) }">{{ data.WZ3DD ? '已到' : '未到' }}</div>
<div class="item" :style="{ color: getYdColor(data.WZ4DD) }">{{ data.WZ4DD ? '已到' : '未到' }}</div>
<div class="item" :style="{ color: getYdColor(data.WZ5DD) }">{{ data.WZ5DD ? '已到' : '未到' }}</div>
<div class="item" :style="{ color: getYdColor(data.SLXZ) }">{{ data.SLXZ ? '已选' : '未选' }}</div>
<div class="item" :style="{ color: getYdColor(data.CDXZ) }">{{ data.CDXZ ? '已选' : '未选' }}</div>
<div class="item" :style="{ color: getYdColor(data.WZ1XZ) }">{{ data.WZ1XZ ? '已选' : '未选' }}</div>
<div class="item" :style="{ color: getYdColor(data.WZ2XZ) }">{{ data.WZ2XZ ? '已选' : '未选' }}</div>
<div class="item" :style="{ color: getYdColor(data.WZ3XZ) }">{{ data.WZ3XZ ? '已选' : '未选' }}</div>
<div class="item" :style="{ color: getYdColor(data.WZ4XZ) }">{{ data.WZ4XZ ? '已选' : '未选' }}</div>
<div class="item" :style="{ color: getYdColor(data.WZ5XZ) }">{{ data.WZ5XZ ? '已选' : '未选' }}</div>
<div class="item">
<el-button type="primary" @mousedown="WritePlc('上料位选择',1)" @mouseup="WritePlc('上料位选择',0)">选择</el-button>
</div>
<div class="item">
<el-button type="primary" @mousedown="WritePlc('充电位选择',1)" @mouseup="WritePlc('充电位选择',0)">选择</el-button>
</div>
<div class="item">
<el-button type="primary" @mousedown="WritePlc('位置1选择',1)" @mouseup="WritePlc('位置1选择',0)">选择</el-button>
</div>
<div class="item">
<el-button type="primary" @mousedown="WritePlc('位置2选择',1)" @mouseup="WritePlc('位置2选择',0)">选择</el-button>
</div>
<div class="item">
<el-button type="primary" @mousedown="WritePlc('位置3选择',1)" @mouseup="WritePlc('位置3选择',0)">选择</el-button>
</div>
<div class="item">
<el-button type="primary" @mousedown="WritePlc('位置4选择',1)" @mouseup="WritePlc('位置4选择',0)">选择</el-button>
</div>
<div class="item">
<el-button type="primary" @mousedown="WritePlc('位置5选择',1)" @mouseup="WritePlc('位置5选择',0)">选择</el-button>
</div>
</div>
<el-button type="primary" @mousedown="WritePlc('定位启动',1)" @mouseup="WritePlc('定位启动',0)">定位启动</el-button>
</fieldset>
</div>
</div>
</template>
<script setup>
import { useAppStore } from "@/store/app.js";
import { inject, onMounted, onUnmounted, reactive, ref } from 'vue'
import mqtt from "mqtt";
import { ElMessage, ElMessageBox } from 'element-plus'
const store = useAppStore();
const CreateData = inject('CreateData')
const ExecDatabase = inject('ExecDatabase')
const ExecReadPLC = inject('ExecReadPLC')
const ExecIMESAPI = inject('ExecIMESAPI')
const ExecWritePLC = inject('ExecWritePLC')
const ExecMultReadPLC = inject('ExecMultReadPLC')
const data = reactive({
batteryLevel: 0, // 当前电量
XCWZ: '0', // 小车位置
SJWZ: '0.00', // 升降位置
carStatus1: false, // 点动
carStatus2: false, // 半自动
carStatus3: false, // 上料自动
carStatus4: false, // 下料自动
SJHYWC: false, // 升降回原完成
ZZCD: false, // 正在充电
DSSCDW: false, // 电刷伸出到位
CDDL: '0.00', // 充电电流
CDDY: '0.00', // 充电电压
CDMS: '0.00', // 充电模式
XT: '0.00', // 心跳
SLDD: false, // 上料到达
CDWDD: false, // 充电位到达
WZ1DD: false, // 位置1到达
WZ2DD: false, // 位置2到达
WZ3DD: false, // 位置3到达
WZ4DD: false, // 位置4到达
WZ5DD: false, // 位置5到达
SLXZ: false, // 上料选择
CDWXZ: false, // 充电位选择
WZ1XZ: false, // 位置1选择
WZ2XZ: false, // 位置2选择
WZ3XZ: false, // 位置3选择
WZ4XZ: false, // 位置4选择
WZ5XZ: false, // 位置5选择
})
var client = null
var timer1 = null
onMounted(() => {
connectMqtt()
initData()
timer1 = setInterval(() => {
readMultiPLCByTagIds()
}, 1000 * 2)
})
onUnmounted(() => {
try {
if (client) {
client.end();
}
} catch (error) {
console.log('关闭连接失败:', error);
}
// 可选:重置 client 为 null
client = null;
// 关闭定时器
clearInterval(timer1);
});
//连接MQTT
const connectMqtt = async () => {
const topics = ['PLCToWebUI']
// 连接选项
const options = {
keepalive: 60, // 心跳时间
reconnectPeriod: 3000, // 重新连接间隔时间
clean: true, // 是否清除会话
clientId: 'WEB_Op_' + getUUID(),//要确保唯一性例如随机数或者UUID,
username: '',//mqtt服务器配置里面的账号,
password: ''//mqtt服务器配置里面的密码,
}
console.log(window.g.Mqtt_Ip)
client = mqtt.connect(window.g.Mqtt_Ip, options);
client.on('reconnect', (error) => {
console.log('正在重连:', error)
ElMessage.warning('正在重连' + error)
})
client.on('error', (error) => {
console.log('连接失败:', error)
ElMessage.warning('连接失败' + error)
})
client.on('connect', (e) => {
console.log('MQTT连接成功');
// 订阅多个主题
client.subscribe(topics, { qos: 0 }, function (err) {
if (!err) {
console.log('订阅成功');
} else {
console.error('订阅失败:', err);
ElMessage.warning('订阅失败' + err)
}
})
})
client.on('message', (topic, message, packet) => {
messageHandle(topic, message.toString())
})
}
//状态灯信号监控
// case 600004://点动选择灯
// case 600005://半自动选择灯
// case 600006://上料自动选择灯
// case 600007://下料自动选择灯
// case 600010://正在充电灯
// case 600011://电刷伸出到位灯
// case 600020://升降回原完成灯
// case 600025://上料位选择状态灯
// case 600026://上料位到达状态灯
// case 600030://充电位选择状态灯
// case 600031://充电位到达状态灯
// case 600033://位置1选择状态灯
// case 600034://位置1到达状态灯
// case 600036://位置2选择状态灯
// case 600037://位置2到达状态灯
// case 600041://位置3选择状态灯
// case 600042://位置3到达状态灯
// case 600044://位置4选择状态灯
// case 600045://位置4到达状态灯
// case 600047://位置5选择状态灯
// case 600050://位置5到达状态灯
// case 600060://当前电量
// case 600100://小车位置
// case 600100://小车位置
// case 600140://升降位置
// case 600180://充电电流
// case 600200://充电电压
// case 600220://心跳计数
// case 600240://充电模式
const correspondenceList = window.plcPos.correspondenceList
const buttonList = window.plcPos.buttonList
const messageHandle = (topic, message) => {
switch (topic) {
case 'PLCToWebUI':
let reqData = JSON.parse(message)
console.log(reqData)
if (reqData.OPCode == store.user.opName) {
// 先判断data对象是否存在该tagId 如果存在 则该对应的数据
if (reqData.tagTypeCodeID in correspondenceList) {
data[correspondenceList[reqData.tagTypeCodeID].value] = reqData.tagValue
console.log('接收到信号【' + correspondenceList[reqData.tagTypeCodeID].name + '】信号值:' + reqData.tagValue)
}
}
break;
default:
return
break;
}
}
const getUUID = () => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// 批量读取PLC标签
const readMultiPLCByTagIds = () => {
// 需要读取的所有标签ID
const tagIds = ["600060", "600100", "600140", "600180", "600200", "600220", "600240"]
ExecMultReadPLC(store.user.opName, tagIds).then(response => {
if (response.data.code == 0) {
const results = response.data.ReadRes_SignalValues
// 处理所有返回的数据
results.forEach(result => {
const tagId = result.tagTypeCodeID
let value = result.tagValue
// 转换布尔值
if (value === 'True') {
value = true
} else if (value === 'False') {
value = false
}
// 如果对应表中有该标签,则更新数据
if (tagId in correspondenceList) {
data[correspondenceList[tagId].value] = value
}
})
}
})
}
// 保留原始方法,但不再使用
const readPLCByTagId = (tagId) => {
ExecReadPLC(store.user.opName, tagId).then(responseReadPLC => {
if (responseReadPLC.data.code == 0) {
if (responseReadPLC.data.tagValue == 'True') {
responseReadPLC.data.tagValue = true
} else if (responseReadPLC.data.tagValue == 'False') {
responseReadPLC.data.tagValue = false
}
data[correspondenceList[tagId].value] = responseReadPLC.data.tagValue
}
})
}
const initData = async () => {
var param = {
OPName: store.user.opName,
Operator: store.user.AID
}
let response = await ExecIMESAPI('WebHMIInit', param)
if (response.data.code != 0) {
ElMessage.error(response.data.msg)
} else {
for (let item of response.data.dataValues) {
if (item.tagValue == 'True') {
item.tagValue = true
} else if (item.tagValue == 'False') {
item.tagValue = false
}
data[correspondenceList[item.tagTypeCodeID].value] = item.tagValue
}
ElMessage.success('初始化成功!')
}
}
const getYdColor = (isOk) => {
if (isOk) {
return '#1afa29'
} else {
return '#000000'
}
}
const WritePlc = async (tagName, value) => {
let tagId = buttonList[tagName]
console.log('WritePlc',tagName,tagId, value)
let responseReadPLC = await ExecWritePLC(store.user.opName, tagId, value)
if (responseReadPLC.data.code == 0) {
if (value == 1) {
ElMessage.warning(tagName+'按钮按下')
} else if(value == 0) {
ElMessage.success(tagName+'按钮抬起')
}
} else {
ElMessage.error(responseReadPLC.data.msg)
}
}
</script>
<style lang="scss" scoped>
// 左侧盒子
.left {
width: 49vw;
padding-left: 1vw;
}
// 右侧盒子
.right {
width: 49vw;
padding-left: 1vw;
}
// 标题
.title {
font-size: 1.5vw;
}
// 椭圆状态球
.ellipseTest {
width: 15vw;
height: 6vh;
line-height: 6vh;
font-size: 1.5vw;
//color: #fff;
border-radius: 50%;
//background-color: #1296db;
border: 10px solid #1296db;
margin: 0 auto;
}
.main {
font-size: 1vw;
.top {
display: flex;
padding: 2vh 0;
font-size: 2vw;
height: 7vh;
.left {
.text {
background-color: #008000;
margin-left: 20px;
color: #fff;
}
}
.right {
.el-button {
width: 9vw;
height: 6vh;
font-size: 1.5vw;
margin: 0 20px;
}
}
}
//中间部分
.center {
display: flex;
// 主界面
.left {
.main {
display: flex;
justify-content: space-around;
.carBattery {
text-align: center;
.battery {
width: 8vw;
height: 30vh;
background-color: #ddd;
position: relative;
border-radius: 3px;
overflow: hidden;
.battery-bar {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
background-color: #32cd32;
transition: height 0.3s ease;
/* 平滑过渡效果 */
}
}
}
.carPosList {
width: 15vw;
text-align: center;
line-height: 8.2vh;
.item {
font-size: 1.5vw;
.unit {
height: 8.2vh;
background-color: #ffffc0;
}
.text {
height: 8.2vh;
background-color: #48d1cc;
}
}
}
.carStatus {
.item {
display: flex;
margin: 1.5vh 0;
.status {
width: 3vw;
height: 3vw;
border-radius: 50%;
background-color: #1afa29;
}
.el-button {
width: 9vw;
height: 6vh;
font-size: 1.5vw;
margin: 0 20px;
}
}
}
}
}
// 手动操作
.right {
.itemList {
display: flex;
flex-wrap: wrap;
height: 100%;
.item {
width: 50%;
height: 25%;
text-align: center;
.el-button {
height: 80%;
width: 60%;
font-size: 1.5vw;
}
}
}
}
}
.bottom {
display: flex;
.left {
.itemList {
display: flex;
flex-wrap: wrap;
height: 100%;
.item {
width: 30%;
height: 25%;
margin: 1vh 1.6%;
text-align: center;
font-size: 1.5vw;
.unit {
height: 5.2vh;
background-color: #ffffc0;
}
.text {
height: 5.2vh;
background-color: #48d1cc;
}
.ellipseTest {
position: relative;
left: -1vw;
top: 2vh;
}
}
}
.el-button {
margin: 2vh 2vw;
width: 9vw;
height: 6vh;
font-size: 1.5vw;
}
}
.right {
.itemList {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
.item {
width: 14.28%;
height: 7vh;
text-align: center;
font-size: 1.5vw;
.el-button {
width: 6vw;
height: 5vh;
font-size: 1.3vw;
margin: 0;
}
}
}
.el-button {
width: 9vw;
height: 6vh;
font-size: 1.5vw;
}
}
}
}
</style>