-
This commit is contained in:
+75
-9
@@ -9,7 +9,8 @@ createApp({
|
||||
missionConfig: { rows: 3, cols: 3, grid: [], machines: [] },
|
||||
selectedMachine: null,
|
||||
sequence: [],
|
||||
poseForm: { name: '', photo_type: 'front', description: '' },
|
||||
poseForm: {},
|
||||
newPoseForm: {},
|
||||
// 地图
|
||||
mapForm: { map_dir: '/home/elephant/agv_pro_ros2/src/agv_pro_navigation2/map/', map_file: 'map.yaml' },
|
||||
mapMsg: '',
|
||||
@@ -45,6 +46,7 @@ createApp({
|
||||
agvMoveInterval: null,
|
||||
agvCameraUrl: API + '/api/camera/refresh',
|
||||
agvCameraTimer: null,
|
||||
armCameraTimer: null,
|
||||
// 机型展开
|
||||
expandedModelId: null,
|
||||
showAddModelModal: false,
|
||||
@@ -61,6 +63,11 @@ createApp({
|
||||
this.refreshAngles()
|
||||
this.loadQrConfigs()
|
||||
this.nav2Timer = setInterval(this.refreshNavStatus, 3000)
|
||||
// 机械臂摄像头自动刷新(每2秒)
|
||||
this.armCameraUrl = API + '/api/camera/arm_refresh?t=' + Date.now()
|
||||
this.armCameraTimer = setInterval(() => {
|
||||
this.armCameraUrl = API + '/api/camera/arm_refresh?t=' + Date.now()
|
||||
}, 2000)
|
||||
},
|
||||
computed: {
|
||||
hasQr() {
|
||||
@@ -90,6 +97,7 @@ createApp({
|
||||
beforeUnmount() {
|
||||
Object.values(this.jogIntervals).forEach(i => clearInterval(i))
|
||||
if (this.agvCameraTimer) clearInterval(this.agvCameraTimer)
|
||||
if (this.armCameraTimer) { clearInterval(this.armCameraTimer); this.armCameraTimer = null }
|
||||
if (this.nav2Timer) clearInterval(this.nav2Timer)
|
||||
},
|
||||
methods: {
|
||||
@@ -280,30 +288,88 @@ createApp({
|
||||
await this.loadAllModels()
|
||||
},
|
||||
// === 姿态管理(属于机型)===
|
||||
async addPose(modelId) {
|
||||
const form = this.poseForm[modelId]
|
||||
if (!form) return
|
||||
async addPose(modelId, type, name) {
|
||||
if (!name) name = '姿态' + (((this.getModel(modelId)?.poses?.length) || 0) + 1)
|
||||
await fetch(API + '/api/models/poses/add', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
model_id: modelId,
|
||||
name: form.name || '姿态' + ((this.getModel(modelId)?.poses?.length || 0) + 1),
|
||||
photo_type: form.photo_type,
|
||||
name: name,
|
||||
photo_type: type || 'front',
|
||||
arm_angles: this.currentAngles,
|
||||
speed: 500,
|
||||
description: form.description || ''
|
||||
description: ''
|
||||
})
|
||||
})
|
||||
await this.loadAllModels()
|
||||
form.name = ''
|
||||
form.description = ''
|
||||
const key = modelId + '_' + (type || 'front')
|
||||
if (this.newPoseForm[key] !== undefined) this.newPoseForm[key] = ''
|
||||
},
|
||||
async deletePose(modelId, poseId) {
|
||||
if (!confirm('确定删除该姿态?')) return
|
||||
await fetch(API + '/api/models/' + modelId + '/poses/' + poseId, { method: 'DELETE' })
|
||||
await this.loadAllModels()
|
||||
},
|
||||
async refreshPoseAngles(modelId, poseId) {
|
||||
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) {
|
||||
const model = this.getModel(modelId)
|
||||
if (model && model.poses) {
|
||||
const pose = model.poses.find(p => p.id === poseId)
|
||||
if (pose) {
|
||||
// Update local immediately for reactive UI
|
||||
pose.arm_angles = [...data.angles]
|
||||
// Persist to backend
|
||||
await fetch(API + '/api/models/' + modelId + '/poses/' + poseId, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ arm_angles: data.angles })
|
||||
})
|
||||
await this.loadAllModels()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) { alert('刷新角度失败: ' + e.message) }
|
||||
},
|
||||
async applyPoseAngles(modelId, poseId) {
|
||||
const model = this.getModel(modelId)
|
||||
if (!model || !model.poses) return
|
||||
const pose = model.poses.find(p => p.id === poseId)
|
||||
if (!pose || !pose.arm_angles) { alert('无效的姿态数据'); return }
|
||||
try {
|
||||
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 })
|
||||
})
|
||||
const data = await res.json()
|
||||
if (data.ok) { alert('姿态已应用到机械臂') }
|
||||
else { alert('应用失败: ' + (data.error || '未知错误')) }
|
||||
} catch (e) { alert('应用姿态失败: ' + e.message) }
|
||||
},
|
||||
adjustPoseAngle(modelId, poseId, jointIdx, delta) {
|
||||
const model = this.getModel(modelId)
|
||||
if (!model || !model.poses) return
|
||||
const pose = model.poses.find(p => p.id === poseId)
|
||||
if (!pose) return
|
||||
if (!pose.arm_angles) pose.arm_angles = [0,0,0,0,0,0]
|
||||
if (!pose.arm_angles[jointIdx]) pose.arm_angles[jointIdx] = 0
|
||||
pose.arm_angles[jointIdx] = Math.round((pose.arm_angles[jointIdx] + delta) * 10) / 10
|
||||
this.setAngle(jointIdx, pose.arm_angles[jointIdx])
|
||||
},
|
||||
async updatePoseAngleAndMove(modelId, poseId, jointIdx, value) {
|
||||
const model = this.getModel(modelId)
|
||||
if (!model || !model.poses) return
|
||||
const pose = model.poses.find(p => p.id === poseId)
|
||||
if (!pose) return
|
||||
if (!pose.arm_angles) pose.arm_angles = [0,0,0,0,0,0]
|
||||
pose.arm_angles[jointIdx] = parseFloat(value) || 0
|
||||
await this.setAngle(jointIdx, pose.arm_angles[jointIdx])
|
||||
},
|
||||
getModel(id) {
|
||||
return this.models.find(m => m.id === id)
|
||||
},
|
||||
|
||||
@@ -9,7 +9,8 @@ createApp({
|
||||
missionConfig: { rows: 3, cols: 3, grid: [], machines: [] },
|
||||
selectedMachine: null,
|
||||
sequence: [],
|
||||
poseForm: { name: '', photo_type: 'front', description: '' },
|
||||
poseForm: {},
|
||||
newPoseForm: {},
|
||||
// 地图
|
||||
mapForm: { map_dir: '/home/elephant/agv_pro_ros2/src/agv_pro_navigation2/map/', map_file: 'map.yaml' },
|
||||
mapMsg: '',
|
||||
@@ -45,6 +46,7 @@ createApp({
|
||||
agvMoveInterval: null,
|
||||
agvCameraUrl: API + '/api/camera/refresh',
|
||||
agvCameraTimer: null,
|
||||
armCameraTimer: null,
|
||||
// 机型展开
|
||||
expandedModelId: null,
|
||||
showAddModelModal: false,
|
||||
@@ -61,6 +63,11 @@ createApp({
|
||||
this.refreshAngles()
|
||||
this.loadQrConfigs()
|
||||
this.nav2Timer = setInterval(this.refreshNavStatus, 3000)
|
||||
// 机械臂摄像头自动刷新(每2秒)
|
||||
this.armCameraUrl = API + '/api/camera/arm_refresh?t=' + Date.now()
|
||||
this.armCameraTimer = setInterval(() => {
|
||||
this.armCameraUrl = API + '/api/camera/arm_refresh?t=' + Date.now()
|
||||
}, 2000)
|
||||
},
|
||||
computed: {
|
||||
hasQr() {
|
||||
@@ -90,6 +97,7 @@ createApp({
|
||||
beforeUnmount() {
|
||||
Object.values(this.jogIntervals).forEach(i => clearInterval(i))
|
||||
if (this.agvCameraTimer) clearInterval(this.agvCameraTimer)
|
||||
if (this.armCameraTimer) { clearInterval(this.armCameraTimer); this.armCameraTimer = null }
|
||||
if (this.nav2Timer) clearInterval(this.nav2Timer)
|
||||
},
|
||||
methods: {
|
||||
@@ -280,30 +288,88 @@ createApp({
|
||||
await this.loadAllModels()
|
||||
},
|
||||
// === 姿态管理(属于机型)===
|
||||
async addPose(modelId) {
|
||||
const form = this.poseForm[modelId]
|
||||
if (!form) return
|
||||
async addPose(modelId, type, name) {
|
||||
if (!name) name = '姿态' + (((this.getModel(modelId)?.poses?.length) || 0) + 1)
|
||||
await fetch(API + '/api/models/poses/add', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
model_id: modelId,
|
||||
name: form.name || '姿态' + ((this.getModel(modelId)?.poses?.length || 0) + 1),
|
||||
photo_type: form.photo_type,
|
||||
name: name,
|
||||
photo_type: type || 'front',
|
||||
arm_angles: this.currentAngles,
|
||||
speed: 500,
|
||||
description: form.description || ''
|
||||
description: ''
|
||||
})
|
||||
})
|
||||
await this.loadAllModels()
|
||||
form.name = ''
|
||||
form.description = ''
|
||||
const key = modelId + '_' + (type || 'front')
|
||||
if (this.newPoseForm[key] !== undefined) this.newPoseForm[key] = ''
|
||||
},
|
||||
async deletePose(modelId, poseId) {
|
||||
if (!confirm('确定删除该姿态?')) return
|
||||
await fetch(API + '/api/models/' + modelId + '/poses/' + poseId, { method: 'DELETE' })
|
||||
await this.loadAllModels()
|
||||
},
|
||||
async refreshPoseAngles(modelId, poseId) {
|
||||
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) {
|
||||
const model = this.getModel(modelId)
|
||||
if (model && model.poses) {
|
||||
const pose = model.poses.find(p => p.id === poseId)
|
||||
if (pose) {
|
||||
// Update local immediately for reactive UI
|
||||
pose.arm_angles = [...data.angles]
|
||||
// Persist to backend
|
||||
await fetch(API + '/api/models/' + modelId + '/poses/' + poseId, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ arm_angles: data.angles })
|
||||
})
|
||||
await this.loadAllModels()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) { alert('刷新角度失败: ' + e.message) }
|
||||
},
|
||||
async applyPoseAngles(modelId, poseId) {
|
||||
const model = this.getModel(modelId)
|
||||
if (!model || !model.poses) return
|
||||
const pose = model.poses.find(p => p.id === poseId)
|
||||
if (!pose || !pose.arm_angles) { alert('无效的姿态数据'); return }
|
||||
try {
|
||||
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 })
|
||||
})
|
||||
const data = await res.json()
|
||||
if (data.ok) { alert('姿态已应用到机械臂') }
|
||||
else { alert('应用失败: ' + (data.error || '未知错误')) }
|
||||
} catch (e) { alert('应用姿态失败: ' + e.message) }
|
||||
},
|
||||
adjustPoseAngle(modelId, poseId, jointIdx, delta) {
|
||||
const model = this.getModel(modelId)
|
||||
if (!model || !model.poses) return
|
||||
const pose = model.poses.find(p => p.id === poseId)
|
||||
if (!pose) return
|
||||
if (!pose.arm_angles) pose.arm_angles = [0,0,0,0,0,0]
|
||||
if (!pose.arm_angles[jointIdx]) pose.arm_angles[jointIdx] = 0
|
||||
pose.arm_angles[jointIdx] = Math.round((pose.arm_angles[jointIdx] + delta) * 10) / 10
|
||||
this.setAngle(jointIdx, pose.arm_angles[jointIdx])
|
||||
},
|
||||
async updatePoseAngleAndMove(modelId, poseId, jointIdx, value) {
|
||||
const model = this.getModel(modelId)
|
||||
if (!model || !model.poses) return
|
||||
const pose = model.poses.find(p => p.id === poseId)
|
||||
if (!pose) return
|
||||
if (!pose.arm_angles) pose.arm_angles = [0,0,0,0,0,0]
|
||||
pose.arm_angles[jointIdx] = parseFloat(value) || 0
|
||||
await this.setAngle(jointIdx, pose.arm_angles[jointIdx])
|
||||
},
|
||||
getModel(id) {
|
||||
return this.models.find(m => m.id === id)
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>设置 - AGV 拍摄系统</title>
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=20260520h">
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=20260520i">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
@@ -135,7 +135,7 @@
|
||||
<!-- 正面姿态 -->
|
||||
<div style="padding:16px;background:#0f1923">
|
||||
<h4 style="margin:0 0 12px 0;color:#388e3c">🟢 正面姿态</h4>
|
||||
<div v-for="pose in m.poses.filter(p => p.photo_type === 'front')" :key="pose.id" style="background:#0f1923;padding:12px;border:1px solid #2a3441;border-radius:6px;margin-bottom:8px">
|
||||
<div v-for="pose in (m.poses || []).filter(p => p.photo_type === 'front')" :key="pose.id" style="background:#0f1923;padding:12px;border:1px solid #2a3441;border-radius:6px;margin-bottom:8px">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
|
||||
<strong>{% raw %}{{ pose.name || '正面姿态' }}{% endraw %}</strong>
|
||||
<button class="btn btn-danger btn-small" @click="deletePose(m.id, pose.id)">删除</button>
|
||||
@@ -182,7 +182,7 @@
|
||||
<!-- 背面姿态 -->
|
||||
<div style="padding:16px;background:#0d1420">
|
||||
<h4 style="margin:0 0 12px 0;color:#d32f2f">🔴 背面姿态</h4>
|
||||
<div v-for="pose in m.poses.filter(p => p.photo_type === 'back')" :key="pose.id" style="background:#0f1923;padding:12px;border:1px solid #2a3441;border-radius:6px;margin-bottom:8px">
|
||||
<div v-for="pose in (m.poses || []).filter(p => p.photo_type === 'back')" :key="pose.id" style="background:#0f1923;padding:12px;border:1px solid #2a3441;border-radius:6px;margin-bottom:8px">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
|
||||
<strong>{% raw %}{{ pose.name || '背面姿态' }}{% endraw %}</strong>
|
||||
<button class="btn btn-danger btn-small" @click="deletePose(m.id, pose.id)">删除</button>
|
||||
@@ -676,7 +676,7 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="/static/js/vue3.global.prod.js?v=20260520h"></script>
|
||||
<script src="/static/js/setting.js?v=20260520h"></script>
|
||||
<script src="/static/js/vue3.global.prod.js?v=20260520i"></script>
|
||||
<script src="/static/js/setting.js?v=20260520i"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user