| | |
| | | import java.awt.datatransfer.*; |
| | | import java.awt.datatransfer.StringSelection; |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | | import java.io.FileOutputStream; |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import ui.UIConfig; |
| | |
| | | import java.util.List; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Objects; |
| | | import java.util.Properties; |
| | | |
| | | import lujing.Lunjingguihua; |
| | | import lujing.MowingPathGenerationPage; |
| | | import zhangaiwu.AddDikuai; |
| | | import zhangaiwu.Obstacledge; |
| | | import zhuye.MapRenderer; |
| | |
| | | private JButton addLandBtn; |
| | | |
| | | private static String currentWorkLandNumber; |
| | | private static final String WORK_LAND_KEY = "currentWorkLandNumber"; |
| | | private static final String PROPERTIES_FILE = "set.properties"; |
| | | private static final Map<String, Boolean> boundaryPointVisibility = new HashMap<>(); |
| | | private ImageIcon workSelectedIcon; |
| | | private ImageIcon workUnselectedIcon; |
| | |
| | | JButton deleteBtn = createDeleteButton(); |
| | | deleteBtn.addActionListener(e -> deleteDikuai(dikuai)); |
| | | |
| | | JButton generatePathBtn = createPrimaryFooterButton("生成割草路径"); |
| | | generatePathBtn.addActionListener(e -> generateMowingPath(dikuai)); |
| | | JButton generatePathBtn = createPrimaryFooterButton("路径规划"); |
| | | generatePathBtn.addActionListener(e -> showPathPlanningPage(dikuai)); |
| | | |
| | | JPanel footerPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); |
| | | footerPanel.setBackground(CARD_BACKGROUND); |
| | |
| | | boolean isCurrent = currentWorkLandNumber != null && currentWorkLandNumber.equals(landNumber); |
| | | if (isCurrent) { |
| | | renderer.setBoundaryPointsVisible(desiredState); |
| | | renderer.setBoundaryPointSizeScale(desiredState ? 0.5d : 1.0d); |
| | | } |
| | | } |
| | | } |
| | |
| | | JScrollPane scrollPane = new JScrollPane(textArea); |
| | | scrollPane.setPreferredSize(new Dimension(360, 240)); |
| | | |
| | | int option = JOptionPane.showConfirmDialog( |
| | | this, |
| | | scrollPane, |
| | | title, |
| | | JOptionPane.OK_CANCEL_OPTION, |
| | | JOptionPane.PLAIN_MESSAGE); |
| | | |
| | | if (option == JOptionPane.OK_OPTION) { |
| | | return textArea.getText(); |
| | | Window owner = SwingUtilities.getWindowAncestor(this); |
| | | JDialog dialog; |
| | | if (owner instanceof Frame) { |
| | | dialog = new JDialog((Frame) owner, title, true); |
| | | } else if (owner instanceof Dialog) { |
| | | dialog = new JDialog((Dialog) owner, title, true); |
| | | } else { |
| | | dialog = new JDialog((Frame) null, title, true); |
| | | } |
| | | return null; |
| | | dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); |
| | | |
| | | JPanel contentPanel = new JPanel(new BorderLayout()); |
| | | contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); |
| | | contentPanel.add(scrollPane, BorderLayout.CENTER); |
| | | |
| | | JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); |
| | | JButton okButton = new JButton("确定"); |
| | | JButton cancelButton = new JButton("取消"); |
| | | JButton copyButton = new JButton("复制"); |
| | | |
| | | final boolean[] confirmed = new boolean[] {false}; |
| | | final String[] resultHolder = new String[1]; |
| | | |
| | | okButton.addActionListener(e -> { |
| | | resultHolder[0] = textArea.getText(); |
| | | confirmed[0] = true; |
| | | dialog.dispose(); |
| | | }); |
| | | |
| | | cancelButton.addActionListener(e -> dialog.dispose()); |
| | | |
| | | copyButton.addActionListener(e -> { |
| | | String text = textArea.getText(); |
| | | if (text == null) { |
| | | text = ""; |
| | | } |
| | | String trimmed = text.trim(); |
| | | if (trimmed.isEmpty() || "-1".equals(trimmed)) { |
| | | JOptionPane.showMessageDialog(dialog, title + " 未设置", "提示", JOptionPane.INFORMATION_MESSAGE); |
| | | return; |
| | | } |
| | | try { |
| | | StringSelection selection = new StringSelection(text); |
| | | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); |
| | | clipboard.setContents(selection, selection); |
| | | JOptionPane.showMessageDialog(dialog, title + " 已复制到剪贴板", "提示", JOptionPane.INFORMATION_MESSAGE); |
| | | } catch (Exception ex) { |
| | | JOptionPane.showMessageDialog(dialog, "复制失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); |
| | | } |
| | | }); |
| | | |
| | | buttonPanel.add(okButton); |
| | | buttonPanel.add(cancelButton); |
| | | buttonPanel.add(copyButton); |
| | | |
| | | contentPanel.add(buttonPanel, BorderLayout.SOUTH); |
| | | dialog.setContentPane(contentPanel); |
| | | dialog.getRootPane().setDefaultButton(okButton); |
| | | dialog.pack(); |
| | | dialog.setLocationRelativeTo(this); |
| | | dialog.setVisible(true); |
| | | |
| | | return confirmed[0] ? resultHolder[0] : null; |
| | | } |
| | | |
| | | private boolean saveFieldAndRefresh(Dikuai dikuai, String fieldName, String value) { |
| | |
| | | switch (trimmed) { |
| | | case "1": |
| | | case "spiral": |
| | | case "螺旋": |
| | | case "螺旋模式": |
| | | return "spiral"; |
| | | case "0": |
| | | case "parallel": |
| | | case "平行": |
| | | case "平行模式": |
| | | default: |
| | | if (trimmed.contains("螺旋")) { |
| | | return "spiral"; |
| | | } |
| | | if (trimmed.contains("spiral")) { |
| | | return "spiral"; |
| | | } |
| | | if (trimmed.contains("parallel")) { |
| | | return "parallel"; |
| | | } |
| | | if (trimmed.contains("平行")) { |
| | | return "parallel"; |
| | | } |
| | | return "parallel"; |
| | | } |
| | | } |
| | |
| | | return trimmed; |
| | | } |
| | | |
| | | /** |
| | | * 显示路径规划页面 |
| | | */ |
| | | private void showPathPlanningPage(Dikuai dikuai) { |
| | | if (dikuai == null) { |
| | | return; |
| | | } |
| | | |
| | | Window owner = SwingUtilities.getWindowAncestor(this); |
| | | |
| | | // 获取地块基本数据 |
| | | String baseStationValue = prepareCoordinateForEditor(dikuai.getBaseStationCoordinates()); |
| | | String boundaryValue = prepareCoordinateForEditor(dikuai.getBoundaryCoordinates()); |
| | | List<Obstacledge.Obstacle> configuredObstacles = getConfiguredObstacles(dikuai); |
| | | String obstacleValue = determineInitialObstacleValue(dikuai, configuredObstacles); |
| | | String widthValue = sanitizeWidthString(dikuai.getMowingWidth()); |
| | | if (widthValue != null) { |
| | | try { |
| | | double widthCm = Double.parseDouble(widthValue); |
| | | widthValue = formatWidthForStorage(widthCm); |
| | | } catch (NumberFormatException ignored) { |
| | | // 保持原始字符串,稍后校验提示 |
| | | } |
| | | } |
| | | String modeValue = sanitizeValueOrNull(dikuai.getMowingPattern()); |
| | | String existingPath = prepareCoordinateForEditor(dikuai.getPlannedPath()); |
| | | |
| | | // 创建保存回调接口实现 |
| | | MowingPathGenerationPage.PathSaveCallback callback = new MowingPathGenerationPage.PathSaveCallback() { |
| | | @Override |
| | | public boolean saveBaseStationCoordinates(Dikuai dikuai, String value) { |
| | | return saveFieldAndRefresh(dikuai, "baseStationCoordinates", value); |
| | | } |
| | | |
| | | @Override |
| | | public boolean saveBoundaryCoordinates(Dikuai dikuai, String value) { |
| | | return saveFieldAndRefresh(dikuai, "boundaryCoordinates", value); |
| | | } |
| | | |
| | | @Override |
| | | public boolean saveObstacleCoordinates(Dikuai dikuai, String baseStationValue, String obstacleValue) { |
| | | return persistObstaclesForLand(dikuai, baseStationValue, obstacleValue); |
| | | } |
| | | |
| | | @Override |
| | | public boolean saveMowingWidth(Dikuai dikuai, String value) { |
| | | return saveFieldAndRefresh(dikuai, "mowingWidth", value); |
| | | } |
| | | |
| | | @Override |
| | | public boolean savePlannedPath(Dikuai dikuai, String value) { |
| | | return saveFieldAndRefresh(dikuai, "plannedPath", value); |
| | | } |
| | | }; |
| | | |
| | | // 显示路径规划页面 |
| | | MowingPathGenerationPage dialog = new MowingPathGenerationPage( |
| | | owner, |
| | | dikuai, |
| | | baseStationValue, |
| | | boundaryValue, |
| | | obstacleValue, |
| | | widthValue, |
| | | modeValue, |
| | | existingPath, |
| | | callback |
| | | ); |
| | | |
| | | dialog.setVisible(true); |
| | | } |
| | | |
| | | private void generateMowingPath(Dikuai dikuai) { |
| | | if (dikuai == null) { |
| | | return; |
| | |
| | | String modeValue, |
| | | String initialGeneratedPath) { |
| | | Window owner = SwingUtilities.getWindowAncestor(this); |
| | | JDialog dialog = new JDialog(owner, "生成割草路径", Dialog.ModalityType.APPLICATION_MODAL); |
| | | dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); |
| | | dialog.getContentPane().setLayout(new BorderLayout()); |
| | | dialog.getContentPane().setBackground(BACKGROUND_COLOR); |
| | | |
| | | JPanel contentPanel = new JPanel(); |
| | | contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); |
| | | contentPanel.setBackground(BACKGROUND_COLOR); |
| | | contentPanel.setBorder(BorderFactory.createEmptyBorder(12, 16, 12, 16)); |
| | | |
| | | String landName = getDisplayValue(dikuai.getLandName(), "未知地块"); |
| | | String landNumber = getDisplayValue(dikuai.getLandNumber(), "未知编号"); |
| | | JLabel headerLabel = new JLabel(landName + " / " + landNumber); |
| | | headerLabel.setFont(new Font("微软雅黑", Font.BOLD, 16)); |
| | | headerLabel.setForeground(TEXT_COLOR); |
| | | headerLabel.setAlignmentX(Component.LEFT_ALIGNMENT); |
| | | contentPanel.add(headerLabel); |
| | | contentPanel.add(Box.createVerticalStrut(12)); |
| | | |
| | | JTextField baseStationField = createInfoTextField(baseStationValue != null ? baseStationValue : "", true); |
| | | contentPanel.add(createTextFieldSection("基站坐标", baseStationField)); |
| | | |
| | | JTextArea boundaryArea = createInfoTextArea(boundaryValue != null ? boundaryValue : "", true, 6); |
| | | contentPanel.add(createTextAreaSection("地块边界", boundaryArea)); |
| | | |
| | | JTextArea obstacleArea = createInfoTextArea(obstacleValue != null ? obstacleValue : "", true, 6); |
| | | contentPanel.add(createTextAreaSection("障碍物坐标", obstacleArea)); |
| | | |
| | | JTextField widthField = createInfoTextField(widthValue != null ? widthValue : "", true); |
| | | contentPanel.add(createTextFieldSection("割草宽度 (厘米)", widthField)); |
| | | contentPanel.add(createInfoValueSection("割草模式", formatMowingPatternForDialog(modeValue))); |
| | | |
| | | String existingPath = prepareCoordinateForEditor(dikuai.getPlannedPath()); |
| | | String pathSeed = initialGeneratedPath != null ? initialGeneratedPath : existingPath; |
| | | JTextArea pathArea = createInfoTextArea(pathSeed != null ? pathSeed : "", true, 10); |
| | | contentPanel.add(createTextAreaSection("割草路径坐标", pathArea)); |
| | | |
| | | JScrollPane dialogScrollPane = new JScrollPane(contentPanel); |
| | | dialogScrollPane.setBorder(BorderFactory.createEmptyBorder()); |
| | | dialogScrollPane.getVerticalScrollBar().setUnitIncrement(16); |
| | | dialog.add(dialogScrollPane, BorderLayout.CENTER); |
| | | |
| | | JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 12, 12)); |
| | | buttonPanel.setBackground(BACKGROUND_COLOR); |
| | | |
| | | JButton generateBtn = createPrimaryFooterButton("生成割草路径"); |
| | | JButton saveBtn = createPrimaryFooterButton("保存路径"); |
| | | JButton cancelBtn = createPrimaryFooterButton("取消"); |
| | | |
| | | generateBtn.addActionListener(e -> { |
| | | String sanitizedWidth = sanitizeWidthString(widthField.getText()); |
| | | if (sanitizedWidth != null) { |
| | | try { |
| | | double widthCm = Double.parseDouble(sanitizedWidth); |
| | | widthField.setText(formatWidthForStorage(widthCm)); |
| | | sanitizedWidth = formatWidthForStorage(widthCm); |
| | | } catch (NumberFormatException ex) { |
| | | widthField.setText(sanitizedWidth); |
| | | } |
| | | |
| | | // 创建保存回调接口实现 |
| | | MowingPathGenerationPage.PathSaveCallback callback = new MowingPathGenerationPage.PathSaveCallback() { |
| | | @Override |
| | | public boolean saveBaseStationCoordinates(Dikuai dikuai, String value) { |
| | | return saveFieldAndRefresh(dikuai, "baseStationCoordinates", value); |
| | | } |
| | | String generated = attemptMowingPathPreview( |
| | | boundaryArea.getText(), |
| | | obstacleArea.getText(), |
| | | sanitizedWidth, |
| | | modeValue, |
| | | dialog, |
| | | true |
| | | ); |
| | | if (generated != null) { |
| | | pathArea.setText(generated); |
| | | pathArea.setCaretPosition(0); |
| | | |
| | | @Override |
| | | public boolean saveBoundaryCoordinates(Dikuai dikuai, String value) { |
| | | return saveFieldAndRefresh(dikuai, "boundaryCoordinates", value); |
| | | } |
| | | }); |
| | | |
| | | saveBtn.addActionListener(e -> { |
| | | String baseStationNormalized = normalizeCoordinateInput(baseStationField.getText()); |
| | | String boundaryNormalized = normalizeCoordinateInput(boundaryArea.getText()); |
| | | if (!"-1".equals(boundaryNormalized)) { |
| | | boundaryNormalized = boundaryNormalized |
| | | .replace("\r\n", ";") |
| | | .replace('\r', ';') |
| | | .replace('\n', ';') |
| | | .replaceAll(";+", ";") |
| | | .replaceAll("\\s*;\\s*", ";") |
| | | .trim(); |
| | | if (boundaryNormalized.isEmpty()) { |
| | | boundaryNormalized = "-1"; |
| | | } |
| | | |
| | | @Override |
| | | public boolean saveObstacleCoordinates(Dikuai dikuai, String baseStationValue, String obstacleValue) { |
| | | return persistObstaclesForLand(dikuai, baseStationValue, obstacleValue); |
| | | } |
| | | String obstacleNormalized = normalizeCoordinateInput(obstacleArea.getText()); |
| | | if (!"-1".equals(obstacleNormalized)) { |
| | | obstacleNormalized = obstacleNormalized |
| | | .replace("\r\n", " ") |
| | | .replace('\r', ' ') |
| | | .replace('\n', ' ') |
| | | .replaceAll("\\s{2,}", " ") |
| | | .trim(); |
| | | if (obstacleNormalized.isEmpty()) { |
| | | obstacleNormalized = "-1"; |
| | | } |
| | | |
| | | @Override |
| | | public boolean saveMowingWidth(Dikuai dikuai, String value) { |
| | | return saveFieldAndRefresh(dikuai, "mowingWidth", value); |
| | | } |
| | | String rawWidthInput = widthField.getText() != null ? widthField.getText().trim() : ""; |
| | | String widthSanitized = sanitizeWidthString(widthField.getText()); |
| | | if (widthSanitized == null) { |
| | | String message = rawWidthInput.isEmpty() ? "请先设置割草宽度(厘米)" : "割草宽度格式不正确"; |
| | | JOptionPane.showMessageDialog(dialog, message, "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | |
| | | @Override |
| | | public boolean savePlannedPath(Dikuai dikuai, String value) { |
| | | return saveFieldAndRefresh(dikuai, "plannedPath", value); |
| | | } |
| | | double widthCm; |
| | | try { |
| | | widthCm = Double.parseDouble(widthSanitized); |
| | | } catch (NumberFormatException ex) { |
| | | JOptionPane.showMessageDialog(dialog, "割草宽度格式不正确", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | if (widthCm <= 0) { |
| | | JOptionPane.showMessageDialog(dialog, "割草宽度必须大于0", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | String widthNormalized = formatWidthForStorage(widthCm); |
| | | widthField.setText(widthNormalized); |
| | | String pathNormalized = normalizeCoordinateInput(pathArea.getText()); |
| | | if (!"-1".equals(pathNormalized)) { |
| | | pathNormalized = pathNormalized |
| | | .replace("\r\n", ";") |
| | | .replace('\r', ';') |
| | | .replace('\n', ';') |
| | | .replaceAll(";+", ";") |
| | | .replaceAll("\\s*;\\s*", ";") |
| | | .trim(); |
| | | if (pathNormalized.isEmpty()) { |
| | | pathNormalized = "-1"; |
| | | } |
| | | } |
| | | if ("-1".equals(pathNormalized)) { |
| | | JOptionPane.showMessageDialog(dialog, "请先生成割草路径", "提示", JOptionPane.INFORMATION_MESSAGE); |
| | | return; |
| | | } |
| | | if (!saveFieldAndRefresh(dikuai, "baseStationCoordinates", baseStationNormalized)) { |
| | | JOptionPane.showMessageDialog(dialog, "无法保存基站坐标", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | if (!saveFieldAndRefresh(dikuai, "boundaryCoordinates", boundaryNormalized)) { |
| | | JOptionPane.showMessageDialog(dialog, "无法保存地块边界", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | if (!persistObstaclesForLand(dikuai, baseStationNormalized, obstacleNormalized)) { |
| | | JOptionPane.showMessageDialog(dialog, "无法保存障碍物坐标", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | if (!saveFieldAndRefresh(dikuai, "mowingWidth", widthNormalized)) { |
| | | JOptionPane.showMessageDialog(dialog, "无法保存割草宽度", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | if (!saveFieldAndRefresh(dikuai, "plannedPath", pathNormalized)) { |
| | | JOptionPane.showMessageDialog(dialog, "无法保存割草路径", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | JOptionPane.showMessageDialog(dialog, "割草路径已保存", "成功", JOptionPane.INFORMATION_MESSAGE); |
| | | dialog.dispose(); |
| | | }); |
| | | |
| | | cancelBtn.addActionListener(e -> dialog.dispose()); |
| | | |
| | | buttonPanel.add(generateBtn); |
| | | buttonPanel.add(saveBtn); |
| | | buttonPanel.add(cancelBtn); |
| | | dialog.add(buttonPanel, BorderLayout.SOUTH); |
| | | |
| | | dialog.pack(); |
| | | dialog.setSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT)); |
| | | dialog.setLocationRelativeTo(owner); |
| | | }; |
| | | |
| | | // 使用新的独立页面类 |
| | | MowingPathGenerationPage dialog = new MowingPathGenerationPage( |
| | | owner, |
| | | dikuai, |
| | | baseStationValue, |
| | | boundaryValue, |
| | | obstacleValue, |
| | | widthValue, |
| | | modeValue, |
| | | initialGeneratedPath, |
| | | callback |
| | | ); |
| | | |
| | | dialog.setVisible(true); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | public static void setCurrentWorkLand(String landNumber, String landName) { |
| | | currentWorkLandNumber = landNumber; |
| | | String sanitizedLandNumber = sanitizeLandNumber(landNumber); |
| | | boolean changed = !Objects.equals(currentWorkLandNumber, sanitizedLandNumber); |
| | | if (!changed) { |
| | | String persisted = readPersistedWorkLandNumber(); |
| | | if (!Objects.equals(persisted, sanitizedLandNumber)) { |
| | | changed = true; |
| | | } |
| | | } |
| | | currentWorkLandNumber = sanitizedLandNumber; |
| | | |
| | | Dikuai dikuai = null; |
| | | if (landNumber != null) { |
| | | dikuai = Dikuai.getDikuai(landNumber); |
| | | if (sanitizedLandNumber != null) { |
| | | dikuai = Dikuai.getDikuai(sanitizedLandNumber); |
| | | if (dikuai != null && (landName == null || "-1".equals(landName) || landName.trim().isEmpty())) { |
| | | landName = dikuai.getLandName(); |
| | | } |
| | |
| | | |
| | | Shouye shouye = Shouye.getInstance(); |
| | | if (shouye != null) { |
| | | if (landNumber == null) { |
| | | if (sanitizedLandNumber == null) { |
| | | shouye.updateCurrentAreaName(null); |
| | | } else { |
| | | shouye.updateCurrentAreaName(landName); |
| | |
| | | renderer.applyLandMetadata(dikuai); |
| | | String boundary = (dikuai != null) ? dikuai.getBoundaryCoordinates() : null; |
| | | String plannedPath = (dikuai != null) ? dikuai.getPlannedPath() : null; |
| | | List<Obstacledge.Obstacle> configuredObstacles = (landNumber != null) ? loadObstaclesFromConfig(landNumber) : Collections.emptyList(); |
| | | List<Obstacledge.Obstacle> configuredObstacles = (sanitizedLandNumber != null) ? loadObstaclesFromConfig(sanitizedLandNumber) : Collections.emptyList(); |
| | | if (configuredObstacles == null) { |
| | | configuredObstacles = Collections.emptyList(); |
| | | } |
| | | renderer.setCurrentBoundary(boundary, landNumber, landNumber == null ? null : landName); |
| | | renderer.setCurrentBoundary(boundary, sanitizedLandNumber, sanitizedLandNumber == null ? null : landName); |
| | | renderer.setCurrentPlannedPath(plannedPath); |
| | | renderer.setCurrentObstacles(configuredObstacles, landNumber); |
| | | boolean showBoundaryPoints = landNumber != null && boundaryPointVisibility.getOrDefault(landNumber, false); |
| | | renderer.setCurrentObstacles(configuredObstacles, sanitizedLandNumber); |
| | | boolean showBoundaryPoints = sanitizedLandNumber != null && boundaryPointVisibility.getOrDefault(sanitizedLandNumber, false); |
| | | renderer.setBoundaryPointsVisible(showBoundaryPoints); |
| | | renderer.setBoundaryPointSizeScale(showBoundaryPoints ? 0.5d : 1.0d); |
| | | } |
| | | shouye.refreshMowingIndicators(); |
| | | } |
| | | |
| | | if (changed) { |
| | | persistCurrentWorkLand(sanitizedLandNumber); |
| | | } |
| | | } |
| | | |
| | | public static String getCurrentWorkLandNumber() { |
| | | return currentWorkLandNumber; |
| | | } |
| | | |
| | | public static String getPersistedWorkLandNumber() { |
| | | return readPersistedWorkLandNumber(); |
| | | } |
| | | |
| | | private static String sanitizeLandNumber(String landNumber) { |
| | | if (landNumber == null) { |
| | | return null; |
| | | } |
| | | String trimmed = landNumber.trim(); |
| | | if (trimmed.isEmpty() || "-1".equals(trimmed)) { |
| | | return null; |
| | | } |
| | | return trimmed; |
| | | } |
| | | |
| | | private static void persistCurrentWorkLand(String landNumber) { |
| | | synchronized (Dikuaiguanli.class) { |
| | | Properties props = new Properties(); |
| | | try (FileInputStream in = new FileInputStream(PROPERTIES_FILE)) { |
| | | props.load(in); |
| | | } catch (IOException ignored) { |
| | | // Use empty defaults when the configuration file is missing. |
| | | } |
| | | |
| | | if (landNumber == null) { |
| | | props.setProperty(WORK_LAND_KEY, "-1"); |
| | | } else { |
| | | props.setProperty(WORK_LAND_KEY, landNumber); |
| | | } |
| | | |
| | | try (FileOutputStream out = new FileOutputStream(PROPERTIES_FILE)) { |
| | | props.store(out, "Current work land selection updated"); |
| | | } catch (IOException ex) { |
| | | System.err.println("无法保存当前作业地块: " + ex.getMessage()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private static String readPersistedWorkLandNumber() { |
| | | Properties props = new Properties(); |
| | | try (FileInputStream in = new FileInputStream(PROPERTIES_FILE)) { |
| | | props.load(in); |
| | | String value = props.getProperty(WORK_LAND_KEY); |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | String trimmed = value.trim(); |
| | | if (trimmed.isEmpty() || "-1".equals(trimmed)) { |
| | | return null; |
| | | } |
| | | return trimmed; |
| | | } catch (IOException ex) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private ImageIcon loadIcon(String path, int width, int height) { |
| | | try { |
| | | ImageIcon rawIcon = new ImageIcon(path); |