<template>
|
<div class="rgv-monitor">
|
<div class="monitor-header">
|
<div class="title">设备状态监控</div>
|
<div class="control-panel">
|
<!-- 监控类型切换 -->
|
<div class="monitor-type-switch">
|
<el-button-group>
|
<el-button
|
:type="currentMonitorType === 'inbound' ? 'primary' : ''"
|
@click="switchMonitorType('inbound')"
|
>
|
入库监控
|
</el-button>
|
<el-button
|
:type="currentMonitorType === 'outbound' ? 'primary' : ''"
|
@click="switchMonitorType('outbound')"
|
>
|
出库监控
|
</el-button>
|
<el-button
|
:type="currentMonitorType === 'safetydoor' ? 'primary' : ''"
|
@click="switchMonitorType('safetydoor')"
|
>
|
安全门监控
|
</el-button>
|
</el-button-group>
|
</div>
|
|
<!-- 一键操作按钮(安全门监控时不显示) -->
|
<div class="one-click-operations" v-if="currentMonitorType !== 'safetydoor'">
|
<el-button-group>
|
<el-button
|
type="primary"
|
@click="handleOneClickOperation('init')"
|
:loading="oneClickLoading.init"
|
>
|
一键初始化
|
</el-button>
|
<el-button
|
type="warning"
|
@click="handleOneClickOperation('reset')"
|
:loading="oneClickLoading.reset"
|
>
|
一键复位
|
</el-button>
|
<el-button
|
type="success"
|
@click="handleOneClickOperation('start')"
|
:loading="oneClickLoading.start"
|
>
|
一键启动
|
</el-button>
|
<el-button
|
type="danger"
|
@click="handleOneClickOperation('stop')"
|
:loading="oneClickLoading.stop"
|
>
|
一键暂停
|
</el-button>
|
</el-button-group>
|
</div>
|
|
<el-button
|
:type="isMonitoring ? 'danger' : 'primary'"
|
@click="toggleMonitoring"
|
>
|
{{ isMonitoring ? '停止监控' : '启动监控' }}
|
</el-button>
|
<el-button type="warning" @click="refreshData">刷新数据</el-button>
|
</div>
|
</div>
|
|
<!-- 监控状态显示 -->
|
<div class="monitor-status">
|
<el-alert
|
:title="getMonitorStatusTitle()"
|
:type="isMonitoring ? 'success' : 'info'"
|
:closable="false"
|
show-icon
|
/>
|
</div>
|
|
<div class="devices-container">
|
<!-- 动态渲染设备 -->
|
<div
|
class="device-card"
|
v-for="(deviceData, deviceKey) in currentDeviceData"
|
:key="deviceKey"
|
>
|
<div class="device-header">
|
<h3>{{ getDeviceDisplayName(deviceKey) }}</h3>
|
<div class="status-indicator" :class="getStatusClass(deviceData)"></div>
|
</div>
|
<div class="device-content">
|
<template v-if="deviceData && Object.keys(deviceData).length > 0">
|
<!-- RGV设备显示 -->
|
<template v-if="currentMonitorType !== 'safetydoor'">
|
<!-- 初始化状态显示 -->
|
<div
|
class="data-row"
|
v-if="deviceData['初始化未完成标志位'] !== undefined"
|
>
|
<span class="label">初始化状态:</span>
|
<span class="value" :class="deviceData['初始化未完成标志位'] === 1 ? 'fault-text' : ''">
|
{{ deviceData['初始化未完成标志位'] === 0 ? '已完成' : '未完成' }}
|
</span>
|
</div>
|
|
<div
|
class="data-row"
|
v-for="(value, key) in deviceData"
|
:key="key"
|
v-if="key !== '初始化未完成标志位'"
|
>
|
<span class="label">{{ getFieldDisplayName(key) }}:</span>
|
<span class="value" :class="getValueClass(key, value, deviceKey)">
|
{{ getFormattedValue(key, value, deviceKey) }}
|
<!-- 为上升信号和下降信号添加指示灯 -->
|
<span v-if="key === '上升信号到位'" class="signal-indicator" :class="getSignalClass(value)"></span>
|
<span v-if="key === '下降信号到位'" class="signal-indicator" :class="getSignalClass(value)"></span>
|
</span>
|
</div>
|
</template>
|
|
<!-- 安全门设备显示 -->
|
<template v-else>
|
<div
|
class="data-row"
|
v-for="(value, key) in deviceData"
|
:key="key"
|
>
|
<span class="label">{{ getFieldDisplayName(key) }}:</span>
|
<span class="value" :class="getSafetyDoorValueClass(key, value)">
|
{{ getSafetyDoorFormattedValue(key, value) }}
|
<!-- 安全门状态指示灯 -->
|
<span v-if="key === '安全门指示灯状态'" class="safety-light" :class="getSafetyLightClass(value)"></span>
|
<span v-if="key === '安全门急停状态'" class="emergency-stop" :class="getEmergencyStopClass(value)"></span>
|
</span>
|
</div>
|
</template>
|
</template>
|
<div v-else class="no-data">
|
设备未连接或暂无数据
|
</div>
|
|
<!-- 操作按钮区域(安全门监控时不显示) -->
|
<div class="operation-buttons" v-if="currentMonitorType !== 'safetydoor'">
|
<!-- 子车操作按钮 -->
|
<template v-if="getDeviceType(deviceKey) === 'child'">
|
<el-button
|
type="primary"
|
size="small"
|
@click="handleOperation(deviceKey, 'cs')"
|
:loading="loadingStates[deviceKey]?.cs"
|
:disabled="deviceData && deviceData['初始化未完成标志位'] === 0"
|
>
|
{{ getInitializationButtonText(deviceData) }}
|
</el-button>
|
<el-button
|
:type="getModeButtonType(deviceData)"
|
size="small"
|
@click="handleModeToggle(deviceKey)"
|
:loading="loadingStates[deviceKey]?.modeToggle"
|
>
|
{{ getCurrentModeText(deviceData) }}
|
</el-button>
|
<el-button
|
type="warning"
|
size="small"
|
@click="handleOperation(deviceKey, 'fw')"
|
:loading="loadingStates[deviceKey]?.fw"
|
>
|
复位
|
</el-button>
|
</template>
|
|
<!-- 母车操作按钮 -->
|
<template v-else-if="getDeviceType(deviceKey) === 'mother'">
|
<el-button
|
type="primary"
|
size="small"
|
@click="handleOperation(deviceKey, 'cs')"
|
:loading="loadingStates[deviceKey]?.cs"
|
:disabled="deviceData && deviceData['初始化未完成标志位'] === 0"
|
>
|
{{ getInitializationButtonText(deviceData) }}
|
</el-button>
|
<el-button
|
:type="getModeButtonType(deviceData)"
|
size="small"
|
@click="handleModeToggle(deviceKey)"
|
:loading="loadingStates[deviceKey]?.modeToggle"
|
>
|
{{ getCurrentModeText(deviceData) }}
|
</el-button>
|
<el-button
|
type="warning"
|
size="small"
|
@click="handleOperation(deviceKey, 'fw')"
|
:loading="loadingStates[deviceKey]?.fw"
|
>
|
复位
|
</el-button>
|
</template>
|
|
<!-- 原料车操作按钮 -->
|
<template v-else-if="getDeviceType(deviceKey) === 'material'">
|
<el-button
|
type="primary"
|
size="small"
|
@click="handleOperation(deviceKey, 'cs')"
|
:loading="loadingStates[deviceKey]?.cs"
|
:disabled="deviceData && deviceData['初始化未完成标志位'] === 0"
|
>
|
{{ getInitializationButtonText(deviceData) }}
|
</el-button>
|
<el-button
|
:type="getModeButtonType(deviceData)"
|
size="small"
|
@click="handleModeToggle(deviceKey)"
|
:loading="loadingStates[deviceKey]?.modeToggle"
|
>
|
{{ getCurrentModeText(deviceData) }}
|
</el-button>
|
<el-button
|
type="warning"
|
size="small"
|
@click="handleOperation(deviceKey, 'fw')"
|
:loading="loadingStates[deviceKey]?.fw"
|
>
|
复位
|
</el-button>
|
<!-- 雷达开关按钮 -->
|
<el-button
|
:type="getRadarButtonType(deviceData)"
|
size="small"
|
@click="handleRadarToggle(deviceKey)"
|
:loading="loadingStates[deviceKey]?.radarToggle"
|
:disabled="!isManualMode(deviceData)"
|
>
|
{{ getRadarButtonText(deviceData) }}
|
</el-button>
|
|
<!-- 入库继续任务按钮(仅对RGV101显示) -->
|
<el-button
|
v-if="deviceKey === 'rgV101'"
|
type="success"
|
size="small"
|
@click="handleInNormal(deviceKey)"
|
:loading="loadingStates[deviceKey]?.inNormal"
|
>
|
入库继续任务
|
</el-button>
|
|
<!-- 入库异常排除按钮(仅对RGV101显示) -->
|
<el-button
|
v-if="deviceKey === 'rgV101'"
|
type="danger"
|
size="small"
|
@click="handleInAbnormal(deviceKey)"
|
:loading="loadingStates[deviceKey]?.inAbnormal"
|
>
|
入库异常排除
|
</el-button>
|
</template>
|
</div>
|
|
<!-- 地址操作区域(安全门监控时不显示) -->
|
<div class="address-operation" v-if="currentMonitorType !== 'safetydoor'">
|
<div class="address-title">地址操作</div>
|
<div class="address-input-group">
|
<el-select
|
v-model="addressValues[deviceKey]"
|
placeholder="选择目标地址"
|
size="small"
|
style="width: 200px; margin-right: 10px;"
|
>
|
<el-option
|
v-for="address in getDeviceAddresses(deviceKey)"
|
:key="address.value"
|
:label="address.label"
|
:value="address.value"
|
/>
|
</el-select>
|
<el-button
|
type="info"
|
size="small"
|
@click="handleAddressOperation(deviceKey)"
|
:loading="loadingStates[deviceKey]?.dz"
|
:disabled="!addressValues[deviceKey]"
|
>
|
前往地址
|
</el-button>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<!-- 全局操作面板(安全门监控时不显示) -->
|
<div class="operation-panel" v-if="currentMonitorType !== 'safetydoor'">
|
<!-- <el-button type="primary" @click="showOperationDialog = true">高级操作</el-button> -->
|
</div>
|
|
<!-- 操作对话框 -->
|
<el-dialog v-model="showOperationDialog" title="设备高级操作" width="600px">
|
<div>设备初始化、写入参数等高级操作界面</div>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script>
|
export default {
|
name: 'RgvMonitor',
|
data() {
|
return {
|
currentMonitorType: 'inbound', // 'inbound' 或 'outbound' 或 'safetydoor'
|
isMonitoring: false,
|
showOperationDialog: false,
|
pollingTimer: null,
|
loadingStates: {},
|
addressValues: {},
|
|
// 一键操作加载状态
|
oneClickLoading: {
|
init: false,
|
reset: false,
|
start: false,
|
stop: false
|
},
|
|
// 设备数据
|
inboundDeviceData: {
|
rgV101: null,
|
rgV103: null,
|
rgV104: null,
|
rgV105: null,
|
rgV107: null,
|
rgV108: null,
|
rgV109: null,
|
// rgV202: null
|
},
|
outboundDeviceData: {
|
rgV116: null,
|
rgV115: null,
|
rgV111: null,
|
rgV112: null,
|
rgV110: null,
|
rgV114: null,
|
rgV118: null
|
},
|
safetyDoorDeviceData: {
|
aqm001: null,
|
aqm002: null,
|
aqm003: null
|
},
|
|
// 字段显示名称映射
|
fieldDisplayNames: {
|
'工作模式': '工作模式',
|
'当前位置': '当前位置',
|
'有货状态': '有货状态',
|
'目标地址': '目标地址',
|
'任务状态': '任务状态',
|
'rgV任务编号': '任务编号',
|
'故障代码': '故障代码',
|
'上升信号到位': '上升信号',
|
'下降信号到位': '下降信号',
|
'初始化未完成标志位': '初始化状态',
|
'雷达状态': '雷达状态',
|
'货叉状态': '货叉状态',
|
// 安全门字段
|
'安全门指示灯状态': '指示灯状态',
|
'安全门请求开门': '请求开门',
|
'安全门断电状态': '断电状态',
|
'安全门急停状态': '急停状态',
|
'安全门锁状态': '门锁状态',
|
'安全门复位状态': '复位状态',
|
'报警信息': '报警信息',
|
'开门信息': '开门信息'
|
},
|
|
// 设备显示名称映射
|
deviceDisplayNames: {
|
// 入库设备
|
'rgV101': 'RGV101 - 原料入库车',
|
'rgV103': 'RGV103 - 母车',
|
'rgV104': 'RGV104 - 子车',
|
'rgV105': 'RGV105 - 母车',
|
'rgV107': 'RGV107 - 子车',
|
'rgV108': 'RGV108 - 母车',
|
'rgV109': 'RGV109 - 母车',
|
// 出库设备
|
'rgV110': 'RGV110 - 母车',
|
'rgV111': 'RGV111 - 子车',
|
'rgV112': 'RGV112 - 母车',
|
'rgV114': 'RGV114 - 母车',
|
'rgV115': 'RGV115 - 母车',
|
'rgV116': 'RGV116 - 子车',
|
'rgV118': 'RGV118 - 原料出库车',
|
// 安全门设备
|
'aqm001': 'AQM001 - 1#安全门',
|
'aqm002': 'AQM002 - 2#安全门',
|
'aqm003': 'AQM003 - 3#安全门'
|
},
|
|
// 操作类型映射
|
operationTypes: {
|
'cs': '初始化',
|
'sd': '切换到手动模式',
|
'zd': '切换到自动模式',
|
'dz': '前往地址',
|
'kld': '开雷达',
|
'gld': '关雷达',
|
'fw': '复位',
|
},
|
|
// 报警代码映射
|
motherCarAlarmCodes: {
|
0: '无报警',
|
1: 'RGV小车急停被按下',
|
2: '前进限位报警',
|
3: '后退限位报警',
|
4: 'PLC模块故障',
|
5: 'PLC扩展模块故障',
|
6: 'RGV长时间空转故障',
|
7: '目的地不等于实际位置故障',
|
8: '与总控通讯故障',
|
9: '行走变频器故障',
|
10: '取货时自身有货物报警',
|
11: '放货时自身无货物报警',
|
12: '停止时位置过冲报警'
|
},
|
|
childCarAlarmCodes: {
|
0: '无报警',
|
1: 'RGV小车急停被按下',
|
2: '前进限位报警',
|
3: '后退限位报警',
|
4: 'PLC模块故障',
|
5: 'PLC扩展模块故障',
|
6: '扫码定位故障',
|
7: 'RGV长时间空转故障',
|
8: '目的地不等于实际位置故障',
|
9: '与总控通讯故障',
|
10: '行走变频器故障',
|
11: '液压单元过载保护故障',
|
12: '液压上升超时报警',
|
13: '液压下降超时报警',
|
14: '取货时自身有货物报警',
|
15: '放货时自身无货物报警',
|
16: '取货检测不到货物报警'
|
},
|
|
materialCarAlarmCodes: {
|
0: '无报警',
|
1: 'RGV小车急停被按下',
|
2: '正转雷达报警',
|
3: '反转雷达报警',
|
4: '前进限位报警',
|
5: '后退限位报警',
|
6: '预留',
|
7: 'PLC模块故障',
|
8: 'PLC扩展模块故障',
|
9: '称重模块故障',
|
10: '扫码定位故障',
|
11: 'RGV长时间空转故障',
|
12: '目的地不等于实际位置故障',
|
13: '与总控通讯故障',
|
14: '前雷达屏蔽警告',
|
15: '后雷达屏蔽警告',
|
16: '行走变频器故障',
|
17: '伸缩叉变频器故障',
|
18: '液压单元过载保护故障',
|
19: '液压上升超时报警',
|
20: '液压下降超时报警',
|
21: '伸缩叉伸出超时报警',
|
22: '伸缩叉缩回超时报警',
|
23: '外形检测报警',
|
24: '称重超重报警',
|
25: '货叉伸出极限限位报警',
|
26: '货叉缩回极限限位报警',
|
27: '取货时自身有货物报警',
|
28: '放货时自身无货物报警',
|
29: '货叉未回到初始位报警',
|
30: '触发仅移动命令时货叉不在初始位报警',
|
31: '货叉到达初始位但中位传感器未检测到报警'
|
},
|
|
// 安全门指示灯状态映射
|
safetyDoorLightCodes: {
|
0: '无输出',
|
1: '红灯常亮+蜂鸣',
|
2: '绿灯常亮',
|
3: '黄灯闪烁(2HZ)',
|
4: '黄灯常亮'
|
},
|
|
// 设备类型分类
|
deviceTypes: {
|
// 入库设备
|
motherCars: ['rgV103', 'rgV105', 'rgV108', 'rgV109'],
|
childCars: ['rgV104', 'rgV107'],
|
materialCars: ['rgV101'],
|
// 出库设备
|
outboundMotherCars: ['rgV110', 'rgV112', 'rgV114', 'rgV115'],
|
outboundChildCars: ['rgV111', 'rgV116'],
|
outboundMaterialCars: ['rgV118'],
|
// 安全门设备
|
safetyDoors: ['aqm001', 'aqm002', 'aqm003']
|
},
|
|
// 设备地址映射
|
deviceAddresses: {
|
// 入库设备地址
|
'rgV101': [
|
{ value: '1001', label: '1001 - 原料入库位' },
|
{ value: '1002', label: '1002 - 原料出库位' },
|
{ value: '1021', label: '1021 - 中转位' }
|
],
|
'rgV103': [
|
{ value: '1031', label: '1031 - 母车停车位1' },
|
{ value: '1032', label: '1032 - 母车停车位2' }
|
],
|
'rgV104': [
|
{ value: '3', label: '停靠点1' },
|
{ value: '5', label: '停靠点2' },
|
],
|
'rgV105': [
|
{ value: '1051', label: '1051 - 母车停车位1' },
|
{ value: '1052', label: '1052 - 母车停车位2' }
|
],
|
'rgV107': [
|
{ value: '3', label: '停靠点1' },
|
{ value: '5', label: '停靠点2' },
|
],
|
'rgV108': [
|
{ value: '1081', label: '1081 - 母车停车位1' },
|
{ value: '1082', label: '1082 - 母车停车位2' }
|
],
|
'rgV109': [
|
{ value: '1091', label: '1091 - 母车停车位1' },
|
{ value: '1092', label: '1092 - 母车停车位2' }
|
],
|
// 'rgV202': [
|
// { value: '2001', label: '2001 - 工位1' },
|
// { value: '2002', label: '2002 - 工位2' },
|
// { value: '2003', label: '2003 - 工位3' },
|
// { value: '2004', label: '2004 - 工位4' },
|
// { value: '2005', label: '2005 - 工位5' },
|
// { value: '2006', label: '2006 - 工位6' },
|
// { value: '2007', label: '2007 - 工位7' },
|
// { value: '2008', label: '2008 - 工位8' },
|
// { value: '2009', label: '2009 - 工位9' }
|
// ],
|
// 出库设备地址
|
'rgV110': [
|
{ value: '1101', label: '1101 - 母车停车位1' },
|
{ value: '1102', label: '1102 - 母车停车位2' }
|
],
|
'rgV111': [
|
{ value: '3', label: '停靠点1' },
|
{ value: '5', label: '停靠点2' },
|
],
|
'rgV112': [
|
{ value: '1121', label: '1121 - 母车停车位1' },
|
{ value: '1122', label: '1122 - 母车停车位2' }
|
],
|
'rgV114': [
|
{ value: '1141', label: '1141 - 母车停车位1' },
|
{ value: '1142', label: '1142 - 母车停车位2' }
|
],
|
'rgV115': [
|
{ value: '1151', label: '1151 - 母车停车位1' },
|
{ value: '1152', label: '1152 - 母车停车位2' }
|
],
|
'rgV116': [
|
{ value: '3', label: '停靠点1' },
|
{ value: '5', label: '停靠点2' },
|
],
|
'rgV118': [
|
{ value: '2016', label: '2016 - 原料处理位1' },
|
{ value: '2017', label: '2017 - 原料处理位2' },
|
{ value: '2018', label: '2018 - 原料处理位3' },
|
{ value: '2019', label: '2019 - 原料处理位4' }
|
]
|
},
|
|
// 特殊字段的值格式化
|
valueFormatters: {
|
'工作模式': (value) => {
|
const modes = { 0: '手动模式', 1: '自动模式' }
|
return modes[value] || `未知模式(${value})`
|
},
|
'有货状态': (value) => value === 0 ? '无货' : '有货',
|
'任务状态': (value) => {
|
const statusMap = { 0: '空闲', 1: '执行中', 2: '完成' }
|
return statusMap[value] || `未知状态(${value})`
|
},
|
'故障代码': (value, deviceKey) => {
|
return this.getAlarmText(value, deviceKey)
|
},
|
'上升信号到位': (value) => value === 0 ? '未到位' : '已到位',
|
'下降信号到位': (value) => value === 0 ? '未到位' : '已到位',
|
'初始化未完成标志位': (value) => value === 0 ? '已完成' : '未完成',
|
'雷达状态': (value) => value === 0 ? '关闭' : '开启',
|
'货叉状态': (value) => value === 0 ? '缩回' : '伸出'
|
},
|
|
// 安全门字段值格式化
|
safetyDoorValueFormatters: {
|
'安全门指示灯状态': (value) => {
|
return this.safetyDoorLightCodes[value] || `未知状态(${value})`
|
},
|
'安全门请求开门': (value) => value === 0 ? '正常' : '请求开门',
|
'安全门断电状态': (value) => value === 0 ? '断电' : '上电',
|
'安全门急停状态': (value) => value === 0 ? '急停' : '正常',
|
'安全门锁状态': (value) => value === 0 ? '开门' : '关门',
|
'安全门复位状态': (value) => value === 0 ? '正常' : '复位中',
|
'报警信息': (value) => value === 0 ? '正常' : '报警',
|
'开门信息': (value) => value === 0 ? '关门' : '开门'
|
}
|
}
|
},
|
computed: {
|
// 当前显示的设备数据
|
currentDeviceData() {
|
switch (this.currentMonitorType) {
|
case 'inbound':
|
return this.inboundDeviceData
|
case 'outbound':
|
return this.outboundDeviceData
|
case 'safetydoor':
|
return this.safetyDoorDeviceData
|
default:
|
return this.inboundDeviceData
|
}
|
}
|
},
|
methods: {
|
// 入库继续任务
|
async handleInNormal(deviceKey) {
|
this.setLoadingState(deviceKey, 'inNormal', true);
|
|
try {
|
const response = await this.http.post("api/RgvOperation/WriteInNormal", {}, "数据处理中...");
|
|
if (response.status) {
|
this.$message.success('入库继续任务成功');
|
this.refreshData();
|
} else {
|
this.$message.error(response.message || '入库继续任务失败');
|
}
|
} catch (error) {
|
this.$message.error('入库继续任务请求失败: ' + error.message);
|
} finally {
|
this.setLoadingState(deviceKey, 'inNormal', false);
|
}
|
},
|
|
// 入库异常排除
|
async handleInAbnormal(deviceKey) {
|
this.setLoadingState(deviceKey, 'inAbnormal', true);
|
|
try {
|
const response = await this.http.post("api/RgvOperation/WriteInAbnormal", {}, "数据处理中...");
|
|
if (response.status) {
|
this.$message.success('入库异常排除成功');
|
this.refreshData();
|
} else {
|
this.$message.error(response.message || '入库异常排除失败');
|
}
|
} catch (error) {
|
this.$message.error('入库异常排除请求失败: ' + error.message);
|
} finally {
|
this.setLoadingState(deviceKey, 'inAbnormal', false);
|
}
|
},
|
|
// 获取监控状态标题
|
getMonitorStatusTitle() {
|
const typeNames = {
|
'inbound': '入库',
|
'outbound': '出库',
|
'safetydoor': '安全门'
|
}
|
const status = this.isMonitoring ? '运行中' : '已停止'
|
return `${typeNames[this.currentMonitorType]}监控状态: ${status}`
|
},
|
|
// 安全门字段值格式化
|
getSafetyDoorFormattedValue(fieldKey, value) {
|
const formatter = this.safetyDoorValueFormatters[fieldKey]
|
return formatter ? formatter(value) : value
|
},
|
|
// 安全门字段值样式
|
getSafetyDoorValueClass(fieldKey, value) {
|
if (fieldKey === '安全门急停状态' && value === 0) {
|
return 'fault-text'
|
}
|
if (fieldKey === '安全门断电状态' && value === 0) {
|
return 'fault-text'
|
}
|
if (fieldKey === '报警信息' && value === 1) {
|
return 'fault-text'
|
}
|
return ''
|
},
|
|
// 安全门指示灯样式
|
getSafetyLightClass(value) {
|
const classMap = {
|
0: 'light-off',
|
1: 'light-red',
|
2: 'light-green',
|
3: 'light-yellow-blink',
|
4: 'light-yellow'
|
}
|
return classMap[value] || 'light-off'
|
},
|
|
// 急停状态样式
|
getEmergencyStopClass(value) {
|
return value === 0 ? 'emergency-stop-active' : 'emergency-stop-normal'
|
},
|
|
// 一键操作处理
|
async handleOneClickOperation(operationType) {
|
// 根据当前监控类型设置参数
|
const monitorType = this.currentMonitorType === 'inbound' ? 'Inbound' : 'Outbound'
|
|
this.oneClickLoading[operationType] = true
|
|
try {
|
const operationNames = {
|
init: '一键初始化',
|
reset: '一键复位',
|
start: '一键启动',
|
stop: '一键暂停'
|
}
|
|
const response = await this.http.post('api/Rgvoperainform/OneClickOperation', {
|
operationType: operationType,
|
monitorType: monitorType
|
}, `${operationNames[operationType]}中...`)
|
|
if (response.status) {
|
this.$message.success(`${operationNames[operationType]} ${monitorType}端设备成功`)
|
this.refreshData()
|
} else {
|
this.$message.error(response.message || `${operationNames[operationType]}失败`)
|
}
|
} catch (error) {
|
this.$message.error(`${operationNames[operationType]}请求失败: ` + error.message)
|
} finally {
|
this.oneClickLoading[operationType] = false
|
}
|
},
|
|
// 获取初始化按钮显示文本
|
getInitializationButtonText(deviceData) {
|
if (!deviceData) return '初始化'
|
return deviceData['初始化未完成标志位'] === 0 ? '已初始化' : '初始化'
|
},
|
|
// 切换监控类型
|
switchMonitorType(type) {
|
if (this.currentMonitorType !== type) {
|
// 停止当前监控
|
if (this.isMonitoring) {
|
this.stopMonitoring()
|
}
|
this.currentMonitorType = type
|
// 如果之前是监控状态,自动启动新类型的监控
|
if (this.isMonitoring) {
|
this.startMonitoring()
|
}
|
}
|
},
|
|
// 获取信号指示灯类名
|
getSignalClass(value) {
|
return value === 1 ? 'signal-active' : 'signal-inactive'
|
},
|
|
// 监控控制方法
|
async toggleMonitoring() {
|
if (this.isMonitoring) {
|
this.stopMonitoring()
|
} else {
|
await this.startMonitoring()
|
}
|
},
|
|
async startMonitoring() {
|
try {
|
const param = {
|
off: 1,
|
monitorType: this.currentMonitorType
|
}
|
|
const response = await this.http.post('api/Rgvoperainform/GetDeviceStatusDto', param)
|
|
if (response.status) {
|
this.isMonitoring = true
|
// 根据监控类型更新对应的设备数据
|
switch (this.currentMonitorType) {
|
case 'inbound':
|
this.inboundDeviceData = response.data
|
break
|
case 'outbound':
|
this.outboundDeviceData = response.data
|
break
|
case 'safetydoor':
|
this.safetyDoorDeviceData = response.data
|
break
|
}
|
this.startPolling()
|
this.$message.success(`${this.getMonitorStatusTitle()}已启动`)
|
} else {
|
this.$message.error(response.message || `启动${this.currentMonitorType}监控失败`)
|
}
|
} catch (error) {
|
this.$message.error('请求失败: ' + error.message)
|
}
|
},
|
|
stopMonitoring() {
|
this.isMonitoring = false
|
if (this.pollingTimer) {
|
clearTimeout(this.pollingTimer)
|
this.pollingTimer = null
|
}
|
|
this.http.post('api/Rgvoperainform/GetDeviceStatusDto', {
|
off: 0,
|
monitorType: this.currentMonitorType
|
})
|
|
this.$message.info(`${this.getMonitorStatusTitle()}已停止`)
|
},
|
|
startPolling() {
|
this.pollingTimer = setTimeout(() => {
|
if (this.isMonitoring) {
|
this.refreshData()
|
this.startPolling()
|
}
|
}, 2000)
|
},
|
|
async refreshData() {
|
if (!this.isMonitoring) return
|
|
try {
|
const response = await this.http.post('api/Rgvoperainform/GetDeviceStatusDto', {
|
off: 1,
|
monitorType: this.currentMonitorType
|
})
|
if (response.status) {
|
// 根据监控类型更新对应的设备数据
|
switch (this.currentMonitorType) {
|
case 'inbound':
|
this.inboundDeviceData = response.data
|
break
|
case 'outbound':
|
this.outboundDeviceData = response.data
|
break
|
case 'safetydoor':
|
this.safetyDoorDeviceData = response.data
|
break
|
}
|
}
|
} catch (error) {
|
console.error('刷新数据失败:', error)
|
}
|
},
|
|
// 其他原有方法保持不变
|
getStatusClass(device) {
|
if (!device || Object.keys(device).length === 0) return 'offline'
|
|
// 安全门状态判断
|
if (this.currentMonitorType === 'safetydoor') {
|
if (device['安全门急停状态'] === 0) return 'fault'
|
if (device['安全门断电状态'] === 0) return 'offline'
|
if (device['报警信息'] === 1) return 'fault'
|
return 'normal'
|
}
|
|
// RGV设备状态判断
|
if (device['故障代码'] !== 0) return 'fault'
|
if (device['任务状态'] === 1) return 'running'
|
if (device['工作模式'] === 0) return 'manual'
|
return 'normal'
|
},
|
|
getDeviceDisplayName(deviceKey) {
|
return this.deviceDisplayNames[deviceKey] || deviceKey.toUpperCase()
|
},
|
|
getFieldDisplayName(fieldKey) {
|
return this.fieldDisplayNames[fieldKey] || fieldKey
|
},
|
|
getFormattedValue(fieldKey, value, deviceKey) {
|
const formatter = this.valueFormatters[fieldKey]
|
return formatter ? formatter(value, deviceKey) : value
|
},
|
|
getValueClass(fieldKey, value, deviceKey) {
|
if (fieldKey === '故障代码' && value !== 0) {
|
return 'fault-text'
|
}
|
return ''
|
},
|
|
// 获取设备类型
|
getDeviceType(deviceKey) {
|
if (this.deviceTypes.motherCars.includes(deviceKey) ||
|
this.deviceTypes.outboundMotherCars.includes(deviceKey)) {
|
return 'mother'
|
} else if (this.deviceTypes.childCars.includes(deviceKey) ||
|
this.deviceTypes.outboundChildCars.includes(deviceKey)) {
|
return 'child'
|
} else if (this.deviceTypes.materialCars.includes(deviceKey) ||
|
this.deviceTypes.outboundMaterialCars.includes(deviceKey)) {
|
return 'material'
|
} else if (this.deviceTypes.safetyDoors.includes(deviceKey)) {
|
return 'safetydoor'
|
}
|
return 'unknown'
|
},
|
|
// 获取设备可用的目标地址
|
getDeviceAddresses(deviceKey) {
|
return this.deviceAddresses[deviceKey] || []
|
},
|
|
// 获取报警文本
|
getAlarmText(alarmCode, deviceKey) {
|
if (alarmCode === 0) return '无报警'
|
|
const deviceType = this.getDeviceType(deviceKey)
|
let alarmMap = {}
|
|
switch (deviceType) {
|
case 'mother':
|
alarmMap = this.motherCarAlarmCodes
|
break
|
case 'child':
|
alarmMap = this.childCarAlarmCodes
|
break
|
case 'material':
|
alarmMap = this.materialCarAlarmCodes
|
break
|
default:
|
alarmMap = this.motherCarAlarmCodes
|
}
|
|
return alarmMap[alarmCode] || `未连接(${alarmCode})`
|
},
|
|
// 获取当前模式显示文本
|
getCurrentModeText(deviceData) {
|
if (!deviceData || !deviceData['工作模式']) return '点击切换自动'
|
return deviceData['工作模式'] === 1 ? '点击切换手动' : '点击切换自动'
|
},
|
|
// 获取模式按钮类型(颜色)
|
getModeButtonType(deviceData) {
|
if (!deviceData || !deviceData['工作模式']) return 'success'
|
return deviceData['工作模式'] === 1 ? 'warning' : 'success'
|
},
|
|
// 判断是否为手动模式
|
isManualMode(deviceData) {
|
return deviceData && deviceData['工作模式'] === 0
|
},
|
|
// 获取雷达按钮文本
|
getRadarButtonText(deviceData) {
|
if (!deviceData) return '开启雷达'
|
return deviceData['雷达状态'] === 1 ? '关闭雷达' : '开启雷达'
|
},
|
|
// 获取雷达按钮类型
|
getRadarButtonType(deviceData) {
|
if (!deviceData) return 'info'
|
return deviceData['雷达状态'] === 1 ? 'warning' : 'info'
|
},
|
|
// 设置加载状态
|
setLoadingState(deviceKey, operationType, loading) {
|
if (!this.loadingStates[deviceKey]) {
|
this.loadingStates[deviceKey] = {}
|
}
|
this.loadingStates[deviceKey][operationType] = loading
|
this.loadingStates = { ...this.loadingStates }
|
},
|
|
// 手动/自动模式切换
|
async handleModeToggle(deviceKey) {
|
this.setLoadingState(deviceKey, 'modeToggle', true)
|
|
try {
|
const deviceData = this.currentDeviceData[deviceKey]
|
const currentMode = deviceData?.['工作模式'] || 0
|
|
const operationType = currentMode === 0 ? 'zd' : 'sd'
|
|
const response = await this.http.post('api/Rgvoperainform/DeviceOperation', {
|
DelKeys: [deviceKey, operationType],
|
Extra: true
|
}, `${this.operationTypes[operationType]}中...`)
|
|
if (response.status) {
|
this.$message.success(`${this.getDeviceDisplayName(deviceKey)} ${this.operationTypes[operationType]}成功`)
|
this.refreshData()
|
} else {
|
this.$message.error(response.message || `${this.operationTypes[operationType]}失败`)
|
}
|
} catch (error) {
|
this.$message.error(`模式切换请求失败: ` + error.message)
|
} finally {
|
this.setLoadingState(deviceKey, 'modeToggle', false)
|
}
|
},
|
|
// 雷达开关切换
|
async handleRadarToggle(deviceKey) {
|
this.setLoadingState(deviceKey, 'radarToggle', true)
|
|
try {
|
const deviceData = this.currentDeviceData[deviceKey]
|
const currentRadarState = deviceData?.['雷达状态'] || 0
|
|
const operationType = currentRadarState === 0 ? 'kld' : 'gld'
|
|
const response = await this.http.post('api/Rgvoperainform/DeviceOperation', {
|
DelKeys: [deviceKey, operationType],
|
Extra: true
|
}, `${this.operationTypes[operationType]}中...`)
|
|
if (response.status) {
|
this.$message.success(`${this.getDeviceDisplayName(deviceKey)} ${this.operationTypes[operationType]}成功`)
|
this.refreshData()
|
} else {
|
this.$message.error(response.message || `${this.operationTypes[operationType]}失败`)
|
}
|
} catch (error) {
|
this.$message.error(`雷达操作请求失败: ` + error.message)
|
} finally {
|
this.setLoadingState(deviceKey, 'radarToggle', false)
|
}
|
},
|
|
// 地址操作
|
async handleAddressOperation(deviceKey) {
|
const address = this.addressValues[deviceKey]
|
if (!address) {
|
this.$message.warning('请选择目标地址')
|
return
|
}
|
|
this.setLoadingState(deviceKey, 'dz', true)
|
|
try {
|
const response = await this.http.post('api/Rgvoperainform/DeviceOperation', {
|
DelKeys: [deviceKey, 'dz', address],
|
Extra: true
|
}, '前往地址中...')
|
|
if (response.status) {
|
this.$message.success(`${this.getDeviceDisplayName(deviceKey)} 前往地址 ${address} 成功`)
|
this.refreshData()
|
} else {
|
this.$message.error(response.message || '前往地址失败')
|
}
|
} catch (error) {
|
this.$message.error('前往地址请求失败: ' + error.message)
|
} finally {
|
this.setLoadingState(deviceKey, 'dz', false)
|
}
|
},
|
|
// 统一操作处理函数
|
async handleOperation(deviceKey, operationType) {
|
this.setLoadingState(deviceKey, operationType, true)
|
|
try {
|
const response = await this.http.post('api/Rgvoperainform/DeviceOperation', {
|
DelKeys: [deviceKey, operationType],
|
Extra: true
|
}, `${this.operationTypes[operationType]}中...`)
|
|
if (response.status) {
|
const operationName = this.operationTypes[operationType]
|
this.$message.success(`${this.getDeviceDisplayName(deviceKey)} ${operationName}操作成功`)
|
|
// 如果是初始化操作,添加额外提示
|
if (operationType === 'cs') {
|
this.$notify({
|
title: '初始化成功',
|
message: `${this.getDeviceDisplayName(deviceKey)} 设备初始化完成`,
|
type: 'success',
|
duration: 3000
|
})
|
}
|
|
this.refreshData()
|
} else {
|
this.$message.error(response.message || `${this.operationTypes[operationType]}操作失败`)
|
}
|
} catch (error) {
|
this.$message.error(`${this.operationTypes[operationType]}请求失败: ` + error.message)
|
} finally {
|
this.setLoadingState(deviceKey, operationType, false)
|
}
|
}
|
},
|
|
beforeUnmount() {
|
this.stopMonitoring()
|
}
|
}
|
</script>
|
|
<style scoped>
|
/* 原有的样式保持不变,添加安全门相关样式 */
|
|
/* 安全门指示灯样式 */
|
.safety-light {
|
width: 12px;
|
height: 12px;
|
border-radius: 50%;
|
display: inline-block;
|
margin-left: 8px;
|
border: 1px solid #ccc;
|
}
|
|
.safety-light.light-off {
|
background-color: #909399;
|
}
|
|
.safety-light.light-red {
|
background-color: #F56C6C;
|
box-shadow: 0 0 6px #F56C6C;
|
animation: blink 1s infinite;
|
}
|
|
.safety-light.light-green {
|
background-color: #67C23A;
|
box-shadow: 0 0 6px #67C23A;
|
}
|
|
.safety-light.light-yellow {
|
background-color: #E6A23C;
|
box-shadow: 0 0 6px #E6A23C;
|
}
|
|
.safety-light.light-yellow-blink {
|
background-color: #E6A23C;
|
box-shadow: 0 0 6px #E6A23C;
|
animation: blink 0.5s infinite;
|
}
|
|
/* 急停状态指示 */
|
.emergency-stop {
|
width: 8px;
|
height: 8px;
|
border-radius: 50%;
|
display: inline-block;
|
margin-left: 8px;
|
}
|
|
.emergency-stop.emergency-stop-normal {
|
background-color: #67C23A;
|
box-shadow: 0 0 4px #67C23A;
|
}
|
|
.emergency-stop.emergency-stop-active {
|
background-color: #F56C6C;
|
box-shadow: 0 0 4px #F56C6C;
|
animation: blink 0.8s infinite;
|
}
|
|
/* 监控类型切换样式 */
|
.monitor-type-switch {
|
margin-right: 20px;
|
}
|
|
.one-click-operations {
|
margin-right: 20px;
|
}
|
|
.control-panel {
|
display: flex;
|
gap: 10px;
|
align-items: center;
|
}
|
|
/* 信号指示灯样式 */
|
.signal-indicator {
|
width: 8px;
|
height: 8px;
|
border-radius: 50%;
|
display: inline-block;
|
margin-left: 8px;
|
}
|
|
.signal-indicator.signal-active {
|
background-color: #67C23A;
|
box-shadow: 0 0 4px #67C23A;
|
}
|
|
.signal-indicator.signal-inactive {
|
background-color: #DCDFE6;
|
border: 1px solid #C0C4CC;
|
}
|
|
/* 修改value样式以容纳指示灯 */
|
.value {
|
color: #303133;
|
font-weight: 500;
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
}
|
|
/* 其他原有样式保持不变 */
|
.rgv-monitor {
|
padding: 20px;
|
background-color: #f5f7fa;
|
min-height: 100vh;
|
}
|
|
.monitor-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20px;
|
padding: 0 10px;
|
}
|
|
.title {
|
font-size: 24px;
|
font-weight: bold;
|
color: #409EFF;
|
}
|
|
.monitor-status {
|
margin-bottom: 20px;
|
}
|
|
.devices-container {
|
display: grid;
|
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
|
gap: 20px;
|
margin-bottom: 20px;
|
}
|
|
.device-card {
|
background: white;
|
border-radius: 8px;
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
overflow: hidden;
|
transition: all 0.3s ease;
|
}
|
|
.device-card:hover {
|
transform: translateY(-2px);
|
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.15);
|
}
|
|
.device-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 15px 20px;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
color: white;
|
}
|
|
.device-header h3 {
|
margin: 0;
|
font-size: 16px;
|
}
|
|
.status-indicator {
|
width: 12px;
|
height: 12px;
|
border-radius: 50%;
|
}
|
|
.status-indicator.normal {
|
background-color: #67C23A;
|
box-shadow: 0 0 8px #67C23A;
|
}
|
|
.status-indicator.running {
|
background-color: #409EFF;
|
box-shadow: 0 0 8px #409EFF;
|
animation: pulse 1.5s infinite;
|
}
|
|
.status-indicator.fault {
|
background-color: #F56C6C;
|
box-shadow: 0 0 8px #F56C6C;
|
animation: blink 1s infinite;
|
}
|
|
.status-indicator.manual {
|
background-color: #E6A23C;
|
box-shadow: 0 0 8px #E6A23C;
|
animation: pulse 1.5s infinite;
|
}
|
|
.status-indicator.offline {
|
background-color: #909399;
|
}
|
|
.device-content {
|
padding: 20px;
|
}
|
|
.data-row {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 8px 0;
|
border-bottom: 1px solid #ebeef5;
|
}
|
|
.data-row:last-child {
|
border-bottom: none;
|
}
|
|
.label {
|
font-weight: 500;
|
color: #606266;
|
}
|
|
.fault-text {
|
color: #F56C6C;
|
font-weight: bold;
|
}
|
|
.no-data {
|
text-align: center;
|
color: #909399;
|
padding: 40px 0;
|
font-style: italic;
|
}
|
|
.operation-buttons {
|
margin-top: 15px;
|
padding-top: 15px;
|
border-top: 1px dashed #ebeef5;
|
display: flex;
|
flex-wrap: wrap;
|
gap: 8px;
|
justify-content: center;
|
}
|
|
.address-operation {
|
margin-top: 15px;
|
padding-top: 15px;
|
border-top: 1px dashed #ebeef5;
|
}
|
|
.address-title {
|
font-weight: 500;
|
color: #606266;
|
margin-bottom: 10px;
|
text-align: center;
|
}
|
|
.address-input-group {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
gap: 10px;
|
}
|
|
.operation-panel {
|
text-align: center;
|
padding: 20px;
|
}
|
|
@keyframes pulse {
|
0% { opacity: 1; }
|
50% { opacity: 0.5; }
|
100% { opacity: 1; }
|
}
|
|
@keyframes blink {
|
0%, 50% { opacity: 1; }
|
51%, 100% { opacity: 0.3; }
|
}
|
|
/* 响应式设计 */
|
@media (max-width: 768px) {
|
.devices-container {
|
grid-template-columns: 1fr;
|
}
|
|
.monitor-header {
|
flex-direction: column;
|
gap: 15px;
|
align-items: flex-start;
|
}
|
|
.control-panel {
|
flex-direction: column;
|
align-items: flex-start;
|
width: 100%;
|
}
|
|
.monitor-type-switch,
|
.one-click-operations {
|
margin-right: 0;
|
margin-bottom: 10px;
|
width: 100%;
|
}
|
|
.monitor-type-switch .el-button-group,
|
.one-click-operations .el-button-group {
|
width: 100%;
|
}
|
|
.monitor-type-switch .el-button,
|
.one-click-operations .el-button {
|
flex: 1;
|
}
|
|
.operation-buttons {
|
flex-direction: column;
|
align-items: stretch;
|
}
|
|
.operation-buttons .el-button {
|
width: 100%;
|
}
|
|
.address-input-group {
|
flex-direction: column;
|
align-items: stretch;
|
}
|
|
.address-input-group .el-select,
|
.address-input-group .el-button {
|
width: 100%;
|
}
|
}
|
</style>
|