可基本执行任务

This commit is contained in:
ywb
2026-05-28 13:55:55 +08:00
parent f507363c34
commit 4c096a4bd1
12 changed files with 1207 additions and 669 deletions
+210 -8
View File
@@ -26,6 +26,9 @@ createApp({
newPointName: '',
newPointMode: 'front',
newPointSequence: ['front', 'back'],
// 点位编辑器弹窗
editingPoint: null,
pointEditor: { x: 0, y: 0, yaw: 0 },
// 机型(姿态组)
models: [],
selectedModelId: null,
@@ -56,6 +59,7 @@ createApp({
qrScanningId: null,
armCameraUrl: API + '/api/camera/arm_refresh',
newQrName: '',
armInitialPose: [0, 0, 0, 0, 0, 0],
}
},
mounted() {
@@ -158,20 +162,41 @@ createApp({
if (res.ok) {
const data = await res.json()
this.nav2Available = data.nav2_available
if (data.current_pos) {
this.navCurrentPos = data.current_pos
if (data.current_position) {
this.navCurrentPos = data.current_position
}
}
} catch (e) {}
},
async onMapClick(e) {
if (!this.mapMeta || !this.agvConnected) return
if (!this.mapMeta) {
this.mapMsg = '❌ 地图未加载'
setTimeout(() => { this.mapMsg = '' }, 3000)
return
}
if (!this.agvConnected) {
this.mapMsg = '❌ AGV 未连接,无法导航'
setTimeout(() => { this.mapMsg = '' }, 3000)
return
}
const rect = e.target.getBoundingClientRect()
const px = (e.clientX - rect.left) / rect.width
const py = (e.clientY - rect.top) / rect.height
let px = (e.clientX - rect.left) / rect.width
let py = (e.clientY - rect.top) / rect.height
// 逆旋转补偿:地图 CSS transform: rotate() 后,点击坐标需反向旋转
// 使同一物理点在不同旋转角度下返回相同的世界坐标
const rotation = (this.mapRotation || 0) * Math.PI / 180
if (rotation !== 0) {
const cx = px - 0.5
const cy = py - 0.5
const cos = Math.cos(-rotation)
const sin = Math.sin(-rotation)
px = cx * cos - cy * sin + 0.5
py = cx * sin + cy * cos + 0.5
}
const { resolution, origin } = this.mapMeta
const wx = origin[0] + px * resolution * this.mapMeta.width
const wy = origin[1] + (1 - py) * resolution * this.mapMeta.height
if (!confirm(`是否导航到该坐标?\nX: ${wx.toFixed(3)}\nY: ${wy.toFixed(3)}`)) return
try {
const res = await fetch(API + '/api/navigate/to', {
method: 'POST',
@@ -254,6 +279,129 @@ createApp({
if (!angles) return '—'
return angles.map(a => (a || 0).toFixed(1) + '°').join(' / ')
},
// === 点位编辑器弹窗 ===
openPointEdit(ri, ci) {
const point = this.getPointAt(ri, ci)
this.editingPoint = { pointRow: ri, col: ci }
if (point && point.coords && point.coords.length >= 3) {
this.pointEditor = { x: point.coords[0], y: point.coords[1], yaw: point.coords[2] || 0 }
} else {
this.pointEditor = { x: 0, y: 0, yaw: 0 }
}
},
closePointEdit() {
this.editingPoint = null
},
getPointOwnerLabel(pointRow, col) {
const rows = this.missionConfig.rows || 0
if (pointRow === 0) {
return `机器行1·正面`
} else if (pointRow >= rows) {
return `机器行${rows}·背面`
} else {
return `机器行${pointRow}·背面 + 机器行${pointRow+1}·正面`
}
},
async loadPointFromAgv() {
try {
const res = await fetch(API + '/api/agv/position')
const data = await res.json()
if (data.ok && data.position && data.position.length >= 3) {
this.pointEditor.x = data.position[0] || 0
this.pointEditor.y = data.position[1] || 0
this.pointEditor.yaw = data.position[2] || 0
} else {
alert('读取AGV位置失败')
}
} catch (e) { alert('读取AGV位置失败: ' + e.message) }
},
async savePoint() {
if (!this.editingPoint) return
const { pointRow, col } = this.editingPoint
const coords = [this.pointEditor.x, this.pointEditor.y, this.pointEditor.yaw]
const rows = this.missionConfig.rows || 0
// 根据点位行确定 side
const sides = []
if (pointRow === 0) {
sides.push('front')
} else if (pointRow >= rows) {
sides.push('back')
} else {
sides.push('back')
sides.push('front')
}
try {
for (const side of sides) {
const res = await fetch(API + '/api/mission/positions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ row: pointRow, col, side, coords, poses: [] })
})
const data = await res.json()
if (!data.ok) { alert(`保存失败(${side}): ` + (data.error || '')); return }
}
alert('点位已保存')
await this.loadMissionConfig()
this.closePointEdit()
} catch (e) { alert('保存失败: ' + e.message) }
},
async navigateToPoint() {
if (!confirm(`确认导航到该点位?\nX: ${this.pointEditor.x} Y: ${this.pointEditor.y} Yaw: ${this.pointEditor.yaw}`)) return
try {
const res = await fetch(API + '/api/navigate/to', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
x: this.pointEditor.x,
y: this.pointEditor.y,
yaw: this.pointEditor.yaw
})
})
const data = await res.json()
if (!data.ok) { alert('导航失败: ' + (data.error || '')) }
} catch (e) { alert('导航失败: ' + e.message) }
},
async goToOrigin() {
if (!confirm('确认导航到原点 (0, 0, 0)?')) return
try {
const res = await fetch(API + '/api/navigate/to', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ x: 0, y: 0, yaw: 0 })
})
const data = await res.json()
if (data.ok) {
this.mapMsg = '✅ 已发送导航到原点'
} else {
this.mapMsg = '❌ ' + (data.error || '导航失败')
}
} catch (e) {
this.mapMsg = '❌ 导航请求失败: ' + e.message
}
setTimeout(() => { this.mapMsg = '' }, 3000)
},
async clearPoint() {
if (!this.editingPoint) return
const { pointRow, col } = this.editingPoint
const rows = this.missionConfig.rows || 0
const sides = pointRow === 0 ? ['front'] : pointRow >= rows ? ['back'] : ['front', 'back']
try {
for (const side of sides) {
await fetch(API + '/api/mission/positions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ row: pointRow, col, side, coords: [0, 0, 0], poses: [] })
})
}
await this.loadMissionConfig()
this.closePointEdit()
} catch (e) { alert('清空失败: ' + e.message) }
},
canClearPoint(pointRow, col) {
const point = this.getPointAt(pointRow, col)
if (!point || !point.coords) return true
return point.coords[0] === 0 && point.coords[1] === 0
},
// === 机型管理 ===
async loadAllModels() {
const res = await fetch(API + '/api/models/list')
@@ -344,7 +492,7 @@ createApp({
const res = await fetch(API + '/api/arm/set_angles', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ angles: pose.arm_angles, speed: 50 })
body: JSON.stringify({ angles: pose.arm_angles, speed: 500 })
})
const data = await res.json()
if (data.ok) { alert('姿态已应用到机械臂') }
@@ -382,6 +530,8 @@ createApp({
this.missionConfig.rows = data.config.rows || 3
this.missionConfig.cols = data.config.cols || 3
this.missionConfig.grid = data.config.grid || []
this.missionConfig.positions = data.config.positions || []
this.armInitialPose = data.config.arm_initial_pose || [0, 0, 0, 0, 0, 0]
}
} catch (e) { console.error('加载任务配置失败', e) }
},
@@ -413,7 +563,8 @@ createApp({
body: JSON.stringify({
rows: this.missionConfig.rows,
cols: this.missionConfig.cols,
grid: this.missionConfig.grid
grid: this.missionConfig.grid,
arm_initial_pose: this.armInitialPose
})
})
const data = await res.json()
@@ -422,6 +573,47 @@ createApp({
}
} catch (e) { alert('保存失败: ' + e.message) }
},
async saveArmInitialPose() {
try {
const res = await fetch(API + '/api/mission/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
rows: this.missionConfig.rows,
cols: this.missionConfig.cols,
grid: this.missionConfig.grid,
arm_initial_pose: this.armInitialPose
})
})
const data = await res.json()
if (data.ok) alert('✅ 机械臂初始姿态已保存')
else alert('❌ 保存失败')
} catch (e) { alert('保存失败: ' + e.message) }
},
async loadArmCurrentAngles() {
if (!this.armConnected) { alert('机械臂未连接'); return }
try {
const res = await fetch(API + '/api/arm/get_angles')
const data = await res.json()
if (data.ok && data.angles) {
this.armInitialPose = [...data.angles]
}
} catch (e) { alert('读取角度失败: ' + e.message) }
},
async applyArmInitialPose() {
if (!this.armConnected) { alert('机械臂未连接'); return }
if (!confirm('确认应用初始姿态到机械臂?')) return
try {
const res = await fetch(API + '/api/arm/set_angles', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ angles: this.armInitialPose, speed: 30 })
})
const data = await res.json()
if (data.ok) alert('✅ 机械臂已移动到初始姿态')
else alert('❌ 应用失败: ' + (data.error || ''))
} catch (e) { alert('应用失败: ' + e.message) }
},
async loadAllMachines() {
try {
const res = await fetch(API + '/api/mission/machines')
@@ -465,6 +657,16 @@ createApp({
this.selectMachine(m)
}
},
toggleMachine(ri, ci, event) {
if (event.target.checked) {
// 无机器 → 创建机器
this.createMachine(ri, ci)
} else {
// 有机器 → 删除机器
const m = this.getMachineAt(ri, ci)
if (m) this.deleteMachine(m.id)
}
},
async createMachine(ri, ci) {
try {
const machineId = 'm_' + ri + '_' + ci
@@ -887,7 +1089,7 @@ createApp({
const res = await fetch(API + '/api/arm/set_angles', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({ angles: q.joint_angles, speed: 50 })
body: JSON.stringify({ angles: q.joint_angles, speed: 500 })
})
const data = await res.json()
if (data.ok) { alert('姿态已应用到机械臂')