Compare commits
3 Commits
2ae34b1f9f
...
b332a5461e
| Author | SHA1 | Date | |
|---|---|---|---|
| b332a5461e | |||
| 02706a39a1 | |||
| e18c4b88be |
18
src/utils/agvRuntimeStatus.js
Normal file
18
src/utils/agvRuntimeStatus.js
Normal file
@ -0,0 +1,18 @@
|
||||
const AGV_RUNTIME_STATUS = {
|
||||
0: { label: '未知', type: 'info', className: 'unknown' },
|
||||
1: { label: '已断开', type: 'info', className: 'offline' },
|
||||
2: { label: '空闲', type: 'success', className: 'online' },
|
||||
3: { label: '手动模式', type: 'primary', className: 'manual' },
|
||||
4: { label: '自动运行', type: 'primary', className: 'running' },
|
||||
5: { label: '充电中', type: 'warning', className: 'charging' },
|
||||
6: { label: '已暂停', type: 'warning', className: 'paused' },
|
||||
7: { label: '已停止', type: 'info', className: 'stopped' },
|
||||
8: { label: '故障', type: 'danger', className: 'fault' },
|
||||
9: { label: '急停', type: 'danger', className: 'emergency' },
|
||||
}
|
||||
|
||||
const resolveStatus = value => AGV_RUNTIME_STATUS[Number(value)] || AGV_RUNTIME_STATUS[0]
|
||||
|
||||
export const agvRuntimeStatusLabel = value => resolveStatus(value).label
|
||||
export const agvRuntimeStatusType = value => resolveStatus(value).type
|
||||
export const agvRuntimeStatusClass = value => resolveStatus(value).className
|
||||
@ -11,7 +11,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<div class="bot-name">{{ item.robotName }}</div>
|
||||
<div class="bot-local">{{ robotStatus[item.status] }}</div>
|
||||
<div class="bot-local">{{ agvRuntimeStatusLabel(item.status) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -122,7 +122,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, onMounted, nextTick } from "vue";
|
||||
import { computed, onMounted, onUnmounted, nextTick } from "vue";
|
||||
import { ElMessage } from 'element-plus'
|
||||
import MapCanvas from "../MapCanvas.vue";
|
||||
import SvgIcon from "@/components/SvgIcon";
|
||||
@ -134,6 +134,7 @@ import { getJointStateApi, moveJApi, torqueOnApi, speedJApi, stopMotionApi, clea
|
||||
import { Remove, CirclePlus } from '@element-plus/icons-vue'
|
||||
import VoiceConversation from "./VoiceConversation.vue";
|
||||
import IPlayer from "@/components/IPlayer/index.vue";
|
||||
import { agvRuntimeStatusLabel } from '@/utils/agvRuntimeStatus'
|
||||
|
||||
const robotList = ref([])
|
||||
const robotDevices = ref([])
|
||||
@ -147,15 +148,18 @@ const loadRobotDevices = async (robotId) => {
|
||||
robotDevices.value = Array.isArray(response.data) ? response.data : []
|
||||
}
|
||||
|
||||
const fetchRobotList = async () => {
|
||||
const fetchRobotList = async (refreshDevices = true) => {
|
||||
try {
|
||||
const res = await getRobotList({
|
||||
robotType: 2,
|
||||
})
|
||||
if (res.code === 200) {
|
||||
const currentRobotId = activeBotData.value.robotId
|
||||
robotList.value = res.rows
|
||||
activeBotData.value = robotList.value[0] || {}
|
||||
await loadRobotDevices(activeBotData.value.robotId)
|
||||
const currentIndex = robotList.value.findIndex(item => item.robotId === currentRobotId)
|
||||
activeBot.value = currentIndex >= 0 ? currentIndex : 0
|
||||
activeBotData.value = robotList.value[activeBot.value] || {}
|
||||
if (refreshDevices) await loadRobotDevices(activeBotData.value.robotId)
|
||||
} else {
|
||||
console.error('获取机器人列表失败:', res.message)
|
||||
}
|
||||
@ -164,13 +168,6 @@ const fetchRobotList = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const robotStatus = {
|
||||
0: '在线',
|
||||
1: '离线',
|
||||
2: '充电中',
|
||||
3: '巡检中'
|
||||
}
|
||||
|
||||
const activeBot = ref(0)
|
||||
const activeBotData = ref({})
|
||||
const changeBot = async (index) => {
|
||||
@ -307,11 +304,17 @@ const stop = async () => {
|
||||
|
||||
const videoUrl = ref('https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8')
|
||||
|
||||
let robotRefreshTimer
|
||||
|
||||
onMounted(() => {
|
||||
fetchRobotList()
|
||||
robotRefreshTimer = window.setInterval(() => {
|
||||
if (!document.hidden) fetchRobotList(false)
|
||||
}, 5000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.clearInterval(robotRefreshTimer)
|
||||
if (animationId.value) {
|
||||
clearInterval(animationId.value)
|
||||
}
|
||||
|
||||
@ -382,6 +382,13 @@ const addRobot = (id, name,x, y, angleRad, color) => {
|
||||
applicationState.robots.push(robot);
|
||||
}
|
||||
|
||||
const parseRobotPosition = (currentPosition) => {
|
||||
if (!currentPosition) return null
|
||||
const values = String(currentPosition).split(',').map(Number)
|
||||
if (values.length < 3 || values.some(value => !Number.isFinite(value))) return null
|
||||
return values
|
||||
}
|
||||
|
||||
const moveRobot = (robot, dx, dy, dtheta = 0) => {
|
||||
robot.x += dx;
|
||||
robot.y += dy;
|
||||
@ -398,7 +405,9 @@ onMounted(() => {
|
||||
setTimeout(() => {
|
||||
bindEvent()
|
||||
props.robotList.forEach(item => {
|
||||
const [x, y, angle] = item.currentPosition.split(',')
|
||||
const position = parseRobotPosition(item.currentPosition)
|
||||
if (!position) return
|
||||
const [x, y, angle] = position
|
||||
addRobot(item.id, item.robotName, x, y, angle, colorList[applicationState.robots.length % colorList.length])
|
||||
})
|
||||
}, 300)
|
||||
@ -412,13 +421,14 @@ watch(() => props.robotList,
|
||||
if (newVal.length > 0) {
|
||||
const ids = applicationState.robots.map(r => r.id);
|
||||
props.robotList.forEach(item => {
|
||||
const position = parseRobotPosition(item.currentPosition)
|
||||
if (!position) return
|
||||
const [x, y, angle] = position
|
||||
if (!ids.includes(item.id)) {
|
||||
const [x, y, angle] = item.currentPosition.split(',')
|
||||
addRobot(item.id, item.robotName, x, y, angle, colorList[applicationState.robots.length % colorList.length])
|
||||
} else {
|
||||
applicationState.robots.forEach(robot => {
|
||||
if (robot.id === item.id) {
|
||||
const [x, y, angle] = item.currentPosition.split(',')
|
||||
// moveRobot(robot, parseFloat(x) - robot.x, parseFloat(y) - robot.y, parseFloat(angle) - robot.angle)
|
||||
robot.x = x
|
||||
robot.y = y
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<div class="title-box">
|
||||
<div><SvgIcon name="bot" :color="index === 0 ? '#00D4FF' : '#FFB300'" /></div>
|
||||
<div class="title">{{ item.robotName }}</div>
|
||||
<div class="state online">{{ robotStatus[item.status] }}</div>
|
||||
<div class="state" :class="agvRuntimeStatusClass(item.status)">{{ agvRuntimeStatusLabel(item.status) }}</div>
|
||||
</div>
|
||||
<div class="info-box">
|
||||
<div class="item-box">
|
||||
@ -71,12 +71,9 @@
|
||||
<!-- <div class="split-line"></div> -->
|
||||
<div class="alarm-container">
|
||||
<div class="container-title">告警记录</div>
|
||||
<div class="alarm-box" v-for="item, index in alarmRecord">
|
||||
<div class="alarm-title" :class="item.resultStatus">
|
||||
<div class="title">{{ item.resultName }}</div>
|
||||
<div class="status">{{ getOptionLabel(resultStatusOptions, item.resultStatus) }}</div>
|
||||
</div>
|
||||
<div class="alarm-info">{{ item.createTime }}</div>
|
||||
<div class="alarm-box" v-for="item, index in 2">
|
||||
<div class="title">#01 检测到异常噪声</div>
|
||||
<div class="alarm-info">14:28:33 — 厂区A 东侧走廊</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -89,15 +86,9 @@ import MapCanvas from "./MapCanvas.vue";
|
||||
import { onMounted, onUnmounted } from "vue";
|
||||
import { getRobotList } from '@/api/inspection/robot'
|
||||
import { getRunTaskList } from '@/api/inspection/runTask'
|
||||
import { getInspectionResultList } from '@/api/inspection/result'
|
||||
import { agvRuntimeStatusClass, agvRuntimeStatusLabel } from '@/utils/agvRuntimeStatus'
|
||||
|
||||
const robotList = ref([])
|
||||
const robotStatus = {
|
||||
"0": "在线",
|
||||
"1": "离线",
|
||||
"2": "充电中",
|
||||
"3": "巡检中"
|
||||
}
|
||||
const getRobot = async () => {
|
||||
let robotType
|
||||
if (import.meta.env.VITE_INSPECTION_TYPE === 'inspection') {
|
||||
@ -127,7 +118,7 @@ const taskStatus = {
|
||||
}
|
||||
const getRunningTask = async () => {
|
||||
let taskType
|
||||
if (import.meta.env.VITE_INSPECTION_TYPE = 'inspection') {
|
||||
if (import.meta.env.VITE_INSPECTION_TYPE === 'inspection') {
|
||||
taskType = '1'
|
||||
} else {
|
||||
taskType = '2'
|
||||
@ -163,31 +154,18 @@ const centerCanvasView = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const alarmRecord = ref([])
|
||||
const resultStatusOptions = [
|
||||
{ label: '待处理', value: 'PENDING' },
|
||||
{ label: '正常', value: 'NORMAL' },
|
||||
{ label: '异常', value: 'ABNORMAL' },
|
||||
{ label: '识别失败', value: 'RECOGNIZE_FAILED' }
|
||||
]
|
||||
const getAlarmRecord = async () => {
|
||||
const res = await getInspectionResultList({
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
if (res.code === 200) {
|
||||
alarmRecord.value = res.rows
|
||||
}
|
||||
}
|
||||
|
||||
const getOptionLabel = (options, value) => {
|
||||
return options.find(item => item.value === value)?.label || value || '-'
|
||||
}
|
||||
let robotRefreshTimer
|
||||
|
||||
onMounted(() => {
|
||||
getRobot()
|
||||
getRunningTask()
|
||||
getAlarmRecord()
|
||||
robotRefreshTimer = window.setInterval(() => {
|
||||
if (!document.hidden) getRobot()
|
||||
}, 5000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.clearInterval(robotRefreshTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -248,6 +226,31 @@ onMounted(() => {
|
||||
background: #0f83;
|
||||
color: #0f8;
|
||||
}
|
||||
|
||||
.unknown,
|
||||
.offline,
|
||||
.stopped {
|
||||
background: rgba(148, 163, 184, 0.2);
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
.manual,
|
||||
.running {
|
||||
background: rgba(0, 212, 255, 0.18);
|
||||
color: #00d4ff;
|
||||
}
|
||||
|
||||
.charging,
|
||||
.paused {
|
||||
background: rgba(255, 179, 0, 0.2);
|
||||
color: #ffb300;
|
||||
}
|
||||
|
||||
.fault,
|
||||
.emergency {
|
||||
background: rgba(255, 77, 79, 0.2);
|
||||
color: #ff6b6d;
|
||||
}
|
||||
}
|
||||
|
||||
.info-box {
|
||||
@ -411,45 +414,13 @@ onMounted(() => {
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.alarm-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
font-family: Inter;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: 12px;
|
||||
border-radius: 12px;
|
||||
padding: 0 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.NORMAL {
|
||||
color: #00D4FF;
|
||||
}
|
||||
|
||||
.ABNORMAL {
|
||||
.title {
|
||||
color: #FF6B6B;
|
||||
font-size: 14px;
|
||||
font-family: Inter;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.PENDING {
|
||||
color: #8899BB;
|
||||
}
|
||||
|
||||
.RECOGNIZE_FAILED {
|
||||
color: #FF6B6B;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.alarm-info {
|
||||
margin: 10px 0;
|
||||
font-size: 12px;
|
||||
|
||||
@ -185,6 +185,7 @@ import { computed, getCurrentInstance, onBeforeUnmount, onMounted, reactive, ref
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { addRobot, deleteRobot, getRobotDevices, getRobotList, getRobotStatus, updateRobot } from '@/api/inspection/robot'
|
||||
import { getMapList } from '@/api/inspection/map'
|
||||
import { agvRuntimeStatusLabel, agvRuntimeStatusType } from '@/utils/agvRuntimeStatus'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { robot_type } = proxy.useDict('robot_type')
|
||||
@ -256,8 +257,8 @@ async function loadDevices(showLoading = true) {
|
||||
try { const res = await getRobotDevices(activeRobot.value.id); devices.value = res.data || [] } finally { deviceLoading.value = false }
|
||||
}
|
||||
function endpointText(row) { return row.ipAddress && row.port ? `${row.ipAddress}:${row.port}` : '等待连接后自动获取' }
|
||||
function runtimeStatusLabel(value) { return ({ 0: '在线', 1: '离线', 2: '充电中', 3: '巡检中' })[value] || '未知' }
|
||||
function runtimeStatusType(value) { return ({ 0: 'success', 1: 'info', 2: 'warning', 3: 'primary' })[value] || 'info' }
|
||||
const runtimeStatusLabel = agvRuntimeStatusLabel
|
||||
const runtimeStatusType = agvRuntimeStatusType
|
||||
function hasBattery(row) { return row.batteryLevel !== null && row.batteryLevel !== undefined && row.batteryLevel !== '' }
|
||||
function normalizeBattery(value) { return Math.max(0, Math.min(100, Math.round(Number(value) || 0))) }
|
||||
function batteryColor(value) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user