| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.hwtd.mes.collect.config; |
| | | |
| | | import com.hwtd.mes.collect.handler.SerialPortListener; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.boot.ApplicationArguments; |
| | | import org.springframework.boot.ApplicationRunner; |
| | | import org.springframework.boot.SpringApplication; |
| | | import org.springframework.context.ApplicationContext; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import javax.annotation.PreDestroy; |
| | | import javax.imageio.ImageIO; |
| | | import javax.swing.AbstractAction; |
| | | import javax.swing.BorderFactory; |
| | | import javax.swing.BoxLayout; |
| | | import javax.swing.JButton; |
| | | import javax.swing.JComponent; |
| | | import javax.swing.JDialog; |
| | | import javax.swing.JLabel; |
| | | import javax.swing.JOptionPane; |
| | | import javax.swing.JPanel; |
| | | import javax.swing.JSeparator; |
| | | import javax.swing.JWindow; |
| | | import javax.swing.KeyStroke; |
| | | import javax.swing.SwingConstants; |
| | | import javax.swing.Timer; |
| | | import javax.swing.WindowConstants; |
| | | import java.awt.AWTException; |
| | | import java.awt.BorderLayout; |
| | | import java.awt.Color; |
| | | import java.awt.Cursor; |
| | | import java.awt.Desktop; |
| | | import java.awt.Dimension; |
| | | import java.awt.EventQueue; |
| | | import java.awt.FlowLayout; |
| | | import java.awt.GridLayout; |
| | | import java.awt.Font; |
| | | import java.awt.Graphics2D; |
| | | import java.awt.GraphicsConfiguration; |
| | | import java.awt.GraphicsDevice; |
| | | import java.awt.GraphicsEnvironment; |
| | | import java.awt.Image; |
| | | import java.awt.MouseInfo; |
| | | import java.awt.Point; |
| | | import java.awt.Rectangle; |
| | | import java.awt.RenderingHints; |
| | | import java.awt.SystemTray; |
| | | import java.awt.TrayIcon; |
| | | import java.awt.event.ActionEvent; |
| | | import java.awt.event.ActionListener; |
| | | import java.awt.event.KeyEvent; |
| | | import java.awt.event.MouseAdapter; |
| | | import java.awt.event.MouseEvent; |
| | | import java.awt.event.WindowAdapter; |
| | | import java.awt.event.WindowEvent; |
| | | import java.awt.image.BufferedImage; |
| | | import java.io.File; |
| | | import java.io.InputStream; |
| | | import java.util.Arrays; |
| | | import java.util.HashSet; |
| | | import java.util.Set; |
| | | |
| | | /** |
| | | * ç³»ç»æç徿 ï¼exeï¼æ jarï¼å¯å¨åå¨ä»»å¡æ å³ä¸è§æç常驻ä¸ä¸ªå°å¾æ ï¼å³é®èåå¯ä»¥å¿«ééåºã |
| | | * <p> |
| | | * 说æï¼ |
| | | * <ul> |
| | | * <li>æçä¾èµ AWTï¼è Spring Boot é»è®¤ {@code java.awt.headless=true}ï¼æ¤æ¶ {@link SystemTray#isSupported()} 为 falseï¼ï¼ |
| | | * æä»¥å¯å¨ç±»éå·²ç»ç¨ {@code setHeadless(false)} å
³æï¼</li> |
| | | * <li>å³é®èåç¨ Swing èªå·±ç»ä¸ä¸ªæ è¾¹æ¡çªå£ï¼{@link JWindow}ï¼ï¼ä¸ç¨ AWT ç PopupMenuï¼ |
| | | * AWT è忝 Windows åçèåï¼æåç¨ç³»ç»èååä½ç»å¶ï¼é¨åæºå¨ä¸ä¸å«ä¸æå形伿¾ç¤ºææ¹åï¼èä¸ {@code setFont} ä¸çæï¼</li> |
| | | * <li>èåå¼¹åºä½ç½®åç³»ç»æçèåä¸è´ï¼æ¾ç¤ºå¨å
æ 䏿¹ãå³è¾¹ç¼ä¸å
æ 对é½ï¼å¹¶ä¿è¯ä¸è¶
åºå±å¹ï¼å¤æ¾ç¤ºå¨æ¶æå
æ æå¨å±å¹è®¡ç®ï¼ï¼</li> |
| | | * <li>èåæ¶èµ·æ¹å¼ï¼ç¹å»èåå¤é¢ï¼çªå£å¤±ç¦ï¼ãæ ESCãåå³é®ä¸æ¬¡ãç¹äºæä¸é¡¹ä¹åï¼ |
| | | * å¦å¤è¿æä¸å±å
åºââé¼ æ 离å¼èåéè¿ä¹ä¼èªå¨æ¶èµ·ï¼é¿å
个å«ç¯å¢çªå£æ¢ä¸å°ç¦ç¹å¯¼è´èåå
³ä¸æï¼ï¼</li> |
| | | * <li>èåéçâæ¥ç串å£ç¶æâï¼æå廿ç徿 ï¼ä¼å¼¹åºä¸ä¸ªç¶æçªå£ï¼ä¸²å£åç§°ãçå¬ç¶æã串å£è¿æ¥ãç¼åæ¡æ°ï¼æ¯ç§èªå¨å·æ°ï¼</li> |
| | | * <li>èåéçâå
³é串å£âåªå
³é串å£çå¬ï¼ä¸éåºç¨åºï¼ï¼æ¹ä¾¿ç°åºæä¸²å£éæ¾ç»å«çç¨åºç¨ï¼ç»æç¨æçæ°æ³¡æç¤ºï¼</li> |
| | | * <li>徿 ä¼å
读å classpath ä¸ç {@code tray.png}ï¼æ¾ä¸å¼ 32x32 ç png å³å¯æ¢æèªå·±ç徿 ï¼ï¼æ²¡æå°±ç¨ä»£ç ç»çé»è®¤å¾æ ï¼</li> |
| | | * <li>ç¹âéåºâä¼å
å
³é Spring 容å¨ï¼è§¦å {@code @PreDestroy}ï¼ä¾å¦å
³é串å£çå¬ãåæ¢è¡¥å¸§çº¿ç¨ï¼ï¼åç»æè¿ç¨ï¼ |
| | | * ç¨ä»»å¡ç®¡çå¨ç»æè¿ç¨ãæç´æ¥å
³æ exe çæ§å¶å°çªå£ï¼åä¸ä¼èµ°è¿å¥æ¸
çæµç¨ï¼</li> |
| | | * <li>以 Windows æå¡ï¼Session 0ï¼æ æ¡é¢ï¼æ¹å¼è¿è¡æ¶æçä¸ä¼æ¾ç¤ºï¼ä»£ç ä¼èªå¨è·³è¿ï¼ä¸å½±åæ¥å£ä½¿ç¨ã</li> |
| | | * </ul> |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | public class SystemTrayInitializer implements ApplicationRunner { |
| | | |
| | | /** é»è®¤å¾æ 尺寸 */ |
| | | private static final int DEFAULT_ICON_SIZE = 32; |
| | | /** èåèæ¯è² */ |
| | | private static final Color MENU_BACKGROUND = Color.WHITE; |
| | | /** èåé¡¹é¼ æ æ¬åèæ¯è² */ |
| | | private static final Color MENU_HOVER_BACKGROUND = new Color(0xE8F0FE); |
| | | /** èåè¾¹æ¡é¢è² */ |
| | | private static final Color MENU_BORDER = new Color(0xC8C8C8); |
| | | |
| | | @Value("${server.port:9527}") |
| | | private int serverPort; |
| | | |
| | | @Value("${logging.file-location:logs}") |
| | | private String logLocation; |
| | | |
| | | private final ApplicationContext applicationContext; |
| | | |
| | | /** æç徿 ï¼éåºæ¶å
ç§»é¤ */ |
| | | private TrayIcon trayIcon; |
| | | |
| | | /** å³é®èåçªå£ï¼ç¬¬ä¸æ¬¡å³é®æ¶å建 */ |
| | | private JWindow popupWindow; |
| | | |
| | | /** ç¶æçªå£ï¼ç¬¬ä¸æ¬¡ç¹âæ¥çééç¶æâæ¶å建 */ |
| | | private JDialog statusDialog; |
| | | |
| | | /** ç¶æçªå£çèªå¨å·æ°å®æ¶å¨ï¼æ¯ç§å·æ°ä¸æ¬¡ï¼ */ |
| | | private Timer statusRefreshTimer; |
| | | |
| | | /** ç¶æçªå£éçå¼ */ |
| | | private JLabel serialPortNameValue; |
| | | private JLabel listeningValue; |
| | | private JLabel portOpenValue; |
| | | private JLabel dataSizeValue; |
| | | |
| | | /** å
åºæ¶èµ·æ£æ¥ï¼ä¸ä¸èåçªå£æ²¡æ¢å°ç¦ç¹ï¼é¼ æ 离å¼èååä¹è½èªå¨æ¶èµ· */ |
| | | private Timer popupWatchdog; |
| | | |
| | | /** å¼¹åºèåæ¶çå
æ ä½ç½®ï¼æç徿 ä½ç½®ï¼ */ |
| | | private Point popupAnchor; |
| | | |
| | | /** å
æ è¿ç»å 次ä¸å¨èåéè¿ */ |
| | | private int outsideCount; |
| | | |
| | | public SystemTrayInitializer(ApplicationContext applicationContext) { |
| | | this.applicationContext = applicationContext; |
| | | } |
| | | |
| | | @Override |
| | | public void run(ApplicationArguments args) { |
| | | if (!SystemTray.isSupported()) { |
| | | log.warn("å½åç¯å¢ä¸æ¯æç³»ç»æçï¼headless 模å¼ãæ æ¡é¢ä¼è¯æç¼ºå° java.desktop 模åï¼ï¼è·³è¿æç徿 "); |
| | | return; |
| | | } |
| | | try { |
| | | // AWT/Swing ç»ä»¶ç»ä¸å¨äºä»¶åå线ç¨ï¼EDTï¼éå建 |
| | | EventQueue.invokeAndWait(this::createTrayIcon); |
| | | } catch (Exception e) { |
| | | log.error("åå»ºç³»ç»æç徿 失败ï¼{}", e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å建æç徿 ï¼å¹¶æ³¨åé¼ æ äºä»¶ï¼å³é®å¼¹èåãå·¦é®å廿å¼ééç¶æ |
| | | */ |
| | | private void createTrayIcon() { |
| | | trayIcon = new TrayIcon(loadTrayImage(), "亨æºç¹å¯¼MESæ°æ®ééå¨"); |
| | | trayIcon.setImageAutoSize(true); |
| | | trayIcon.addMouseListener(new MouseAdapter() { |
| | | @Override |
| | | public void mouseReleased(MouseEvent event) { |
| | | if (event.isPopupTrigger() || event.getButton() == MouseEvent.BUTTON3) { |
| | | showPopupWindow(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void mouseClicked(MouseEvent event) { |
| | | if (event.getButton() == MouseEvent.BUTTON1 && event.getClickCount() >= 2) { |
| | | showStatusDialog(); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | try { |
| | | SystemTray.getSystemTray().add(trayIcon); |
| | | log.info("ç³»ç»æç徿 å·²å¯å¨ï¼å³é®æç徿 å¯ä»¥éåºç¨åºï¼ç«¯å£ {}ï¼", serverPort); |
| | | } catch (AWTException e) { |
| | | log.error("æ·»å ç³»ç»æç徿 失败ï¼{}", e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å¨å
æ ä½ç½®å¼¹åºå³é®èåï¼å·²ç»å¼¹çå°±æ¶èµ·æ¥ |
| | | */ |
| | | private void showPopupWindow() { |
| | | Point mouse = MouseInfo.getPointerInfo() == null ? null : MouseInfo.getPointerInfo().getLocation(); |
| | | if (mouse == null) { |
| | | return; |
| | | } |
| | | JWindow window = ensurePopupWindow(mouse); |
| | | if (window.isVisible()) { |
| | | hidePopupWindow(); |
| | | return; |
| | | } |
| | | Rectangle screen = screenConfigurationOf(mouse).getBounds(); |
| | | Dimension size = window.getSize(); |
| | | // åç³»ç»æçèå䏿 ·ï¼æ¾ç¤ºå¨å
æ 䏿¹ï¼å³è¾¹ç¼ä¸å
æ å¯¹é½ |
| | | int x = mouse.x - size.width; |
| | | int y = mouse.y - size.height - 2; |
| | | if (y < screen.y) { |
| | | // 䏿¹æ¾ä¸ä¸ï¼ä¾å¦ä»»å¡æ å¨é¡¶é¨ï¼å°±æ¾å°å
æ 䏿¹ |
| | | y = mouse.y + 12; |
| | | } |
| | | // ä¿è¯èå宿´æ¾ç¤ºå¨å±å¹å
|
| | | x = Math.max(screen.x, Math.min(x, screen.x + screen.width - size.width)); |
| | | y = Math.max(screen.y, Math.min(y, screen.y + screen.height - size.height)); |
| | | |
| | | window.setLocation(x, y); |
| | | window.setVisible(true); |
| | | window.toFront(); |
| | | window.requestFocus(); |
| | | startPopupWatchdog(mouse); |
| | | } |
| | | |
| | | /** |
| | | * æ¶èµ·å³é®èå |
| | | */ |
| | | private void hidePopupWindow() { |
| | | stopPopupWatchdog(); |
| | | JWindow window = popupWindow; |
| | | if (window != null && window.isVisible()) { |
| | | window.setVisible(false); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å¯å¨å
åºæ¶èµ·æ£æ¥ï¼æ£å¸¸æ
åµä¸ç¹å»å«å¤çªå£å¤±ç¦å°±ä¼æ¶èµ·ï¼ |
| | | * ä¸ä¸çªå£æ²¡æ¢å°ç¦ç¹ï¼é¨åç¯å¢ä¼è¿æ ·ï¼ï¼é é¼ æ 离å¼èåéè¿ä¹è½æ¶èµ·ã |
| | | */ |
| | | private void startPopupWatchdog(Point anchor) { |
| | | stopPopupWatchdog(); |
| | | popupAnchor = anchor; |
| | | outsideCount = 0; |
| | | popupWatchdog = new Timer(250, event -> checkPopupDismiss()); |
| | | // çä¸ç¹æ¶é´ç»ç¨æ·ä»æç徿 ç§»å¨å°èåä¸ |
| | | popupWatchdog.setInitialDelay(600); |
| | | popupWatchdog.start(); |
| | | } |
| | | |
| | | /** |
| | | * å
åºæ£æ¥ï¼å
æ æ¢ä¸å¨èåä¸ãä¹ä¸å¨æç徿 éè¿æ¶ï¼è¿ç»ä¸¤æ¬¡å°±æ¶èµ·èå |
| | | */ |
| | | private void checkPopupDismiss() { |
| | | JWindow window = popupWindow; |
| | | if (window == null || !window.isVisible()) { |
| | | stopPopupWatchdog(); |
| | | return; |
| | | } |
| | | Point mouse = MouseInfo.getPointerInfo() == null ? null : MouseInfo.getPointerInfo().getLocation(); |
| | | if (mouse == null) { |
| | | return; |
| | | } |
| | | boolean nearMenu = window.getBounds().contains(mouse); |
| | | boolean nearAnchor = popupAnchor != null && popupAnchor.distance(mouse) < 80; |
| | | if (nearMenu || nearAnchor) { |
| | | outsideCount = 0; |
| | | return; |
| | | } |
| | | if (++outsideCount >= 2) { |
| | | hidePopupWindow(); |
| | | } |
| | | } |
| | | |
| | | private void stopPopupWatchdog() { |
| | | if (popupWatchdog != null) { |
| | | popupWatchdog.stop(); |
| | | popupWatchdog = null; |
| | | } |
| | | outsideCount = 0; |
| | | } |
| | | |
| | | /** |
| | | * èåçªå£æå±å¹å建ï¼å¤æ¾ç¤ºå¨æ¶å建å¨å
æ æå¨çé£åå±å¹ä¸ |
| | | */ |
| | | private JWindow ensurePopupWindow(Point mouse) { |
| | | GraphicsConfiguration configuration = screenConfigurationOf(mouse); |
| | | if (popupWindow == null || popupWindow.getGraphicsConfiguration() != configuration) { |
| | | if (popupWindow != null) { |
| | | popupWindow.dispose(); |
| | | } |
| | | popupWindow = createPopupWindow(configuration); |
| | | } |
| | | return popupWindow; |
| | | } |
| | | |
| | | /** |
| | | * å建èåçªå£ï¼æ è¾¹æ¡ã置顶ãå¯è·åç¦ç¹ï¼ç¹å«å¤å¤±ç¦å°±è½èªå¨æ¶èµ·ï¼ |
| | | */ |
| | | private JWindow createPopupWindow(GraphicsConfiguration configuration) { |
| | | Font menuFont = resolveMenuFont(); |
| | | |
| | | JPanel panel = new JPanel(); |
| | | panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); |
| | | panel.setBackground(MENU_BACKGROUND); |
| | | panel.setBorder(BorderFactory.createCompoundBorder( |
| | | BorderFactory.createLineBorder(MENU_BORDER), |
| | | BorderFactory.createEmptyBorder(4, 0, 4, 0))); |
| | | panel.add(createMenuItem("æ¥ç串å£ç¶æ", menuFont, e -> showStatusDialog())); |
| | | panel.add(createMenuItem("å
³é串å£çå¬", menuFont, e -> closeSerialPort())); |
| | | panel.add(createMenuItem("æå¼æ¥å¿ç®å½", menuFont, e -> openLogDirectory())); |
| | | panel.add(createSeparator()); |
| | | panel.add(createMenuItem("éåº", menuFont, e -> exit())); |
| | | |
| | | // æ ESC æ¶èµ·èå |
| | | panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) |
| | | .put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "hidePopupWindow"); |
| | | panel.getActionMap().put("hidePopupWindow", new AbstractAction() { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Override |
| | | public void actionPerformed(ActionEvent event) { |
| | | hidePopupWindow(); |
| | | } |
| | | }); |
| | | |
| | | JWindow window = new JWindow(configuration); |
| | | window.setContentPane(panel); |
| | | window.setAlwaysOnTop(true); |
| | | window.setFocusable(true); |
| | | // å
³é®ï¼çªå£å¿
é¡»è½è·å¾ç¦ç¹ï¼ç¹èåå¤é¢æ¶æä¼è§¦å失ç¦ãèåæä¼èªå¨æ¶èµ· |
| | | window.setFocusableWindowState(true); |
| | | window.addWindowFocusListener(new WindowAdapter() { |
| | | @Override |
| | | public void windowLostFocus(WindowEvent event) { |
| | | hidePopupWindow(); |
| | | } |
| | | }); |
| | | window.addWindowListener(new WindowAdapter() { |
| | | @Override |
| | | public void windowDeactivated(WindowEvent event) { |
| | | hidePopupWindow(); |
| | | } |
| | | }); |
| | | window.pack(); |
| | | return window; |
| | | } |
| | | |
| | | /** |
| | | * èåé¡¹ï¼æå¹³æé® + æ¬åé«äº® |
| | | */ |
| | | private JButton createMenuItem(String text, Font font, ActionListener actionListener) { |
| | | JButton item = new JButton(text); |
| | | item.setFont(font); |
| | | item.setHorizontalAlignment(SwingConstants.LEFT); |
| | | item.setBorder(BorderFactory.createEmptyBorder(6, 18, 6, 24)); |
| | | item.setBorderPainted(false); |
| | | item.setFocusPainted(false); |
| | | item.setContentAreaFilled(false); |
| | | item.setOpaque(true); |
| | | item.setBackground(MENU_BACKGROUND); |
| | | item.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); |
| | | item.setMaximumSize(new Dimension(Integer.MAX_VALUE, item.getPreferredSize().height)); |
| | | item.addMouseListener(new MouseAdapter() { |
| | | @Override |
| | | public void mouseEntered(MouseEvent event) { |
| | | item.setBackground(MENU_HOVER_BACKGROUND); |
| | | } |
| | | |
| | | @Override |
| | | public void mouseExited(MouseEvent event) { |
| | | item.setBackground(MENU_BACKGROUND); |
| | | } |
| | | }); |
| | | item.addActionListener(event -> { |
| | | // å
æ¶èµ·èåï¼åæ§è¡å¨ä½ |
| | | hidePopupWindow(); |
| | | actionListener.actionPerformed(event); |
| | | }); |
| | | return item; |
| | | } |
| | | |
| | | /** |
| | | * èååé线 |
| | | */ |
| | | private JSeparator createSeparator() { |
| | | JSeparator separator = new JSeparator(); |
| | | separator.setForeground(MENU_BORDER); |
| | | separator.setMaximumSize(new Dimension(Integer.MAX_VALUE, 1)); |
| | | return separator; |
| | | } |
| | | |
| | | /** |
| | | * @return å
æ æå¨å±å¹çæ¾ç¤ºé
ç½®ï¼æ¾ä¸å°å°±ç¨ä¸»å±å¹ |
| | | */ |
| | | private GraphicsConfiguration screenConfigurationOf(Point point) { |
| | | GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); |
| | | for (GraphicsDevice device : environment.getScreenDevices()) { |
| | | GraphicsConfiguration configuration = device.getDefaultConfiguration(); |
| | | if (configuration.getBounds().contains(point)) { |
| | | return configuration; |
| | | } |
| | | } |
| | | return environment.getDefaultScreenDevice().getDefaultConfiguration(); |
| | | } |
| | | |
| | | /** |
| | | * éåºç¨åºï¼å
å
³é Spring 容å¨ï¼è§¦å @PreDestroyï¼å
³é串å£ãåæ¢è¡¥å¸§çº¿ç¨ï¼ï¼åç»æè¿ç¨ |
| | | */ |
| | | private void exit() { |
| | | log.info("ä»ç³»ç»æçéåºç¨åº"); |
| | | removeTrayIcon(); |
| | | int exitCode = SpringApplication.exit(applicationContext, () -> 0); |
| | | System.exit(exitCode); |
| | | } |
| | | |
| | | /** |
| | | * 项ç®åæ¢æ¶ç§»é¤æç徿 ã鿝èåçªå£ |
| | | */ |
| | | @PreDestroy |
| | | public void destroy() { |
| | | removeTrayIcon(); |
| | | stopPopupWatchdog(); |
| | | stopStatusRefreshTimer(); |
| | | if (statusDialog != null) { |
| | | statusDialog.dispose(); |
| | | statusDialog = null; |
| | | } |
| | | if (popupWindow != null) { |
| | | popupWindow.dispose(); |
| | | popupWindow = null; |
| | | } |
| | | } |
| | | |
| | | private void removeTrayIcon() { |
| | | TrayIcon icon = trayIcon; |
| | | trayIcon = null; |
| | | if (icon != null) { |
| | | SystemTray.getSystemTray().remove(icon); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å¼¹åºééç¶æçªå£ï¼ä¸æ¯æå¼æµè§å¨ï¼ï¼çªå£éçç¶ææ¯ç§èªå¨å·æ° |
| | | */ |
| | | private void showStatusDialog() { |
| | | if (statusDialog == null) { |
| | | statusDialog = createStatusDialog(); |
| | | } |
| | | refreshStatus(); |
| | | statusDialog.setLocationRelativeTo(null); |
| | | statusDialog.setVisible(true); |
| | | statusDialog.toFront(); |
| | | } |
| | | |
| | | /** |
| | | * å建串å£ç¶æçªå£ï¼ä¸²å£åç§° / çå¬ç¶æ / 串å£è¿æ¥ / ç¼åæ¡æ° + å·æ°ãå
³éæé® |
| | | */ |
| | | private JDialog createStatusDialog() { |
| | | Font font = resolveMenuFont(); |
| | | |
| | | JPanel fields = new JPanel(new GridLayout(4, 2, 12, 10)); |
| | | fields.setBackground(MENU_BACKGROUND); |
| | | serialPortNameValue = addStatusRow(fields, "串å£åç§°", font); |
| | | listeningValue = addStatusRow(fields, "çå¬ç¶æ", font); |
| | | portOpenValue = addStatusRow(fields, "串å£è¿æ¥", font); |
| | | dataSizeValue = addStatusRow(fields, "ç¼åæ°æ®", font); |
| | | |
| | | JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0)); |
| | | buttons.setBackground(MENU_BACKGROUND); |
| | | JButton refreshButton = new JButton("å·æ°"); |
| | | refreshButton.setFont(font); |
| | | refreshButton.addActionListener(event -> refreshStatus()); |
| | | JButton closeButton = new JButton("å
³é"); |
| | | closeButton.setFont(font); |
| | | closeButton.addActionListener(event -> statusDialog.setVisible(false)); |
| | | buttons.add(refreshButton); |
| | | buttons.add(closeButton); |
| | | |
| | | JPanel content = new JPanel(new BorderLayout(0, 14)); |
| | | content.setBackground(MENU_BACKGROUND); |
| | | content.setBorder(BorderFactory.createEmptyBorder(18, 22, 14, 22)); |
| | | content.add(fields, BorderLayout.CENTER); |
| | | content.add(buttons, BorderLayout.SOUTH); |
| | | |
| | | JDialog dialog = new JDialog(); |
| | | dialog.setTitle("串å£ç¶æ"); |
| | | dialog.setModal(false); |
| | | dialog.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); |
| | | dialog.setContentPane(content); |
| | | dialog.addWindowListener(new WindowAdapter() { |
| | | @Override |
| | | public void windowOpened(WindowEvent event) { |
| | | refreshStatus(); |
| | | startStatusRefreshTimer(); |
| | | } |
| | | |
| | | @Override |
| | | public void windowClosing(WindowEvent event) { |
| | | // å
³éï¼å
¶å®æ¯éèï¼æ¶åæèªå¨å·æ° |
| | | stopStatusRefreshTimer(); |
| | | } |
| | | }); |
| | | dialog.pack(); |
| | | dialog.setLocationRelativeTo(null); |
| | | return dialog; |
| | | } |
| | | |
| | | /** |
| | | * ç¶æçªå£éçä¸è¡ï¼å·¦è¾¹æ¯æ é¢ï¼å³è¾¹æ¯å¼ |
| | | * |
| | | * @return å¼é£ä¸åçæ ç¾ï¼å·æ°æ¶ç´æ¥ setText |
| | | */ |
| | | private JLabel addStatusRow(JPanel parent, String title, Font font) { |
| | | JLabel titleLabel = new JLabel(title + "ï¼", SwingConstants.RIGHT); |
| | | titleLabel.setFont(font); |
| | | titleLabel.setForeground(new Color(0x666666)); |
| | | JLabel valueLabel = new JLabel("-"); |
| | | valueLabel.setFont(font); |
| | | valueLabel.setForeground(new Color(0x222222)); |
| | | parent.add(titleLabel); |
| | | parent.add(valueLabel); |
| | | return valueLabel; |
| | | } |
| | | |
| | | /** |
| | | * å·æ°ç¶æçªå£éçå
容 |
| | | */ |
| | | private void refreshStatus() { |
| | | if (serialPortNameValue == null) { |
| | | return; |
| | | } |
| | | SerialPortListener listener = serialPortListener(); |
| | | if (listener == null) { |
| | | serialPortNameValue.setText("-"); |
| | | listeningValue.setText("è·å串å£çå¬å¨å¤±è´¥"); |
| | | portOpenValue.setText("-"); |
| | | dataSizeValue.setText("-"); |
| | | return; |
| | | } |
| | | String serialPortName = listener.getListenName(); |
| | | serialPortNameValue.setText(StringUtils.hasText(serialPortName) ? serialPortName : "-"); |
| | | listeningValue.setText(listener.isListening() ? "çå¬ä¸" : "æªçå¬"); |
| | | portOpenValue.setText(listener.isPortOpen() ? "å·²æå¼" : "æªæå¼"); |
| | | dataSizeValue.setText(listener.dataSize() + " æ¡"); |
| | | } |
| | | |
| | | /** |
| | | * @return 串å£çå¬å¨ï¼åä¸å°æ¶è¿å null |
| | | */ |
| | | private SerialPortListener serialPortListener() { |
| | | if (applicationContext == null) { |
| | | return null; |
| | | } |
| | | try { |
| | | return applicationContext.getBean(SerialPortListener.class); |
| | | } catch (Exception e) { |
| | | log.warn("è·å串å£çå¬å¨å¤±è´¥ï¼{}", e.getMessage()); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private void startStatusRefreshTimer() { |
| | | if (statusRefreshTimer == null) { |
| | | statusRefreshTimer = new Timer(1000, event -> refreshStatus()); |
| | | } |
| | | statusRefreshTimer.start(); |
| | | } |
| | | |
| | | private void stopStatusRefreshTimer() { |
| | | if (statusRefreshTimer != null) { |
| | | statusRefreshTimer.stop(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å
³é串å£çå¬ï¼åªå
³ä¸²å£ï¼ä¸éåºç¨åºï¼ï¼æ¹ä¾¿ç°åºç´æ¥æä¸²å£éæ¾ç»å«çç¨åºç¨ |
| | | */ |
| | | private void closeSerialPort() { |
| | | SerialPortListener listener = serialPortListener(); |
| | | if (listener == null) { |
| | | showError("è·å串å£çå¬å¨å¤±è´¥ï¼æ æ³å
³é串å£"); |
| | | return; |
| | | } |
| | | String serialPortName = listener.getListenName(); |
| | | serialPortName = StringUtils.hasText(serialPortName) ? serialPortName : "-"; |
| | | if (!listener.isListening() && !listener.isPortOpen()) { |
| | | notifyMessage("ä¸²å£ " + serialPortName + " å½åæªå¼å¯çå¬"); |
| | | return; |
| | | } |
| | | if (listener.closeListening()) { |
| | | log.info("ä»ç³»ç»æçå
³éä¸²å£ {} çå¬", serialPortName); |
| | | notifyMessage("ä¸²å£ " + serialPortName + " çå¬å·²å
³é"); |
| | | } else { |
| | | showError("ä¸²å£ " + serialPortName + " å
³é失败ï¼è¯·æ£æ¥ä¸²å£ç¶æ"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æçæ°æ³¡æç¤ºï¼ä¸ææå½åæä½ |
| | | */ |
| | | private void notifyMessage(String message) { |
| | | if (trayIcon == null) { |
| | | log.info(message); |
| | | return; |
| | | } |
| | | trayIcon.displayMessage("亨æºç¹å¯¼MESæ°æ®ééå¨", message, TrayIcon.MessageType.INFO); |
| | | } |
| | | |
| | | /** |
| | | * åºéæ¶å¼¹çªæç¤ºï¼æ°æ³¡æç¤ºå¯è½è¢«ç³»ç»è®¾ç½®å±è½ï¼å¤±è´¥å¿
须让人çè§ï¼ |
| | | */ |
| | | private void showError(String message) { |
| | | log.warn(message); |
| | | try { |
| | | JOptionPane.showMessageDialog(null, message, "æ°æ®ééå¨", JOptionPane.ERROR_MESSAGE); |
| | | } catch (Exception e) { |
| | | log.warn("å¼¹åºé误æç¤ºå¤±è´¥ï¼{}", e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æå¼æ¥å¿ç®å½ï¼æ¹ä¾¿ç°åºææ¥é®é¢ |
| | | */ |
| | | private void openLogDirectory() { |
| | | try { |
| | | File directory = new File(StringUtils.hasText(logLocation) ? logLocation : "logs"); |
| | | if (!directory.exists() && !directory.mkdirs()) { |
| | | log.warn("æ¥å¿ç®å½ä¸åå¨ä¸å建失败ï¼{}", directory.getAbsolutePath()); |
| | | } |
| | | Desktop.getDesktop().open(directory); |
| | | } catch (Exception e) { |
| | | log.warn("æå¼æ¥å¿ç®å½å¤±è´¥ï¼{}", e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * èååä½ï¼ä¼å
æä¸ä¸ªç³»ç»éåå¨ç䏿åä½ |
| | | * |
| | | * @return èååä½ |
| | | */ |
| | | private Font resolveMenuFont() { |
| | | Set<String> fontFamilies = new HashSet<>(Arrays.asList( |
| | | GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames())); |
| | | for (String name : new String[]{"Microsoft YaHei", "微软é
é»", "SimSun", "å®ä½"}) { |
| | | if (fontFamilies.contains(name)) { |
| | | return new Font(name, Font.PLAIN, 12); |
| | | } |
| | | } |
| | | return new Font(Font.DIALOG, Font.PLAIN, 12); |
| | | } |
| | | |
| | | /** |
| | | * æç徿 ï¼ä¼å
ç¨ classpath ä¸ç tray.pngï¼æ²¡æå°±ç°ç»ä¸ä¸ªï¼é¿å
å°ä¸ä¸ªå¾çæä»¶å°±è·ä¸èµ·æ¥ |
| | | */ |
| | | private Image loadTrayImage() { |
| | | try (InputStream in = getClass().getClassLoader().getResourceAsStream("tray.png")) { |
| | | if (in != null) { |
| | | BufferedImage image = ImageIO.read(in); |
| | | if (image != null) { |
| | | return image; |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("读åæç徿 tray.png å¤±è´¥ï¼æ¹ç¨é»è®¤å¾æ ï¼{}", e.getMessage()); |
| | | } |
| | | return createDefaultTrayImage(); |
| | | } |
| | | |
| | | /** |
| | | * é»è®¤å¾æ ï¼èåºç½å S |
| | | */ |
| | | private Image createDefaultTrayImage() { |
| | | BufferedImage image = new BufferedImage(DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, BufferedImage.TYPE_INT_ARGB); |
| | | Graphics2D graphics = image.createGraphics(); |
| | | try { |
| | | graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| | | graphics.setColor(new Color(0x2D8CF0)); |
| | | graphics.fillRoundRect(0, 0, DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, 8, 8); |
| | | graphics.setColor(Color.WHITE); |
| | | graphics.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 20)); |
| | | graphics.drawString("S", 10, 24); |
| | | } finally { |
| | | graphics.dispose(); |
| | | } |
| | | return image; |
| | | } |
| | | } |