2.8.2 grid mission
This commit is contained in:
+18
-6
@@ -147,11 +147,11 @@ class ToggleSwitch(QWidget):
|
|||||||
class ControlStationUI(QMainWindow):
|
class ControlStationUI(QMainWindow):
|
||||||
planning_finished = pyqtSignal(object)
|
planning_finished = pyqtSignal(object)
|
||||||
|
|
||||||
VERSION = '2.8.0'
|
VERSION = '2.8.2'
|
||||||
FONT_SCALE_MIN = 70
|
FONT_SCALE_MIN = 70
|
||||||
FONT_SCALE_MAX = 180
|
FONT_SCALE_MAX = 180
|
||||||
FONT_SCALE_DEFAULT = 100
|
FONT_SCALE_DEFAULT = 100
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.setWindowTitle(f'GCS v{self.VERSION}')
|
self.setWindowTitle(f'GCS v{self.VERSION}')
|
||||||
@@ -1596,6 +1596,8 @@ class ControlStationUI(QMainWindow):
|
|||||||
group.planned_waypoints = None
|
group.planned_waypoints = None
|
||||||
group.mission_target = None
|
group.mission_target = None
|
||||||
self.drone_map.clear_mission_plan_for_group(group_id)
|
self.drone_map.clear_mission_plan_for_group(group_id)
|
||||||
|
self.drone_map.clear_grid_sweep_area_for_group(
|
||||||
|
group_id, restart_selection=(group.mission_type == 'GRID_SWEEP'))
|
||||||
panel = self.group_panels.get(group_id)
|
panel = self.group_panels.get(group_id)
|
||||||
if panel:
|
if panel:
|
||||||
panel.update_status()
|
panel.update_status()
|
||||||
@@ -1876,6 +1878,7 @@ class ControlStationUI(QMainWindow):
|
|||||||
if group.executor:
|
if group.executor:
|
||||||
group.executor.stop()
|
group.executor.stop()
|
||||||
self.drone_map.clear_mission_plan_for_group(group_id)
|
self.drone_map.clear_mission_plan_for_group(group_id)
|
||||||
|
self.drone_map.clear_grid_sweep_area_for_group(group_id)
|
||||||
|
|
||||||
# 只有當操作目標組是 active 組時,才更新 UI
|
# 只有當操作目標組是 active 組時,才更新 UI
|
||||||
if group_id == self.active_group_id:
|
if group_id == self.active_group_id:
|
||||||
@@ -1910,6 +1913,7 @@ class ControlStationUI(QMainWindow):
|
|||||||
|
|
||||||
# 移除地圖上的任務計畫
|
# 移除地圖上的任務計畫
|
||||||
self.drone_map.clear_mission_plan_for_group(group_id)
|
self.drone_map.clear_mission_plan_for_group(group_id)
|
||||||
|
self.drone_map.clear_grid_sweep_area_for_group(group_id)
|
||||||
|
|
||||||
# 移除 tab
|
# 移除 tab
|
||||||
for i in range(self.group_tab_widget.count()):
|
for i in range(self.group_tab_widget.count()):
|
||||||
@@ -2256,10 +2260,14 @@ class ControlStationUI(QMainWindow):
|
|||||||
center_lat, center_lon, _ = center_origin
|
center_lat, center_lon, _ = center_origin
|
||||||
target_lat, target_lon = context['draw_target']
|
target_lat, target_lon = context['draw_target']
|
||||||
group.mission_target = (float(target_lat), float(target_lon))
|
group.mission_target = (float(target_lat), float(target_lon))
|
||||||
self.drone_map.draw_mission_plan_for_group(
|
if expected_mission_type == 'GRID_SWEEP':
|
||||||
group_id, group.color,
|
# Grid Sweep 的範圍已由矩形圖層表示,不另外顯示中心/目標點。
|
||||||
center_lat, center_lon, target_lat, target_lon
|
self.drone_map.clear_mission_plan_for_group(group_id)
|
||||||
)
|
else:
|
||||||
|
self.drone_map.draw_mission_plan_for_group(
|
||||||
|
group_id, group.color,
|
||||||
|
center_lat, center_lon, target_lat, target_lon
|
||||||
|
)
|
||||||
|
|
||||||
self._launch_verification(
|
self._launch_verification(
|
||||||
context['verification_type'], drone_gps_positions, selected_drones,
|
context['verification_type'], drone_gps_positions, selected_drones,
|
||||||
@@ -2351,6 +2359,10 @@ class ControlStationUI(QMainWindow):
|
|||||||
self.statusBar().showMessage("矩形座標解析失敗", 3000)
|
self.statusBar().showMessage("矩形座標解析失敗", 3000)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 將確認後的框選範圍存入群組專屬圖層,供停止任務時精準清除。
|
||||||
|
self.drone_map.draw_grid_sweep_area_for_group(
|
||||||
|
group.group_id, group.color, rect_corners)
|
||||||
|
|
||||||
panel = self.group_panels.get(group.group_id)
|
panel = self.group_panels.get(group.group_id)
|
||||||
params = panel.get_mission_params() if panel else {}
|
params = panel.get_mission_params() if panel else {}
|
||||||
base_alt = params.get('altitude', 10.0)
|
base_alt = params.get('altitude', 10.0)
|
||||||
|
|||||||
+54
-17
@@ -368,6 +368,8 @@ class DroneMap:
|
|||||||
|
|
||||||
// 多群組任務規劃 — 每個 group 各自的 layer group
|
// 多群組任務規劃 — 每個 group 各自的 layer group
|
||||||
var groupMissionLayers = {}; // group_id → L.layerGroup
|
var groupMissionLayers = {}; // group_id → L.layerGroup
|
||||||
|
// Grid Sweep 框選範圍也依群組保存,停止時可單獨清除。
|
||||||
|
var gridSweepAreaLayers = {}; // group_id → L.layerGroup
|
||||||
|
|
||||||
// 選擇工具變量
|
// 選擇工具變量
|
||||||
var selectionMode = null; // 'drones', 'rect', 'polygon', 'route', null
|
var selectionMode = null; // 'drones', 'rect', 'polygon', 'route', null
|
||||||
@@ -581,23 +583,6 @@ class DroneMap:
|
|||||||
[bounds.getSouth(), bounds.getEast()],
|
[bounds.getSouth(), bounds.getEast()],
|
||||||
[bounds.getSouth(), bounds.getWest()]
|
[bounds.getSouth(), bounds.getWest()]
|
||||||
];
|
];
|
||||||
|
|
||||||
L.rectangle(bounds, {
|
|
||||||
color: '#FF6B6B',
|
|
||||||
fillColor: '#FF6B6B',
|
|
||||||
fillOpacity: 0.2,
|
|
||||||
weight: 2
|
|
||||||
}).addTo(selectionLayer);
|
|
||||||
|
|
||||||
rectPoints.forEach(point => {
|
|
||||||
L.circleMarker(point, {
|
|
||||||
radius: 5,
|
|
||||||
color: '#FF6B6B',
|
|
||||||
fillColor: '#FF6B6B',
|
|
||||||
fillOpacity: 0.8
|
|
||||||
}).addTo(selectionLayer);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (bridge) {
|
if (bridge) {
|
||||||
var pointsStr = JSON.stringify(rectPoints);
|
var pointsStr = JSON.stringify(rectPoints);
|
||||||
bridge.rectangleSelected(pointsStr);
|
bridge.rectangleSelected(pointsStr);
|
||||||
@@ -974,6 +959,41 @@ class DroneMap:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function drawGridSweepAreaForGroup(groupId, color, rectPoints) {
|
||||||
|
clearGridSweepAreaForGroup(groupId);
|
||||||
|
var layerGroup = L.layerGroup().addTo(map);
|
||||||
|
gridSweepAreaLayers[groupId] = layerGroup;
|
||||||
|
var bounds = L.latLngBounds(rectPoints);
|
||||||
|
|
||||||
|
L.rectangle(bounds, {
|
||||||
|
color: color,
|
||||||
|
fillColor: color,
|
||||||
|
fillOpacity: 0.2,
|
||||||
|
weight: 2
|
||||||
|
}).addTo(layerGroup);
|
||||||
|
rectPoints.forEach(point => {
|
||||||
|
L.circleMarker(point, {
|
||||||
|
radius: 5,
|
||||||
|
color: color,
|
||||||
|
fillColor: color,
|
||||||
|
fillOpacity: 0.8
|
||||||
|
}).addTo(layerGroup);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearGridSweepAreaForGroup(groupId, restartSelection) {
|
||||||
|
if (gridSweepAreaLayers[groupId]) {
|
||||||
|
map.removeLayer(gridSweepAreaLayers[groupId]);
|
||||||
|
delete gridSweepAreaLayers[groupId];
|
||||||
|
}
|
||||||
|
if (restartSelection) {
|
||||||
|
clearSelectionMode();
|
||||||
|
selectionMode = 'rect';
|
||||||
|
map.dragging.disable();
|
||||||
|
console.log('Grid Sweep: 已清除範圍並重新進入框選模式');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function clearAllMissionPlans() {
|
function clearAllMissionPlans() {
|
||||||
for (var gid in groupMissionLayers) {
|
for (var gid in groupMissionLayers) {
|
||||||
map.removeLayer(groupMissionLayers[gid]);
|
map.removeLayer(groupMissionLayers[gid]);
|
||||||
@@ -1110,6 +1130,23 @@ class DroneMap:
|
|||||||
self.map_view.page().runJavaScript(f"clearMissionPlanForGroup('{group_id}');")
|
self.map_view.page().runJavaScript(f"clearMissionPlanForGroup('{group_id}');")
|
||||||
_log("INFO", f"地圖已清除 Group {group_id} 任務規劃")
|
_log("INFO", f"地圖已清除 Group {group_id} 任務規劃")
|
||||||
|
|
||||||
|
def draw_grid_sweep_area_for_group(self, group_id, color, rect_corners):
|
||||||
|
"""繪製指定群組的 Grid Sweep 框選範圍。"""
|
||||||
|
if self.map_loaded:
|
||||||
|
group_json = json.dumps(group_id)
|
||||||
|
color_json = json.dumps(color)
|
||||||
|
corners_json = json.dumps(rect_corners)
|
||||||
|
self.map_view.page().runJavaScript(
|
||||||
|
f"drawGridSweepAreaForGroup({group_json}, {color_json}, {corners_json});")
|
||||||
|
|
||||||
|
def clear_grid_sweep_area_for_group(self, group_id, restart_selection=False):
|
||||||
|
"""清除指定群組的 Grid Sweep 範圍,可選擇重新進入框選模式。"""
|
||||||
|
if self.map_loaded:
|
||||||
|
group_json = json.dumps(group_id)
|
||||||
|
restart_js = str(bool(restart_selection)).lower()
|
||||||
|
self.map_view.page().runJavaScript(
|
||||||
|
f"clearGridSweepAreaForGroup({group_json}, {restart_js});")
|
||||||
|
|
||||||
def set_mission_mode(self, mode):
|
def set_mission_mode(self, mode):
|
||||||
"""從 Python 端切換地圖的任務模式(觸發框選/路徑標記等)"""
|
"""從 Python 端切換地圖的任務模式(觸發框選/路徑標記等)"""
|
||||||
if self.map_loaded:
|
if self.map_loaded:
|
||||||
|
|||||||
Reference in New Issue
Block a user