From c498385fb7e372d13e2ee76d7b54ae2381728082 Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期三, 17 十二月 2025 19:35:57 +0800
Subject: [PATCH] 新增了测量模式
---
src/zhuye/Shouye.java | 4799 ++++++++++++++++++++++++++++++++++++++++++++++-------------
1 files changed, 3,719 insertions(+), 1,080 deletions(-)
diff --git a/src/zhuye/Shouye.java b/src/zhuye/Shouye.java
index 14565cb..30c1444 100644
--- a/src/zhuye/Shouye.java
+++ b/src/zhuye/Shouye.java
@@ -1,1107 +1,3746 @@
package zhuye;
import javax.swing.*;
+import javax.swing.Timer;
import baseStation.BaseStation;
+import set.Setsys;
import baseStation.BaseStationDialog;
import java.awt.*;
import java.awt.event.*;
+import chuankou.dellmessage;
+import chuankou.sendmessage;
+import chuankou.SerialPortService;
import dikuai.Dikuai;
import dikuai.Dikuaiguanli;
import dikuai.addzhangaiwu;
import gecaoji.Device;
+import gecaoji.Gecaoji;
+import gecaoji.MowerBoundaryChecker;
import set.Sets;
+import set.debug;
import udpdell.UDPServer;
+import zhangaiwu.AddDikuai;
+import yaokong.Control04;
+import yaokong.RemoteControlDialog;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+import java.awt.geom.Point2D;
/**
* 棣栭〉鐣岄潰 - 閫傞厤6.5瀵哥珫灞忥紝浣跨敤鐙珛鐨凪apRenderer杩涜缁樺埗
*/
public class Shouye extends JPanel {
- private static final long serialVersionUID = 1L;
- private static Shouye instance;
+ private static final long serialVersionUID = 1L;
+ private static Shouye instance;
+
+ // 涓婚棰滆壊
+ private final Color THEME_COLOR = new Color(46, 139, 87);
+ private final Color THEME_HOVER_COLOR = new Color(30, 107, 69);
+ private final Color BACKGROUND_COLOR = new Color(250, 250, 250);
+ private final Color PANEL_BACKGROUND = new Color(255, 255, 255);
+ private final Color STATUS_PAUSE_COLOR = new Color(255, 165, 0);
+
+ // 缁勪欢
+ private JPanel headerPanel;
+ private JPanel mainContentPanel;
+ private JPanel controlPanel;
+ private JPanel navigationPanel;
+ private JPanel visualizationPanel;
+
+ // 鎸夐挳
+ private JButton startBtn;
+ private JButton stopBtn;
+ private JButton legendBtn;
+ private JButton remoteBtn;
+ private JButton areaSelectBtn;
+ private JButton baseStationBtn;
+ private JButton bluetoothBtn;
+ private JLabel dataPacketCountLabel;
+ private JLabel mowerSpeedValueLabel;
+ private JLabel mowerSpeedUnitLabel;
+ private JLabel mowingProgressLabel;
+ private gpszhuangtai fixQualityIndicator;
+
+ // 瀵艰埅鎸夐挳
+ private JButton homeNavBtn;
+ private JButton areasNavBtn;
+ private JButton settingsNavBtn;
+
+ // 鐘舵�佹樉绀�
+ private JLabel statusLabel;
+ private JLabel speedLabel; // 閫熷害鏄剧ず鏍囩
+ private JLabel areaNameLabel;
+
+ // 杈圭晫璀﹀憡鐩稿叧
+ private Timer boundaryWarningTimer; // 杈圭晫璀﹀憡妫�鏌ュ畾鏃跺櫒
+ private Timer warningBlinkTimer; // 璀﹀憡鍥炬爣闂儊瀹氭椂鍣�
+ private boolean isMowerOutsideBoundary = false; // 鍓茶崏鏈烘槸鍚﹀湪杈圭晫澶�
+ private boolean warningIconVisible = true; // 璀﹀憡鍥炬爣鏄剧ず鐘舵�侊紙鐢ㄤ簬闂儊锛�
+
+ // 浠ュ壊鑽夋満涓轰腑蹇冭鍥炬ā寮�
+ private boolean centerOnMowerMode = false; // 鏄惁澶勪簬浠ュ壊鑽夋満涓轰腑蹇冪殑妯″紡
+
+ // 褰撳墠閫変腑鐨勫鑸寜閽�
+ private JButton currentNavButton;
+
+ // 瀵硅瘽妗嗗紩鐢�
+ private LegendDialog legendDialog;
+ // 浣跨敤瀹屽叏闄愬畾鍚嶄互閬垮厤涓庡悓鍖呯殑 RemoteControlDialog 鍐茬獊锛岀‘淇濅娇鐢� yaokong 鍖呬笅鐨勫疄鐜�
+ private yaokong.RemoteControlDialog remoteDialog;
+ private AreaSelectionDialog areaDialog;
+ private BaseStationDialog baseStationDialog;
+ private Sets settingsDialog;
+ private BaseStation baseStation;
+
+ // 鍦板浘娓叉煋鍣�
+ private MapRenderer mapRenderer;
+
+ private final Consumer<String> serialLineListener = line -> {
+ SwingUtilities.invokeLater(() -> {
+ updateDataPacketCountLabel();
+ // 濡傛灉鏀跺埌$GNGGA鏁版嵁锛岀珛鍗虫洿鏂版嫋灏�
+ if (line != null && line.trim().startsWith("$GNGGA")) {
+ if (mapRenderer != null) {
+ mapRenderer.forceUpdateIdleMowerTrail();
+ }
+ }
+ });
+ };
+ private static final int FLOAT_ICON_SIZE = 32;
+ private JButton endDrawingButton;
+ private JButton drawingPauseButton;
+ private JPanel floatingButtonPanel;
+ private JPanel floatingButtonColumn;
+ private Runnable endDrawingCallback;
+ private JButton pathPreviewReturnButton;
+ private boolean pathPreviewActive;
+ private Runnable pathPreviewReturnAction;
+ private String previewRestoreLandNumber;
+ private String previewRestoreLandName;
+ private boolean drawingPaused;
+ private ImageIcon pauseIcon;
+ private ImageIcon pauseActiveIcon;
+ private ImageIcon endIcon;
+ private ImageIcon bluetoothIcon;
+ private ImageIcon bluetoothLinkedIcon;
+ private JPanel circleGuidancePanel;
+ private JLabel circleGuidanceLabel;
+ private JButton circleGuidancePrimaryButton;
+ private JButton circleGuidanceSecondaryButton;
+ private int circleGuidanceStep;
+ private JDialog circleGuidanceDialog;
+ private boolean circleDialogMode;
+ private ComponentAdapter circleDialogOwnerAdapter;
+ private static final double METERS_PER_DEGREE_LAT = 111320.0d;
+ private static final double HANDHELD_DUPLICATE_THRESHOLD_METERS = 0.01d;
+ private final List<double[]> circleCapturedPoints = new ArrayList<>();
+ private double[] circleBaseLatLon;
+ private Timer circleDataMonitor;
+ private Coordinate lastCapturedCoordinate;
+ private boolean handheldCaptureActive;
+ private int handheldCapturedPoints;
+ private final List<Point2D.Double> handheldTemporaryPoints = new ArrayList<>();
+ private final List<Point2D.Double> mowerTemporaryPoints = new ArrayList<>();
+ private enum BoundaryCaptureMode { NONE, HANDHELD, MOWER }
+ private BoundaryCaptureMode activeBoundaryMode = BoundaryCaptureMode.NONE;
+ private boolean mowerBoundaryCaptureActive;
+ private Timer mowerBoundaryMonitor;
+ private Coordinate lastMowerCoordinate;
+ private double[] mowerBaseLatLon;
+ private boolean startButtonShowingPause = true;
+ private boolean stopButtonActive = false;
+ private boolean bluetoothConnected = false;
+ private Timer mowerSpeedRefreshTimer;
+ private boolean drawingControlModeActive;
+ private boolean storedStartButtonShowingPause;
+ private boolean storedStopButtonActive;
+ private String storedStatusBeforeDrawing;
+ private boolean handheldCaptureInlineUiActive;
+ private Timer handheldCaptureStatusTimer;
+ private String handheldCaptureStoredStatusText;
+ private Color handheldStartButtonOriginalBackground;
+ private Color handheldStartButtonOriginalForeground;
+ private Color handheldStopButtonOriginalBackground;
+ private Color handheldStopButtonOriginalForeground;
+
+ public Shouye() {
+ instance = this;
+ baseStation = new BaseStation();
+ baseStation.load();
+ dellmessage.registerLineListener(serialLineListener);
+ initializeUI();
+ setupEventHandlers();
+ scheduleIdentifierCheck();
+ }
+
+ public static Shouye getInstance() {
+ return instance;
+ }
+
+ private void initializeUI() {
+ setLayout(new BorderLayout());
+ setBackground(BACKGROUND_COLOR);
+
+ // 鍒涘缓鍚勪釜闈㈡澘
+ createHeaderPanel();
+ createMainContentPanel();
+ createControlPanel();
+ createNavigationPanel();
+
+ // 娣诲姞鍒颁富闈㈡澘
+ add(headerPanel, BorderLayout.NORTH);
+ add(mainContentPanel, BorderLayout.CENTER);
+ add(controlPanel, BorderLayout.SOUTH);
+
+ // 鍒濆鍖栧湴鍥炬覆鏌撳櫒
+ mapRenderer = new MapRenderer(visualizationPanel);
+ applyIdleTrailDurationFromSettings();
+
+ // 鍒濆鍖栧璇濇寮曠敤涓簄ull锛屽欢杩熷垱寤�
+ legendDialog = null;
+ remoteDialog = null;
+ areaDialog = null;
+ baseStationDialog = null;
+ settingsDialog = null;
+
+ // 璁剧疆榛樿鐘舵��
+ setNavigationActive(homeNavBtn);
+
+ initializeDefaultAreaSelection();
+ refreshMapForSelectedArea();
+
+ // 鍚姩杈圭晫璀﹀憡妫�鏌ュ畾鏃跺櫒
+ startBoundaryWarningTimer();
+ }
+
+ private void scheduleIdentifierCheck() {
+ HierarchyListener listener = new HierarchyListener() {
+ @Override
+ public void hierarchyChanged(HierarchyEvent e) {
+ if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && Shouye.this.isShowing()) {
+ Shouye.this.removeHierarchyListener(this);
+ SwingUtilities.invokeLater(() -> {
+ Shouye.this.checkIdentifiersAndPromptIfNeeded();
+ Shouye.this.showInitialMowerSelfCheckDialogIfNeeded();
+ // 璁剧疆绐楀彛鍏抽棴鐩戝惉鍣紝鍦ㄥ叧闂椂淇濆瓨缂╂斁姣斾緥
+ setupWindowCloseListener();
+ });
+ }
+ }
+ };
+ addHierarchyListener(listener);
+ }
+
+ /**
+ * 璁剧疆绐楀彛鍏抽棴鐩戝惉鍣紝鍦ㄧ獥鍙e叧闂椂淇濆瓨褰撳墠缂╂斁姣斾緥
+ */
+ private void setupWindowCloseListener() {
+ Window window = SwingUtilities.getWindowAncestor(this);
+ if (window != null && window instanceof JFrame) {
+ JFrame frame = (JFrame) window;
+ frame.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent e) {
+ // 淇濆瓨褰撳墠缂╂斁姣斾緥
+ saveCurrentScale();
+ // 鍋滄杈圭晫璀﹀憡瀹氭椂鍣�
+ if (boundaryWarningTimer != null && boundaryWarningTimer.isRunning()) {
+ boundaryWarningTimer.stop();
+ }
+ // 鍋滄闂儊瀹氭椂鍣�
+ if (warningBlinkTimer != null && warningBlinkTimer.isRunning()) {
+ warningBlinkTimer.stop();
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * 淇濆瓨褰撳墠鍦板浘缂╂斁姣斾緥鍜岃鍥句腑蹇冨潗鏍囧埌閰嶇疆鏂囦欢
+ */
+ public void saveCurrentScale() {
+ if (mapRenderer != null) {
+ double currentScale = mapRenderer.getScale();
+ double translateX = mapRenderer.getTranslateX();
+ double translateY = mapRenderer.getTranslateY();
+ Setsys setsys = new Setsys();
+ // 淇濈暀2浣嶅皬鏁�
+ setsys.updateProperty("mapScale", String.format("%.2f", currentScale));
+ setsys.updateProperty("viewCenterX", String.format("%.2f", translateX));
+ setsys.updateProperty("viewCenterY", String.format("%.2f", translateY));
+ }
+ }
+
+ /**
+ * 鍚姩杈圭晫璀﹀憡妫�鏌ュ畾鏃跺櫒
+ */
+ private void startBoundaryWarningTimer() {
+ // 杈圭晫妫�鏌ュ畾鏃跺櫒锛氭瘡500ms妫�鏌ヤ竴娆″壊鑽夋満鏄惁鍦ㄨ竟鐣屽唴
+ boundaryWarningTimer = new Timer(500, e -> {
+ checkMowerBoundaryStatus();
+ // 鍚屾椂鏇存柊钃濈墮鍥炬爣鐘舵��
+ updateBluetoothButtonIcon();
+ });
+ boundaryWarningTimer.setInitialDelay(0);
+ boundaryWarningTimer.start();
+
+ // 闂儊瀹氭椂鍣細姣�1绉掑垏鎹竴娆¤鍛婂浘鏍囨樉绀虹姸鎬�
+ warningBlinkTimer = new Timer(1000, e -> {
+ if (isMowerOutsideBoundary) {
+ warningIconVisible = !warningIconVisible;
+ if (visualizationPanel != null) {
+ visualizationPanel.repaint();
+ }
+ }
+ });
+ warningBlinkTimer.setInitialDelay(0);
+ warningBlinkTimer.start();
+ }
+
+ /**
+ * 鍒囨崲浠ュ壊鑽夋満涓轰腑蹇冪殑妯″紡
+ */
+ private void toggleCenterOnMowerMode() {
+ centerOnMowerMode = !centerOnMowerMode;
+
+ if (centerOnMowerMode) {
+ // 寮�鍚ā寮忥細绔嬪嵆灏嗚鍥句腑蹇冪Щ鍔ㄥ埌鍓茶崏鏈轰綅缃�
+ updateViewToCenterOnMower();
+ }
+ // 鍏抽棴妯″紡鏃朵笉闇�瑕佸仛浠讳綍鎿嶄綔锛岀敤鎴峰凡缁忓彲浠ヨ嚜鐢辩Щ鍔ㄥ湴鍥�
+
+ // 鏇存柊鍥炬爣鏄剧ず锛堥噸缁樹互鍒囨崲鍥炬爣锛�
+ if (visualizationPanel != null) {
+ visualizationPanel.repaint();
+ }
+ }
+
+ /**
+ * 鏇存柊瑙嗗浘涓績鍒板壊鑽夋満浣嶇疆
+ */
+ private void updateViewToCenterOnMower() {
+ if (mapRenderer == null) {
+ return;
+ }
+
+ Gecaoji mower = mapRenderer.getMower();
+ if (mower != null && mower.hasValidPosition()) {
+ Point2D.Double mowerPosition = mower.getPosition();
+ if (mowerPosition != null) {
+ // 鑾峰彇褰撳墠缂╂斁姣斾緥
+ double currentScale = mapRenderer.getScale();
+ // 璁剧疆瑙嗗浘鍙樻崲锛屼娇鍓茶崏鏈轰綅缃搴斿埌灞忓箷涓績
+ // translateX = -mowerX, translateY = -mowerY 鍙互璁╁壊鑽夋満鍦ㄥ睆骞曚腑蹇�
+ mapRenderer.setViewTransform(currentScale, -mowerPosition.x, -mowerPosition.y);
+ }
+ }
+ }
+
+ /**
+ * 妫�鏌ュ壊鑽夋満杈圭晫鐘舵��
+ */
+ private void checkMowerBoundaryStatus() {
+ // 濡傛灉澶勪簬浠ュ壊鑽夋満涓轰腑蹇冪殑妯″紡锛屽疄鏃舵洿鏂拌鍥句腑蹇�
+ if (centerOnMowerMode) {
+ updateViewToCenterOnMower();
+ }
+
+ // 妫�鏌ユ槸鍚﹀湪浣滀笟涓�
+ if (statusLabel == null || !"浣滀笟涓�".equals(statusLabel.getText())) {
+ // 涓嶅湪浣滀笟涓紝閲嶇疆鐘舵��
+ if (isMowerOutsideBoundary) {
+ isMowerOutsideBoundary = false;
+ warningIconVisible = true;
+ if (visualizationPanel != null) {
+ visualizationPanel.repaint();
+ }
+ }
+ return;
+ }
+
+ // 鍦ㄤ綔涓氫腑锛屾鏌ユ槸鍚﹀湪杈圭晫鍐�
+ if (mapRenderer == null) {
+ return;
+ }
+
+ // 鑾峰彇褰撳墠杈圭晫
+ List<Point2D.Double> boundary = mapRenderer.getCurrentBoundary();
+ if (boundary == null || boundary.size() < 3) {
+ // 娌℃湁杈圭晫锛岄噸缃姸鎬�
+ if (isMowerOutsideBoundary) {
+ isMowerOutsideBoundary = false;
+ warningIconVisible = true;
+ if (visualizationPanel != null) {
+ visualizationPanel.repaint();
+ }
+ }
+ return;
+ }
+
+ // 鑾峰彇鍓茶崏鏈轰綅缃�
+ Gecaoji mower = mapRenderer.getMower();
+ if (mower == null || !mower.hasValidPosition()) {
+ // 鏃犳硶鑾峰彇浣嶇疆锛岄噸缃姸鎬�
+ if (isMowerOutsideBoundary) {
+ isMowerOutsideBoundary = false;
+ warningIconVisible = true;
+ if (visualizationPanel != null) {
+ visualizationPanel.repaint();
+ }
+ }
+ return;
+ }
+
+ Point2D.Double mowerPosition = mower.getPosition();
+ if (mowerPosition == null) {
+ return;
+ }
+
+ // 浣跨敤 MowerBoundaryChecker 妫�鏌ユ槸鍚﹀湪杈圭晫鍐�
+ boolean isInside = MowerBoundaryChecker.isInsideBoundaryPoints(
+ boundary,
+ mowerPosition.x,
+ mowerPosition.y
+ );
+
+ // 鏇存柊鐘舵��
+ boolean wasOutside = isMowerOutsideBoundary;
+ isMowerOutsideBoundary = !isInside;
+
+ // 濡傛灉鐘舵�佹敼鍙橈紝绔嬪嵆閲嶇粯
+ if (wasOutside != isMowerOutsideBoundary) {
+ warningIconVisible = true;
+ if (visualizationPanel != null) {
+ visualizationPanel.repaint();
+ }
+ }
+ }
+
+ private void showInitialMowerSelfCheckDialogIfNeeded() {
+ // 宸茬Щ闄よ繘鍏ヤ富椤垫椂鐨勮嚜妫�鎻愮ず锛堟寜鐢ㄦ埛瑕佹眰鍒犻櫎锛�
+ // 浠ュ墠杩欓噷浼氳皟鐢� zijian.showInitialPromptIfNeeded(...) 灞曠ず鑷瀵硅瘽妗嗭紝鐜板凡绂佺敤銆�
+ }
+
+ private void applyIdleTrailDurationFromSettings() {
+ if (mapRenderer == null) {
+ return;
+ }
+ int durationSeconds = MapRenderer.DEFAULT_IDLE_TRAIL_DURATION_SECONDS;
+ String configuredValue = Setsys.getPropertyValue("idleTrailDurationSeconds");
+ if (configuredValue != null) {
+ String trimmed = configuredValue.trim();
+ if (!trimmed.isEmpty()) {
+ try {
+ int parsed = Integer.parseInt(trimmed);
+ if (parsed >= 5 && parsed <= 600) {
+ durationSeconds = parsed;
+ }
+ } catch (NumberFormatException ignored) {
+ durationSeconds = MapRenderer.DEFAULT_IDLE_TRAIL_DURATION_SECONDS;
+ }
+ }
+ }
+ mapRenderer.setIdleTrailDurationSeconds(durationSeconds);
+
+ // 搴旂敤杈圭晫璺濈鏄剧ず璁剧疆鍜屾祴閲忔ā寮忚缃�
+ Setsys setsys = new Setsys();
+ setsys.initializeFromProperties();
+ mapRenderer.setBoundaryLengthVisible(setsys.isBoundaryLengthVisible());
+ // 鍒濆鍖栨祴閲忔ā寮�
+ boolean measurementEnabled = setsys.isMeasurementModeEnabled();
+ mapRenderer.setMeasurementMode(measurementEnabled);
+ if (measurementEnabled) {
+ celiangmoshi.start();
+ } else {
+ celiangmoshi.stop();
+ }
+ }
+
+ private void createHeaderPanel() {
+ headerPanel = new JPanel(new BorderLayout());
+ headerPanel.setBackground(PANEL_BACKGROUND);
+ headerPanel.setBorder(BorderFactory.createEmptyBorder(15, 20, 15, 20));
+ headerPanel.setPreferredSize(new Dimension(0, 80));
+
+ // 宸︿晶淇℃伅鍖哄煙锛堝瀭鐩存帓鍒楋細鍦板潡鍚嶇О鍦ㄤ笂锛岀姸鎬佽鍦ㄤ笅锛�
+ JPanel leftInfoPanel = new JPanel();
+ leftInfoPanel.setBackground(PANEL_BACKGROUND);
+ leftInfoPanel.setLayout(new BoxLayout(leftInfoPanel, BoxLayout.Y_AXIS));
+ // 淇濊瘉瀛愮粍浠跺乏瀵归綈
+ leftInfoPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ areaNameLabel = new JLabel("鏈�夋嫨鍦板潡");
+ areaNameLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 18));
+ areaNameLabel.setForeground(Color.BLACK);
+ areaNameLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ areaNameLabel.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ Dikuaiguanli.showDikuaiManagement(Shouye.this, null);
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ areaNameLabel.setForeground(THEME_COLOR);
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ areaNameLabel.setForeground(Color.BLACK);
+ }
+ });
+
+ statusLabel = new JLabel("寰呮満");
+ statusLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+ statusLabel.setForeground(Color.GRAY);
+ statusLabel.addPropertyChangeListener("text", evt -> {
+ Object newValue = evt.getNewValue();
+ applyStatusLabelColor(newValue instanceof String ? (String) newValue : null);
+ });
+ applyStatusLabelColor(statusLabel.getText());
+
+ // 娣诲姞閫熷害鏄剧ず鏍囩
+ speedLabel = new JLabel("");
+ speedLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+ speedLabel.setForeground(Color.GRAY);
+ speedLabel.setVisible(false); // 榛樿闅愯棌
+
+ // 灏嗙姸鎬佷笌閫熷害鏀惧湪鍚屼竴琛岋紝鏄剧ず鍦ㄥ湴鍧楀悕绉颁笅闈竴琛�
+ JPanel statusRow = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 0));
+ statusRow.setOpaque(false);
+ statusRow.add(statusLabel);
+ statusRow.add(speedLabel);
+
+ // 宸﹀榻愭爣绛句笌鐘舵�佽锛岀‘淇濆畠浠湪 BoxLayout 涓潬宸︽樉绀�
+ areaNameLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ statusRow.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ leftInfoPanel.add(areaNameLabel);
+ leftInfoPanel.add(statusRow);
+
+ // 鍙充晶鎿嶄綔鍖哄煙
+ JPanel rightActionPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+ rightActionPanel.setBackground(PANEL_BACKGROUND);
+
+ bluetoothBtn = createBluetoothButton();
+
+ // 淇敼璁剧疆鎸夐挳锛氫娇鐢ㄥ浘鐗囨浛浠nicode鍥炬爣
+ JButton settingsBtn = new JButton();
+ try {
+ ImageIcon settingsIcon = new ImageIcon("image/sets.png");
+ // 璋冩暣鍥剧墖澶у皬浠ラ�傚簲鎸夐挳
+ Image scaledImage = settingsIcon.getImage().getScaledInstance(30, 30, Image.SCALE_SMOOTH);
+ settingsBtn.setIcon(new ImageIcon(scaledImage));
+ } catch (Exception e) {
+ // 濡傛灉鍥剧墖鍔犺浇澶辫触锛屼娇鐢ㄩ粯璁ゆ枃鏈�
+ settingsBtn.setText("璁剧疆");
+ System.err.println("鏃犳硶鍔犺浇璁剧疆鍥炬爣: " + e.getMessage());
+ }
+ settingsBtn.setPreferredSize(new Dimension(40, 40));
+ settingsBtn.setBackground(PANEL_BACKGROUND);
+ settingsBtn.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ settingsBtn.setFocusPainted(false);
+
+ // 娣诲姞鎮仠鏁堟灉
+ settingsBtn.addMouseListener(new MouseAdapter() {
+ public void mouseEntered(MouseEvent e) {
+ settingsBtn.setBackground(new Color(240, 240, 240));
+ }
+ public void mouseExited(MouseEvent e) {
+ settingsBtn.setBackground(PANEL_BACKGROUND);
+ }
+ });
+
+ // 娣诲姞璁剧疆鎸夐挳浜嬩欢
+ settingsBtn.addActionListener(e -> showSettingsDialog());
+
+ rightActionPanel.add(bluetoothBtn);
+ rightActionPanel.add(settingsBtn);
+
+ headerPanel.add(leftInfoPanel, BorderLayout.WEST);
+ headerPanel.add(rightActionPanel, BorderLayout.EAST);
+ }
+
+ private void createMainContentPanel() {
+ mainContentPanel = new JPanel(new BorderLayout());
+ mainContentPanel.setBackground(BACKGROUND_COLOR);
+
+ // 鍙鍖栧尯鍩� - 浣跨敤MapRenderer杩涜缁樺埗
+ visualizationPanel = new JPanel() {
+ private ImageIcon gecaojiIcon1 = null; // 榛樿鍥炬爣
+ private ImageIcon gecaojiIcon2 = null; // 浠ュ壊鑽夋満涓轰腑蹇冩ā寮忓浘鏍�
+ private static final int GECAOJI_ICON_X = 37;
+ private static final int GECAOJI_ICON_Y = 10;
+ private static final int GECAOJI_ICON_SIZE = 20;
+
+ {
+ // 鍔犺浇鍓茶崏鏈哄浘鏍囷紝澶у皬20x20鍍忕礌
+ gecaojiIcon1 = loadScaledIcon("image/gecaojishijiao1.png", GECAOJI_ICON_SIZE, GECAOJI_ICON_SIZE);
+ gecaojiIcon2 = loadScaledIcon("image/gecaojishijiao2.png", GECAOJI_ICON_SIZE, GECAOJI_ICON_SIZE);
+ }
+
+ /**
+ * 妫�鏌ラ紶鏍囦綅缃槸鍚﹀湪鍓茶崏鏈哄浘鏍囧尯鍩熷唴
+ */
+ private boolean isMouseOnGecaojiIcon(Point mousePoint) {
+ return mousePoint.x >= GECAOJI_ICON_X &&
+ mousePoint.x <= GECAOJI_ICON_X + GECAOJI_ICON_SIZE &&
+ mousePoint.y >= GECAOJI_ICON_Y &&
+ mousePoint.y <= GECAOJI_ICON_Y + GECAOJI_ICON_SIZE;
+ }
+
+ @Override
+ public String getToolTipText(MouseEvent event) {
+ // 濡傛灉榧犳爣鍦ㄥ壊鑽夋満鍥炬爣鍖哄煙鍐咃紝鏄剧ず鎻愮ず鏂囧瓧
+ if (isMouseOnGecaojiIcon(event.getPoint())) {
+ // 鏍规嵁褰撳墠妯″紡鏄剧ず涓嶅悓鐨勬彁绀烘枃瀛�
+ return centerOnMowerMode ? "鍙栨秷浠ュ壊鑽夋満涓轰腑蹇�" : "浠ュ壊鑽夋満涓轰腑蹇�";
+ }
+ // 涓嶅湪鍥炬爣涓婃椂杩斿洖null锛屼笉鏄剧ず宸ュ叿鎻愮ず妗�
+ return null;
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ // 濮旀墭缁橫apRenderer杩涜缁樺埗
+ if (mapRenderer != null) {
+ mapRenderer.renderMap(g);
+ }
+
+ // 妫�鏌ユ槸鍚﹂渶瑕佹樉绀鸿鍛婂浘鏍�
+ if (isMowerOutsideBoundary && warningIconVisible) {
+ // 缁樺埗绾㈣壊涓夎褰㈣鍛婂浘鏍囷紙甯﹀徆鍙凤級
+ drawWarningIcon(g, GECAOJI_ICON_X, GECAOJI_ICON_Y, GECAOJI_ICON_SIZE);
+ } else {
+ // 鏍规嵁妯″紡閫夋嫨涓嶅悓鐨勫浘鏍�
+ ImageIcon iconToDraw = centerOnMowerMode ? gecaojiIcon2 : gecaojiIcon1;
+ if (iconToDraw != null) {
+ // 缁樺埗鍓茶崏鏈哄浘鏍�
+ // 姘村钩鏂瑰悜涓庨�熷害鎸囩ず鍣ㄥ榻愶紙x=37锛�
+ // 鍨傜洿鏂瑰悜涓庡崼鏄熺姸鎬佸浘鏍囧榻愶紙y=10锛岄�熷害鎸囩ず鍣ㄩ潰鏉块《閮ㄨ竟璺�10鍍忕礌锛屼娇鍥炬爣涓績瀵归綈锛�
+ g.drawImage(iconToDraw.getImage(), GECAOJI_ICON_X, GECAOJI_ICON_Y, null);
+ }
+ }
+ }
+
+ /**
+ * 缁樺埗绾㈣壊涓夎褰㈣鍛婂浘鏍囷紙甯﹀徆鍙凤級
+ */
+ private void drawWarningIcon(Graphics g, int x, int y, int size) {
+ Graphics2D g2d = (Graphics2D) g.create();
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+
+ // 缁樺埗绾㈣壊涓夎褰�
+ int[] xPoints = {x + size / 2, x, x + size};
+ int[] yPoints = {y, y + size, y + size};
+ g2d.setColor(Color.RED);
+ g2d.fillPolygon(xPoints, yPoints, 3);
+
+ // 缁樺埗鐧借壊杈规
+ g2d.setColor(Color.WHITE);
+ g2d.setStroke(new BasicStroke(1.5f));
+ g2d.drawPolygon(xPoints, yPoints, 3);
+
+ // 缁樺埗鐧借壊鍙瑰彿
+ g2d.setColor(Color.WHITE);
+ g2d.setFont(new Font("Arial", Font.BOLD, size * 3 / 4));
+ FontMetrics fm = g2d.getFontMetrics();
+ String exclamation = "!";
+ int textWidth = fm.stringWidth(exclamation);
+ int textHeight = fm.getAscent();
+ g2d.drawString(exclamation, x + (size - textWidth) / 2, y + (size + textHeight) / 2 - 2);
+
+ g2d.dispose();
+ }
+ };
+ visualizationPanel.setLayout(new BorderLayout());
+
+ // 娣诲姞榧犳爣鐐瑰嚮鐩戝惉鍣紝妫�娴嬫槸鍚︾偣鍑讳簡鍓茶崏鏈哄浘鏍�
+ visualizationPanel.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (SwingUtilities.isLeftMouseButton(e)) {
+ Point clickPoint = e.getPoint();
+ // 妫�鏌ユ槸鍚︾偣鍑讳簡鍓茶崏鏈哄浘鏍囧尯鍩燂紙37, 10, 20, 20锛�
+ if (clickPoint.x >= 37 && clickPoint.x <= 57 &&
+ clickPoint.y >= 10 && clickPoint.y <= 30) {
+ // 鍒囨崲浠ュ壊鑽夋満涓轰腑蹇冪殑妯″紡
+ toggleCenterOnMowerMode();
+ }
+ }
+ }
+ });
+
+ JPanel speedIndicatorPanel = createSpeedIndicatorPanel();
+ visualizationPanel.add(speedIndicatorPanel, BorderLayout.NORTH);
+
+ // 鍒涘缓鍔熻兘鎸夐挳闈㈡澘
+ JPanel functionButtonsPanel = new JPanel();
+ functionButtonsPanel.setLayout(new BoxLayout(functionButtonsPanel, BoxLayout.Y_AXIS));
+ functionButtonsPanel.setOpaque(false);
+ functionButtonsPanel.setBorder(BorderFactory.createEmptyBorder(20, 5, 0, 0)); // 宸﹁竟璺濇敼涓�5鍍忕礌
+
+ legendBtn = createFunctionButton("鍥句緥", "馃摉");
+ baseStationBtn = createFunctionButton("鍩哄噯绔�", "馃摗"); // 璋冩暣鍒扮浜屼綅
+ areaSelectBtn = createFunctionButton("鍦板潡", "馃尶"); // 璋冩暣鍒扮涓変綅
+ remoteBtn = createFunctionButton("閬ユ帶", "馃幃"); // 璋冩暣鍒版渶鍚�
+
+ functionButtonsPanel.add(legendBtn);
+ functionButtonsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+ functionButtonsPanel.add(baseStationBtn);
+ functionButtonsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+ functionButtonsPanel.add(areaSelectBtn);
+ functionButtonsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+ functionButtonsPanel.add(remoteBtn);
+
+ visualizationPanel.add(functionButtonsPanel, BorderLayout.WEST);
+
+ JPanel zoomControlPanel = createZoomControlPanel();
+ visualizationPanel.add(zoomControlPanel, BorderLayout.EAST);
+
+ mainContentPanel.add(visualizationPanel, BorderLayout.CENTER);
+
+ startMowerSpeedUpdates();
+ }
+
+ private JPanel createZoomControlPanel() {
+ JPanel container = new JPanel(new BorderLayout());
+ container.setOpaque(false);
+ container.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 20));
+
+ JPanel alignmentPanel = new JPanel();
+ alignmentPanel.setOpaque(false);
+ alignmentPanel.setLayout(new BoxLayout(alignmentPanel, BoxLayout.Y_AXIS));
+ alignmentPanel.add(Box.createVerticalGlue());
+
+ JPanel buttonStack = new JPanel();
+ buttonStack.setOpaque(false);
+ buttonStack.setLayout(new BoxLayout(buttonStack, BoxLayout.Y_AXIS));
+
+ JButton zoomInButton = createZoomButton("+");
+ JButton zoomOutButton = createZoomButton("-");
+
+ AtomicBoolean skipZoomInClick = new AtomicBoolean(false);
+ AtomicBoolean skipZoomOutClick = new AtomicBoolean(false);
+
+ Timer zoomInHoldTimer = new Timer(120, event -> {
+ if (mapRenderer == null || !zoomInButton.getModel().isPressed()) {
+ ((Timer) event.getSource()).stop();
+ return;
+ }
+ if (!mapRenderer.canZoomIn()) {
+ ((Timer) event.getSource()).stop();
+ return;
+ }
+ skipZoomInClick.set(true);
+ mapRenderer.zoomInFromCenter();
+ if (!mapRenderer.canZoomIn()) {
+ ((Timer) event.getSource()).stop();
+ }
+ });
+ zoomInHoldTimer.setInitialDelay(180);
+ zoomInHoldTimer.setDelay(120);
+
+ Timer zoomOutHoldTimer = new Timer(120, event -> {
+ if (mapRenderer == null || !zoomOutButton.getModel().isPressed()) {
+ ((Timer) event.getSource()).stop();
+ return;
+ }
+ if (!mapRenderer.canZoomOut()) {
+ ((Timer) event.getSource()).stop();
+ return;
+ }
+ skipZoomOutClick.set(true);
+ mapRenderer.zoomOutFromCenter();
+ if (!mapRenderer.canZoomOut()) {
+ ((Timer) event.getSource()).stop();
+ }
+ });
+ zoomOutHoldTimer.setInitialDelay(180);
+ zoomOutHoldTimer.setDelay(120);
+
+ zoomInButton.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mousePressed(MouseEvent e) {
+ if (!SwingUtilities.isLeftMouseButton(e) || mapRenderer == null || !zoomInButton.isEnabled()) {
+ return;
+ }
+ if (mapRenderer.canZoomIn()) {
+ skipZoomInClick.set(true);
+ mapRenderer.zoomInFromCenter();
+ if (mapRenderer.canZoomIn()) {
+ zoomInHoldTimer.restart();
+ } else {
+ zoomInHoldTimer.stop();
+ }
+ } else {
+ skipZoomInClick.set(false);
+ zoomInHoldTimer.stop();
+ }
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ zoomInHoldTimer.stop();
+ if (!zoomInButton.contains(e.getPoint())) {
+ skipZoomInClick.set(false);
+ }
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ if (!zoomInButton.getModel().isPressed()) {
+ zoomInHoldTimer.stop();
+ }
+ }
+ });
+
+ zoomOutButton.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mousePressed(MouseEvent e) {
+ if (!SwingUtilities.isLeftMouseButton(e) || mapRenderer == null || !zoomOutButton.isEnabled()) {
+ return;
+ }
+ if (mapRenderer.canZoomOut()) {
+ skipZoomOutClick.set(true);
+ mapRenderer.zoomOutFromCenter();
+ if (mapRenderer.canZoomOut()) {
+ zoomOutHoldTimer.restart();
+ } else {
+ zoomOutHoldTimer.stop();
+ }
+ } else {
+ skipZoomOutClick.set(false);
+ zoomOutHoldTimer.stop();
+ }
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ zoomOutHoldTimer.stop();
+ if (!zoomOutButton.contains(e.getPoint())) {
+ skipZoomOutClick.set(false);
+ }
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ if (!zoomOutButton.getModel().isPressed()) {
+ zoomOutHoldTimer.stop();
+ }
+ }
+ });
+
+ zoomInButton.addActionListener(e -> {
+ if (skipZoomInClick.getAndSet(false)) {
+ return;
+ }
+ if (mapRenderer != null) {
+ mapRenderer.zoomInFromCenter();
+ }
+ });
+
+ zoomOutButton.addActionListener(e -> {
+ if (skipZoomOutClick.getAndSet(false)) {
+ return;
+ }
+ if (mapRenderer != null) {
+ mapRenderer.zoomOutFromCenter();
+ }
+ });
+
+ buttonStack.add(zoomInButton);
+ buttonStack.add(Box.createRigidArea(new Dimension(0, 8)));
+ buttonStack.add(zoomOutButton);
+
+ buttonStack.setAlignmentX(Component.CENTER_ALIGNMENT);
+ alignmentPanel.add(buttonStack);
+
+ container.add(alignmentPanel, BorderLayout.CENTER);
+ return container;
+ }
+
+ private JButton createZoomButton(String symbol) {
+ JButton button = new JButton(symbol);
+ Dimension size = new Dimension(44, 44);
+ button.setPreferredSize(size);
+ button.setMinimumSize(size);
+ button.setMaximumSize(size);
+ button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 22));
+ button.setMargin(new Insets(0, 0, 0, 0));
+ button.setFocusPainted(false);
+ button.setBackground(Color.WHITE);
+ button.setForeground(Color.DARK_GRAY);
+ button.setBorder(BorderFactory.createLineBorder(new Color(210, 210, 210), 1, true));
+ button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ button.setOpaque(true);
+
+ button.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ button.setBackground(new Color(245, 245, 245));
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ button.setBackground(Color.WHITE);
+ }
+ });
+
+ return button;
+ }
+
+ private void createControlPanel() {
+ controlPanel = new JPanel(new BorderLayout());
+ controlPanel.setBackground(PANEL_BACKGROUND);
+ controlPanel.setBorder(BorderFactory.createEmptyBorder(15, 20, 15, 20));
+ controlPanel.setPreferredSize(new Dimension(0, 100));
+
+ JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 20, 0));
+ buttonPanel.setBackground(PANEL_BACKGROUND);
+
+ startBtn = createControlButton("鏆傚仠", THEME_COLOR);
+ updateStartButtonAppearance();
+
+ stopBtn = createControlButton("缁撴潫", Color.ORANGE);
+ updateStopButtonIcon();
+
+ buttonPanel.add(startBtn);
+ buttonPanel.add(stopBtn);
+
+ controlPanel.add(buttonPanel, BorderLayout.CENTER);
+ }
+
+ private void createNavigationPanel() {
+ navigationPanel = new JPanel(new GridLayout(1, 3));
+ navigationPanel.setBackground(PANEL_BACKGROUND);
+ navigationPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ navigationPanel.setPreferredSize(new Dimension(0, 70));
+
+ homeNavBtn = createNavButton("棣栭〉", "馃彔");
+ areasNavBtn = createNavButton("鍦板潡", "馃尶");
+ settingsNavBtn = createNavButton("璁剧疆", "鈿欙笍");
+
+ navigationPanel.add(homeNavBtn);
+ navigationPanel.add(areasNavBtn);
+ navigationPanel.add(settingsNavBtn);
+
+ // 娣诲姞鍒颁富鐣岄潰搴曢儴
+ add(navigationPanel, BorderLayout.SOUTH);
+ }
+
+ private JButton createIconButton(String icon, int size) {
+ JButton button = new JButton(icon);
+ button.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 20));
+ button.setPreferredSize(new Dimension(size, size));
+ button.setBackground(PANEL_BACKGROUND);
+ button.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ button.setFocusPainted(false);
+
+ // 鎮仠鏁堟灉
+ button.addMouseListener(new MouseAdapter() {
+ public void mouseEntered(MouseEvent e) {
+ button.setBackground(new Color(240, 240, 240));
+ }
+ public void mouseExited(MouseEvent e) {
+ button.setBackground(PANEL_BACKGROUND);
+ }
+ });
+
+ return button;
+ }
+
+ private JButton createBluetoothButton() {
+ JButton button = new JButton();
+ button.setPreferredSize(new Dimension(40, 40));
+ button.setBackground(PANEL_BACKGROUND);
+ button.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ button.setFocusPainted(false);
+ button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ button.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ button.setBackground(new Color(240, 240, 240));
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ button.setBackground(PANEL_BACKGROUND);
+ }
+ });
+ ensureBluetoothIconsLoaded();
+ // 鏍规嵁涓插彛杩炴帴鐘舵�佹樉绀哄浘鏍�
+ SerialPortService service = sendmessage.getActiveService();
+ boolean serialConnected = (service != null && service.isOpen());
+ ImageIcon initialIcon = serialConnected ? bluetoothLinkedIcon : bluetoothIcon;
+ if (initialIcon != null) {
+ button.setIcon(initialIcon);
+ } else {
+ button.setText(serialConnected ? "宸茶繛" : "钃濈墮");
+ }
+ return button;
+ }
+
+ private JButton createFunctionButton(String text, String icon) {
+ JButton button = new JButton("<html><center>" + icon + "<br>" + text + "</center></html>");
+ button.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+ button.setPreferredSize(new Dimension(80, 70));
+ button.setBackground(new Color(0, 0, 0, 0)); // 瀹屽叏閫忔槑鑳屾櫙
+ button.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); // 鍙繚鐣欏唴杈硅窛
+ button.setFocusPainted(false);
+ button.setOpaque(false); // 璁剧疆涓轰笉閫忔槑锛岀‘淇濊儗鏅�忔槑
+
+ // 鍘绘帀鎮仠鏁堟灉
+ button.addMouseListener(new MouseAdapter() {
+ public void mouseEntered(MouseEvent e) {
+ // 鎮仠鏃朵笉鏀瑰彉浠讳綍鏍峰紡
+ }
+ public void mouseExited(MouseEvent e) {
+ // 鎮仠鏃朵笉鏀瑰彉浠讳綍鏍峰紡
+ }
+ });
+
+ return button;
+ }
+
+ private JButton createControlButton(String text, Color color) {
+ JButton button = new JButton(text);
+ button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
+ button.setBackground(color);
+ button.setForeground(Color.WHITE);
+ button.setBorder(BorderFactory.createEmptyBorder(15, 0, 15, 0));
+ button.setFocusPainted(false);
+
+ // 鎮仠鏁堟灉
+ button.addMouseListener(new MouseAdapter() {
+ public void mouseEntered(MouseEvent e) {
+ if (button.isEnabled()) {
+ if (color == THEME_COLOR) {
+ button.setBackground(THEME_HOVER_COLOR);
+ } else {
+ button.setBackground(new Color(255, 165, 0));
+ }
+ }
+ }
+ public void mouseExited(MouseEvent e) {
+ if (button.isEnabled()) {
+ button.setBackground(color);
+ }
+ }
+ });
+
+ return button;
+ }
+
+ private void applyButtonIcon(JButton button, String imagePath) {
+ try {
+ ImageIcon icon = new ImageIcon(imagePath);
+ Image scaledImage = icon.getImage().getScaledInstance(28, 28, Image.SCALE_SMOOTH);
+ button.setIcon(new ImageIcon(scaledImage));
+ button.setHorizontalAlignment(SwingConstants.CENTER);
+ button.setIconTextGap(10);
+ button.setHorizontalTextPosition(SwingConstants.RIGHT);
+ button.setVerticalTextPosition(SwingConstants.CENTER);
+ } catch (Exception e) {
+ System.err.println("鏃犳硶鍔犺浇鎸夐挳鍥炬爣: " + imagePath + " -> " + e.getMessage());
+ }
+ }
+
+ private JButton createNavButton(String text, String icon) {
+ JButton button = new JButton("<html><center>" + icon + "<br>" + text + "</center></html>");
+ button.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+ button.setBackground(PANEL_BACKGROUND);
+ button.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 5));
+ button.setFocusPainted(false);
+
+ // 鎮仠鏁堟灉
+ button.addMouseListener(new MouseAdapter() {
+ public void mouseEntered(MouseEvent e) {
+ if (button != currentNavButton) {
+ button.setBackground(new Color(240, 240, 240));
+ }
+ }
+ public void mouseExited(MouseEvent e) {
+ if (button != currentNavButton) {
+ button.setBackground(PANEL_BACKGROUND);
+ }
+ }
+ });
+
+ return button;
+ }
+
+ private void setNavigationActive(JButton navButton) {
+ // 閲嶇疆鎵�鏈夊鑸寜閽牱寮�
+ for (Component comp : navigationPanel.getComponents()) {
+ if (comp instanceof JButton) {
+ JButton btn = (JButton) comp;
+ btn.setBackground(PANEL_BACKGROUND);
+ btn.setForeground(Color.BLACK);
+ }
+ }
+
+ // 璁剧疆褰撳墠閫変腑鎸夐挳鏍峰紡
+ navButton.setBackground(THEME_COLOR);
+ navButton.setForeground(Color.WHITE);
+ currentNavButton = navButton;
+ }
+
+ private void setupEventHandlers() {
+ // 瀵艰埅鎸夐挳浜嬩欢
+ homeNavBtn.addActionListener(e -> setNavigationActive(homeNavBtn));
+ areasNavBtn.addActionListener(e -> setNavigationActive(areasNavBtn));
+ settingsNavBtn.addActionListener(e -> setNavigationActive(settingsNavBtn));
+
+ // 鍔熻兘鎸夐挳浜嬩欢
+ legendBtn.addActionListener(e -> showLegendDialog());
+ if (bluetoothBtn != null) {
+ bluetoothBtn.addActionListener(e -> toggleBluetoothConnection());
+ }
+ remoteBtn.addActionListener(e -> showRemoteControlDialog());
+ areaSelectBtn.addActionListener(e -> {
+ // 鐐瑰嚮鈥滃湴鍧椻�濈洿鎺ユ墦寮�鍦板潡绠$悊瀵硅瘽妗嗭紙鑻ラ渶瑕佸彲浼犲叆鐗瑰畾鍦板潡缂栧彿锛�
+ Dikuaiguanli.showDikuaiManagement(this, null);
+ });
+ baseStationBtn.addActionListener(e -> showBaseStationDialog());
+
+ // 鎺у埗鎸夐挳浜嬩欢
+ startBtn.addActionListener(e -> toggleStartPause());
+ stopBtn.addActionListener(e -> handleStopAction());
+ }
+
+ private void showSettingsDialog() {
+ if (settingsDialog == null) {
+ Window parentWindow = SwingUtilities.getWindowAncestor(this);
+ if (parentWindow instanceof JFrame) {
+ settingsDialog = new Sets((JFrame) parentWindow, THEME_COLOR);
+ } else if (parentWindow instanceof JDialog) {
+ settingsDialog = new Sets((JDialog) parentWindow, THEME_COLOR);
+ } else {
+ // Fallback to a frameless dialog when no parent is available
+ settingsDialog = new Sets((JFrame) null, THEME_COLOR);
+ }
+ }
+ settingsDialog.setVisible(true);
+ }
+
+ private void showLegendDialog() {
+ if (legendDialog == null) {
+ Window parentWindow = SwingUtilities.getWindowAncestor(this);
+ if (parentWindow != null) {
+ legendDialog = new LegendDialog(this, THEME_COLOR);
+ } else {
+ // 濡傛灉娌℃湁鐖剁獥鍙o紝鍒涘缓鏃犵埗绐楀彛鐨勫璇濇
+ legendDialog = new LegendDialog((JFrame) null, THEME_COLOR);
+ }
+ }
+ legendDialog.setVisible(true);
+ }
+
+ private void showRemoteControlDialog() {
+ if (remoteDialog == null) {
+ Window parentWindow = SwingUtilities.getWindowAncestor(this);
+ if (parentWindow != null) {
+ // 浣跨敤 yaokong 鍖呬腑鐨� RemoteControlDialog 瀹炵幇
+ remoteDialog = new yaokong.RemoteControlDialog(this, THEME_COLOR, speedLabel);
+ } else {
+ remoteDialog = new yaokong.RemoteControlDialog((JFrame) null, THEME_COLOR, speedLabel);
+ }
+ }
+ if (remoteDialog != null) {
+ positionRemoteDialogBottomCenter(remoteDialog);
+ zijian.markSelfCheckCompleted();
+ remoteDialog.setVisible(true);
+ }
+ }
+
+ private void positionRemoteDialogBottomCenter(RemoteControlDialog dialog) {
+ if (dialog == null) {
+ return;
+ }
+ // 灏嗗璇濇搴曢儴涓庢暣涓椤碉紙Shouye 闈㈡澘锛夌殑涓嬭竟妗嗗榻愶紝
+ // 鑰屼笉鏄笌 visualizationPanel 瀵归綈锛屼互婊¤冻璁捐瑕佹眰銆�
+ Dimension dialogSize = dialog.getSize();
+
+ // 鑾峰彇褰撳墠 Shouye 闈㈡澘鍦ㄥ睆骞曚笂鐨勪綅缃拰灏哄
+ Point parentOnScreen = null;
+ try {
+ parentOnScreen = this.getLocationOnScreen();
+ } catch (IllegalComponentStateException ex) {
+ // 濡傛灉缁勪欢灏氭湭鏄剧ず鎴栨棤娉曡幏鍙栧睆骞曚綅缃紝鍥為��鍒扮獥鍙e眰绾т綅缃幏鍙�
+ Window owner = SwingUtilities.getWindowAncestor(this);
+ if (owner != null) {
+ parentOnScreen = owner.getLocationOnScreen();
+ }
+ }
+
+ int x = 0, y = 0;
+ if (parentOnScreen != null) {
+ int parentWidth = this.getWidth();
+ int parentHeight = this.getHeight();
+ x = parentOnScreen.x + (parentWidth - dialogSize.width) / 2;
+ y = parentOnScreen.y + parentHeight - dialogSize.height;
+ } else {
+ // 浣滀负鏈�鍚庣殑鍥為��锛屼娇鐢ㄥ睆骞曚腑蹇冨榻愬簳閮�
+ Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
+ x = (screen.width - dialogSize.width) / 2;
+ y = screen.height - dialogSize.height;
+ }
+
+ dialog.setLocation(Math.max(x, 0), Math.max(y, 0));
+ }
+
+ private Rectangle computeVisualizationBoundsOnScreen() {
+ if (visualizationPanel == null || !visualizationPanel.isShowing()) {
+ return null;
+ }
+ Point location = visualizationPanel.getLocationOnScreen();
+ Dimension size = visualizationPanel.getSize();
+ return new Rectangle(location.x, location.y, size.width, size.height);
+ }
+
+ private void showAreaSelectionDialog() {
+ if (areaDialog == null) {
+ Window parentWindow = SwingUtilities.getWindowAncestor(this);
+ if (parentWindow != null) {
+ areaDialog = new AreaSelectionDialog(this, THEME_COLOR, areaNameLabel, statusLabel);
+ } else {
+ areaDialog = new AreaSelectionDialog((JFrame) null, THEME_COLOR, areaNameLabel, statusLabel);
+ }
+ }
+ areaDialog.setVisible(true);
+ }
+
+ private void showBaseStationDialog() {
+ if (baseStation == null) {
+ baseStation = new BaseStation();
+ }
+ baseStation.load();
+
+ Component dialogParent = this;
+
+ if (!hasValidBaseStationId()) {
+ boolean recorded = promptForBaseStationId(dialogParent);
+ if (!recorded) {
+ return;
+ }
+ }
+
+ Device device = Device.getGecaoji();
+ if (device == null) {
+ device = new Device();
+ device.initFromProperties();
+ Device.setGecaoji(device);
+ }
+
+ if (baseStationDialog == null) {
+ baseStationDialog = new BaseStationDialog(dialogParent, THEME_COLOR, device, baseStation);
+ } else {
+ baseStationDialog.refreshData();
+ }
+ baseStationDialog.setVisible(true);
+ }
+
+ private void checkIdentifiersAndPromptIfNeeded() {
+ if (baseStation == null) {
+ baseStation = new BaseStation();
+ }
+ baseStation.load();
+
+ String currentMowerId = Setsys.getPropertyValue("mowerId");
+ String currentBaseStationId = baseStation.getDeviceId();
+
+ if (!isIdentifierMissing(currentMowerId) && !isIdentifierMissing(currentBaseStationId)) {
+ return;
+ }
+
+ Window owner = SwingUtilities.getWindowAncestor(this);
+ promptForMissingIdentifiers(owner, currentMowerId, currentBaseStationId);
+ }
+
+ private void promptForMissingIdentifiers(Window owner, String currentMowerId, String currentBaseStationId) {
+ while (true) {
+ JTextField mowerField = new JTextField(10);
+ JTextField baseField = new JTextField(10);
+
+ if (!isIdentifierMissing(currentMowerId)) {
+ mowerField.setText(currentMowerId.trim());
+ }
+ if (!isIdentifierMissing(currentBaseStationId)) {
+ baseField.setText(currentBaseStationId.trim());
+ }
+
+ JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ panel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
+
+ JLabel mowerLabel = new JLabel("鍓茶崏鏈虹紪鍙�");
+ JLabel baseLabel = new JLabel("宸垎鍩哄噯绔欑紪鍙�");
+
+ mowerField.setMaximumSize(new Dimension(Integer.MAX_VALUE, mowerField.getPreferredSize().height));
+ baseField.setMaximumSize(new Dimension(Integer.MAX_VALUE, baseField.getPreferredSize().height));
+
+ panel.add(mowerLabel);
+ panel.add(Box.createVerticalStrut(4));
+ panel.add(mowerField);
+ panel.add(Box.createVerticalStrut(10));
+ panel.add(baseLabel);
+ panel.add(Box.createVerticalStrut(4));
+ panel.add(baseField);
+
+ Object[] options = {"淇濆瓨", "鍙栨秷"};
+ int result = JOptionPane.showOptionDialog(owner, panel, "瀹屽杽璁惧淇℃伅",
+ JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
+
+ if (result != 0) {
+ break;
+ }
+
+ String mowerInput = mowerField.getText().trim();
+ String baseInput = baseField.getText().trim();
+
+ if (mowerInput.isEmpty()) {
+ JOptionPane.showMessageDialog(owner, "鍓茶崏鏈虹紪鍙蜂笉鑳戒负绌恒��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ continue;
+ }
+
+ if (baseInput.isEmpty()) {
+ JOptionPane.showMessageDialog(owner, "宸垎鍩哄噯绔欑紪鍙蜂笉鑳戒负绌恒��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ continue;
+ }
+
+ boolean mowerSaved = persistMowerIdentifier(mowerInput);
+ boolean baseSaved = persistBaseStationIdentifier(baseInput);
+
+ if (mowerSaved && baseSaved) {
+ JOptionPane.showMessageDialog(owner, "缂栧彿宸蹭繚瀛樸��", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+ break;
+ }
+
+ StringBuilder errorBuilder = new StringBuilder();
+ if (!mowerSaved) {
+ errorBuilder.append("鍓茶崏鏈虹紪鍙蜂繚瀛樺け璐ャ��");
+ }
+ if (!baseSaved) {
+ if (errorBuilder.length() > 0) {
+ errorBuilder.append('\n');
+ }
+ errorBuilder.append("宸垎鍩哄噯绔欑紪鍙蜂繚瀛樺け璐ャ��");
+ }
+
+ JOptionPane.showMessageDialog(owner, errorBuilder.toString(), "淇濆瓨澶辫触", JOptionPane.ERROR_MESSAGE);
+
+ currentMowerId = Setsys.getPropertyValue("mowerId");
+ baseStation.load();
+ currentBaseStationId = baseStation.getDeviceId();
+ }
+ }
+
+ private boolean isIdentifierMissing(String value) {
+ if (value == null) {
+ return true;
+ }
+ String trimmed = value.trim();
+ return trimmed.isEmpty() || "-1".equals(trimmed);
+ }
+
+ private boolean persistMowerIdentifier(String mowerId) {
+ try {
+ Setsys setsys = new Setsys();
+ setsys.initializeFromProperties();
+ boolean updated = setsys.updateProperty("mowerId", mowerId);
+ if (updated) {
+ Device.initializeActiveDevice(mowerId);
+ }
+ return updated;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return false;
+ }
+ }
+
+ private boolean persistBaseStationIdentifier(String baseStationId) {
+ if (baseStation == null) {
+ baseStation = new BaseStation();
+ }
+ try {
+ baseStation.updateByDeviceId(baseStationId,
+ baseStation.getInstallationCoordinates(),
+ baseStation.getIotSimCardNumber(),
+ baseStation.getDeviceActivationTime(),
+ baseStation.getDataUpdateTime());
+ baseStation.load();
+ return true;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return false;
+ }
+ }
+
+ private boolean hasValidBaseStationId() {
+ if (baseStation == null) {
+ return false;
+ }
+ String deviceId = baseStation.getDeviceId();
+ if (deviceId == null) {
+ return false;
+ }
+ String trimmed = deviceId.trim();
+ return !trimmed.isEmpty() && !"-1".equals(trimmed);
+ }
+
+ private boolean promptForBaseStationId(Component parentComponent) {
+ if (baseStation == null) {
+ baseStation = new BaseStation();
+ }
+
+ while (true) {
+ String input = JOptionPane.showInputDialog(parentComponent,
+ "璇疯緭鍏ュ熀鍑嗙珯缂栧彿", "褰曞叆鍩哄噯绔欑紪鍙�", JOptionPane.PLAIN_MESSAGE);
+
+ if (input == null) {
+ return false;
+ }
+
+ String trimmed = input.trim();
+ if (trimmed.isEmpty()) {
+ JOptionPane.showMessageDialog(parentComponent,
+ "鍩哄噯绔欑紪鍙蜂笉鑳戒负绌猴紝璇烽噸鏂拌緭鍏ャ��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ continue;
+ }
+
+ try {
+ baseStation.updateByDeviceId(trimmed,
+ baseStation.getInstallationCoordinates(),
+ baseStation.getIotSimCardNumber(),
+ baseStation.getDeviceActivationTime(),
+ baseStation.getDataUpdateTime());
+ baseStation.load();
+ JOptionPane.showMessageDialog(parentComponent,
+ "鍩哄噯绔欑紪鍙峰凡淇濆瓨銆�", "鎿嶄綔鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+ return true;
+ } catch (IllegalArgumentException ex) {
+ JOptionPane.showMessageDialog(parentComponent,
+ ex.getMessage(), "杈撳叆閿欒", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ private void toggleStartPause() {
+ if (handheldCaptureInlineUiActive) {
+ handleHandheldConfirmAction();
+ return;
+ }
+ if (drawingControlModeActive) {
+ toggleDrawingPause();
+ return;
+ }
+ if (startBtn == null) {
+ return;
+ }
+ if (startButtonShowingPause) {
+ // 鐐瑰嚮寮�濮嬫寜閽椂涓嶅啀寮瑰嚭鑷鎻愮ず锛堟寜鐢ㄦ埛瑕佹眰鍒犻櫎锛�
+ // 鏃ч�昏緫锛氳皟鐢� zijian.ensureBeforeMowing(...) 骞跺湪鏈‘璁よ嚜妫�鏃堕樆姝㈠紑濮�
+ // 鏂伴�昏緫锛氱洿鎺ュ厑璁稿紑濮嬩綔涓�
+ }
+ startButtonShowingPause = !startButtonShowingPause;
+ if (!startButtonShowingPause) {
+ // 妫�鏌ュ壊鑽夋満鏄惁鍦ㄤ綔涓氬湴鍧楄竟鐣岃寖鍥村唴
+ if (!checkMowerInBoundary()) {
+ startButtonShowingPause = true;
+ statusLabel.setText("寰呮満");
+ updateStartButtonAppearance();
+ return;
+ }
+
+ statusLabel.setText("浣滀笟涓�");
+ if (stopButtonActive) {
+ stopButtonActive = false;
+ updateStopButtonIcon();
+ }
+ if (!beginMowingSession()) {
+ startButtonShowingPause = true;
+ statusLabel.setText("寰呮満");
+ updateStartButtonAppearance();
+ return;
+ }
+ } else {
+ statusLabel.setText("鏆傚仠涓�");
+ pauseMowingSession();
+ }
+ updateStartButtonAppearance();
+ }
+
+ /**
+ * 妫�鏌ュ壊鑽夋満鏄惁鍦ㄥ綋鍓嶉�変腑鐨勪綔涓氬湴鍧楄竟鐣岃寖鍥村唴
+ * @return 濡傛灉鍓茶崏鏈哄湪杈圭晫鍐呰繑鍥瀟rue锛屽惁鍒欒繑鍥瀎alse骞舵樉绀烘彁绀�
+ */
+ private boolean checkMowerInBoundary() {
+ if (mapRenderer == null) {
+ return true; // 濡傛灉娌℃湁鍦板浘娓叉煋鍣紝璺宠繃妫�鏌�
+ }
+
+ // 鑾峰彇褰撳墠杈圭晫
+ List<Point2D.Double> boundary = mapRenderer.getCurrentBoundary();
+ if (boundary == null || boundary.size() < 3) {
+ return true; // 濡傛灉娌℃湁杈圭晫鎴栬竟鐣岀偣涓嶈冻锛岃烦杩囨鏌�
+ }
+
+ // 鑾峰彇鍓茶崏鏈轰綅缃�
+ Gecaoji mower = mapRenderer.getMower();
+ if (mower == null || !mower.hasValidPosition()) {
+ showCustomMessageDialog("鏃犳硶鑾峰彇鍓茶崏鏈轰綅缃紝璇锋鏌ヨ澶囪繛鎺�", "鎻愮ず");
+ return false;
+ }
+
+ Point2D.Double mowerPosition = mower.getPosition();
+ if (mowerPosition == null) {
+ showCustomMessageDialog("鏃犳硶鑾峰彇鍓茶崏鏈轰綅缃紝璇锋鏌ヨ澶囪繛鎺�", "鎻愮ず");
+ return false;
+ }
+
+ // 浣跨敤 MowerBoundaryChecker 妫�鏌ユ槸鍚﹀湪杈圭晫鍐�
+ boolean isInside = MowerBoundaryChecker.isInsideBoundaryPoints(
+ boundary,
+ mowerPosition.x,
+ mowerPosition.y
+ );
+
+ if (!isInside) {
+ showCustomMessageDialog("璇峰皢鍓茶崏鏈哄紑鍒颁綔涓氬湴鍧楀唴鐒跺悗鐐瑰嚮寮�濮嬩綔涓�", "鎻愮ず");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * 鏄剧ず鑷畾涔夋秷鎭璇濇锛屼娇鐢� buttonset 鍒涘缓纭畾鎸夐挳
+ * @param message 娑堟伅鍐呭
+ * @param title 瀵硅瘽妗嗘爣棰�
+ */
+ private void showCustomMessageDialog(String message, String title) {
+ Window parentWindow = SwingUtilities.getWindowAncestor(this);
+ JDialog dialog = new JDialog(parentWindow, title, Dialog.ModalityType.APPLICATION_MODAL);
+ dialog.setLayout(new BorderLayout(20, 20));
+ dialog.setResizable(false);
+
+ // 鍐呭闈㈡澘
+ JPanel contentPanel = new JPanel(new BorderLayout(0, 15));
+ contentPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 10, 20));
+ contentPanel.setBackground(Color.WHITE);
+
+ // 娑堟伅鏍囩
+ JLabel messageLabel = new JLabel("<html><div style='text-align: center;'>" + message + "</div></html>");
+ messageLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+ messageLabel.setHorizontalAlignment(SwingConstants.CENTER);
+ contentPanel.add(messageLabel, BorderLayout.CENTER);
+
+ // 鎸夐挳闈㈡澘
+ JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0));
+ buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
+ buttonPanel.setOpaque(false);
+
+ // 浣跨敤 buttonset 鍒涘缓纭畾鎸夐挳
+ JButton okButton = buttonset.createStyledButton("纭畾", THEME_COLOR);
+ okButton.addActionListener(e -> dialog.dispose());
+ buttonPanel.add(okButton);
+
+ contentPanel.add(buttonPanel, BorderLayout.SOUTH);
+ dialog.add(contentPanel, BorderLayout.CENTER);
+
+ dialog.pack();
+ dialog.setLocationRelativeTo(this);
+ dialog.setVisible(true);
+ }
+
+ private void handleStopAction() {
+ if (handheldCaptureInlineUiActive) {
+ handleHandheldFinishAction();
+ return;
+ }
+ if (drawingControlModeActive) {
+ handleDrawingStopFromControlPanel();
+ return;
+ }
+ stopButtonActive = !stopButtonActive;
+ updateStopButtonIcon();
+ if (stopButtonActive) {
+ statusLabel.setText("宸茬粨鏉�");
+ startButtonShowingPause = false;
+ stopMowingSession();
+ } else {
+ statusLabel.setText("寰呮満");
+ startButtonShowingPause = true;
+ pauseMowingSession();
+ }
+ updateStartButtonAppearance();
+ }
+
+ private void handleDrawingStopFromControlPanel() {
+ if (endDrawingCallback != null) {
+ endDrawingCallback.run();
+ } else {
+ addzhangaiwu.finishDrawingSession();
+ }
+ }
+
+ private void handleHandheldConfirmAction() {
+ if (!handheldCaptureInlineUiActive) {
+ return;
+ }
+ if (!canConfirmHandheldPoint()) {
+ refreshHandheldCaptureUiState();
+ return;
+ }
+ int count = captureHandheldBoundaryPoint();
+ if (count <= 0) {
+ refreshHandheldCaptureUiState();
+ return;
+ }
+ refreshHandheldCaptureUiState();
+ }
+
+ private void handleHandheldFinishAction() {
+ if (!handheldCaptureInlineUiActive) {
+ return;
+ }
+ if (stopBtn != null && !stopBtn.isEnabled()) {
+ refreshHandheldCaptureUiState();
+ return;
+ }
+ if (!finishHandheldBoundaryCapture()) {
+ refreshHandheldCaptureUiState();
+ }
+ }
+
+ private void enterHandheldCaptureInlineUi() {
+ if (handheldCaptureInlineUiActive) {
+ refreshHandheldCaptureUiState();
+ return;
+ }
+ handheldCaptureInlineUiActive = true;
+ handheldCaptureStoredStatusText = statusLabel != null ? statusLabel.getText() : null;
+ if (statusLabel != null) {
+ statusLabel.setText("鎵嬫寔閲囬泦涓�");
+ }
+ if (startBtn != null) {
+ handheldStartButtonOriginalBackground = startBtn.getBackground();
+ handheldStartButtonOriginalForeground = startBtn.getForeground();
+ startBtn.setIcon(null);
+ startBtn.setIconTextGap(0);
+ startBtn.setHorizontalAlignment(SwingConstants.CENTER);
+ startBtn.setHorizontalTextPosition(SwingConstants.CENTER);
+ startBtn.setVerticalTextPosition(SwingConstants.CENTER);
+ }
+ if (stopBtn != null) {
+ handheldStopButtonOriginalBackground = stopBtn.getBackground();
+ handheldStopButtonOriginalForeground = stopBtn.getForeground();
+ stopBtn.setIcon(null);
+ stopBtn.setIconTextGap(0);
+ stopBtn.setHorizontalAlignment(SwingConstants.CENTER);
+ stopBtn.setHorizontalTextPosition(SwingConstants.CENTER);
+ stopBtn.setVerticalTextPosition(SwingConstants.CENTER);
+ stopBtn.setText("缁撴潫");
+ }
+ startHandheldCaptureStatusTimer();
+ refreshHandheldCaptureUiState();
+ }
+
+ private void exitHandheldCaptureInlineUi() {
+ if (!handheldCaptureInlineUiActive) {
+ return;
+ }
+ handheldCaptureInlineUiActive = false;
+ stopHandheldCaptureStatusTimer();
+ if (statusLabel != null) {
+ statusLabel.setText(handheldCaptureStoredStatusText != null ? handheldCaptureStoredStatusText : "寰呮満");
+ }
+ if (startBtn != null) {
+ startBtn.setToolTipText(null);
+ if (handheldStartButtonOriginalBackground != null) {
+ startBtn.setBackground(handheldStartButtonOriginalBackground);
+ }
+ if (handheldStartButtonOriginalForeground != null) {
+ startBtn.setForeground(handheldStartButtonOriginalForeground);
+ }
+ startBtn.setEnabled(true);
+ updateStartButtonAppearance();
+ }
+ if (stopBtn != null) {
+ stopBtn.setToolTipText(null);
+ if (handheldStopButtonOriginalBackground != null) {
+ stopBtn.setBackground(handheldStopButtonOriginalBackground);
+ }
+ if (handheldStopButtonOriginalForeground != null) {
+ stopBtn.setForeground(handheldStopButtonOriginalForeground);
+ }
+ stopBtn.setEnabled(true);
+ stopBtn.setText("缁撴潫");
+ updateStopButtonIcon();
+ }
+ handheldCaptureStoredStatusText = null;
+ handheldStartButtonOriginalBackground = null;
+ handheldStartButtonOriginalForeground = null;
+ handheldStopButtonOriginalBackground = null;
+ handheldStopButtonOriginalForeground = null;
+ }
+
+ private void startHandheldCaptureStatusTimer() {
+ if (handheldCaptureStatusTimer == null) {
+ handheldCaptureStatusTimer = new Timer(400, e -> refreshHandheldCaptureUiState());
+ handheldCaptureStatusTimer.setRepeats(true);
+ }
+ if (!handheldCaptureStatusTimer.isRunning()) {
+ handheldCaptureStatusTimer.start();
+ }
+ }
+
+ private void stopHandheldCaptureStatusTimer() {
+ if (handheldCaptureStatusTimer != null && handheldCaptureStatusTimer.isRunning()) {
+ handheldCaptureStatusTimer.stop();
+ }
+ }
+
+ // Update inline handheld capture buttons based on the current device reading.
+ private void refreshHandheldCaptureUiState() {
+ if (!handheldCaptureInlineUiActive) {
+ return;
+ }
+ int nextIndex = handheldCapturedPoints + 1;
+ boolean hasFix = hasHighPrecisionFix();
+ boolean hasValid = hasValidRealtimeHandheldPosition();
+ boolean duplicate = hasValid && isCurrentHandheldPointDuplicate();
+ boolean canConfirm = handheldCaptureActive && hasFix && hasValid && !duplicate;
+
+ if (startBtn != null) {
+ String prompt = "<html><center>閲囬泦鐐�" + nextIndex + "<br>纭畾</center></html>";
+ startBtn.setText(prompt);
+ startBtn.setEnabled(canConfirm);
+ if (canConfirm) {
+ if (handheldStartButtonOriginalBackground != null) {
+ startBtn.setBackground(handheldStartButtonOriginalBackground);
+ }
+ if (handheldStartButtonOriginalForeground != null) {
+ startBtn.setForeground(handheldStartButtonOriginalForeground);
+ }
+ startBtn.setToolTipText(null);
+ } else {
+ startBtn.setBackground(new Color(200, 200, 200));
+ startBtn.setForeground(new Color(130, 130, 130));
+ startBtn.setToolTipText(resolveHandheldConfirmTooltip(hasFix, hasValid, duplicate));
+ }
+ }
+
+ if (stopBtn != null) {
+ boolean canFinish = handheldCapturedPoints >= 3;
+ stopBtn.setText("缁撴潫");
+ stopBtn.setEnabled(canFinish);
+ if (canFinish) {
+ if (handheldStopButtonOriginalBackground != null) {
+ stopBtn.setBackground(handheldStopButtonOriginalBackground);
+ }
+ if (handheldStopButtonOriginalForeground != null) {
+ stopBtn.setForeground(handheldStopButtonOriginalForeground);
+ }
+ stopBtn.setToolTipText("缁撴潫閲囬泦骞惰繑鍥炴柊澧炲湴鍧�");
+ } else {
+ stopBtn.setBackground(new Color(220, 220, 220));
+ stopBtn.setForeground(new Color(130, 130, 130));
+ stopBtn.setToolTipText("鑷冲皯閲囬泦涓変釜鐐规墠鑳界粨鏉�");
+ }
+ }
+ }
+
+ private String resolveHandheldConfirmTooltip(boolean hasFix, boolean hasValidPosition, boolean duplicate) {
+ if (!hasFix) {
+ return "褰撳墠瀹氫綅璐ㄩ噺涓嶈冻锛屾棤娉曢噰闆�";
+ }
+ if (!hasValidPosition) {
+ return "褰撳墠瀹氫綅鏁版嵁鏃犳晥锛岃绋嶅悗鍐嶈瘯";
+ }
+ if (duplicate) {
+ return "褰撳墠鍧愭爣宸查噰闆嗭紝璇风Щ鍔ㄥ埌鏂扮殑浣嶇疆";
+ }
+ return null;
+ }
+
+ private boolean hasHighPrecisionFix() {
+ Device device = Device.getGecaoji();
+ if (device == null) {
+ return false;
+ }
+ String status = device.getPositioningStatus();
+ return status != null && "4".equals(status.trim());
+ }
+
+ private boolean canConfirmHandheldPoint() {
+ return handheldCaptureActive
+ && hasHighPrecisionFix()
+ && hasValidRealtimeHandheldPosition()
+ && !isCurrentHandheldPointDuplicate();
+ }
+
+ private void enterDrawingControlMode() {
+ if (drawingControlModeActive) {
+ return;
+ }
+ storedStartButtonShowingPause = startButtonShowingPause;
+ storedStopButtonActive = stopButtonActive;
+ storedStatusBeforeDrawing = statusLabel != null ? statusLabel.getText() : null;
+ drawingControlModeActive = true;
+ applyDrawingPauseState(false, false);
+ updateDrawingControlButtonLabels();
+ }
+
+ private void exitDrawingControlMode() {
+ if (!drawingControlModeActive) {
+ return;
+ }
+ drawingControlModeActive = false;
+ applyDrawingPauseState(false, false);
+ drawingPaused = false;
+ stopButtonActive = storedStopButtonActive;
+ startButtonShowingPause = storedStartButtonShowingPause;
+ if (startBtn != null) {
+ updateStartButtonAppearance();
+ }
+ if (stopBtn != null) {
+ stopBtn.setText("缁撴潫");
+ updateStopButtonIcon();
+ }
+ if (statusLabel != null) {
+ statusLabel.setText(storedStatusBeforeDrawing != null ? storedStatusBeforeDrawing : "寰呮満");
+ }
+ storedStatusBeforeDrawing = null;
+ }
+
+ private void updateDrawingControlButtonLabels() {
+ if (!drawingControlModeActive) {
+ return;
+ }
+ configureButtonForDrawingMode(startBtn);
+ configureButtonForDrawingMode(stopBtn);
+ if (startBtn != null) {
+ startBtn.setText(drawingPaused ? "寮�濮嬬粯鍒�" : "鏆傚仠缁樺埗");
+ }
+ if (stopBtn != null) {
+ stopBtn.setText("缁撴潫缁樺埗");
+ }
+ }
+
+ private void configureButtonForDrawingMode(JButton button) {
+ if (button == null) {
+ return;
+ }
+ button.setIcon(null);
+ button.setIconTextGap(0);
+ button.setHorizontalAlignment(SwingConstants.CENTER);
+ button.setHorizontalTextPosition(SwingConstants.CENTER);
+ }
+
+ private void updateStartButtonAppearance() {
+ if (startBtn == null) {
+ return;
+ }
+ String iconPath = startButtonShowingPause ? "image/start0.png" : "image/start1.png";
+ startBtn.setText(startButtonShowingPause ? "鏆傚仠" : "寮�濮�");
+ applyButtonIcon(startBtn, iconPath);
+ }
+
+ private void updateStopButtonIcon() {
+ if (stopBtn == null) {
+ return;
+ }
+ String iconPath = stopButtonActive ? "image/stop1.png" : "image/stop0.png";
+ applyButtonIcon(stopBtn, iconPath);
+ }
+
+ private void toggleBluetoothConnection() {
+ if (bluetoothBtn == null) {
+ return;
+ }
+ // 寮瑰嚭绯荤粺璋冭瘯椤甸潰
+ showDebugDialog();
+ }
+
+ private void showDebugDialog() {
+ Window parentWindow = SwingUtilities.getWindowAncestor(this);
+ debug debugDialog = new debug(parentWindow, THEME_COLOR);
+ debugDialog.setLocationRelativeTo(this); // 灞呬腑鏄剧ず鍦ㄩ椤�
+ debugDialog.setVisible(true);
+ }
+
+ private void updateBluetoothButtonIcon() {
+ if (bluetoothBtn == null) {
+ return;
+ }
+ ensureBluetoothIconsLoaded();
+ // 鏍规嵁涓插彛杩炴帴鐘舵�佹樉绀哄浘鏍�
+ SerialPortService service = sendmessage.getActiveService();
+ boolean serialConnected = (service != null && service.isOpen());
+ ImageIcon icon = serialConnected ? bluetoothLinkedIcon : bluetoothIcon;
+ if (icon != null) {
+ bluetoothBtn.setIcon(icon);
+ bluetoothBtn.setText(null);
+ } else {
+ bluetoothBtn.setText(serialConnected ? "宸茶繛" : "钃濈墮");
+ }
+ }
+
+ private JPanel createSpeedIndicatorPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.setOpaque(false);
+ panel.setBorder(BorderFactory.createEmptyBorder(10, 20, 5, 20));
+
+ JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0));
+ rightPanel.setOpaque(false);
+
+ fixQualityIndicator = new gpszhuangtai(THEME_COLOR);
+ fixQualityIndicator.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ fixQualityIndicator.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (SwingUtilities.isLeftMouseButton(e) && mapRenderer != null) {
+ mapRenderer.showMowerInfo();
+ }
+ }
+ });
+ mowingProgressLabel = new JLabel("--%");
+ mowingProgressLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
+ mowingProgressLabel.setForeground(THEME_COLOR);
+
+ mowerSpeedValueLabel = new JLabel("--");
+ mowerSpeedValueLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+ mowerSpeedValueLabel.setForeground(THEME_COLOR);
+
+ mowerSpeedUnitLabel = new JLabel("km/h");
+ mowerSpeedUnitLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 9));
+ mowerSpeedUnitLabel.setForeground(THEME_COLOR);
+
+ dataPacketCountLabel = new JLabel("--");
+ dataPacketCountLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
+ dataPacketCountLabel.setForeground(THEME_COLOR);
+
+ rightPanel.add(fixQualityIndicator);
+
+ JSeparator areaSeparator = new JSeparator(SwingConstants.VERTICAL);
+ areaSeparator.setPreferredSize(new Dimension(1, 16));
+ rightPanel.add(areaSeparator);
+
+ rightPanel.add(mowingProgressLabel);
+ JSeparator speedSeparator = new JSeparator(SwingConstants.VERTICAL);
+ speedSeparator.setPreferredSize(new Dimension(1, 16));
+ rightPanel.add(speedSeparator);
+ rightPanel.add(mowerSpeedValueLabel);
+ rightPanel.add(mowerSpeedUnitLabel);
+
+ JSeparator separator = new JSeparator(SwingConstants.VERTICAL);
+ separator.setPreferredSize(new Dimension(1, 16));
+ rightPanel.add(separator);
+
+ rightPanel.add(dataPacketCountLabel);
+
+ panel.add(rightPanel, BorderLayout.EAST);
+ updateFixQualityIndicator();
+ updateDataPacketCountLabel();
+ return panel;
+ }
+
+ private void startMowerSpeedUpdates() {
+ if (mowerSpeedRefreshTimer == null) {
+ mowerSpeedRefreshTimer = new Timer(1000, e -> refreshMowerSpeedLabel());
+ mowerSpeedRefreshTimer.setRepeats(true);
+ }
+ if (!mowerSpeedRefreshTimer.isRunning()) {
+ mowerSpeedRefreshTimer.start();
+ }
+ refreshMowerSpeedLabel();
+ }
+
+ private void refreshMowerSpeedLabel() {
+ if (mowerSpeedValueLabel == null) {
+ return;
+ }
+ String display = "--";
+ Device device = Device.getGecaoji();
+ if (device != null) {
+ String sanitized = sanitizeSpeedValue(device.getRealtimeSpeed());
+ if (sanitized != null) {
+ display = sanitized;
+ }
+ }
+ mowerSpeedValueLabel.setText(display);
+ if (mowerSpeedUnitLabel != null) {
+ mowerSpeedUnitLabel.setText("km/h");
+ }
+ updateMowingProgressLabel();
+ updateFixQualityIndicator();
+ updateDataPacketCountLabel();
+ }
+
+ private void updateDataPacketCountLabel() {
+ if (dataPacketCountLabel == null) {
+ return;
+ }
+ int udpCount = UDPServer.getReceivedPacketCount();
+ int serialCount = dellmessage.getProcessedLineCount();
+ int displayCount = Math.max(udpCount, serialCount);
+
+ if (displayCount <= 0) {
+ dataPacketCountLabel.setText("--");
+ dataPacketCountLabel.setToolTipText(null);
+ } else {
+ dataPacketCountLabel.setText(String.valueOf(displayCount));
+ dataPacketCountLabel.setToolTipText(String.format("涓插彛: %d UDP: %d", serialCount, udpCount));
+ }
+ }
+
+ private void updateFixQualityIndicator() {
+ if (fixQualityIndicator == null) {
+ return;
+ }
+ Device device = Device.getGecaoji();
+ String code = null;
+ if (device != null) {
+ code = sanitizeDeviceValue(device.getPositioningStatus());
+ }
+ fixQualityIndicator.setQuality(code);
+ }
+
+ private Color resolveFixQualityColor(String code) {
+ if (code == null) {
+ return new Color(160, 160, 160);
+ }
+ switch (code) {
+ case "0":
+ return new Color(160, 160, 160);
+ case "1":
+ return new Color(52, 152, 219);
+ case "2":
+ return new Color(26, 188, 156);
+ case "3":
+ return new Color(155, 89, 182);
+ case "4":
+ return THEME_COLOR;
+ case "5":
+ return new Color(241, 196, 15);
+ case "6":
+ return new Color(231, 76, 60);
+ case "7":
+ return new Color(230, 126, 34);
+ default:
+ return new Color(95, 95, 95);
+ }
+ }
+
+ private String resolveFixQualityDescription(String code) {
+ if (code == null) {
+ return "鏈煡";
+ }
+ switch (code) {
+ case "0":
+ return "鏈畾浣�";
+ case "1":
+ return "鍗曠偣瀹氫綅";
+ case "2":
+ return "鐮佸樊鍒�";
+ case "3":
+ return "鏃犳晥PPS";
+ case "4":
+ return "鍥哄畾瑙�";
+ case "5":
+ return "娴偣瑙�";
+ case "6":
+ return "姝e湪浼扮畻";
+ case "7":
+ return "浜哄伐杈撳叆鍥哄畾鍊�";
+ default:
+ return "鍏朵粬";
+ }
+ }
+
+ private String sanitizeSpeedValue(String raw) {
+ if (raw == null) {
+ return null;
+ }
+ String trimmed = raw.trim();
+ if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+ return null;
+ }
+ if (trimmed.toLowerCase().endsWith("km/h")) {
+ trimmed = trimmed.substring(0, trimmed.length() - 4).trim();
+ }
+ return trimmed;
+ }
+
+ private String sanitizeDeviceValue(String raw) {
+ if (raw == null) {
+ return null;
+ }
+ String trimmed = raw.trim();
+ if (trimmed.isEmpty() || "-1".equals(trimmed) || "null".equalsIgnoreCase(trimmed)) {
+ return null;
+ }
+ return trimmed;
+ }
+
+ private void updateMowingProgressLabel() {
+ if (mowingProgressLabel == null) {
+ return;
+ }
+ if (mapRenderer == null) {
+ mowingProgressLabel.setText("--%");
+ mowingProgressLabel.setToolTipText(null);
+ return;
+ }
+
+ double totalArea = mapRenderer.getTotalLandAreaSqMeters();
+ double completedArea = mapRenderer.getCompletedMowingAreaSqMeters();
+ double ratio = mapRenderer.getMowingCompletionRatio();
+
+ if (totalArea <= 0) {
+ mowingProgressLabel.setText("--%");
+ mowingProgressLabel.setToolTipText("鏆傛棤鍦板潡闈㈢Н鏁版嵁");
+ return;
+ }
+
+ double percent = Math.max(0.0, Math.min(1.0, ratio)) * 100.0;
+ mowingProgressLabel.setText(String.format(Locale.US, "%.1f%%", percent));
+ mowingProgressLabel.setToolTipText(String.format(Locale.US, "%.1f銕� / %.1f銕�", completedArea, totalArea));
+ }
+
+ public void refreshMowingIndicators() {
+ refreshMowerSpeedLabel();
+ }
+
+ public void setHandheldMowerIconActive(boolean active) {
+ if (mapRenderer == null) {
+ return;
+ }
+ mapRenderer.setHandheldMowerIconActive(active);
+ }
+
+ public boolean startMowerBoundaryCapture() {
+ if (mapRenderer == null) {
+ return false;
+ }
+ double[] baseLatLonCandidate = resolveCircleBaseLatLon();
+ if (baseLatLonCandidate == null) {
+ return false;
+ }
+
+ mapRenderer.clearIdleTrail();
+
+ activeBoundaryMode = BoundaryCaptureMode.MOWER;
+ mowerBoundaryCaptureActive = true;
+ mowerBaseLatLon = baseLatLonCandidate;
+ lastMowerCoordinate = null;
+
+ synchronized (Coordinate.coordinates) {
+ Coordinate.coordinates.clear();
+ }
+ synchronized (mowerTemporaryPoints) {
+ mowerTemporaryPoints.clear();
+ }
+
+ AddDikuai.recordTemporaryBoundaryPoints(Collections.emptyList());
+ Coordinate.setStartSaveGngga(true);
+
+ if (mapRenderer != null) {
+ mapRenderer.setBoundaryPreviewMarkerScale(2.0d);
+ mapRenderer.beginHandheldBoundaryPreview();
+ }
+
+ setHandheldMowerIconActive(false);
+
+ startMowerBoundaryMonitor();
+ return true;
+ }
+
+ public boolean startHandheldBoundaryCapture() {
+ if (mapRenderer == null) {
+ return false;
+ }
+ if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
+ stopMowerBoundaryCapture();
+ }
+
+ mapRenderer.clearIdleTrail();
+
+ activeBoundaryMode = BoundaryCaptureMode.HANDHELD;
+ handheldCaptureActive = true;
+ handheldCapturedPoints = 0;
+ Coordinate.setStartSaveGngga(false);
+ synchronized (Coordinate.coordinates) {
+ Coordinate.coordinates.clear();
+ }
+ synchronized (handheldTemporaryPoints) {
+ handheldTemporaryPoints.clear();
+ }
+ AddDikuai.recordTemporaryBoundaryPoints(Collections.emptyList());
+ mapRenderer.setBoundaryPreviewMarkerScale(1.0d);
+ mapRenderer.beginHandheldBoundaryPreview();
+ setHandheldMowerIconActive(true);
+ enterHandheldCaptureInlineUi();
+ return true;
+ }
+
+ private void startMowerBoundaryMonitor() {
+ if (mowerBoundaryMonitor == null) {
+ mowerBoundaryMonitor = new Timer(600, e -> pollMowerBoundaryCoordinate());
+ mowerBoundaryMonitor.setRepeats(true);
+ }
+ if (!mowerBoundaryMonitor.isRunning()) {
+ mowerBoundaryMonitor.start();
+ }
+ pollMowerBoundaryCoordinate();
+ }
+
+ private void stopMowerBoundaryMonitor() {
+ if (mowerBoundaryMonitor != null && mowerBoundaryMonitor.isRunning()) {
+ mowerBoundaryMonitor.stop();
+ }
+ }
+
+ private void pollMowerBoundaryCoordinate() {
+ if (!mowerBoundaryCaptureActive) {
+ return;
+ }
+
+ Coordinate latest = getLatestCoordinate();
+ if (latest == null || latest == lastMowerCoordinate) {
+ return;
+ }
+
+ double[] base = mowerBaseLatLon;
+ if (base == null || base.length < 2) {
+ discardLatestCoordinate(latest);
+ lastMowerCoordinate = latest;
+ return;
+ }
+
+ double lat = parseDMToDecimal(latest.getLatitude(), latest.getLatDirection());
+ double lon = parseDMToDecimal(latest.getLongitude(), latest.getLonDirection());
+ if (!Double.isFinite(lat) || !Double.isFinite(lon)) {
+ discardLatestCoordinate(latest);
+ lastMowerCoordinate = latest;
+ return;
+ }
+
+ double[] local = convertLatLonToLocal(lat, lon, base[0], base[1]);
+ Point2D.Double candidate = new Point2D.Double(local[0], local[1]);
+ if (!Double.isFinite(candidate.x) || !Double.isFinite(candidate.y)) {
+ discardLatestCoordinate(latest);
+ lastMowerCoordinate = latest;
+ return;
+ }
+
+ List<Point2D.Double> snapshot;
+ synchronized (mowerTemporaryPoints) {
+ for (Point2D.Double existing : mowerTemporaryPoints) {
+ if (existing != null && arePointsClose(existing, candidate)) {
+ discardLatestCoordinate(latest);
+ lastMowerCoordinate = latest;
+ return;
+ }
+ }
+ mowerTemporaryPoints.add(candidate);
+ snapshot = new ArrayList<>(mowerTemporaryPoints.size() + 1);
+ for (Point2D.Double point : mowerTemporaryPoints) {
+ if (point != null) {
+ snapshot.add(new Point2D.Double(point.x, point.y));
+ }
+ }
+ }
+
+ ensureClosed(snapshot);
+ AddDikuai.recordTemporaryBoundaryPoints(snapshot);
+ if (mapRenderer != null) {
+ mapRenderer.addHandheldBoundaryPoint(candidate.x, candidate.y);
+ }
+ lastMowerCoordinate = latest;
+ }
+
+ private void stopMowerBoundaryCapture() {
+ stopMowerBoundaryMonitor();
+ mowerBoundaryCaptureActive = false;
+ lastMowerCoordinate = null;
+ mowerBaseLatLon = null;
+ if (mapRenderer != null) {
+ mapRenderer.clearHandheldBoundaryPreview();
+ }
+ Coordinate.setStartSaveGngga(false);
+ if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
+ activeBoundaryMode = BoundaryCaptureMode.NONE;
+ }
+ setHandheldMowerIconActive(false);
+ }
+
+ private void discardLatestCoordinate(Coordinate coordinate) {
+ if (coordinate == null) {
+ return;
+ }
+ synchronized (Coordinate.coordinates) {
+ int size = Coordinate.coordinates.size();
+ if (size == 0) {
+ return;
+ }
+ int lastIndex = size - 1;
+ if (Coordinate.coordinates.get(lastIndex) == coordinate) {
+ Coordinate.coordinates.remove(lastIndex);
+ } else {
+ Coordinate.coordinates.remove(coordinate);
+ }
+ }
+ }
+
+ private void ensureClosed(List<Point2D.Double> points) {
+ if (points == null || points.size() < 3) {
+ return;
+ }
+ Point2D.Double first = points.get(0);
+ Point2D.Double last = points.get(points.size() - 1);
+ if (first == null || last == null) {
+ return;
+ }
+ if (!arePointsClose(first, last)) {
+ points.add(new Point2D.Double(first.x, first.y));
+ }
+ }
+
+ int captureHandheldBoundaryPoint() {
+ if (!handheldCaptureActive) {
+ return -1;
+ }
+ Device device = Device.getGecaoji();
+ if (device == null) {
+ JOptionPane.showMessageDialog(this, "鏈娴嬪埌閲囬泦璁惧锛岃妫�鏌ヨ繛鎺ャ��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return -1;
+ }
+
+ String[] latParts = splitCoordinateComponents(device.getRealtimeLatitude(), true);
+ String[] lonParts = splitCoordinateComponents(device.getRealtimeLongitude(), false);
+ if (latParts == null || lonParts == null) {
+ JOptionPane.showMessageDialog(this, "褰撳墠瀹氫綅鏃犳晥锛岃鍦ㄥ畾浣嶇ǔ瀹氬悗鍐嶈瘯銆�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return -1;
+ }
+
+ double x = parseMetersValue(device.getRealtimeX());
+ double y = parseMetersValue(device.getRealtimeY());
+ if (!Double.isFinite(x) || !Double.isFinite(y)) {
+ JOptionPane.showMessageDialog(this, "褰撳墠瀹氫綅鏁版嵁鏃犳晥锛岃绋嶅悗鍐嶈瘯銆�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return -1;
+ }
+ if (isDuplicateHandheldPoint(x, y)) {
+ JOptionPane.showMessageDialog(this, "褰撳墠鍧愭爣宸查噰闆嗭紝璇风Щ鍔ㄥ埌鏂扮殑浣嶇疆鍚庡啀璇曘��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return -1;
+ }
+
+ double altitude = parseAltitudeValue(device.getRealtimeAltitude());
+ Coordinate coordinate = new Coordinate(latParts[0], latParts[1], lonParts[0], lonParts[1], altitude);
+ synchronized (Coordinate.coordinates) {
+ Coordinate.coordinates.add(coordinate);
+ }
+
+ if (mapRenderer != null) {
+ mapRenderer.addHandheldBoundaryPoint(x, y);
+ }
+
+ List<Point2D.Double> snapshot;
+ synchronized (handheldTemporaryPoints) {
+ handheldTemporaryPoints.add(new Point2D.Double(x, y));
+ snapshot = new ArrayList<>(handheldTemporaryPoints);
+ }
+ AddDikuai.recordTemporaryBoundaryPoints(snapshot);
+
+ handheldCapturedPoints++;
+ return handheldCapturedPoints;
+ }
+
+ boolean finishHandheldBoundaryCapture() {
+ if (!handheldCaptureActive) {
+ return false;
+ }
+ if (handheldCapturedPoints < 3) {
+ JOptionPane.showMessageDialog(this, "鑷冲皯閲囬泦涓変釜鐐规墠鑳界敓鎴愯竟鐣屻��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return false;
+ }
+
+ List<Point2D.Double> closedSnapshot = createClosedHandheldPointSnapshot();
+ handheldCaptureActive = false;
+ activeBoundaryMode = BoundaryCaptureMode.NONE;
+ Coordinate.setStartSaveGngga(false);
+ if (mapRenderer != null) {
+ mapRenderer.clearHandheldBoundaryPreview();
+ }
+
+ AddDikuai.recordTemporaryBoundaryPoints(closedSnapshot);
+
+ exitHandheldCaptureInlineUi();
+
+ SwingUtilities.invokeLater(AddDikuai::finishDrawingSession);
+ return true;
+ }
+
+ int getHandheldCapturedPointCount() {
+ return handheldCapturedPoints;
+ }
+
+ public List<Point2D.Double> getHandheldTemporaryPointsSnapshot() {
+ if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
+ return createClosedMowerPointSnapshot();
+ }
+ if (!handheldCaptureActive) {
+ return createClosedHandheldPointSnapshot();
+ }
+ synchronized (handheldTemporaryPoints) {
+ return new ArrayList<>(handheldTemporaryPoints);
+ }
+ }
+
+ public boolean isCurrentHandheldPointDuplicate() {
+ Device device = Device.getGecaoji();
+ if (device == null) {
+ return false;
+ }
+ double x = parseMetersValue(device.getRealtimeX());
+ double y = parseMetersValue(device.getRealtimeY());
+ if (!Double.isFinite(x) || !Double.isFinite(y)) {
+ return false;
+ }
+ return isDuplicateHandheldPoint(x, y);
+ }
+
+ public boolean hasValidRealtimeHandheldPosition() {
+ Device device = Device.getGecaoji();
+ if (device == null) {
+ return false;
+ }
+ double x = parseMetersValue(device.getRealtimeX());
+ double y = parseMetersValue(device.getRealtimeY());
+ return Double.isFinite(x) && Double.isFinite(y);
+ }
+
+ private boolean isDuplicateHandheldPoint(double x, double y) {
+ Point2D.Double candidate = new Point2D.Double(x, y);
+ synchronized (handheldTemporaryPoints) {
+ for (Point2D.Double existing : handheldTemporaryPoints) {
+ if (existing == null) {
+ continue;
+ }
+ if (arePointsClose(existing, candidate)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean arePointsClose(Point2D.Double a, Point2D.Double b) {
+ if (a == null || b == null) {
+ return false;
+ }
+ double dx = a.x - b.x;
+ double dy = a.y - b.y;
+ return Math.hypot(dx, dy) < HANDHELD_DUPLICATE_THRESHOLD_METERS;
+ }
+
+ private List<Point2D.Double> createClosedHandheldPointSnapshot() {
+ List<Point2D.Double> copy = new ArrayList<>();
+ synchronized (handheldTemporaryPoints) {
+ for (Point2D.Double point : handheldTemporaryPoints) {
+ if (point != null) {
+ copy.add(new Point2D.Double(point.x, point.y));
+ }
+ }
+ }
+ ensureClosed(copy);
+ return copy;
+ }
+
+ private List<Point2D.Double> createClosedMowerPointSnapshot() {
+ List<Point2D.Double> copy = new ArrayList<>();
+ synchronized (mowerTemporaryPoints) {
+ for (Point2D.Double point : mowerTemporaryPoints) {
+ if (point != null) {
+ copy.add(new Point2D.Double(point.x, point.y));
+ }
+ }
+ }
+ ensureClosed(copy);
+ return copy;
+ }
+
+ private String[] splitCoordinateComponents(String combined, boolean latitude) {
+ if (combined == null) {
+ return null;
+ }
+ String trimmed = combined.trim();
+ if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+ return null;
+ }
+
+ String valuePart;
+ String directionPart = null;
+ String[] parts = trimmed.split(",");
+ if (parts.length >= 2) {
+ valuePart = parts[0].trim();
+ directionPart = parts[1].trim();
+ } else {
+ valuePart = trimmed;
+ }
+
+ if (valuePart.isEmpty()) {
+ return null;
+ }
+
+ if (directionPart == null || directionPart.isEmpty()) {
+ char lastChar = valuePart.charAt(valuePart.length() - 1);
+ if (Character.isLetter(lastChar)) {
+ directionPart = String.valueOf(lastChar);
+ valuePart = valuePart.substring(0, valuePart.length() - 1).trim();
+ }
+ }
+
+ if (valuePart.isEmpty()) {
+ return null;
+ }
+
+ directionPart = normalizeHemisphere(directionPart, latitude);
+ return new String[]{valuePart, directionPart};
+ }
+
+ private String normalizeHemisphere(String direction, boolean latitude) {
+ if (direction == null || direction.trim().isEmpty()) {
+ return latitude ? "N" : "E";
+ }
+ String normalized = direction.trim().toUpperCase(Locale.ROOT);
+ if (latitude) {
+ if (!"N".equals(normalized) && !"S".equals(normalized)) {
+ return "N";
+ }
+ } else {
+ if (!"E".equals(normalized) && !"W".equals(normalized)) {
+ return "E";
+ }
+ }
+ return normalized;
+ }
+
+ private double parseMetersValue(String raw) {
+ if (raw == null) {
+ return Double.NaN;
+ }
+ String trimmed = raw.trim();
+ if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+ return Double.NaN;
+ }
+ try {
+ return Double.parseDouble(trimmed);
+ } catch (NumberFormatException ex) {
+ return Double.NaN;
+ }
+ }
+
+ private double parseAltitudeValue(String raw) {
+ if (raw == null) {
+ return 0.0;
+ }
+ String trimmed = raw.trim();
+ if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+ return 0.0;
+ }
+ try {
+ return Double.parseDouble(trimmed);
+ } catch (NumberFormatException ex) {
+ return 0.0;
+ }
+ }
+
+ private boolean beginMowingSession() {
+ if (mapRenderer == null) {
+ return false;
+ }
+ String landNumber = Dikuaiguanli.getCurrentWorkLandNumber();
+ if (!isMeaningfulValue(landNumber)) {
+ JOptionPane.showMessageDialog(this, "璇峰厛閫夋嫨鍦板潡鍚庡啀寮�濮嬩綔涓�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return false;
+ }
+
+ double widthMeters = resolveMowerWidthMeters(landNumber);
+ if (widthMeters <= 0) {
+ JOptionPane.showMessageDialog(this, "鏈厤缃壊鑽夊搴︼紝灏嗘棤娉曡绠椾綔涓氶潰绉�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ }
+
+ mapRenderer.startRealtimeTrackRecording(landNumber, widthMeters);
+ refreshMowerSpeedLabel();
+ return true;
+ }
+
+ private void pauseMowingSession() {
+ if (mapRenderer == null) {
+ return;
+ }
+ mapRenderer.pauseRealtimeTrackRecording();
+ refreshMowerSpeedLabel();
+ }
+
+ private void stopMowingSession() {
+ if (mapRenderer == null) {
+ return;
+ }
+ mapRenderer.stopRealtimeTrackRecording();
+ refreshMowerSpeedLabel();
+ }
+
+ private double resolveMowerWidthMeters(String landNumber) {
+ double width = 0.0;
+ if (isMeaningfulValue(landNumber)) {
+ Dikuai current = Dikuai.getDikuai(landNumber);
+ if (current != null) {
+ width = parseMowerWidthMeters(current.getMowingWidth());
+ }
+ }
+ if (width > 0) {
+ return width;
+ }
+ return parseMowerWidthFromDevice();
+ }
+
+ private double parseMowerWidthFromDevice() {
+ Device device = Device.getGecaoji();
+ if (device == null) {
+ return 0.0;
+ }
+ return parseMowerWidthMeters(device.getMowingWidth());
+ }
+
+ private double parseMowerWidthMeters(String raw) {
+ if (raw == null) {
+ return 0.0;
+ }
+ String sanitized = raw.trim().toLowerCase(Locale.ROOT);
+ if (sanitized.isEmpty() || "-1".equals(sanitized)) {
+ return 0.0;
+ }
+ sanitized = sanitized.replace("鍘樼背", "cm");
+ sanitized = sanitized.replace("鍏垎", "cm");
+ sanitized = sanitized.replace("绫�", "m");
+ sanitized = sanitized.replace("cm", "");
+ sanitized = sanitized.replace("m", "");
+ sanitized = sanitized.trim();
+ if (sanitized.isEmpty()) {
+ return 0.0;
+ }
+ try {
+ double value = Double.parseDouble(sanitized);
+ if (value <= 0) {
+ return 0.0;
+ }
+ if (value > 10) {
+ return value / 100.0;
+ }
+ return value;
+ } catch (NumberFormatException ex) {
+ return 0.0;
+ }
+ }
+
+ private void applyStatusLabelColor(String statusText) {
+ if (statusLabel == null) {
+ return;
+ }
+ if ("浣滀笟涓�".equals(statusText) || "缁樺埗涓�".equals(statusText)) {
+ statusLabel.setForeground(THEME_COLOR);
+ } else if ("鏆傚仠涓�".equals(statusText) || "缁樺埗鏆傚仠".equals(statusText)) {
+ statusLabel.setForeground(STATUS_PAUSE_COLOR);
+ } else {
+ statusLabel.setForeground(Color.GRAY);
+ }
+ }
+
+ private void ensureBluetoothIconsLoaded() {
+ if (bluetoothIcon == null) {
+ bluetoothIcon = loadScaledIcon("image/blue.png", 28, 28);
+ }
+ if (bluetoothLinkedIcon == null) {
+ bluetoothLinkedIcon = loadScaledIcon("image/bluelink.png", 28, 28);
+ }
+ }
+
+ private JButton createFloatingIconButton() {
+ JButton button = new JButton();
+ button.setContentAreaFilled(false);
+ button.setBorder(BorderFactory.createEmptyBorder());
+ button.setFocusPainted(false);
+ button.setOpaque(false);
+ button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ int size = FLOAT_ICON_SIZE + 8;
+ button.setPreferredSize(new Dimension(size, size));
+ return button;
+ }
+
+ private JButton createFloatingTextButton(String text) {
+ JButton button = new JButton(text);
+ button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 15));
+ button.setForeground(Color.WHITE);
+ button.setBackground(THEME_COLOR);
+ button.setBorder(BorderFactory.createEmptyBorder(10, 18, 10, 18));
+ button.setFocusPainted(false);
+ button.setOpaque(true);
+ button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ button.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ button.setBackground(THEME_HOVER_COLOR);
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ button.setBackground(THEME_COLOR);
+ }
+ });
+ return button;
+ }
+
+ private ImageIcon loadScaledIcon(String path, int width, int height) {
+ try {
+ ImageIcon icon = new ImageIcon(path);
+ if (icon.getIconWidth() <= 0 || icon.getIconHeight() <= 0) {
+ return null;
+ }
+ Image scaled = icon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH);
+ return new ImageIcon(scaled);
+ } catch (Exception ex) {
+ System.err.println("鍔犺浇鍥炬爣澶辫触: " + path + " - " + ex.getMessage());
+ return null;
+ }
+ }
+
+ private void ensureFloatingIconsLoaded() {
+ if (pauseIcon == null) {
+ pauseIcon = loadScaledIcon("image/zanting.png", FLOAT_ICON_SIZE, FLOAT_ICON_SIZE);
+ }
+ if (pauseActiveIcon == null) {
+ pauseActiveIcon = loadScaledIcon("image/zantingzhong.png", FLOAT_ICON_SIZE, FLOAT_ICON_SIZE);
+ }
+ if (endIcon == null) {
+ endIcon = loadScaledIcon("image/end.png", FLOAT_ICON_SIZE, FLOAT_ICON_SIZE);
+ }
+ }
+
+ private void updatePauseButtonVisual() {
+ if (drawingPauseButton == null) {
+ return;
+ }
+ if (drawingPaused) {
+ if (pauseActiveIcon != null) {
+ drawingPauseButton.setIcon(pauseActiveIcon);
+ drawingPauseButton.setText(null);
+ } else {
+ drawingPauseButton.setText("鏆傚仠涓�");
+ drawingPauseButton.setIcon(null);
+ }
+ drawingPauseButton.setToolTipText("鐐瑰嚮鎭㈠缁樺埗");
+ } else {
+ if (pauseIcon != null) {
+ drawingPauseButton.setIcon(pauseIcon);
+ drawingPauseButton.setText(null);
+ } else {
+ drawingPauseButton.setText("鏆傚仠");
+ drawingPauseButton.setIcon(null);
+ }
+ drawingPauseButton.setToolTipText("鐐瑰嚮鏆傚仠缁樺埗");
+ }
+ }
+
+ private void applyDrawingPauseState(boolean paused, boolean notifyCoordinate) {
+ drawingPaused = paused;
+ updatePauseButtonVisual();
+ if (notifyCoordinate) {
+ Coordinate.setStartSaveGngga(!paused);
+ }
+ if (drawingControlModeActive) {
+ updateDrawingControlButtonLabels();
+ if (statusLabel != null) {
+ statusLabel.setText(paused ? "缁樺埗鏆傚仠" : "缁樺埗涓�");
+ }
+ }
+ }
+
+ private void toggleDrawingPause() {
+ applyDrawingPauseState(!drawingPaused, true);
+ }
+
+ public void showEndDrawingButton(Runnable callback) {
+ showEndDrawingButton(callback, null);
+ }
+
+ public void showEndDrawingButton(Runnable callback, String drawingShape) {
+ endDrawingCallback = callback;
+ circleDialogMode = false;
+ hideCircleGuidancePanel();
+ enterDrawingControlMode();
+
+ boolean enableCircleGuidance = drawingShape != null
+ && "circle".equalsIgnoreCase(drawingShape.trim());
+ if (enableCircleGuidance) {
+ ensureFloatingIconsLoaded();
+ ensureFloatingButtonInfrastructure();
+ if (drawingPauseButton != null) {
+ drawingPauseButton.setVisible(false);
+ }
+ if (endDrawingButton != null) {
+ endDrawingButton.setVisible(false);
+ }
+ prepareCircleGuidanceState();
+ showCircleGuidanceStep(1);
+ floatingButtonPanel.setVisible(true);
+ if (floatingButtonPanel.getParent() != visualizationPanel) {
+ visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
+ }
+ rebuildFloatingButtonColumn();
+ } else {
+ clearCircleGuidanceArtifacts();
+ hideFloatingDrawingControls();
+ }
+
+ visualizationPanel.revalidate();
+ visualizationPanel.repaint();
+ }
+
+ private void ensureFloatingButtonInfrastructure() {
+ if (endDrawingButton == null) {
+ endDrawingButton = createFloatingIconButton();
+ endDrawingButton.addActionListener(e -> {
+ if (endDrawingCallback != null) {
+ endDrawingCallback.run();
+ }
+ });
+ }
+ if (endIcon != null) {
+ endDrawingButton.setIcon(endIcon);
+ endDrawingButton.setText(null);
+ } else {
+ endDrawingButton.setText("缁撴潫缁樺埗");
+ }
+ endDrawingButton.setToolTipText("缁撴潫缁樺埗");
+
+ if (drawingPauseButton == null) {
+ drawingPauseButton = createFloatingIconButton();
+ drawingPauseButton.addActionListener(e -> toggleDrawingPause());
+ }
+ updatePauseButtonVisual();
+
+ if (floatingButtonPanel == null) {
+ floatingButtonPanel = new JPanel(new BorderLayout());
+ floatingButtonPanel.setOpaque(false);
+ floatingButtonPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 20));
+
+ floatingButtonColumn = new JPanel();
+ floatingButtonColumn.setOpaque(false);
+ floatingButtonColumn.setLayout(new BoxLayout(floatingButtonColumn, BoxLayout.Y_AXIS));
+ floatingButtonPanel.add(floatingButtonColumn, BorderLayout.EAST);
+ }
+ }
+
+ private void hideFloatingDrawingControls() {
+ if (drawingPauseButton != null) {
+ drawingPauseButton.setVisible(false);
+ }
+ if (endDrawingButton != null) {
+ endDrawingButton.setVisible(false);
+ }
+ if (floatingButtonPanel != null) {
+ floatingButtonPanel.setVisible(false);
+ }
+ if (!circleDialogMode) {
+ rebuildFloatingButtonColumn();
+ }
+ }
+
+ private void rebuildFloatingButtonColumn() {
+ if (floatingButtonColumn == null) {
+ return;
+ }
+ floatingButtonColumn.removeAll();
+ boolean added = false;
+ if (!circleDialogMode && circleGuidancePanel != null && circleGuidancePanel.isVisible()) {
+ floatingButtonColumn.add(circleGuidancePanel);
+ added = true;
+ }
+ if (!circleDialogMode && drawingPauseButton != null && drawingPauseButton.isVisible()) {
+ if (added) {
+ floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
+ }
+ floatingButtonColumn.add(drawingPauseButton);
+ added = true;
+ }
+ if (!circleDialogMode && endDrawingButton != null && endDrawingButton.isVisible()) {
+ if (added) {
+ floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
+ }
+ floatingButtonColumn.add(endDrawingButton);
+ added = true;
+ }
+ if (pathPreviewReturnButton != null && pathPreviewReturnButton.isVisible()) {
+ if (added) {
+ floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
+ }
+ floatingButtonColumn.add(pathPreviewReturnButton);
+ added = true;
+ }
+ floatingButtonColumn.revalidate();
+ floatingButtonColumn.repaint();
+ }
+
+ private void showCircleGuidanceStep(int step) {
+ ensureCircleGuidancePanel();
+ if (circleGuidancePanel == null) {
+ return;
+ }
+ circleGuidanceStep = step;
+
+ if (step == 1) {
+ circleGuidanceLabel.setText("閲囬泦绗�1涓偣");
+ circleGuidancePrimaryButton.setText("纭绗�1鐐�");
+ circleGuidanceSecondaryButton.setText("杩斿洖");
+ circleGuidanceSecondaryButton.setVisible(true);
+ } else if (step == 2) {
+ circleGuidanceLabel.setText("閲囬泦绗�2涓偣");
+ circleGuidancePrimaryButton.setText("纭绗�2鐐�");
+ circleGuidanceSecondaryButton.setText("杩斿洖");
+ circleGuidanceSecondaryButton.setVisible(true);
+ } else if (step == 3) {
+ circleGuidanceLabel.setText("閲囬泦绗�3涓偣");
+ circleGuidancePrimaryButton.setText("纭绗�3鐐�");
+ circleGuidanceSecondaryButton.setText("杩斿洖");
+ circleGuidanceSecondaryButton.setVisible(true);
+ } else if (step == 4) {
+ circleGuidanceLabel.setText("宸查噰闆嗕笁涓偣");
+ circleGuidancePrimaryButton.setText("缁撴潫缁樺埗");
+ circleGuidanceSecondaryButton.setText("閲嶆柊閲囬泦");
+ circleGuidanceSecondaryButton.setVisible(true);
+ } else {
+ hideCircleGuidancePanel();
+ return;
+ }
+ circleGuidancePanel.setVisible(true);
+
+ refreshCircleGuidanceButtonAvailability();
+
+ if (circleDialogMode) {
+ ensureCircleGuidanceDialog();
+ if (circleGuidanceDialog != null) {
+ circleGuidanceDialog.pack();
+ positionCircleGuidanceDialog();
+ circleGuidanceDialog.setVisible(true);
+ circleGuidanceDialog.toFront();
+ }
+ } else {
+ rebuildFloatingButtonColumn();
+ }
+ }
+
+ private void ensureCircleGuidancePanel() {
+ if (circleGuidancePanel != null) {
+ return;
+ }
+ circleGuidancePanel = new JPanel();
+ circleGuidancePanel.setLayout(new BoxLayout(circleGuidancePanel, BoxLayout.Y_AXIS));
+ circleGuidancePanel.setOpaque(true);
+ circleGuidancePanel.setBackground(new Color(255, 255, 255, 235));
+ circleGuidancePanel.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createLineBorder(THEME_COLOR, 1),
+ BorderFactory.createEmptyBorder(10, 12, 10, 12)));
+ circleGuidancePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ circleGuidanceLabel = new JLabel();
+ circleGuidanceLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 13));
+ circleGuidanceLabel.setForeground(new Color(33, 37, 41));
+ circleGuidanceLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ JPanel buttonRow = new JPanel();
+ buttonRow.setLayout(new BoxLayout(buttonRow, BoxLayout.X_AXIS));
+ buttonRow.setOpaque(false);
+ buttonRow.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ circleGuidancePrimaryButton = createGuidanceButton("鏄�", THEME_COLOR, Color.WHITE, true);
+ circleGuidancePrimaryButton.addActionListener(e -> onCircleGuidancePrimary());
+
+ circleGuidanceSecondaryButton = createGuidanceButton("鍚�", Color.WHITE, THEME_COLOR, false);
+ circleGuidanceSecondaryButton.addActionListener(e -> onCircleGuidanceSecondary());
+
+ buttonRow.add(circleGuidancePrimaryButton);
+ buttonRow.add(Box.createRigidArea(new Dimension(8, 0)));
+ buttonRow.add(circleGuidanceSecondaryButton);
+
+ circleGuidancePanel.add(circleGuidanceLabel);
+ circleGuidancePanel.add(Box.createRigidArea(new Dimension(0, 8)));
+ circleGuidancePanel.add(buttonRow);
+ circleGuidancePanel.setVisible(false);
+ }
+
+ private void ensureCircleGuidanceDialog() {
+ if (circleGuidancePanel == null) {
+ return;
+ }
+ if (circleGuidanceDialog != null) {
+ if (circleGuidancePanel.getParent() != circleGuidanceDialog.getContentPane()) {
+ detachCircleGuidancePanel();
+ circleGuidanceDialog.getContentPane().removeAll();
+ circleGuidanceDialog.getContentPane().add(circleGuidancePanel, BorderLayout.CENTER);
+ circleGuidanceDialog.pack();
+ }
+ return;
+ }
+
+ Window owner = SwingUtilities.getWindowAncestor(this);
+ circleGuidanceDialog = new JDialog(owner, "缁樺埗鎻愮ず", Dialog.ModalityType.MODELESS);
+ circleGuidanceDialog.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
+ circleGuidanceDialog.setResizable(false);
+ circleGuidanceDialog.setAlwaysOnTop(true);
+
+ if (owner != null && circleDialogOwnerAdapter == null) {
+ circleDialogOwnerAdapter = new ComponentAdapter() {
+ @Override
+ public void componentMoved(ComponentEvent e) {
+ positionCircleGuidanceDialog();
+ }
+
+ @Override
+ public void componentResized(ComponentEvent e) {
+ positionCircleGuidanceDialog();
+ }
+ };
+ owner.addComponentListener(circleDialogOwnerAdapter);
+ }
+
+ detachCircleGuidancePanel();
+ circleGuidanceDialog.getContentPane().setLayout(new BorderLayout());
+ circleGuidanceDialog.getContentPane().add(circleGuidancePanel, BorderLayout.CENTER);
+ circleGuidanceDialog.pack();
+ }
+
+ private void detachCircleGuidancePanel() {
+ if (circleGuidancePanel == null) {
+ return;
+ }
+ Container parent = circleGuidancePanel.getParent();
+ if (parent != null) {
+ parent.remove(circleGuidancePanel);
+ parent.revalidate();
+ parent.repaint();
+ }
+ }
+
+ private void positionCircleGuidanceDialog() {
+ if (circleGuidanceDialog == null) {
+ return;
+ }
+ Window owner = SwingUtilities.getWindowAncestor(this);
+ if (owner == null || !owner.isShowing()) {
+ return;
+ }
+ try {
+ Point ownerLocation = owner.getLocationOnScreen();
+ int x = ownerLocation.x + owner.getWidth() - circleGuidanceDialog.getWidth() - 30;
+ int y = ownerLocation.y + owner.getHeight() - circleGuidanceDialog.getHeight() - 40;
+ x = Math.max(ownerLocation.x, x);
+ y = Math.max(ownerLocation.y, y);
+ circleGuidanceDialog.setLocation(x, y);
+ } catch (IllegalComponentStateException ex) {
+ // Owner not yet displayable; skip positioning
+ }
+ }
+
+ private JButton createGuidanceButton(String text, Color bg, Color fg, boolean filled) {
+ JButton button = new JButton(text);
+ button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
+ button.setForeground(fg);
+ button.setBackground(bg);
+ button.setOpaque(true);
+ button.setFocusPainted(false);
+ button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ button.setAlignmentX(Component.LEFT_ALIGNMENT);
+ if (filled) {
+ button.setBorder(BorderFactory.createEmptyBorder(6, 14, 6, 14));
+ } else {
+ button.setBackground(Color.WHITE);
+ button.setOpaque(true);
+ button.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createLineBorder(THEME_COLOR, 1),
+ BorderFactory.createEmptyBorder(5, 12, 5, 12)));
+ }
+ button.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ if (!button.isEnabled()) {
+ return;
+ }
+ if (filled) {
+ button.setBackground(THEME_HOVER_COLOR);
+ } else {
+ button.setBackground(new Color(245, 245, 245));
+ }
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ if (!button.isEnabled()) {
+ return;
+ }
+ if (filled) {
+ button.setBackground(bg);
+ } else {
+ button.setBackground(Color.WHITE);
+ }
+ }
+ });
+ return button;
+ }
+
+ private void onCircleGuidancePrimary() {
+ if (circleGuidanceStep >= 1 && circleGuidanceStep <= 3) {
+ if (!recordCircleSamplePoint()) {
+ return;
+ }
+ if (circleGuidanceStep == 3) {
+ showCircleGuidanceStep(4);
+ } else {
+ showCircleGuidanceStep(circleGuidanceStep + 1);
+ }
+ } else if (circleGuidanceStep == 4) {
+ handleCircleCompletion();
+ }
+ }
+
+ private void onCircleGuidanceSecondary() {
+ if (circleGuidanceStep == 1 || circleGuidanceStep == 2 || circleGuidanceStep == 3) {
+ handleCircleAbort(null);
+ } else if (circleGuidanceStep == 4) {
+ restartCircleCapture();
+ }
+ }
+
+ private void hideCircleGuidancePanel() {
+ circleGuidanceStep = 0;
+ if (circleGuidancePanel != null) {
+ circleGuidancePanel.setVisible(false);
+ }
+ if (circleGuidanceDialog != null) {
+ circleGuidanceDialog.setVisible(false);
+ }
+ if (!circleDialogMode) {
+ rebuildFloatingButtonColumn();
+ }
+ }
+
+ private void handleCircleAbort(String message) {
+ // Return the wizard to step 2 without committing any captured points
+ hideEndDrawingButton();
+ addzhangaiwu.abortCircleDrawingAndReturn(message);
+ }
+
+ private void handleCircleCompletion() {
+ hideCircleGuidancePanel();
+ clearCircleGuidanceArtifacts();
+ if (endDrawingCallback != null) {
+ endDrawingCallback.run();
+ } else {
+ addzhangaiwu.finishDrawingSession();
+ }
+ }
+
+ private void prepareCircleGuidanceState() {
+ clearCircleGuidanceArtifacts();
+ circleBaseLatLon = resolveCircleBaseLatLon();
+ startCircleDataMonitor();
+ }
+
+ private void clearCircleGuidanceArtifacts() {
+ stopCircleDataMonitor();
+ circleCapturedPoints.clear();
+ circleBaseLatLon = null;
+ lastCapturedCoordinate = null;
+ if (mapRenderer != null) {
+ mapRenderer.clearCircleCaptureOverlay();
+ mapRenderer.clearCircleSampleMarkers();
+ }
+ }
+
+ private void restartCircleCapture() {
+ clearCircleGuidanceArtifacts();
+ synchronized (Coordinate.coordinates) {
+ Coordinate.coordinates.clear();
+ }
+ Coordinate.setStartSaveGngga(true);
+ circleBaseLatLon = resolveCircleBaseLatLon();
+ showCircleGuidanceStep(1);
+ startCircleDataMonitor();
+ }
+
+ private boolean recordCircleSamplePoint() {
+ Coordinate latest = getLatestCoordinate();
+ if (latest == null) {
+ JOptionPane.showMessageDialog(this, "鏈幏鍙栧埌褰撳墠浣嶇疆鍧愭爣锛岃绋嶅悗閲嶈瘯銆�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return false;
+ }
+
+ double[] base = ensureCircleBaseLatLon();
+ if (base == null) {
+ JOptionPane.showMessageDialog(this, "鍩哄噯绔欏潗鏍囨棤鏁堬紝璇峰厛鍦ㄥ熀鍑嗙珯绠$悊涓畬鎴愯缃��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return false;
+ }
+
+ double lat = parseDMToDecimal(latest.getLatitude(), latest.getLatDirection());
+ double lon = parseDMToDecimal(latest.getLongitude(), latest.getLonDirection());
+ if (!Double.isFinite(lat) || !Double.isFinite(lon)) {
+ JOptionPane.showMessageDialog(this, "閲囬泦鐐瑰潗鏍囨棤鏁堬紝璇烽噸鏂伴噰闆嗐��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return false;
+ }
+
+ double[] local = convertLatLonToLocal(lat, lon, base[0], base[1]);
+ circleCapturedPoints.add(local);
+ if (mapRenderer != null) {
+ mapRenderer.updateCircleSampleMarkers(circleCapturedPoints);
+ }
+ lastCapturedCoordinate = latest;
+ refreshCircleGuidanceButtonAvailability();
+
+ if (circleCapturedPoints.size() >= 3) {
+ CircleSolution solution = fitCircleFromPoints(circleCapturedPoints);
+ if (solution == null) {
+ circleCapturedPoints.remove(circleCapturedPoints.size() - 1);
+ JOptionPane.showMessageDialog(this, "鏃犳硶鏍规嵁褰撳墠涓変釜鐐圭敓鎴愬渾锛岃閲嶆柊閲囬泦鐐广��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ restartCircleCapture();
+ return false;
+ }
+ if (mapRenderer != null) {
+ mapRenderer.showCircleCaptureOverlay(solution.centerX, solution.centerY, solution.radius, circleCapturedPoints);
+ }
+ }
+
+ return true;
+ }
+
+ private void startCircleDataMonitor() {
+ if (circleDataMonitor == null) {
+ circleDataMonitor = new Timer(600, e -> refreshCircleGuidanceButtonAvailability());
+ circleDataMonitor.setRepeats(true);
+ }
+ if (!circleDataMonitor.isRunning()) {
+ circleDataMonitor.start();
+ }
+ refreshCircleGuidanceButtonAvailability();
+ }
+
+ private void stopCircleDataMonitor() {
+ if (circleDataMonitor != null) {
+ circleDataMonitor.stop();
+ }
+ }
+
+ private void refreshCircleGuidanceButtonAvailability() {
+ if (circleGuidancePrimaryButton == null) {
+ return;
+ }
+ boolean shouldEnable = circleGuidanceStep >= 1 && circleGuidanceStep <= 3
+ ? isCircleDataAvailable()
+ : true;
+ applyCirclePrimaryButtonState(shouldEnable);
+ }
+
+ private boolean isCircleDataAvailable() {
+ Coordinate latest = getLatestCoordinate();
+ if (latest == null) {
+ return false;
+ }
+
+ // 妫�鏌ユ槸鍚︽湁鏂扮殑鍧愭爣锛堜笌涓婃閲囬泦鐨勪笉鍚岋級
+ if (lastCapturedCoordinate != null && latest == lastCapturedCoordinate) {
+ return false;
+ }
+
+ // 妫�鏌ュ畾浣嶇姸鎬佹槸鍚︿负4锛堝浐瀹氳В锛�
+ // 褰撻�夋嫨鍓茶崏鏈虹粯鍒跺渾褰㈤殰纰嶇墿鏃讹紝闇�瑕佹鏌ヨ澶囩紪鍙峰拰瀹氫綅鐘舵��
+ Device device = Device.getGecaoji();
+ if (device == null) {
+ return false;
+ }
+
+ String positioningStatus = device.getPositioningStatus();
+ if (positioningStatus == null || !"4".equals(positioningStatus.trim())) {
+ return false;
+ }
+
+ // 妫�鏌ヨ澶囩紪鍙锋槸鍚﹀尮閰嶅壊鑽夋満缂栧彿
+ String mowerId = Setsys.getPropertyValue("mowerId");
+ String deviceId = device.getMowerNumber();
+ if (mowerId != null && !mowerId.trim().isEmpty()) {
+ if (deviceId == null || !mowerId.trim().equals(deviceId.trim())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void applyCirclePrimaryButtonState(boolean enabled) {
+ if (circleGuidancePrimaryButton == null) {
+ return;
+ }
+ if (circleGuidanceStep >= 4) {
+ enabled = true;
+ }
+ circleGuidancePrimaryButton.setEnabled(enabled);
+ if (enabled) {
+ circleGuidancePrimaryButton.setBackground(THEME_COLOR);
+ circleGuidancePrimaryButton.setForeground(Color.WHITE);
+ circleGuidancePrimaryButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ } else {
+ circleGuidancePrimaryButton.setBackground(new Color(200, 200, 200));
+ circleGuidancePrimaryButton.setForeground(new Color(120, 120, 120));
+ circleGuidancePrimaryButton.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
+ }
+
+ private Coordinate getLatestCoordinate() {
+ synchronized (Coordinate.coordinates) {
+ int size = Coordinate.coordinates.size();
+ if (size == 0) {
+ return null;
+ }
+ return Coordinate.coordinates.get(size - 1);
+ }
+ }
+
+ private double[] ensureCircleBaseLatLon() {
+ if (circleBaseLatLon == null) {
+ circleBaseLatLon = resolveCircleBaseLatLon();
+ }
+ return circleBaseLatLon;
+ }
+
+ private double[] resolveCircleBaseLatLon() {
+ String coords = null;
+
+ if (baseStation == null) {
+ baseStation = new BaseStation();
+ }
+ baseStation.load();
+ coords = baseStation.getInstallationCoordinates();
+
+ if (!isMeaningfulValue(coords)) {
+ String landNumber = Dikuaiguanli.getCurrentWorkLandNumber();
+ if (isMeaningfulValue(landNumber)) {
+ Dikuai current = Dikuai.getDikuai(landNumber);
+ if (current != null) {
+ coords = current.getBaseStationCoordinates();
+ }
+ }
+ if (!isMeaningfulValue(coords)) {
+ coords = addzhangaiwu.getActiveSessionBaseStation();
+ }
+ if (!isMeaningfulValue(coords)) {
+ return null;
+ }
+ }
+ String[] parts = coords.split(",");
+ if (parts.length < 4) {
+ return null;
+ }
+ double baseLat = parseDMToDecimal(parts[0], parts[1]);
+ double baseLon = parseDMToDecimal(parts[2], parts[3]);
+ if (!Double.isFinite(baseLat) || !Double.isFinite(baseLon)) {
+ return null;
+ }
+ return new double[]{baseLat, baseLon};
+ }
+
+ private double parseDMToDecimal(String dmm, String direction) {
+ if (dmm == null || dmm.trim().isEmpty()) {
+ return Double.NaN;
+ }
+ try {
+ String trimmed = dmm.trim();
+ int dotIndex = trimmed.indexOf('.');
+ if (dotIndex < 2) {
+ return Double.NaN;
+ }
+ int degrees = Integer.parseInt(trimmed.substring(0, dotIndex - 2));
+ double minutes = Double.parseDouble(trimmed.substring(dotIndex - 2));
+ double decimal = degrees + minutes / 60.0;
+ if ("S".equalsIgnoreCase(direction) || "W".equalsIgnoreCase(direction)) {
+ decimal = -decimal;
+ }
+ return decimal;
+ } catch (NumberFormatException ex) {
+ return Double.NaN;
+ }
+ }
+
+ private double[] convertLatLonToLocal(double lat, double lon, double baseLat, double baseLon) {
+ double deltaLat = lat - baseLat;
+ double deltaLon = lon - baseLon;
+ double meanLatRad = Math.toRadians((baseLat + lat) / 2.0);
+ double eastMeters = deltaLon * METERS_PER_DEGREE_LAT * Math.cos(meanLatRad);
+ double northMeters = deltaLat * METERS_PER_DEGREE_LAT;
+ return new double[]{eastMeters, northMeters};
+ }
+
+ private CircleSolution fitCircleFromPoints(List<double[]> points) {
+ if (points == null || points.size() < 3) {
+ return null;
+ }
+ CircleSolution best = null;
+ double bestScore = 0.0;
+ int n = points.size();
+ for (int i = 0; i < n - 2; i++) {
+ double[] p1 = points.get(i);
+ for (int j = i + 1; j < n - 1; j++) {
+ double[] p2 = points.get(j);
+ for (int k = j + 1; k < n; k++) {
+ double[] p3 = points.get(k);
+ CircleSolution candidate = circleFromThreePoints(p1, p2, p3);
+ if (candidate == null || candidate.radius <= 0) {
+ continue;
+ }
+ double minEdge = Math.min(distance(p1, p2), Math.min(distance(p2, p3), distance(p1, p3)));
+ if (minEdge > bestScore) {
+ bestScore = minEdge;
+ best = candidate;
+ }
+ }
+ }
+ }
+ return best;
+ }
+
+ private CircleSolution circleFromThreePoints(double[] p1, double[] p2, double[] p3) {
+ if (p1 == null || p2 == null || p3 == null) {
+ return null;
+ }
+ double x1 = p1[0];
+ double y1 = p1[1];
+ double x2 = p2[0];
+ double y2 = p2[1];
+ double x3 = p3[0];
+ double y3 = p3[1];
+
+ double a = x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2;
+ double d = 2.0 * a;
+ if (Math.abs(d) < 1e-6) {
+ return null;
+ }
+
+ double x1Sq = x1 * x1 + y1 * y1;
+ double x2Sq = x2 * x2 + y2 * y2;
+ double x3Sq = x3 * x3 + y3 * y3;
+
+ double centerX = (x1Sq * (y2 - y3) + x2Sq * (y3 - y1) + x3Sq * (y1 - y2)) / d;
+ double centerY = (x1Sq * (x3 - x2) + x2Sq * (x1 - x3) + x3Sq * (x2 - x1)) / d;
+ double radius = Math.hypot(centerX - x1, centerY - y1);
+ if (!Double.isFinite(centerX) || !Double.isFinite(centerY) || !Double.isFinite(radius)) {
+ return null;
+ }
+ if (radius < 0.05) {
+ return null;
+ }
+ return new CircleSolution(centerX, centerY, radius);
+ }
+
+ private double distance(double[] a, double[] b) {
+ if (a == null || b == null) {
+ return 0.0;
+ }
+ double dx = a[0] - b[0];
+ double dy = a[1] - b[1];
+ return Math.hypot(dx, dy);
+ }
+
+ private static final class CircleSolution {
+ final double centerX;
+ final double centerY;
+ final double radius;
+
+ CircleSolution(double centerX, double centerY, double radius) {
+ this.centerX = centerX;
+ this.centerY = centerY;
+ this.radius = radius;
+ }
+ }
+
+ public void hideEndDrawingButton() {
+ hideCircleGuidancePanel();
+ clearCircleGuidanceArtifacts();
+ hideFloatingDrawingControls();
+ circleDialogMode = false;
+ exitHandheldCaptureInlineUi();
+ handheldCaptureActive = false;
+ exitDrawingControlMode();
+ if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
+ stopMowerBoundaryCapture();
+ } else if (activeBoundaryMode == BoundaryCaptureMode.HANDHELD && !handheldCaptureActive) {
+ activeBoundaryMode = BoundaryCaptureMode.NONE;
+ }
+ endDrawingCallback = null;
+ visualizationPanel.revalidate();
+ visualizationPanel.repaint();
+ setHandheldMowerIconActive(false);
+ }
+
+ private void showPathPreviewReturnControls() {
+ ensureFloatingButtonInfrastructure();
+ if (drawingPauseButton != null) {
+ drawingPauseButton.setVisible(false);
+ }
+ if (endDrawingButton != null) {
+ endDrawingButton.setVisible(false);
+ }
+ if (pathPreviewReturnButton == null) {
+ pathPreviewReturnButton = createFloatingTextButton("杩斿洖");
+ pathPreviewReturnButton.setToolTipText("杩斿洖鏂板鍦板潡姝ラ");
+ pathPreviewReturnButton.addActionListener(e -> handlePathPreviewReturn());
+ }
+ pathPreviewReturnButton.setVisible(true);
+ if (floatingButtonPanel != null) {
+ floatingButtonPanel.setVisible(true);
+ if (floatingButtonPanel.getParent() != visualizationPanel) {
+ visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
+ }
+ }
+ rebuildFloatingButtonColumn();
+ }
+
+ private void hidePathPreviewReturnControls() {
+ if (pathPreviewReturnButton != null) {
+ pathPreviewReturnButton.setVisible(false);
+ }
+ rebuildFloatingButtonColumn();
+ if (floatingButtonPanel != null && floatingButtonColumn != null
+ && floatingButtonColumn.getComponentCount() == 0) {
+ floatingButtonPanel.setVisible(false);
+ }
+ }
+
+ private void handlePathPreviewReturn() {
+ Runnable callback = pathPreviewReturnAction;
+ exitMowingPathPreview();
+ if (callback != null) {
+ callback.run();
+ }
+ }
+
+ public boolean startMowingPathPreview(String landNumber,
+ String landName,
+ String boundary,
+ String obstacles,
+ String plannedPath,
+ Runnable returnAction) {
+ if (mapRenderer == null) {
+ return false;
+ }
+ // 鍏佽娌℃湁璺緞鐨勯瑙堬紙渚嬪闅滅鐗╅瑙堬級锛屽彧瑕佹湁杩斿洖鍥炶皟鍗冲彲
+ if (!isMeaningfulValue(plannedPath) && returnAction == null) {
+ return false;
+ }
+
+ if (pathPreviewActive) {
+ exitMowingPathPreview();
+ }
+
+ exitDrawingControlMode();
+ hideCircleGuidancePanel();
+ clearCircleGuidanceArtifacts();
+
+ pathPreviewReturnAction = returnAction;
+ pathPreviewActive = true;
+ mapRenderer.setPathPreviewSizingEnabled(true);
+
+ previewRestoreLandNumber = Dikuaiguanli.getCurrentWorkLandNumber();
+ previewRestoreLandName = null;
+ if (isMeaningfulValue(previewRestoreLandNumber)) {
+ Dikuai existing = Dikuai.getDikuai(previewRestoreLandNumber);
+ if (existing != null) {
+ previewRestoreLandName = existing.getLandName();
+ }
+ }
+
+ mapRenderer.setCurrentBoundary(boundary, landNumber, landName);
+ mapRenderer.setCurrentObstacles(obstacles, landNumber);
+ // 鍙湁鍦ㄦ湁璺緞鏃舵墠璁剧疆璺緞
+ if (isMeaningfulValue(plannedPath)) {
+ mapRenderer.setCurrentPlannedPath(plannedPath);
+ } else {
+ mapRenderer.setCurrentPlannedPath(null);
+ }
+ mapRenderer.clearHandheldBoundaryPreview();
+ mapRenderer.setBoundaryPointSizeScale(1.0d);
+ mapRenderer.setBoundaryPointsVisible(isMeaningfulValue(boundary));
+ // 鍚敤闅滅鐗╄竟鐣岀偣鏄剧ず
+ mapRenderer.setObstaclePointsVisible(isMeaningfulValue(obstacles));
+
+ String displayName = isMeaningfulValue(landName) ? landName : landNumber;
+ updateCurrentAreaName(displayName);
+
+ showPathPreviewReturnControls();
+ visualizationPanel.revalidate();
+ visualizationPanel.repaint();
+ return true;
+ }
+
+ public void exitMowingPathPreview() {
+ if (!pathPreviewActive) {
+ return;
+ }
+ pathPreviewActive = false;
+ if (mapRenderer != null) {
+ mapRenderer.setPathPreviewSizingEnabled(false);
+ }
+ hidePathPreviewReturnControls();
+
+ String restoreNumber = previewRestoreLandNumber;
+ String restoreName = previewRestoreLandName;
+ previewRestoreLandNumber = null;
+ previewRestoreLandName = null;
+ pathPreviewReturnAction = null;
+
+ if (restoreNumber != null) {
+ Dikuaiguanli.setCurrentWorkLand(restoreNumber, restoreName);
+ } else if (mapRenderer != null) {
+ mapRenderer.setCurrentBoundary(null, null, null);
+ mapRenderer.setCurrentObstacles((String) null, null);
+ mapRenderer.setCurrentPlannedPath(null);
+ mapRenderer.setBoundaryPointsVisible(false);
+ mapRenderer.setBoundaryPointSizeScale(1.0d);
+ mapRenderer.clearHandheldBoundaryPreview();
+ mapRenderer.resetView();
+ updateCurrentAreaName(null);
+ }
+
+ visualizationPanel.revalidate();
+ visualizationPanel.repaint();
+ }
+
+ /**
+ * 鑾峰彇鍦板浘娓叉煋鍣ㄥ疄渚�
+ */
+ public MapRenderer getMapRenderer() {
+ return mapRenderer;
+ }
+
+ public void updateCurrentAreaName(String areaName) {
+ if (areaNameLabel == null) {
+ return;
+ }
+ if (areaName == null || areaName.trim().isEmpty()) {
+ areaNameLabel.setText("鏈�夋嫨鍦板潡");
+ } else {
+ areaNameLabel.setText(areaName);
+ }
+ }
+
+ /**
+ * 閲嶇疆鍦板浘瑙嗗浘
+ */
+ public void resetMapView() {
+ if (mapRenderer != null) {
+ mapRenderer.resetView();
+ }
+ }
+
+ private void initializeDefaultAreaSelection() {
+ Dikuai.initFromProperties();
+ String persistedLandNumber = Dikuaiguanli.getPersistedWorkLandNumber();
+ if (persistedLandNumber != null) {
+ Dikuai stored = Dikuai.getDikuai(persistedLandNumber);
+ if (stored != null) {
+ Dikuaiguanli.setCurrentWorkLand(persistedLandNumber, stored.getLandName());
+ return;
+ }
+ Dikuaiguanli.setCurrentWorkLand(null, null);
+ }
+
+ Map<String, Dikuai> all = Dikuai.getAllDikuai();
+ if (all.isEmpty()) {
+ Dikuaiguanli.setCurrentWorkLand(null, null);
+ } else if (all.size() == 1) {
+ Dikuai only = all.values().iterator().next();
+ if (only != null) {
+ Dikuaiguanli.setCurrentWorkLand(only.getLandNumber(), only.getLandName());
+ }
+ }
+ }
+
+ private void refreshMapForSelectedArea() {
+ if (mapRenderer == null) {
+ return;
+ }
+
+ String currentLandNumber = Dikuaiguanli.getCurrentWorkLandNumber();
+ if (isMeaningfulValue(currentLandNumber)) {
+ Dikuai current = Dikuai.getDikuai(currentLandNumber);
+ String landName = current != null ? current.getLandName() : null;
+ Dikuaiguanli.setCurrentWorkLand(currentLandNumber, landName);
+ return;
+ }
+
+ String labelName = areaNameLabel != null ? areaNameLabel.getText() : null;
+ if (!isMeaningfulAreaName(labelName)) {
+ Dikuaiguanli.setCurrentWorkLand(null, null);
+ return;
+ }
+
+ Map<String, Dikuai> all = Dikuai.getAllDikuai();
+ for (Dikuai dikuai : all.values()) {
+ if (dikuai == null) {
+ continue;
+ }
+ String candidateName = dikuai.getLandName();
+ if (candidateName != null && candidateName.trim().equals(labelName.trim())) {
+ Dikuaiguanli.setCurrentWorkLand(dikuai.getLandNumber(), candidateName);
+ return;
+ }
+ }
+
+ Dikuaiguanli.setCurrentWorkLand(null, null);
+ }
+
+ private boolean isMeaningfulValue(String value) {
+ if (value == null) {
+ return false;
+ }
+ String trimmed = value.trim();
+ return !trimmed.isEmpty() && !"-1".equals(trimmed);
+ }
+
+ private boolean isMeaningfulAreaName(String value) {
+ if (value == null) {
+ return false;
+ }
+ String trimmed = value.trim();
+ if (trimmed.isEmpty()) {
+ return false;
+ }
+ return !"鏈�夋嫨鍦板潡".equals(trimmed);
+ }
+
- // 涓婚棰滆壊
- private final Color THEME_COLOR = new Color(46, 139, 87);
- private final Color THEME_HOVER_COLOR = new Color(30, 107, 69);
- private final Color BACKGROUND_COLOR = new Color(250, 250, 250);
- private final Color PANEL_BACKGROUND = new Color(255, 255, 255);
-
- // 缁勪欢
- private JPanel headerPanel;
- private JPanel mainContentPanel;
- private JPanel controlPanel;
- private JPanel navigationPanel;
- private JPanel visualizationPanel;
-
- // 鎸夐挳
- private JButton startBtn;
- private JButton pauseBtn;
- private JButton legendBtn;
- private JButton remoteBtn;
- private JButton areaSelectBtn;
- private JButton baseStationBtn;
-
- // 瀵艰埅鎸夐挳
- private JButton homeNavBtn;
- private JButton areasNavBtn;
- private JButton settingsNavBtn;
-
- // 鐘舵�佹樉绀�
- private JLabel statusLabel;
- private JLabel areaNameLabel;
-
- // 褰撳墠閫変腑鐨勫鑸寜閽�
- private JButton currentNavButton;
-
- // 瀵硅瘽妗嗗紩鐢�
- private LegendDialog legendDialog;
- private RemoteControlDialog remoteDialog;
- private AreaSelectionDialog areaDialog;
- private BaseStationDialog baseStationDialog;
- private Sets settingsDialog;
- private BaseStation baseStation;
-
- // 鍦板浘娓叉煋鍣�
- private MapRenderer mapRenderer;
- private static final int FLOAT_ICON_SIZE = 32;
- private JButton endDrawingButton;
- private JButton drawingPauseButton;
- private JPanel floatingButtonPanel;
- private JPanel floatingButtonColumn;
- private Runnable endDrawingCallback;
- private boolean drawingPaused;
- private ImageIcon pauseIcon;
- private ImageIcon pauseActiveIcon;
- private ImageIcon endIcon;
- private JPanel circleGuidancePanel;
- private JLabel circleGuidanceLabel;
- private JButton circleGuidancePrimaryButton;
- private JButton circleGuidanceSecondaryButton;
- private int circleGuidanceStep;
- private JDialog circleGuidanceDialog;
- private boolean circleDialogMode;
- private ComponentAdapter circleDialogOwnerAdapter;
-
- public Shouye() {
- instance = this;
- baseStation = new BaseStation();
- baseStation.load();
- initializeUI();
- setupEventHandlers();
- }
- public static Shouye getInstance() {
- return instance;
- }
-
- private void initializeUI() {
- setLayout(new BorderLayout());
- setBackground(BACKGROUND_COLOR);
-
- // 鍒涘缓鍚勪釜闈㈡澘
- createHeaderPanel();
- createMainContentPanel();
- createControlPanel();
- createNavigationPanel();
-
- // 娣诲姞鍒颁富闈㈡澘
- add(headerPanel, BorderLayout.NORTH);
- add(mainContentPanel, BorderLayout.CENTER);
- add(controlPanel, BorderLayout.SOUTH);
-
- // 鍒濆鍖栧湴鍥炬覆鏌撳櫒
- mapRenderer = new MapRenderer(visualizationPanel);
-
- // 鍒濆鍖栧璇濇寮曠敤涓簄ull锛屽欢杩熷垱寤�
- legendDialog = null;
- remoteDialog = null;
- areaDialog = null;
- baseStationDialog = null;
- settingsDialog = null;
-
- // 璁剧疆榛樿鐘舵��
- setNavigationActive(homeNavBtn);
-
- initializeDefaultAreaSelection();
- refreshMapForSelectedArea();
- }
-
- private void createHeaderPanel() {
- headerPanel = new JPanel(new BorderLayout());
- headerPanel.setBackground(PANEL_BACKGROUND);
- headerPanel.setBorder(BorderFactory.createEmptyBorder(15, 20, 15, 20));
- headerPanel.setPreferredSize(new Dimension(0, 80));
-
- // 宸︿晶淇℃伅鍖哄煙
- JPanel leftInfoPanel = new JPanel(new GridLayout(2, 1));
- leftInfoPanel.setBackground(PANEL_BACKGROUND);
-
- areaNameLabel = new JLabel("鏈�夋嫨鍦板潡");
- areaNameLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 18));
- areaNameLabel.setForeground(Color.BLACK);
- areaNameLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
- areaNameLabel.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseClicked(MouseEvent e) {
- Dikuaiguanli.showDikuaiManagement(Shouye.this, null);
- }
-
- @Override
- public void mouseEntered(MouseEvent e) {
- areaNameLabel.setForeground(THEME_COLOR);
- }
-
- @Override
- public void mouseExited(MouseEvent e) {
- areaNameLabel.setForeground(Color.BLACK);
- }
- });
-
- statusLabel = new JLabel("寰呮満");
- statusLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
- statusLabel.setForeground(Color.GRAY);
-
- leftInfoPanel.add(areaNameLabel);
- leftInfoPanel.add(statusLabel);
-
- // 鍙充晶鎿嶄綔鍖哄煙
- JPanel rightActionPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
- rightActionPanel.setBackground(PANEL_BACKGROUND);
-
- JButton notificationBtn = createIconButton("馃敂", 40);
-
- // 淇敼璁剧疆鎸夐挳锛氫娇鐢ㄥ浘鐗囨浛浠nicode鍥炬爣
- JButton settingsBtn = new JButton();
- try {
- ImageIcon settingsIcon = new ImageIcon("image/sets.png");
- // 璋冩暣鍥剧墖澶у皬浠ラ�傚簲鎸夐挳
- Image scaledImage = settingsIcon.getImage().getScaledInstance(30, 30, Image.SCALE_SMOOTH);
- settingsBtn.setIcon(new ImageIcon(scaledImage));
- } catch (Exception e) {
- // 濡傛灉鍥剧墖鍔犺浇澶辫触锛屼娇鐢ㄩ粯璁ゆ枃鏈�
- settingsBtn.setText("璁剧疆");
- System.err.println("鏃犳硶鍔犺浇璁剧疆鍥炬爣: " + e.getMessage());
- }
- settingsBtn.setPreferredSize(new Dimension(40, 40));
- settingsBtn.setBackground(PANEL_BACKGROUND);
- settingsBtn.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
- settingsBtn.setFocusPainted(false);
-
- // 娣诲姞鎮仠鏁堟灉
- settingsBtn.addMouseListener(new MouseAdapter() {
- public void mouseEntered(MouseEvent e) {
- settingsBtn.setBackground(new Color(240, 240, 240));
- }
- public void mouseExited(MouseEvent e) {
- settingsBtn.setBackground(PANEL_BACKGROUND);
- }
- });
-
- // 娣诲姞璁剧疆鎸夐挳浜嬩欢
- settingsBtn.addActionListener(e -> showSettingsDialog());
-
- rightActionPanel.add(notificationBtn);
- rightActionPanel.add(settingsBtn);
-
- headerPanel.add(leftInfoPanel, BorderLayout.WEST);
- headerPanel.add(rightActionPanel, BorderLayout.EAST);
- }
-
- private void createMainContentPanel() {
- mainContentPanel = new JPanel(new BorderLayout());
- mainContentPanel.setBackground(BACKGROUND_COLOR);
-
- // 鍙鍖栧尯鍩� - 浣跨敤MapRenderer杩涜缁樺埗
- visualizationPanel = new JPanel() {
- @Override
- protected void paintComponent(Graphics g) {
- super.paintComponent(g);
- // 濮旀墭缁橫apRenderer杩涜缁樺埗
- if (mapRenderer != null) {
- mapRenderer.renderMap(g);
- }
- }
- };
- visualizationPanel.setLayout(new BorderLayout());
-
- // 鍒涘缓鍔熻兘鎸夐挳闈㈡澘锛堟斁鍦ㄥ乏涓婅锛�
- JPanel functionButtonsPanel = new JPanel();
- functionButtonsPanel.setLayout(new BoxLayout(functionButtonsPanel, BoxLayout.Y_AXIS));
- functionButtonsPanel.setOpaque(false);
- functionButtonsPanel.setBorder(BorderFactory.createEmptyBorder(20, 5, 0, 0)); // 宸﹁竟璺濇敼涓�5鍍忕礌
-
- legendBtn = createFunctionButton("鍥句緥", "馃摉");
- baseStationBtn = createFunctionButton("鍩哄噯绔�", "馃摗"); // 璋冩暣鍒扮浜屼綅
- areaSelectBtn = createFunctionButton("鍦板潡", "馃尶"); // 璋冩暣鍒扮涓変綅
- remoteBtn = createFunctionButton("閬ユ帶", "馃幃"); // 璋冩暣鍒版渶鍚�
-
- functionButtonsPanel.add(legendBtn);
- functionButtonsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
- functionButtonsPanel.add(baseStationBtn);
- functionButtonsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
- functionButtonsPanel.add(areaSelectBtn);
- functionButtonsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
- functionButtonsPanel.add(remoteBtn);
-
- visualizationPanel.add(functionButtonsPanel, BorderLayout.WEST);
-
- mainContentPanel.add(visualizationPanel, BorderLayout.CENTER);
- }
-
- private void createControlPanel() {
- controlPanel = new JPanel(new BorderLayout());
- controlPanel.setBackground(PANEL_BACKGROUND);
- controlPanel.setBorder(BorderFactory.createEmptyBorder(15, 20, 15, 20));
- controlPanel.setPreferredSize(new Dimension(0, 100));
-
- JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 20, 0));
- buttonPanel.setBackground(PANEL_BACKGROUND);
-
- startBtn = createControlButton("寮�濮�", THEME_COLOR);
- applyButtonIcon(startBtn, "image/startzuoye.png");
- pauseBtn = createControlButton("鏆傚仠", Color.ORANGE);
- applyButtonIcon(pauseBtn, "image/zantingzuoye.png");
- pauseBtn.setEnabled(false);
-
- buttonPanel.add(startBtn);
- buttonPanel.add(pauseBtn);
-
- controlPanel.add(buttonPanel, BorderLayout.CENTER);
- }
-
- private void createNavigationPanel() {
- navigationPanel = new JPanel(new GridLayout(1, 3));
- navigationPanel.setBackground(PANEL_BACKGROUND);
- navigationPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
- navigationPanel.setPreferredSize(new Dimension(0, 70));
-
- homeNavBtn = createNavButton("棣栭〉", "馃彔");
- areasNavBtn = createNavButton("鍦板潡", "馃尶");
- settingsNavBtn = createNavButton("璁剧疆", "鈿欙笍");
-
- navigationPanel.add(homeNavBtn);
- navigationPanel.add(areasNavBtn);
- navigationPanel.add(settingsNavBtn);
-
- // 娣诲姞鍒颁富鐣岄潰搴曢儴
- add(navigationPanel, BorderLayout.SOUTH);
- }
-
- private JButton createIconButton(String icon, int size) {
- JButton button = new JButton(icon);
- button.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 20));
- button.setPreferredSize(new Dimension(size, size));
- button.setBackground(PANEL_BACKGROUND);
- button.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
- button.setFocusPainted(false);
-
- // 鎮仠鏁堟灉
- button.addMouseListener(new MouseAdapter() {
- public void mouseEntered(MouseEvent e) {
- button.setBackground(new Color(240, 240, 240));
- }
- public void mouseExited(MouseEvent e) {
- button.setBackground(PANEL_BACKGROUND);
- }
- });
-
- return button;
- }
-
- private JButton createFunctionButton(String text, String icon) {
- JButton button = new JButton("<html><center>" + icon + "<br>" + text + "</center></html>");
- button.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
- button.setPreferredSize(new Dimension(80, 70));
- button.setBackground(new Color(0, 0, 0, 0)); // 瀹屽叏閫忔槑鑳屾櫙
- button.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); // 鍙繚鐣欏唴杈硅窛
- button.setFocusPainted(false);
- button.setOpaque(false); // 璁剧疆涓轰笉閫忔槑锛岀‘淇濊儗鏅�忔槑
-
- // 鍘绘帀鎮仠鏁堟灉
- button.addMouseListener(new MouseAdapter() {
- public void mouseEntered(MouseEvent e) {
- // 鎮仠鏃朵笉鏀瑰彉浠讳綍鏍峰紡
- }
- public void mouseExited(MouseEvent e) {
- // 鎮仠鏃朵笉鏀瑰彉浠讳綍鏍峰紡
- }
- });
-
- return button;
- }
-
- private JButton createControlButton(String text, Color color) {
- JButton button = new JButton(text);
- button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
- button.setBackground(color);
- button.setForeground(Color.WHITE);
- button.setBorder(BorderFactory.createEmptyBorder(15, 0, 15, 0));
- button.setFocusPainted(false);
-
- // 鎮仠鏁堟灉
- button.addMouseListener(new MouseAdapter() {
- public void mouseEntered(MouseEvent e) {
- if (button.isEnabled()) {
- if (color == THEME_COLOR) {
- button.setBackground(THEME_HOVER_COLOR);
- } else {
- button.setBackground(new Color(255, 165, 0));
- }
- }
- }
- public void mouseExited(MouseEvent e) {
- if (button.isEnabled()) {
- button.setBackground(color);
- }
- }
- });
-
- return button;
- }
-
- private void applyButtonIcon(JButton button, String imagePath) {
- try {
- ImageIcon icon = new ImageIcon(imagePath);
- Image scaledImage = icon.getImage().getScaledInstance(28, 28, Image.SCALE_SMOOTH);
- button.setIcon(new ImageIcon(scaledImage));
- button.setHorizontalAlignment(SwingConstants.CENTER);
- button.setIconTextGap(10);
- button.setHorizontalTextPosition(SwingConstants.RIGHT);
- button.setVerticalTextPosition(SwingConstants.CENTER);
- } catch (Exception e) {
- System.err.println("鏃犳硶鍔犺浇鎸夐挳鍥炬爣: " + imagePath + " -> " + e.getMessage());
- }
- }
-
- private JButton createNavButton(String text, String icon) {
- JButton button = new JButton("<html><center>" + icon + "<br>" + text + "</center></html>");
- button.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
- button.setBackground(PANEL_BACKGROUND);
- button.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 5));
- button.setFocusPainted(false);
-
- // 鎮仠鏁堟灉
- button.addMouseListener(new MouseAdapter() {
- public void mouseEntered(MouseEvent e) {
- if (button != currentNavButton) {
- button.setBackground(new Color(240, 240, 240));
- }
- }
- public void mouseExited(MouseEvent e) {
- if (button != currentNavButton) {
- button.setBackground(PANEL_BACKGROUND);
- }
- }
- });
-
- return button;
- }
-
- private void setNavigationActive(JButton navButton) {
- // 閲嶇疆鎵�鏈夊鑸寜閽牱寮�
- for (Component comp : navigationPanel.getComponents()) {
- if (comp instanceof JButton) {
- JButton btn = (JButton) comp;
- btn.setBackground(PANEL_BACKGROUND);
- btn.setForeground(Color.BLACK);
- }
- }
-
- // 璁剧疆褰撳墠閫変腑鎸夐挳鏍峰紡
- navButton.setBackground(THEME_COLOR);
- navButton.setForeground(Color.WHITE);
- currentNavButton = navButton;
- }
-
- private void setupEventHandlers() {
- // 瀵艰埅鎸夐挳浜嬩欢
- homeNavBtn.addActionListener(e -> setNavigationActive(homeNavBtn));
- areasNavBtn.addActionListener(e -> setNavigationActive(areasNavBtn));
- settingsNavBtn.addActionListener(e -> setNavigationActive(settingsNavBtn));
-
- // 鍔熻兘鎸夐挳浜嬩欢
- legendBtn.addActionListener(e -> showLegendDialog());
- remoteBtn.addActionListener(e -> showRemoteControlDialog());
- areaSelectBtn.addActionListener(e -> {
- // 鐐瑰嚮鈥滃湴鍧椻�濈洿鎺ユ墦寮�鍦板潡绠$悊瀵硅瘽妗嗭紙鑻ラ渶瑕佸彲浼犲叆鐗瑰畾鍦板潡缂栧彿锛�
- Dikuaiguanli.showDikuaiManagement(this, null);
- });
- baseStationBtn.addActionListener(e -> showBaseStationDialog());
-
- // 鎺у埗鎸夐挳浜嬩欢
- startBtn.addActionListener(e -> startMowing());
- pauseBtn.addActionListener(e -> pauseMowing());
- }
-
- private void showSettingsDialog() {
- if (settingsDialog == null) {
- Window parentWindow = SwingUtilities.getWindowAncestor(this);
- if (parentWindow instanceof JFrame) {
- settingsDialog = new Sets((JFrame) parentWindow, THEME_COLOR);
- } else if (parentWindow instanceof JDialog) {
- settingsDialog = new Sets((JDialog) parentWindow, THEME_COLOR);
- } else {
- // Fallback to a frameless dialog when no parent is available
- settingsDialog = new Sets((JFrame) null, THEME_COLOR);
- }
- }
- settingsDialog.setVisible(true);
- }
-
- private void showLegendDialog() {
- if (legendDialog == null) {
- Window parentWindow = SwingUtilities.getWindowAncestor(this);
- if (parentWindow != null) {
- legendDialog = new LegendDialog(this, THEME_COLOR);
- } else {
- // 濡傛灉娌℃湁鐖剁獥鍙o紝鍒涘缓鏃犵埗绐楀彛鐨勫璇濇
- legendDialog = new LegendDialog((JFrame) null, THEME_COLOR);
- }
- }
- legendDialog.setVisible(true);
- }
-
- private void showRemoteControlDialog() {
- if (remoteDialog == null) {
- Window parentWindow = SwingUtilities.getWindowAncestor(this);
- if (parentWindow != null) {
- remoteDialog = new RemoteControlDialog(this, THEME_COLOR);
- } else {
- remoteDialog = new RemoteControlDialog((JFrame) null, THEME_COLOR);
- }
- }
- remoteDialog.setVisible(true);
- }
-
- private void showAreaSelectionDialog() {
- if (areaDialog == null) {
- Window parentWindow = SwingUtilities.getWindowAncestor(this);
- if (parentWindow != null) {
- areaDialog = new AreaSelectionDialog(this, THEME_COLOR, areaNameLabel, statusLabel);
- } else {
- areaDialog = new AreaSelectionDialog((JFrame) null, THEME_COLOR, areaNameLabel, statusLabel);
- }
- }
- areaDialog.setVisible(true);
- }
-
- private void showBaseStationDialog() {
- if (baseStation == null) {
- baseStation = new BaseStation();
- }
- baseStation.load();
-
- Component dialogParent = this;
-
- if (!hasValidBaseStationId()) {
- boolean recorded = promptForBaseStationId(dialogParent);
- if (!recorded) {
- return;
- }
- }
-
- Device device = new Device();
- device.initFromProperties();
-
- if (baseStationDialog == null) {
- baseStationDialog = new BaseStationDialog(dialogParent, THEME_COLOR, device, baseStation);
- } else {
- baseStationDialog.refreshData();
- }
- baseStationDialog.setVisible(true);
- }
-
- private boolean hasValidBaseStationId() {
- if (baseStation == null) {
- return false;
- }
- String deviceId = baseStation.getDeviceId();
- if (deviceId == null) {
- return false;
- }
- String trimmed = deviceId.trim();
- return !trimmed.isEmpty() && !"-1".equals(trimmed);
- }
-
- private boolean promptForBaseStationId(Component parentComponent) {
- if (baseStation == null) {
- baseStation = new BaseStation();
- }
-
- while (true) {
- String input = JOptionPane.showInputDialog(parentComponent,
- "璇疯緭鍏ュ熀鍑嗙珯缂栧彿", "褰曞叆鍩哄噯绔欑紪鍙�", JOptionPane.PLAIN_MESSAGE);
-
- if (input == null) {
- return false;
- }
-
- String trimmed = input.trim();
- if (trimmed.isEmpty()) {
- JOptionPane.showMessageDialog(parentComponent,
- "鍩哄噯绔欑紪鍙蜂笉鑳戒负绌猴紝璇烽噸鏂拌緭鍏ャ��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
- continue;
- }
-
- try {
- baseStation.updateByDeviceId(trimmed,
- baseStation.getInstallationCoordinates(),
- baseStation.getIotSimCardNumber(),
- baseStation.getDeviceActivationTime(),
- baseStation.getDataUpdateTime());
- baseStation.load();
- JOptionPane.showMessageDialog(parentComponent,
- "鍩哄噯绔欑紪鍙峰凡淇濆瓨銆�", "鎿嶄綔鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
- return true;
- } catch (IllegalArgumentException ex) {
- JOptionPane.showMessageDialog(parentComponent,
- ex.getMessage(), "杈撳叆閿欒", JOptionPane.ERROR_MESSAGE);
- }
- }
- }
-
- private void startMowing() {
- statusLabel.setText("浣滀笟涓�");
- startBtn.setEnabled(false);
- pauseBtn.setEnabled(true);
- }
-
- private void pauseMowing() {
- statusLabel.setText("鏆傚仠涓�");
- startBtn.setEnabled(true);
- pauseBtn.setEnabled(false);
- }
-
- private JButton createFloatingIconButton() {
- JButton button = new JButton();
- button.setContentAreaFilled(false);
- button.setBorder(BorderFactory.createEmptyBorder());
- button.setFocusPainted(false);
- button.setOpaque(false);
- button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
- int size = FLOAT_ICON_SIZE + 8;
- button.setPreferredSize(new Dimension(size, size));
- return button;
- }
-
- private ImageIcon loadScaledIcon(String path, int width, int height) {
- try {
- ImageIcon icon = new ImageIcon(path);
- if (icon.getIconWidth() <= 0 || icon.getIconHeight() <= 0) {
- return null;
- }
- Image scaled = icon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH);
- return new ImageIcon(scaled);
- } catch (Exception ex) {
- System.err.println("鍔犺浇鍥炬爣澶辫触: " + path + " - " + ex.getMessage());
- return null;
- }
- }
-
- private void ensureFloatingIconsLoaded() {
- if (pauseIcon == null) {
- pauseIcon = loadScaledIcon("image/zanting.png", FLOAT_ICON_SIZE, FLOAT_ICON_SIZE);
- }
- if (pauseActiveIcon == null) {
- pauseActiveIcon = loadScaledIcon("image/zantingzhong.png", FLOAT_ICON_SIZE, FLOAT_ICON_SIZE);
- }
- if (endIcon == null) {
- endIcon = loadScaledIcon("image/end.png", FLOAT_ICON_SIZE, FLOAT_ICON_SIZE);
- }
- }
-
- private void updatePauseButtonVisual() {
- if (drawingPauseButton == null) {
- return;
- }
- if (drawingPaused) {
- if (pauseActiveIcon != null) {
- drawingPauseButton.setIcon(pauseActiveIcon);
- drawingPauseButton.setText(null);
- } else {
- drawingPauseButton.setText("鏆傚仠涓�");
- drawingPauseButton.setIcon(null);
- }
- drawingPauseButton.setToolTipText("鐐瑰嚮鎭㈠缁樺埗");
- } else {
- if (pauseIcon != null) {
- drawingPauseButton.setIcon(pauseIcon);
- drawingPauseButton.setText(null);
- } else {
- drawingPauseButton.setText("鏆傚仠");
- drawingPauseButton.setIcon(null);
- }
- drawingPauseButton.setToolTipText("鐐瑰嚮鏆傚仠缁樺埗");
- }
- }
-
- private void applyDrawingPauseState(boolean paused, boolean notifyCoordinate) {
- drawingPaused = paused;
- updatePauseButtonVisual();
- if (notifyCoordinate) {
- Coordinate.setStartSaveGngga(!paused);
- }
- }
-
- private void toggleDrawingPause() {
- applyDrawingPauseState(!drawingPaused, true);
- }
-
- public void showEndDrawingButton(Runnable callback) {
- showEndDrawingButton(callback, null);
- }
-
- public void showEndDrawingButton(Runnable callback, String drawingShape) {
- endDrawingCallback = callback;
- applyDrawingPauseState(false, false);
- circleDialogMode = drawingShape != null && "circle".equalsIgnoreCase(drawingShape);
-
- if (circleDialogMode) {
- hideFloatingDrawingControls();
- ensureCircleGuidancePanel();
- showCircleGuidanceStep(1);
- visualizationPanel.revalidate();
- visualizationPanel.repaint();
- return;
- }
-
- circleDialogMode = false;
- hideCircleGuidancePanel();
-
- ensureFloatingIconsLoaded();
- ensureFloatingButtonInfrastructure();
-
- endDrawingButton.setVisible(true);
- if (drawingPauseButton != null) {
- drawingPauseButton.setVisible(true);
- }
-
- floatingButtonPanel.setVisible(true);
- if (floatingButtonPanel.getParent() != visualizationPanel) {
- visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
- }
-
- rebuildFloatingButtonColumn();
- visualizationPanel.revalidate();
- visualizationPanel.repaint();
- }
-
- private void ensureFloatingButtonInfrastructure() {
- if (endDrawingButton == null) {
- endDrawingButton = createFloatingIconButton();
- endDrawingButton.addActionListener(e -> {
- if (endDrawingCallback != null) {
- endDrawingCallback.run();
- }
- });
- }
- if (endIcon != null) {
- endDrawingButton.setIcon(endIcon);
- endDrawingButton.setText(null);
- } else {
- endDrawingButton.setText("缁撴潫缁樺埗");
- }
- endDrawingButton.setToolTipText("缁撴潫缁樺埗");
-
- if (drawingPauseButton == null) {
- drawingPauseButton = createFloatingIconButton();
- drawingPauseButton.addActionListener(e -> toggleDrawingPause());
- }
- updatePauseButtonVisual();
-
- if (floatingButtonPanel == null) {
- floatingButtonPanel = new JPanel(new BorderLayout());
- floatingButtonPanel.setOpaque(false);
- floatingButtonPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 20));
-
- floatingButtonColumn = new JPanel();
- floatingButtonColumn.setOpaque(false);
- floatingButtonColumn.setLayout(new BoxLayout(floatingButtonColumn, BoxLayout.Y_AXIS));
- floatingButtonPanel.add(floatingButtonColumn, BorderLayout.EAST);
- }
- }
-
- private void hideFloatingDrawingControls() {
- if (drawingPauseButton != null) {
- drawingPauseButton.setVisible(false);
- }
- if (endDrawingButton != null) {
- endDrawingButton.setVisible(false);
- }
- if (floatingButtonPanel != null) {
- floatingButtonPanel.setVisible(false);
- }
- if (!circleDialogMode) {
- rebuildFloatingButtonColumn();
- }
- }
-
- private void rebuildFloatingButtonColumn() {
- if (floatingButtonColumn == null) {
- return;
- }
- floatingButtonColumn.removeAll();
- boolean added = false;
- if (!circleDialogMode && circleGuidancePanel != null && circleGuidancePanel.isVisible()) {
- floatingButtonColumn.add(circleGuidancePanel);
- added = true;
- }
- if (!circleDialogMode && drawingPauseButton != null && drawingPauseButton.isVisible()) {
- if (added) {
- floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
- }
- floatingButtonColumn.add(drawingPauseButton);
- added = true;
- }
- if (!circleDialogMode && endDrawingButton != null && endDrawingButton.isVisible()) {
- if (added) {
- floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
- }
- floatingButtonColumn.add(endDrawingButton);
- }
- floatingButtonColumn.revalidate();
- floatingButtonColumn.repaint();
- }
-
- private void showCircleGuidanceStep(int step) {
- ensureCircleGuidancePanel();
- if (circleGuidancePanel == null) {
- return;
- }
- circleGuidanceStep = step;
- if (step == 1) {
- circleGuidanceLabel.setText("鏄惁灏嗗綋鍓嶇偣璁剧疆涓哄渾蹇冿紵");
- circleGuidancePrimaryButton.setText("鏄�");
- circleGuidanceSecondaryButton.setText("杩斿洖");
- circleGuidanceSecondaryButton.setVisible(true);
- } else if (step == 2) {
- circleGuidanceLabel.setText("鏄惁灏嗗綋鍓嶇偣璁剧疆涓哄崐寰勭偣锛�");
- circleGuidancePrimaryButton.setText("鏄�");
- circleGuidanceSecondaryButton.setVisible(false);
- } else {
- hideCircleGuidancePanel();
- return;
- }
- circleGuidancePanel.setVisible(true);
-
- if (circleDialogMode) {
- ensureCircleGuidanceDialog();
- if (circleGuidanceDialog != null) {
- circleGuidanceDialog.pack();
- positionCircleGuidanceDialog();
- circleGuidanceDialog.setVisible(true);
- circleGuidanceDialog.toFront();
- }
- } else {
- rebuildFloatingButtonColumn();
- }
- }
-
- private void ensureCircleGuidancePanel() {
- if (circleGuidancePanel != null) {
- return;
- }
- circleGuidancePanel = new JPanel();
- circleGuidancePanel.setLayout(new BoxLayout(circleGuidancePanel, BoxLayout.Y_AXIS));
- circleGuidancePanel.setOpaque(true);
- circleGuidancePanel.setBackground(new Color(255, 255, 255, 235));
- circleGuidancePanel.setBorder(BorderFactory.createCompoundBorder(
- BorderFactory.createLineBorder(THEME_COLOR, 1),
- BorderFactory.createEmptyBorder(10, 12, 10, 12)));
- circleGuidancePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
-
- circleGuidanceLabel = new JLabel();
- circleGuidanceLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 13));
- circleGuidanceLabel.setForeground(new Color(33, 37, 41));
- circleGuidanceLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
-
- JPanel buttonRow = new JPanel();
- buttonRow.setLayout(new BoxLayout(buttonRow, BoxLayout.X_AXIS));
- buttonRow.setOpaque(false);
- buttonRow.setAlignmentX(Component.LEFT_ALIGNMENT);
-
- circleGuidancePrimaryButton = createGuidanceButton("鏄�", THEME_COLOR, Color.WHITE, true);
- circleGuidancePrimaryButton.addActionListener(e -> onCircleGuidancePrimary());
-
- circleGuidanceSecondaryButton = createGuidanceButton("鍚�", Color.WHITE, THEME_COLOR, false);
- circleGuidanceSecondaryButton.addActionListener(e -> onCircleGuidanceSecondary());
-
- buttonRow.add(circleGuidancePrimaryButton);
- buttonRow.add(Box.createRigidArea(new Dimension(8, 0)));
- buttonRow.add(circleGuidanceSecondaryButton);
-
- circleGuidancePanel.add(circleGuidanceLabel);
- circleGuidancePanel.add(Box.createRigidArea(new Dimension(0, 8)));
- circleGuidancePanel.add(buttonRow);
- circleGuidancePanel.setVisible(false);
- }
-
- private void ensureCircleGuidanceDialog() {
- if (circleGuidancePanel == null) {
- return;
- }
- if (circleGuidanceDialog != null) {
- if (circleGuidancePanel.getParent() != circleGuidanceDialog.getContentPane()) {
- detachCircleGuidancePanel();
- circleGuidanceDialog.getContentPane().removeAll();
- circleGuidanceDialog.getContentPane().add(circleGuidancePanel, BorderLayout.CENTER);
- circleGuidanceDialog.pack();
- }
- return;
- }
-
- Window owner = SwingUtilities.getWindowAncestor(this);
- circleGuidanceDialog = new JDialog(owner, "缁樺埗鎻愮ず", Dialog.ModalityType.MODELESS);
- circleGuidanceDialog.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
- circleGuidanceDialog.setResizable(false);
- circleGuidanceDialog.setAlwaysOnTop(true);
-
- if (owner != null && circleDialogOwnerAdapter == null) {
- circleDialogOwnerAdapter = new ComponentAdapter() {
- @Override
- public void componentMoved(ComponentEvent e) {
- positionCircleGuidanceDialog();
- }
-
- @Override
- public void componentResized(ComponentEvent e) {
- positionCircleGuidanceDialog();
- }
- };
- owner.addComponentListener(circleDialogOwnerAdapter);
- }
-
- detachCircleGuidancePanel();
- circleGuidanceDialog.getContentPane().setLayout(new BorderLayout());
- circleGuidanceDialog.getContentPane().add(circleGuidancePanel, BorderLayout.CENTER);
- circleGuidanceDialog.pack();
- }
-
- private void detachCircleGuidancePanel() {
- if (circleGuidancePanel == null) {
- return;
- }
- Container parent = circleGuidancePanel.getParent();
- if (parent != null) {
- parent.remove(circleGuidancePanel);
- parent.revalidate();
- parent.repaint();
- }
- }
-
- private void positionCircleGuidanceDialog() {
- if (circleGuidanceDialog == null) {
- return;
- }
- Window owner = SwingUtilities.getWindowAncestor(this);
- if (owner == null || !owner.isShowing()) {
- return;
- }
- try {
- Point ownerLocation = owner.getLocationOnScreen();
- int x = ownerLocation.x + owner.getWidth() - circleGuidanceDialog.getWidth() - 30;
- int y = ownerLocation.y + owner.getHeight() - circleGuidanceDialog.getHeight() - 40;
- x = Math.max(ownerLocation.x, x);
- y = Math.max(ownerLocation.y, y);
- circleGuidanceDialog.setLocation(x, y);
- } catch (IllegalComponentStateException ex) {
- // Owner not yet displayable; skip positioning
- }
- }
-
- private JButton createGuidanceButton(String text, Color bg, Color fg, boolean filled) {
- JButton button = new JButton(text);
- button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
- button.setForeground(fg);
- button.setBackground(bg);
- button.setOpaque(true);
- button.setFocusPainted(false);
- button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
- button.setAlignmentX(Component.LEFT_ALIGNMENT);
- if (filled) {
- button.setBorder(BorderFactory.createEmptyBorder(6, 14, 6, 14));
- } else {
- button.setBackground(Color.WHITE);
- button.setOpaque(true);
- button.setBorder(BorderFactory.createCompoundBorder(
- BorderFactory.createLineBorder(THEME_COLOR, 1),
- BorderFactory.createEmptyBorder(5, 12, 5, 12)));
- }
- button.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseEntered(MouseEvent e) {
- if (filled) {
- button.setBackground(THEME_HOVER_COLOR);
- } else {
- button.setBackground(new Color(245, 245, 245));
- }
- }
-
- @Override
- public void mouseExited(MouseEvent e) {
- if (filled) {
- button.setBackground(bg);
- } else {
- button.setBackground(Color.WHITE);
- }
- }
- });
- return button;
- }
-
- private void onCircleGuidancePrimary() {
- if (circleGuidanceStep == 1) {
- showCircleGuidanceStep(2);
- } else {
- handleCircleCompletion();
- }
- }
-
- private void onCircleGuidanceSecondary() {
- if (circleGuidanceStep == 1) {
- handleCircleAbort(null);
- } else if (circleGuidanceStep == 2) {
- handleCircleAbort(null);
- } else {
- hideCircleGuidancePanel();
- }
- }
-
- private void hideCircleGuidancePanel() {
- circleGuidanceStep = 0;
- if (circleGuidancePanel != null) {
- circleGuidancePanel.setVisible(false);
- }
- if (circleGuidanceDialog != null) {
- circleGuidanceDialog.setVisible(false);
- }
- if (!circleDialogMode) {
- rebuildFloatingButtonColumn();
- }
- }
-
- private void handleCircleAbort(String message) {
- // Return the wizard to step 2 without committing any captured points
- hideEndDrawingButton();
- addzhangaiwu.abortCircleDrawingAndReturn(message);
- }
-
- private void handleCircleCompletion() {
- hideCircleGuidancePanel();
- if (endDrawingCallback != null) {
- endDrawingCallback.run();
- } else {
- addzhangaiwu.finishDrawingSession();
- }
- }
-
- public void hideEndDrawingButton() {
- hideCircleGuidancePanel();
- hideFloatingDrawingControls();
- circleDialogMode = false;
- applyDrawingPauseState(false, false);
- endDrawingCallback = null;
- visualizationPanel.revalidate();
- visualizationPanel.repaint();
- }
-
- /**
- * 鑾峰彇鍦板浘娓叉煋鍣ㄥ疄渚�
- */
- public MapRenderer getMapRenderer() {
- return mapRenderer;
- }
-
- public void updateCurrentAreaName(String areaName) {
- if (areaNameLabel == null) {
- return;
- }
- if (areaName == null || areaName.trim().isEmpty()) {
- areaNameLabel.setText("鏈�夋嫨鍦板潡");
- } else {
- areaNameLabel.setText(areaName);
- }
- }
-
- /**
- * 閲嶇疆鍦板浘瑙嗗浘
- */
- public void resetMapView() {
- if (mapRenderer != null) {
- mapRenderer.resetView();
- }
- }
-
- private void initializeDefaultAreaSelection() {
- Dikuai.initFromProperties();
- Map<String, Dikuai> all = Dikuai.getAllDikuai();
- if (all.isEmpty()) {
- Dikuaiguanli.setCurrentWorkLand(null, null);
- } else if (all.size() == 1) {
- Dikuai only = all.values().iterator().next();
- if (only != null) {
- Dikuaiguanli.setCurrentWorkLand(only.getLandNumber(), only.getLandName());
- }
- }
- }
-
- private void refreshMapForSelectedArea() {
- if (mapRenderer == null) {
- return;
- }
-
- String currentLandNumber = Dikuaiguanli.getCurrentWorkLandNumber();
- if (isMeaningfulValue(currentLandNumber)) {
- Dikuai current = Dikuai.getDikuai(currentLandNumber);
- String landName = current != null ? current.getLandName() : null;
- Dikuaiguanli.setCurrentWorkLand(currentLandNumber, landName);
- return;
- }
-
- String labelName = areaNameLabel != null ? areaNameLabel.getText() : null;
- if (!isMeaningfulAreaName(labelName)) {
- Dikuaiguanli.setCurrentWorkLand(null, null);
- return;
- }
-
- Map<String, Dikuai> all = Dikuai.getAllDikuai();
- for (Dikuai dikuai : all.values()) {
- if (dikuai == null) {
- continue;
- }
- String candidateName = dikuai.getLandName();
- if (candidateName != null && candidateName.trim().equals(labelName.trim())) {
- Dikuaiguanli.setCurrentWorkLand(dikuai.getLandNumber(), candidateName);
- return;
- }
- }
-
- Dikuaiguanli.setCurrentWorkLand(null, null);
- }
-
- private boolean isMeaningfulValue(String value) {
- if (value == null) {
- return false;
- }
- String trimmed = value.trim();
- return !trimmed.isEmpty() && !"-1".equals(trimmed);
- }
-
- private boolean isMeaningfulAreaName(String value) {
- if (value == null) {
- return false;
- }
- String trimmed = value.trim();
- if (trimmed.isEmpty()) {
- return false;
- }
- return !"鏈�夋嫨鍦板潡".equals(trimmed);
- }
-
- // 娴嬭瘯鏂规硶
+ // 娴嬭瘯鏂规硶
public static void main(String[] args) {
JFrame frame = new JFrame("AutoMow - 棣栭〉");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
--
Gitblit v1.10.0