diff --git a/agv_app/app.py b/agv_app/app.py index b27e229..b215f0d 100644 --- a/agv_app/app.py +++ b/agv_app/app.py @@ -433,6 +433,15 @@ def api_navigate_stop(): return jsonify({"ok": False, "error": "导航器未初始化"}), 400 +@app.route("/api/navigate/cancel", methods=["POST"]) +def api_navigate_cancel(): + """取消当前导航(别名)""" + if gs.navigator: + gs.navigator.stop() + return jsonify({"ok": True, "message": "导航已取消"}) + return jsonify({"ok": True, "message": "无活动导航"}) + + @app.route("/api/navigate/status", methods=["GET"]) def api_navigate_status(): """获取导航状态""" diff --git a/agv_app/static/js/setting.js b/agv_app/static/js/setting.js index 4604ad2..ffcde9b 100644 --- a/agv_app/static/js/setting.js +++ b/agv_app/static/js/setting.js @@ -48,6 +48,11 @@ const app = createApp({ agvPosition: null, agvSpeed: 0.5, agvMoveInterval: null, + initPoseLoading: false, + initPoseMsg: '', + nav2Available: false, + navStatus: 'idle', + navCurrentPos: null, agvCameraUrl: API + '/api/camera/refresh', agvCameraTimer: null, initPoseLoading: false, @@ -56,6 +61,12 @@ const app = createApp({ mounted() { this.refresh() this.refreshAngles() + this.refreshNavStatus() + setInterval(() => { + if (this.nav2Available && this.navStatus === 'navigating') { + this.refreshNavStatus() + } + }, 3000) }, watch: { // 监听点位数据变化,自动刷新地图 @@ -999,6 +1010,76 @@ const app = createApp({ alert('❌ 复位请求失败: ' + e.message) } }, + + // === AMCL 初始化定位 === + async initAmclPose() { + if (!this.agvConnected) { alert('请先连接AGV'); return } + this.initPoseLoading = true + this.initPoseMsg = '' + try { + var res = await fetch(API + '/api/mission/init_pose', { method: 'POST' }) + var data = await res.json() + if (data.ok) { + this.initPoseMsg = data.message || '✅ 已初始化定位' + setTimeout(() => { this.initPoseMsg = '' }, 5000) + await this.refreshNavStatus() + } else { + alert('❌ 初始化失败: ' + (data.error || '未知错误')) + } + } catch (e) { + alert('❌ 请求失败: ' + e.message) + } finally { + this.initPoseLoading = false + } + }, + + // === 导航到点位 === + async navigateToPoint() { + if (!this.pointEditor.x && !this.pointEditor.y) { + alert('该点位坐标无效,请先读取或输入坐标'); return + } + if (!this.nav2Available) { alert('Nav2 不可用,请先连接AGV并初始化定位'); return } + if (!confirm(`确定导航到点位 (${this.pointEditor.x.toFixed(2)}, ${this.pointEditor.y.toFixed(2)})?`)) return + try { + var 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 }) + }) + var data = await res.json() + if (data.ok) { + alert('✅ 导航已启动,请观察AGV移动') + this.closePointEdit() + this.$nextTick(() => this.refreshNavStatus()) + } else { + alert('❌ 导航失败: ' + (data.error || '未知错误')) + } + } catch (e) { + alert('❌ 请求失败: ' + e.message) + } + }, + + // === Nav2 状态刷新 === + async refreshNavStatus() { + try { + var res = await fetch(API + '/api/navigate/status') + var data = await res.json() + this.nav2Available = data.nav2_available + this.navStatus = data.status + this.navCurrentPos = data.current_position + } catch (e) { + this.nav2Available = false + } + }, + + // === 取消导航 === + async cancelNav() { + if (!this.agvConnected) return + try { + await fetch(API + '/api/navigate/cancel', { method: 'POST' }) + await this.refreshNavStatus() + } catch (e) {} + }, } }) const vm = app.mount('#app') diff --git a/agv_app/templates/setting.html b/agv_app/templates/setting.html index f511b5a..5f9cc55 100644 --- a/agv_app/templates/setting.html +++ b/agv_app/templates/setting.html @@ -4,7 +4,7 @@