| | |
| | | import javax.swing.Timer; |
| | | |
| | | import baseStation.BaseStation; |
| | | import set.Setsys; |
| | | import baseStation.BaseStationDialog; |
| | | |
| | | import java.awt.*; |
| | | import java.awt.event.*; |
| | | |
| | | import chuankou.dellmessage; |
| | | import dikuai.Dikuai; |
| | | import dikuai.Dikuaiguanli; |
| | | import dikuai.addzhangaiwu; |
| | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Locale; |
| | | import java.util.Objects; |
| | | import java.util.function.Consumer; |
| | | import java.awt.geom.Point2D; |
| | | |
| | | /** |
| | |
| | | private JButton areaSelectBtn; |
| | | private JButton baseStationBtn; |
| | | private JButton bluetoothBtn; |
| | | private JLabel dataPacketCountLabel; |
| | | private JLabel mowerSpeedValueLabel; |
| | | private JLabel mowerSpeedUnitLabel; |
| | | private JLabel mowingProgressLabel; |
| | | private FixQualityIndicator fixQualityIndicator; |
| | | |
| | | // 导航按钮 |
| | | private JButton homeNavBtn; |
| | |
| | | private BaseStationDialog baseStationDialog; |
| | | private Sets settingsDialog; |
| | | private BaseStation baseStation; |
| | | |
| | | private final Consumer<String> serialLineListener = line -> SwingUtilities.invokeLater(this::updateDataPacketCountLabel); |
| | | |
| | | // 地图渲染器 |
| | | private MapRenderer mapRenderer; |
| | |
| | | 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 double[] circleBaseLatLon; |
| | | private Timer circleDataMonitor; |
| | | private Coordinate lastCapturedCoordinate; |
| | | private HandheldBoundaryCaptureDialog handheldCaptureDialog; |
| | | private boolean handheldCaptureActive; |
| | | private int handheldCapturedPoints; |
| | | private final List<Point2D.Double> handheldTemporaryPoints = new ArrayList<>(); |
| | |
| | | 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() { |
| | |
| | | add(controlPanel, BorderLayout.SOUTH); |
| | | |
| | | // 初始化地图渲染器 |
| | | mapRenderer = new MapRenderer(visualizationPanel); |
| | | mapRenderer = new MapRenderer(visualizationPanel); |
| | | applyIdleTrailDurationFromSettings(); |
| | | |
| | | // 初始化对话框引用为null,延迟创建 |
| | | legendDialog = null; |
| | |
| | | initializeDefaultAreaSelection(); |
| | | refreshMapForSelectedArea(); |
| | | } |
| | | |
| | | 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(); |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | addHierarchyListener(listener); |
| | | } |
| | | |
| | | private void showInitialMowerSelfCheckDialogIfNeeded() { |
| | | zijian.showInitialPromptIfNeeded(this, this::showRemoteControlDialog); |
| | | } |
| | | |
| | | 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); |
| | | } |
| | | |
| | | private void createHeaderPanel() { |
| | | headerPanel = new JPanel(new BorderLayout()); |
| | |
| | | |
| | | mainContentPanel.add(visualizationPanel, BorderLayout.CENTER); |
| | | |
| | | startMowerSpeedUpdates(); |
| | | startMowerSpeedUpdates(); |
| | | } |
| | | |
| | | private void createControlPanel() { |
| | |
| | | } |
| | | if (remoteDialog != null) { |
| | | positionRemoteDialogBottomCenter(remoteDialog); |
| | | zijian.markSelfCheckCompleted(); |
| | | remoteDialog.setVisible(true); |
| | | } |
| | | remoteDialog.setVisible(true); |
| | | } |
| | | |
| | | private void positionRemoteDialogBottomCenter(RemoteControlDialog dialog) { |
| | |
| | | } |
| | | } |
| | | |
| | | Device device = new Device(); |
| | | device.initFromProperties(); |
| | | 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); |
| | |
| | | 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; |
| | |
| | | } |
| | | |
| | | private void toggleStartPause() { |
| | | if (handheldCaptureInlineUiActive) { |
| | | handleHandheldConfirmAction(); |
| | | return; |
| | | } |
| | | if (drawingControlModeActive) { |
| | | toggleDrawingPause(); |
| | | return; |
| | | } |
| | | if (startBtn == null) { |
| | | return; |
| | | } |
| | | startButtonShowingPause = !startButtonShowingPause; |
| | | if (startButtonShowingPause) { |
| | | if (!zijian.ensureBeforeMowing(this, this::showRemoteControlDialog)) { |
| | | return; |
| | | } |
| | | } |
| | | startButtonShowingPause = !startButtonShowingPause; |
| | | if (!startButtonShowingPause) { |
| | | statusLabel.setText("作业中"); |
| | | if (stopButtonActive) { |
| | | stopButtonActive = false; |
| | | updateStopButtonIcon(); |
| | | } |
| | | if (!beginMowingSession()) { |
| | | startButtonShowingPause = false; |
| | | startButtonShowingPause = true; |
| | | statusLabel.setText("待机"); |
| | | updateStartButtonAppearance(); |
| | | return; |
| | |
| | | } |
| | | |
| | | private void handleStopAction() { |
| | | if (handheldCaptureInlineUiActive) { |
| | | handleHandheldFinishAction(); |
| | | return; |
| | | } |
| | | if (drawingControlModeActive) { |
| | | handleDrawingStopFromControlPanel(); |
| | | return; |
| | | } |
| | | stopButtonActive = !stopButtonActive; |
| | | updateStopButtonIcon(); |
| | | if (stopButtonActive) { |
| | |
| | | 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; |
| | |
| | | } |
| | | |
| | | private JPanel createSpeedIndicatorPanel() { |
| | | JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0)); |
| | | 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 FixQualityIndicator(); |
| | | 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); |
| | |
| | | mowerSpeedUnitLabel.setFont(new Font("微软雅黑", Font.BOLD, 9)); |
| | | mowerSpeedUnitLabel.setForeground(THEME_COLOR); |
| | | |
| | | panel.add(mowingProgressLabel); |
| | | panel.add(mowerSpeedValueLabel); |
| | | panel.add(mowerSpeedUnitLabel); |
| | | 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; |
| | | } |
| | | |
| | |
| | | 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 "正在估算"; |
| | | case "7": |
| | | return "人工输入固定值"; |
| | | default: |
| | | return "其他"; |
| | | } |
| | | } |
| | | |
| | | private String sanitizeSpeedValue(String raw) { |
| | |
| | | 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; |
| | |
| | | refreshMowerSpeedLabel(); |
| | | } |
| | | |
| | | public void setHandheldMowerIconActive(boolean active) { |
| | | if (mapRenderer == null) { |
| | | return; |
| | | } |
| | | mapRenderer.setHandheldMowerIconActive(active); |
| | | } |
| | | |
| | | public boolean startMowerBoundaryCapture() { |
| | | if (mapRenderer == null) { |
| | | return false; |
| | |
| | | return false; |
| | | } |
| | | |
| | | mapRenderer.clearIdleTrail(); |
| | | |
| | | activeBoundaryMode = BoundaryCaptureMode.MOWER; |
| | | mowerBoundaryCaptureActive = true; |
| | | mowerBaseLatLon = baseLatLonCandidate; |
| | |
| | | Coordinate.setStartSaveGngga(true); |
| | | |
| | | if (mapRenderer != null) { |
| | | mapRenderer.setBoundaryPreviewMarkerScale(2.0d); |
| | | mapRenderer.beginHandheldBoundaryPreview(); |
| | | } |
| | | |
| | | setHandheldMowerIconActive(false); |
| | | |
| | | startMowerBoundaryMonitor(); |
| | | return true; |
| | | } |
| | |
| | | if (mapRenderer == null) { |
| | | return false; |
| | | } |
| | | if (handheldCaptureDialog != null && handheldCaptureDialog.isShowing()) { |
| | | handheldCaptureDialog.toFront(); |
| | | return true; |
| | | } |
| | | |
| | | if (activeBoundaryMode == BoundaryCaptureMode.MOWER) { |
| | | stopMowerBoundaryCapture(); |
| | | } |
| | | |
| | | mapRenderer.clearIdleTrail(); |
| | | |
| | | activeBoundaryMode = BoundaryCaptureMode.HANDHELD; |
| | | handheldCaptureActive = true; |
| | | handheldCapturedPoints = 0; |
| | |
| | | handheldTemporaryPoints.clear(); |
| | | } |
| | | AddDikuai.recordTemporaryBoundaryPoints(Collections.emptyList()); |
| | | mapRenderer.beginHandheldBoundaryPreview(); |
| | | |
| | | Window ownerWindow = SwingUtilities.getWindowAncestor(this); |
| | | SwingUtilities.invokeLater(() -> { |
| | | Window targetOwner = ownerWindow; |
| | | if (targetOwner == null) { |
| | | targetOwner = SwingUtilities.getWindowAncestor(Shouye.this); |
| | | } |
| | | HandheldBoundaryCaptureDialog dialog = new HandheldBoundaryCaptureDialog(targetOwner, Shouye.this, visualizationPanel, THEME_COLOR); |
| | | handheldCaptureDialog = dialog; |
| | | dialog.setVisible(true); |
| | | }); |
| | | |
| | | mapRenderer.setBoundaryPreviewMarkerScale(1.0d); |
| | | mapRenderer.beginHandheldBoundaryPreview(); |
| | | setHandheldMowerIconActive(true); |
| | | enterHandheldCaptureInlineUi(); |
| | | return true; |
| | | } |
| | | |
| | |
| | | if (activeBoundaryMode == BoundaryCaptureMode.MOWER) { |
| | | activeBoundaryMode = BoundaryCaptureMode.NONE; |
| | | } |
| | | setHandheldMowerIconActive(false); |
| | | } |
| | | |
| | | private void discardLatestCoordinate(Coordinate coordinate) { |
| | |
| | | |
| | | 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; |
| | | } |
| | | |
| | | void handheldBoundaryCaptureDialogClosed(HandheldBoundaryCaptureDialog dialog) { |
| | | if (handheldCaptureDialog == dialog) { |
| | | handheldCaptureDialog = null; |
| | | } |
| | | handheldCaptureActive = false; |
| | | if (activeBoundaryMode == BoundaryCaptureMode.HANDHELD) { |
| | | activeBoundaryMode = BoundaryCaptureMode.NONE; |
| | | } |
| | | } |
| | | |
| | | int getHandheldCapturedPointCount() { |
| | | return handheldCapturedPoints; |
| | | } |
| | |
| | | if (statusLabel == null) { |
| | | return; |
| | | } |
| | | if ("作业中".equals(statusText)) { |
| | | if ("作业中".equals(statusText) || "绘制中".equals(statusText)) { |
| | | statusLabel.setForeground(THEME_COLOR); |
| | | } else if ("暂停中".equals(statusText)) { |
| | | } else if ("暂停中".equals(statusText) || "绘制暂停".equals(statusText)) { |
| | | statusLabel.setForeground(STATUS_PAUSE_COLOR); |
| | | } else { |
| | | statusLabel.setForeground(Color.GRAY); |
| | |
| | | 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 (notifyCoordinate) { |
| | | Coordinate.setStartSaveGngga(!paused); |
| | | } |
| | | if (drawingControlModeActive) { |
| | | updateDrawingControlButtonLabels(); |
| | | if (statusLabel != null) { |
| | | statusLabel.setText(paused ? "绘制暂停" : "绘制中"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void toggleDrawingPause() { |
| | |
| | | |
| | | public void showEndDrawingButton(Runnable callback, String drawingShape) { |
| | | endDrawingCallback = callback; |
| | | applyDrawingPauseState(false, false); |
| | | circleDialogMode = false; |
| | | hideCircleGuidancePanel(); |
| | | |
| | | ensureFloatingIconsLoaded(); |
| | | ensureFloatingButtonInfrastructure(); |
| | | enterDrawingControlMode(); |
| | | |
| | | boolean enableCircleGuidance = drawingShape != null |
| | | && "circle".equalsIgnoreCase(drawingShape.trim()); |
| | | if (enableCircleGuidance) { |
| | | prepareCircleGuidanceState(); |
| | | showCircleGuidanceStep(1); |
| | | endDrawingButton.setVisible(false); |
| | | 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(); |
| | | endDrawingButton.setVisible(true); |
| | | if (drawingPauseButton != null) { |
| | | drawingPauseButton.setVisible(true); |
| | | } |
| | | hideFloatingDrawingControls(); |
| | | } |
| | | |
| | | floatingButtonPanel.setVisible(true); |
| | | if (floatingButtonPanel.getParent() != visualizationPanel) { |
| | | visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH); |
| | | } |
| | | |
| | | rebuildFloatingButtonColumn(); |
| | | visualizationPanel.revalidate(); |
| | | visualizationPanel.repaint(); |
| | | } |
| | |
| | | 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(); |
| | |
| | | return; |
| | | } |
| | | circleGuidanceStep = step; |
| | | |
| | | if (step == 1) { |
| | | circleGuidanceLabel.setText("采集第1个点"); |
| | | circleGuidancePrimaryButton.setText("确认第1点"); |
| | |
| | | |
| | | private double[] resolveCircleBaseLatLon() { |
| | | String coords = null; |
| | | String landNumber = Dikuaiguanli.getCurrentWorkLandNumber(); |
| | | if (isMeaningfulValue(landNumber)) { |
| | | Dikuai current = Dikuai.getDikuai(landNumber); |
| | | if (current != null) { |
| | | coords = current.getBaseStationCoordinates(); |
| | | |
| | | 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) && baseStation != null) { |
| | | coords = baseStation.getInstallationCoordinates(); |
| | | } |
| | | if (!isMeaningfulValue(coords)) { |
| | | return null; |
| | | if (!isMeaningfulValue(coords)) { |
| | | coords = addzhangaiwu.getActiveSessionBaseStation(); |
| | | } |
| | | if (!isMeaningfulValue(coords)) { |
| | | return null; |
| | | } |
| | | } |
| | | String[] parts = coords.split(","); |
| | | if (parts.length < 4) { |
| | |
| | | clearCircleGuidanceArtifacts(); |
| | | hideFloatingDrawingControls(); |
| | | circleDialogMode = false; |
| | | applyDrawingPauseState(false, false); |
| | | exitHandheldCaptureInlineUi(); |
| | | handheldCaptureActive = false; |
| | | exitDrawingControlMode(); |
| | | if (activeBoundaryMode == BoundaryCaptureMode.MOWER) { |
| | | stopMowerBoundaryCapture(); |
| | | } else if (activeBoundaryMode == BoundaryCaptureMode.HANDHELD && !handheldCaptureActive) { |
| | |
| | | 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 || !isMeaningfulValue(plannedPath)) { |
| | | 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); |
| | | mapRenderer.setCurrentPlannedPath(plannedPath); |
| | | mapRenderer.clearHandheldBoundaryPreview(); |
| | | mapRenderer.setBoundaryPointSizeScale(1.0d); |
| | | mapRenderer.setBoundaryPointsVisible(isMeaningfulValue(boundary)); |
| | | |
| | | 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(); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | 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); |
| | |
| | | return !"未选择地块".equals(trimmed); |
| | | } |
| | | |
| | | private final class FixQualityIndicator extends JComponent { |
| | | private static final long serialVersionUID = 1L; |
| | | private static final int DIAMETER = 16; |
| | | private String currentCode; |
| | | private Color currentColor = new Color(160, 160, 160); |
| | | |
| | | private FixQualityIndicator() { |
| | | setPreferredSize(new Dimension(DIAMETER, DIAMETER)); |
| | | setMinimumSize(new Dimension(DIAMETER, DIAMETER)); |
| | | setMaximumSize(new Dimension(DIAMETER, DIAMETER)); |
| | | setToolTipText("未知"); |
| | | } |
| | | |
| | | private void setQuality(String code) { |
| | | if (Objects.equals(currentCode, code)) { |
| | | return; |
| | | } |
| | | currentCode = code; |
| | | currentColor = resolveFixQualityColor(code); |
| | | setToolTipText(resolveFixQualityDescription(code)); |
| | | repaint(); |
| | | } |
| | | |
| | | @Override |
| | | protected void paintComponent(Graphics g) { |
| | | super.paintComponent(g); |
| | | Graphics2D g2 = (Graphics2D) g.create(); |
| | | try { |
| | | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| | | int diameter = Math.min(getWidth(), getHeight()) - 2; |
| | | int x = (getWidth() - diameter) / 2; |
| | | int y = (getHeight() - diameter) / 2; |
| | | g2.setColor(currentColor); |
| | | g2.fillOval(x, y, diameter, diameter); |
| | | g2.setColor(new Color(255, 255, 255, 128)); |
| | | g2.drawOval(x, y, diameter, diameter); |
| | | } finally { |
| | | g2.dispose(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 测试方法 |
| | | public static void main(String[] args) { |
| | | JFrame frame = new JFrame("AutoMow - 首页"); |