826220679@qq.com
2 天以前 48ee74129bb09a817a0bbbabe860c4007b74c66b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
package dikuai;
 
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.util.List;
import java.util.ArrayList;
import zhuye.Shouye;
import zhuye.MapRenderer;
import gecaoji.Gecaoji;
import gecaoji.lujingdraw;
import publicway.buttonset;
 
/**
 * 导航预览功能类
 * 实现割草机沿路径模拟导航
 */
public class daohangyulan {
    private static daohangyulan instance;
    private Shouye shouye;
    private MapRenderer mapRenderer;
    private Gecaoji mower;
    private Dikuai currentDikuai;
    
    // 路径相关
    private List<Point2D.Double> pathPoints;
    private int currentPathIndex;
    private double currentSpeed; // 米/秒
    private static final double DEFAULT_SPEED = 1.0; // 默认1米/秒
    private static final double SPEED_MULTIPLIER = 1.0; // 每次加速/减速的倍数
    
    // 定时器
    private Timer navigationTimer;
    private static final int TIMER_INTERVAL_MS = 50; // 50毫秒更新一次
    
    // 导航预览按钮
    private JButton speedUpBtn;
    private JButton speedDownBtn;
    private JButton exitBtn;
    
    // 保存原始按钮和面板的引用
    private JButton originalStartBtn;
    private JButton originalStopBtn;
    private JPanel originalButtonPanel;
    private LayoutManager originalButtonPanelLayout;
    
    // 导航预览轨迹
    private List<Point2D.Double> navigationTrack;
    
    // 是否正在导航预览
    private boolean isNavigating;
    
    private daohangyulan() {
        this.currentSpeed = DEFAULT_SPEED;
        this.navigationTrack = new ArrayList<>();
        this.isNavigating = false;
    }
    
    public static daohangyulan getInstance() {
        if (instance == null) {
            instance = new daohangyulan();
        }
        return instance;
    }
    
    /**
     * 启动导航预览
     * @param dikuai 地块对象
     */
    public void startNavigationPreview(Dikuai dikuai) {
        if (dikuai == null) {
            JOptionPane.showMessageDialog(null, "地块数据无效", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }
        
        // 获取地块的割草路径坐标
        String plannedPath = dikuai.getPlannedPath();
        if (plannedPath == null || plannedPath.trim().isEmpty() || "-1".equals(plannedPath.trim())) {
            JOptionPane.showMessageDialog(null, "当前地块没有规划路径,无法进行导航预览", "提示", JOptionPane.WARNING_MESSAGE);
            return;
        }
        
        // 解析路径坐标
        pathPoints = lujingdraw.parsePlannedPath(plannedPath);
        if (pathPoints == null || pathPoints.size() < 2) {
            JOptionPane.showMessageDialog(null, "路径坐标解析失败,无法进行导航预览", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }
        
        this.currentDikuai = dikuai;
        this.currentPathIndex = 0;
        this.currentSpeed = DEFAULT_SPEED;
        this.navigationTrack.clear();
        
        // 获取首页和地图渲染器
        shouye = Shouye.getInstance();
        if (shouye == null) {
            JOptionPane.showMessageDialog(null, "无法获取首页实例", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }
        
        mapRenderer = shouye.getMapRenderer();
        if (mapRenderer == null) {
            JOptionPane.showMessageDialog(null, "无法获取地图渲染器", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }
        
        mower = mapRenderer.getMower();
        if (mower == null) {
            JOptionPane.showMessageDialog(null, "无法获取割草机实例", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }
        
        // 设置割草机初始位置为路径起点
        Point2D.Double startPoint = pathPoints.get(0);
        mower.setPosition(startPoint.x, startPoint.y);
        
        // 计算初始方向(朝向第一个目标点)
        if (pathPoints.size() > 1) {
            Point2D.Double nextPoint = pathPoints.get(1);
            double heading = calculateHeading(startPoint, nextPoint);
            setMowerHeading(heading);
        } else {
            // 如果只有一个点,设置默认方向(0度,向右)
            setMowerHeading(0.0);
        }
        
        // 初始化导航预览轨迹
        navigationTrack.clear();
        navigationTrack.add(new Point2D.Double(startPoint.x, startPoint.y));
        mapRenderer.clearNavigationPreviewTrack();
        mapRenderer.addNavigationPreviewTrackPoint(startPoint);
        
        // 获取割草机割刀宽度
        String bladeWidthStr = dikuai.getMowingBladeWidth();
        double bladeWidthMeters = 0.5; // 默认0.5米
        if (bladeWidthStr != null && !bladeWidthStr.trim().isEmpty() && !"-1".equals(bladeWidthStr.trim())) {
            try {
                bladeWidthMeters = Double.parseDouble(bladeWidthStr.trim());
            } catch (NumberFormatException e) {
                // 使用默认值
            }
        }
        mapRenderer.setNavigationPreviewWidth(bladeWidthMeters);
        
        // 设置初始速度显示
        mapRenderer.setNavigationPreviewSpeed(currentSpeed);
        
        // 创建并显示导航预览按钮(替换底部按钮)
        createNavigationButtons();
        
        // 显示导航预览模式标签
        if (shouye != null) {
            shouye.setNavigationPreviewLabelVisible(true);
        }
        
        // 启动导航定时器
        startNavigationTimer();
        
        isNavigating = true;
        
        // 刷新地图显示
        mapRenderer.repaint();
    }
    
    /**
     * 创建导航预览按钮(替换底部的暂停、结束按钮)
     */
    private void createNavigationButtons() {
        // 移除旧的按钮(如果存在)
        removeNavigationButtons();
        
        if (shouye == null) {
            return;
        }
        
        // 获取原始按钮
        originalStartBtn = shouye.getStartButton();
        originalStopBtn = shouye.getStopButton();
        
        if (originalStartBtn == null || originalStopBtn == null) {
            return;
        }
        
        // 获取控制面板
        JPanel controlPanel = shouye.getControlPanel();
        if (controlPanel == null) {
            return;
        }
        
        // 查找按钮面板(包含 startBtn 和 stopBtn 的面板)
        JPanel buttonPanel = null;
        for (Component comp : controlPanel.getComponents()) {
            if (comp instanceof JPanel) {
                JPanel panel = (JPanel) comp;
                // 检查是否是按钮面板(包含 startBtn 和 stopBtn)
                boolean hasStartBtn = false;
                boolean hasStopBtn = false;
                for (Component child : panel.getComponents()) {
                    if (child == originalStartBtn) {
                        hasStartBtn = true;
                    }
                    if (child == originalStopBtn) {
                        hasStopBtn = true;
                    }
                }
                if (hasStartBtn && hasStopBtn) {
                    buttonPanel = panel;
                    break;
                }
            }
        }
        
        if (buttonPanel == null) {
            return;
        }
        
        // 保存原始按钮面板和布局
        originalButtonPanel = buttonPanel;
        originalButtonPanelLayout = buttonPanel.getLayout();
        
        // 隐藏原始按钮
        if (originalStartBtn != null) {
            originalStartBtn.setVisible(false);
        }
        if (originalStopBtn != null) {
            originalStopBtn.setVisible(false);
        }
        
        // 修改按钮面板布局为3列(加速、减速、退出)
        // 减少按钮间距,给按钮更多空间显示文字
        buttonPanel.setLayout(new GridLayout(1, 3, 10, 0));
        
        // 创建加速按钮
        speedUpBtn = createControlButton("加速", new Color(46, 139, 87));
        speedUpBtn.addActionListener(e -> speedUp());
        
        // 创建减速按钮
        speedDownBtn = createControlButton("减速", new Color(255, 140, 0));
        speedDownBtn.addActionListener(e -> speedDown());
        
        // 创建退出按钮
        exitBtn = createControlButton("退出", new Color(255, 107, 107));
        exitBtn.addActionListener(e -> exitNavigationPreview());
        
        // 添加新按钮到按钮面板
        buttonPanel.add(speedUpBtn);
        buttonPanel.add(speedDownBtn);
        buttonPanel.add(exitBtn);
        
        // 刷新显示
        buttonPanel.revalidate();
        buttonPanel.repaint();
        controlPanel.revalidate();
        controlPanel.repaint();
    }
    
    /**
     * 创建控制按钮(使用buttonset风格,尺寸40x80)
     */
    private JButton createControlButton(String text, Color color) {
        // 使用buttonset创建按钮
        JButton button = buttonset.createStyledButton(text, color);
        
        // 设置按钮尺寸:宽度40像素,高度80像素
        Dimension buttonSize = new Dimension(80, 40);
        button.setPreferredSize(buttonSize);
        button.setMinimumSize(buttonSize);
        button.setMaximumSize(buttonSize);
        
        return button;
    }
    
    /**
     * 移除导航预览按钮(恢复原始按钮)
     */
    private void removeNavigationButtons() {
        if (originalButtonPanel == null) {
            return;
        }
        
        // 移除导航预览按钮
        if (speedUpBtn != null && speedUpBtn.getParent() == originalButtonPanel) {
            originalButtonPanel.remove(speedUpBtn);
        }
        if (speedDownBtn != null && speedDownBtn.getParent() == originalButtonPanel) {
            originalButtonPanel.remove(speedDownBtn);
        }
        if (exitBtn != null && exitBtn.getParent() == originalButtonPanel) {
            originalButtonPanel.remove(exitBtn);
        }
        
        // 恢复原始布局
        if (originalButtonPanelLayout != null) {
            originalButtonPanel.setLayout(originalButtonPanelLayout);
        }
        
        // 恢复原始按钮显示
        if (originalStartBtn != null) {
            originalStartBtn.setVisible(true);
            if (originalStartBtn.getParent() != originalButtonPanel) {
                originalButtonPanel.add(originalStartBtn);
            }
        }
        if (originalStopBtn != null) {
            originalStopBtn.setVisible(true);
            if (originalStopBtn.getParent() != originalButtonPanel) {
                originalButtonPanel.add(originalStopBtn);
            }
        }
        
        // 刷新显示
        originalButtonPanel.revalidate();
        originalButtonPanel.repaint();
        
        if (shouye != null && shouye.getControlPanel() != null) {
            shouye.getControlPanel().revalidate();
            shouye.getControlPanel().repaint();
        }
        
        // 清空引用
        speedUpBtn = null;
        speedDownBtn = null;
        exitBtn = null;
        originalButtonPanel = null;
        originalButtonPanelLayout = null;
        originalStartBtn = null;
        originalStopBtn = null;
    }
    
    /**
     * 启动导航定时器
     */
    private void startNavigationTimer() {
        if (navigationTimer != null) {
            navigationTimer.stop();
        }
        
        navigationTimer = new Timer(TIMER_INTERVAL_MS, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                updateNavigation();
            }
        });
        navigationTimer.start();
    }
    
    /**
     * 更新导航状态
     */
    private void updateNavigation() {
        if (pathPoints == null || pathPoints.size() < 2 || currentPathIndex >= pathPoints.size() - 1) {
            // 路径完成
            stopNavigation();
            JOptionPane.showMessageDialog(null, "导航预览完成", "提示", JOptionPane.INFORMATION_MESSAGE);
            return;
        }
        
        Point2D.Double currentPos = mower.getPosition();
        if (currentPos == null) {
            return;
        }
        
        Point2D.Double targetPoint = pathPoints.get(currentPathIndex + 1);
        
        // 计算到目标点的距离
        double dx = targetPoint.x - currentPos.x;
        double dy = targetPoint.y - currentPos.y;
        double distance = Math.hypot(dx, dy);
        
        // 计算每帧移动的距离(米)
        double moveDistance = currentSpeed * (TIMER_INTERVAL_MS / 1000.0);
        
        if (distance <= moveDistance) {
            // 到达目标点,移动到下一个点
            mower.setPosition(targetPoint.x, targetPoint.y);
            navigationTrack.add(new Point2D.Double(targetPoint.x, targetPoint.y));
            mapRenderer.addNavigationPreviewTrackPoint(targetPoint);
            
            currentPathIndex++;
            
            // 如果还有下一个点,计算方向
            if (currentPathIndex < pathPoints.size() - 1) {
                Point2D.Double nextPoint = pathPoints.get(currentPathIndex + 1);
                double heading = calculateHeading(targetPoint, nextPoint);
                setMowerHeading(heading);
            }
        } else {
            // 向目标点移动
            // 先更新方向,确保车头朝向目标点
            double heading = calculateHeading(currentPos, targetPoint);
            setMowerHeading(heading);
            
            double ratio = moveDistance / distance;
            double newX = currentPos.x + dx * ratio;
            double newY = currentPos.y + dy * ratio;
            
            mower.setPosition(newX, newY);
            navigationTrack.add(new Point2D.Double(newX, newY));
            mapRenderer.addNavigationPreviewTrackPoint(new Point2D.Double(newX, newY));
        }
        
        // 更新速度显示到地图渲染器
        if (mapRenderer != null) {
            mapRenderer.setNavigationPreviewSpeed(currentSpeed);
        }
        
        // 更新地图显示
        mapRenderer.repaint();
        
        // 更新速度显示(如果需要)
        updateSpeedDisplay();
    }
    
    /**
     * 计算两点之间的方向角(度)
     * 图标默认朝上,向右旋转90度车头朝右
     * atan2返回的角度:向右是0度,向上是90度
     * 需要转换为图标旋转角度:向右需要90度,向上需要0度
     */
    private double calculateHeading(Point2D.Double from, Point2D.Double to) {
        double dx = to.x - from.x;
        double dy = to.y - from.y;
        // atan2返回的角度:向右是0度,向上是90度,向左是180度,向下是-90度(270度)
        double atan2Angle = Math.toDegrees(Math.atan2(dy, dx));
        
        // 转换为0-360度范围
        if (atan2Angle < 0) {
            atan2Angle += 360;
        }
        
        // 图标默认朝上(0度),向右旋转90度车头朝右
        // 所以:运动方向向右(0度)→ 需要旋转90度
        //      运动方向向上(90度)→ 需要旋转0度
        //      运动方向向左(180度)→ 需要旋转270度
        //      运动方向向下(270度)→ 需要旋转180度
        // 公式:heading = (90 - atan2Angle + 360) % 360
        double heading = (90.0 - atan2Angle + 360.0) % 360.0;
        
        return heading;
    }
    
    /**
     * 设置割草机方向
     */
    private void setMowerHeading(double headingDegrees) {
        if (mower != null) {
            mower.setHeading(headingDegrees);
        }
    }
    
    /**
     * 加速
     */
    private void speedUp() {
        currentSpeed += SPEED_MULTIPLIER;
        updateSpeedDisplay();
    }
    
    /**
     * 减速
     */
    private void speedDown() {
        if (currentSpeed > 0.1) { // 最小速度0.1米/秒
            currentSpeed -= SPEED_MULTIPLIER;
            if (currentSpeed < 0.1) {
                currentSpeed = 0.1;
            }
        }
        updateSpeedDisplay();
    }
    
    /**
     * 更新速度显示
     */
    private void updateSpeedDisplay() {
        // 可以在地图上显示当前速度
        // 这里暂时不实现,如果需要可以在MapRenderer中添加速度显示
    }
    
    /**
     * 停止导航
     */
    private void stopNavigation() {
        if (navigationTimer != null) {
            navigationTimer.stop();
            navigationTimer = null;
        }
        isNavigating = false;
    }
    
    /**
     * 退出导航预览
     */
    public void exitNavigationPreview() {
        stopNavigation();
        removeNavigationButtons();
        
        // 隐藏导航预览模式标签
        if (shouye != null) {
            shouye.setNavigationPreviewLabelVisible(false);
        }
        
        // 清除导航预览轨迹
        if (mapRenderer != null) {
            mapRenderer.clearNavigationPreviewTrack();
            mapRenderer.setNavigationPreviewSpeed(0.0); // 清除速度显示
            mapRenderer.repaint();
        }
        
        // 恢复地块管理页面
        // 在清空currentDikuai之前保存地块编号,使用final变量以便在lambda中使用
        final String landNumber = (currentDikuai != null) ? currentDikuai.getLandNumber() : null;
        
        isNavigating = false;
        currentDikuai = null;
        
        // 如果有地块编号,显示地块管理页面
        if (landNumber != null && !landNumber.trim().isEmpty()) {
            SwingUtilities.invokeLater(() -> {
                try {
                    Component parent = null;
                    if (shouye != null) {
                        Window owner = SwingUtilities.getWindowAncestor(shouye);
                        if (owner instanceof Component) {
                            parent = (Component) owner;
                        } else {
                            parent = shouye;
                        }
                    }
                    Dikuaiguanli.showDikuaiManagement(parent, landNumber);
                } catch (Exception e) {
                    System.err.println("显示地块管理页面失败: " + e.getMessage());
                    e.printStackTrace();
                }
            });
        }
    }
    
    /**
     * 检查是否正在导航预览
     */
    public boolean isNavigating() {
        return isNavigating;
    }
}