diff --git a/src/main/java/com/goeing/printserver/main/gui/PrintQueueGUI.java b/src/main/java/com/goeing/printserver/main/gui/PrintQueueGUI.java index 47b881d..cd639c7 100644 --- a/src/main/java/com/goeing/printserver/main/gui/PrintQueueGUI.java +++ b/src/main/java/com/goeing/printserver/main/gui/PrintQueueGUI.java @@ -33,6 +33,8 @@ public class PrintQueueGUI implements LocaleChangeListener { private final PrintStatisticsPanel statisticsPanel; private final PrintTaskSearchPanel searchPanel; private final PrintSettingsPanel settingsPanel; + private final WebSocketStatusPanel webSocketStatusPanel; + private final SystemLogPanel systemLogPanel; private JFrame frame; private JTable currentTaskTable; private JTable queuedTasksTable; @@ -45,11 +47,14 @@ public class PrintQueueGUI implements LocaleChangeListener { @Autowired public PrintQueueGUI(PrintQueueService printQueueService, PrintStatisticsPanel statisticsPanel, PrintTaskSearchPanel searchPanel, - PrintSettingsPanel settingsPanel) { + PrintSettingsPanel settingsPanel, WebSocketStatusPanel webSocketStatusPanel, + SystemLogPanel systemLogPanel) { this.printQueueService = printQueueService; this.statisticsPanel = statisticsPanel; this.searchPanel = searchPanel; this.settingsPanel = settingsPanel; + this.webSocketStatusPanel = webSocketStatusPanel; + this.systemLogPanel = systemLogPanel; // 注册语言变更监听器 LocaleManager.getInstance().addLocaleChangeListener(this); @@ -123,6 +128,12 @@ public class PrintQueueGUI implements LocaleChangeListener { // 添加设置面板 tabbedPane.addTab(MessageUtils.getMessage("tab.settings"), settingsPanel); + + // 添加WebSocket状态面板 + tabbedPane.addTab(MessageUtils.getMessage("tab.websocket.status"), webSocketStatusPanel); + + // 添加系统日志面板 + tabbedPane.addTab(MessageUtils.getMessage("tab.system.log"), systemLogPanel); frame.add(tabbedPane, BorderLayout.CENTER); diff --git a/src/main/java/com/goeing/printserver/main/gui/SwingLogAppender.java b/src/main/java/com/goeing/printserver/main/gui/SwingLogAppender.java new file mode 100644 index 0000000..dbc4e15 --- /dev/null +++ b/src/main/java/com/goeing/printserver/main/gui/SwingLogAppender.java @@ -0,0 +1,64 @@ +package com.goeing.printserver.main.gui; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import lombok.extern.slf4j.Slf4j; + +/** + * 自定义日志Appender,将日志输出到Swing界面 + */ +public class SwingLogAppender extends AppenderBase { + + private static SystemLogPanel logPanel; + + /** + * 设置日志面板 + */ + public static void setLogPanel(SystemLogPanel panel) { + logPanel = panel; + } + + @Override + protected void append(ILoggingEvent event) { + if (logPanel != null && isStarted()) { + try { + // 转换日志级别 + SystemLogPanel.LogLevel level = convertLogLevel(event.getLevel()); + + // 格式化日志消息 + String message = String.format("[%s] %s", + event.getLoggerName(), + event.getFormattedMessage()); + + // 如果有异常信息,添加到消息中 + if (event.getThrowableProxy() != null) { + message += "\n" + event.getThrowableProxy().getMessage(); + } + + // 添加到日志面板 + logPanel.addLogEntry(level, message); + } catch (Exception e) { + // 避免日志记录本身出错导致的循环问题 + System.err.println("SwingLogAppender error: " + e.getMessage()); + } + } + } + + /** + * 转换日志级别 + */ + private SystemLogPanel.LogLevel convertLogLevel(ch.qos.logback.classic.Level level) { + switch (level.toInt()) { + case ch.qos.logback.classic.Level.ERROR_INT: + return SystemLogPanel.LogLevel.ERROR; + case ch.qos.logback.classic.Level.WARN_INT: + return SystemLogPanel.LogLevel.WARN; + case ch.qos.logback.classic.Level.INFO_INT: + return SystemLogPanel.LogLevel.INFO; + case ch.qos.logback.classic.Level.DEBUG_INT: + case ch.qos.logback.classic.Level.TRACE_INT: + default: + return SystemLogPanel.LogLevel.DEBUG; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/goeing/printserver/main/gui/SystemLogPanel.java b/src/main/java/com/goeing/printserver/main/gui/SystemLogPanel.java new file mode 100644 index 0000000..d1a61cd --- /dev/null +++ b/src/main/java/com/goeing/printserver/main/gui/SystemLogPanel.java @@ -0,0 +1,332 @@ +package com.goeing.printserver.main.gui; + +import com.goeing.printserver.main.utils.LocaleChangeListener; +import com.goeing.printserver.main.utils.LocaleManager; +import com.goeing.printserver.main.utils.MessageUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.swing.*; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * 系统日志面板 + */ +@Component +@Slf4j +public class SystemLogPanel extends JPanel implements LocaleChangeListener { + + private static final int MAX_LOG_LINES = 1000; // 最大日志行数 + private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private final BlockingQueue logQueue = new LinkedBlockingQueue<>(); + + // UI组件 + private JLabel titleLabel; + private JTextPane logTextPane; + private JScrollPane scrollPane; + private JButton clearButton; + private JButton saveButton; + private JComboBox logLevelComboBox; + private JCheckBox autoScrollCheckBox; + + // 样式 + private SimpleAttributeSet infoStyle; + private SimpleAttributeSet warnStyle; + private SimpleAttributeSet errorStyle; + private SimpleAttributeSet debugStyle; + + // 当前日志级别过滤 + private LogLevel currentLogLevel = LogLevel.INFO; + + public SystemLogPanel() { + initializeStyles(); + initializeUI(); + startLogProcessor(); + initializeLogPanel(); + + // 注册语言变更监听器 + LocaleManager.getInstance().addLocaleChangeListener(this); + } + + /** + * 初始化文本样式 + */ + private void initializeStyles() { + infoStyle = new SimpleAttributeSet(); + StyleConstants.setForeground(infoStyle, Color.BLACK); + + warnStyle = new SimpleAttributeSet(); + StyleConstants.setForeground(warnStyle, Color.ORANGE); + StyleConstants.setBold(warnStyle, true); + + errorStyle = new SimpleAttributeSet(); + StyleConstants.setForeground(errorStyle, Color.RED); + StyleConstants.setBold(errorStyle, true); + + debugStyle = new SimpleAttributeSet(); + StyleConstants.setForeground(debugStyle, Color.GRAY); + StyleConstants.setItalic(debugStyle, true); + } + + /** + * 初始化用户界面 + */ + private void initializeUI() { + setLayout(new BorderLayout()); + setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + + // 创建顶部面板 + JPanel topPanel = new JPanel(new BorderLayout()); + + // 标题 + titleLabel = new JLabel(MessageUtils.getMessage("log.panel.title")); + titleLabel.setFont(new Font(titleLabel.getFont().getName(), Font.BOLD, 16)); + titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); + topPanel.add(titleLabel, BorderLayout.WEST); + + // 控制面板 + JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + + // 日志级别过滤 + JLabel levelLabel = new JLabel(MessageUtils.getMessage("log.level.filter")); + controlPanel.add(levelLabel); + + logLevelComboBox = new JComboBox<>(LogLevel.values()); + logLevelComboBox.setSelectedItem(currentLogLevel); + logLevelComboBox.addActionListener(e -> { + currentLogLevel = (LogLevel) logLevelComboBox.getSelectedItem(); + refreshLogDisplay(); + }); + controlPanel.add(logLevelComboBox); + + // 自动滚动 + autoScrollCheckBox = new JCheckBox(MessageUtils.getMessage("log.auto.scroll"), true); + controlPanel.add(autoScrollCheckBox); + + // 清空按钮 + clearButton = new JButton(MessageUtils.getMessage("log.button.clear")); + clearButton.addActionListener(e -> clearLogs()); + controlPanel.add(clearButton); + + // 保存按钮 + saveButton = new JButton(MessageUtils.getMessage("log.button.save")); + saveButton.addActionListener(e -> saveLogs()); + controlPanel.add(saveButton); + + topPanel.add(controlPanel, BorderLayout.EAST); + add(topPanel, BorderLayout.NORTH); + + // 创建日志显示区域 + logTextPane = new JTextPane(); + logTextPane.setEditable(false); + logTextPane.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); + logTextPane.setBackground(Color.WHITE); + + scrollPane = new JScrollPane(logTextPane); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + add(scrollPane, BorderLayout.CENTER); + } + + /** + * 启动日志处理器 + */ + private void startLogProcessor() { + Thread logProcessor = new Thread(() -> { + while (!Thread.currentThread().isInterrupted()) { + try { + LogEntry entry = logQueue.take(); + SwingUtilities.invokeLater(() -> appendLogEntry(entry)); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + }); + logProcessor.setDaemon(true); + logProcessor.setName("LogProcessor"); + logProcessor.start(); + } + + /** + * 初始化日志面板 + */ + private void initializeLogPanel() { + // 注册到SwingLogAppender + SwingLogAppender.setLogPanel(this); + + // 添加初始化日志 + addLogEntry(LogLevel.INFO, "日志面板已初始化"); + } + + /** + * 添加日志条目(公开方法,供SwingLogAppender调用) + */ + public void addLogEntry(LogLevel level, String message) { + LogEntry entry = new LogEntry(level, message, LocalDateTime.now()); + logQueue.offer(entry); + } + + /** + * 添加日志条目到显示区域 + */ + private void appendLogEntry(LogEntry entry) { + if (entry.level.ordinal() < currentLogLevel.ordinal()) { + return; // 过滤掉低级别的日志 + } + + Document doc = logTextPane.getDocument(); + try { + // 检查是否超过最大行数 + if (doc.getLength() > 0) { + String text = doc.getText(0, doc.getLength()); + int lineCount = text.split("\n").length; + if (lineCount > MAX_LOG_LINES) { + // 删除前面的行 + int firstLineEnd = text.indexOf('\n'); + if (firstLineEnd > 0) { + doc.remove(0, firstLineEnd + 1); + } + } + } + + // 格式化日志消息 + String formattedMessage = String.format("[%s] [%s] %s\n", + entry.timestamp.format(dateFormatter), + entry.level.name(), + entry.message); + + // 选择样式 + SimpleAttributeSet style = getStyleForLevel(entry.level); + + // 添加到文档 + doc.insertString(doc.getLength(), formattedMessage, style); + + // 自动滚动到底部 + if (autoScrollCheckBox.isSelected()) { + logTextPane.setCaretPosition(doc.getLength()); + } + + } catch (BadLocationException e) { + log.error("添加日志条目失败", e); + } + } + + /** + * 获取日志级别对应的样式 + */ + private SimpleAttributeSet getStyleForLevel(LogLevel level) { + switch (level) { + case ERROR: + return errorStyle; + case WARN: + return warnStyle; + case DEBUG: + return debugStyle; + case INFO: + default: + return infoStyle; + } + } + + /** + * 刷新日志显示 + */ + private void refreshLogDisplay() { + // 这里可以重新过滤和显示日志 + // 为简化实现,暂时不做复杂的过滤重显示 + } + + /** + * 清空日志 + */ + private void clearLogs() { + int option = JOptionPane.showConfirmDialog(this, + MessageUtils.getMessage("log.clear.confirm"), + MessageUtils.getMessage("dialog.confirm"), + JOptionPane.YES_NO_OPTION); + + if (option == JOptionPane.YES_OPTION) { + logTextPane.setText(""); + log.info("日志已清空"); + } + } + + /** + * 保存日志到文件 + */ + private void saveLogs() { + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDialogTitle(MessageUtils.getMessage("log.save.dialog.title")); + fileChooser.setSelectedFile(new java.io.File("printserver_logs_" + + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".txt")); + + int result = fileChooser.showSaveDialog(this); + if (result == JFileChooser.APPROVE_OPTION) { + try { + java.io.File file = fileChooser.getSelectedFile(); + java.nio.file.Files.write(file.toPath(), logTextPane.getText().getBytes()); + JOptionPane.showMessageDialog(this, + MessageUtils.getMessage("log.save.success", new Object[]{file.getAbsolutePath()}), + MessageUtils.getMessage("dialog.success"), + JOptionPane.INFORMATION_MESSAGE); + } catch (Exception e) { + log.error("保存日志文件失败", e); + JOptionPane.showMessageDialog(this, + MessageUtils.getMessage("log.save.error", new Object[]{e.getMessage()}), + MessageUtils.getMessage("dialog.error"), + JOptionPane.ERROR_MESSAGE); + } + } + } + + @Override + public void onLocaleChanged(Locale newLocale) { + SwingUtilities.invokeLater(() -> { + titleLabel.setText(MessageUtils.getMessage("log.panel.title")); + clearButton.setText(MessageUtils.getMessage("log.button.clear")); + saveButton.setText(MessageUtils.getMessage("log.button.save")); + autoScrollCheckBox.setText(MessageUtils.getMessage("log.auto.scroll")); + }); + } + + /** + * 日志级别枚举 + */ + public enum LogLevel { + DEBUG, INFO, WARN, ERROR + } + + /** + * 日志条目类 + */ + private static class LogEntry { + final LogLevel level; + final String message; + final LocalDateTime timestamp; + + LogEntry(LogLevel level, String message, LocalDateTime timestamp) { + this.level = level; + this.message = message; + this.timestamp = timestamp; + } + } + + /** + * 清理资源 + */ + public void shutdown() { + LocaleManager.getInstance().removeLocaleChangeListener(this); + } +} \ No newline at end of file diff --git a/src/main/java/com/goeing/printserver/main/gui/WebSocketStatusPanel.java b/src/main/java/com/goeing/printserver/main/gui/WebSocketStatusPanel.java new file mode 100644 index 0000000..3e33853 --- /dev/null +++ b/src/main/java/com/goeing/printserver/main/gui/WebSocketStatusPanel.java @@ -0,0 +1,258 @@ +package com.goeing.printserver.main.gui; + +import com.goeing.printserver.main.sse.PrinterClient; +import com.goeing.printserver.main.utils.LocaleChangeListener; +import com.goeing.printserver.main.utils.LocaleManager; +import com.goeing.printserver.main.utils.MessageUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.swing.*; +import java.awt.*; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * WebSocket连接状态面板 + */ +@Component +@Slf4j +public class WebSocketStatusPanel extends JPanel implements LocaleChangeListener { + + private final PrinterClient printerClient; + private final ScheduledExecutorService refreshExecutor = Executors.newSingleThreadScheduledExecutor(); + private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + // UI组件 + private JLabel titleLabel; + private JLabel connectionStatusLabel; + private JLabel connectionUrlLabel; + private JLabel lastConnectTimeLabel; + private JLabel reconnectCountLabel; + private JButton reconnectButton; + private JButton disconnectButton; + + // 状态数据 + private boolean isConnected = false; + private String connectionUrl = ""; + private LocalDateTime lastConnectTime; + private int reconnectCount = 0; + + @Autowired + public WebSocketStatusPanel(PrinterClient printerClient) { + this.printerClient = printerClient; + initializeUI(); + startStatusRefresh(); + + // 注册语言变更监听器 + LocaleManager.getInstance().addLocaleChangeListener(this); + } + + /** + * 初始化用户界面 + */ + private void initializeUI() { + setLayout(new BorderLayout()); + setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + + // 创建标题 + titleLabel = new JLabel(MessageUtils.getMessage("websocket.status.title")); + titleLabel.setFont(new Font(titleLabel.getFont().getName(), Font.BOLD, 16)); + titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); + add(titleLabel, BorderLayout.NORTH); + + // 创建状态信息面板 + JPanel statusPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(5, 5, 5, 5); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + + // 连接状态 + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 0.0; + JLabel statusTitleLabel = new JLabel(MessageUtils.getMessage("websocket.status.connection")); + statusPanel.add(statusTitleLabel, gbc); + + gbc.gridx = 1; + gbc.weightx = 1.0; + connectionStatusLabel = new JLabel(MessageUtils.getMessage("websocket.status.disconnected")); + connectionStatusLabel.setForeground(Color.RED); + statusPanel.add(connectionStatusLabel, gbc); + + // 连接URL + gbc.gridx = 0; + gbc.gridy = 1; + gbc.weightx = 0.0; + JLabel urlTitleLabel = new JLabel(MessageUtils.getMessage("websocket.status.url")); + statusPanel.add(urlTitleLabel, gbc); + + gbc.gridx = 1; + gbc.weightx = 1.0; + connectionUrlLabel = new JLabel("-"); + statusPanel.add(connectionUrlLabel, gbc); + + // 最后连接时间 + gbc.gridx = 0; + gbc.gridy = 2; + gbc.weightx = 0.0; + JLabel timeTitleLabel = new JLabel(MessageUtils.getMessage("websocket.status.last.connect")); + statusPanel.add(timeTitleLabel, gbc); + + gbc.gridx = 1; + gbc.weightx = 1.0; + lastConnectTimeLabel = new JLabel("-"); + statusPanel.add(lastConnectTimeLabel, gbc); + + // 重连次数 + gbc.gridx = 0; + gbc.gridy = 3; + gbc.weightx = 0.0; + JLabel reconnectTitleLabel = new JLabel(MessageUtils.getMessage("websocket.status.reconnect.count")); + statusPanel.add(reconnectTitleLabel, gbc); + + gbc.gridx = 1; + gbc.weightx = 1.0; + reconnectCountLabel = new JLabel("0"); + statusPanel.add(reconnectCountLabel, gbc); + + // 添加弹性空间 + gbc.gridx = 0; + gbc.gridy = 4; + gbc.gridwidth = 2; + gbc.weighty = 1.0; + statusPanel.add(Box.createVerticalGlue(), gbc); + + add(statusPanel, BorderLayout.CENTER); + + // 创建按钮面板 + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + + reconnectButton = new JButton(MessageUtils.getMessage("websocket.button.reconnect")); + reconnectButton.addActionListener(e -> reconnectWebSocket()); + buttonPanel.add(reconnectButton); + + disconnectButton = new JButton(MessageUtils.getMessage("websocket.button.disconnect")); + disconnectButton.addActionListener(e -> disconnectWebSocket()); + buttonPanel.add(disconnectButton); + + add(buttonPanel, BorderLayout.SOUTH); + } + + /** + * 启动状态刷新 + */ + private void startStatusRefresh() { + refreshExecutor.scheduleAtFixedRate(this::updateStatus, 0, 1, TimeUnit.SECONDS); + } + + /** + * 更新状态信息 + */ + private void updateStatus() { + SwingUtilities.invokeLater(() -> { + try { + // 这里需要从PrinterClient获取实际状态 + // 由于PrinterClient没有公开状态方法,我们使用反射或添加公开方法 + updateConnectionStatus(); + } catch (Exception e) { + log.error("更新WebSocket状态失败", e); + } + }); + } + + /** + * 更新连接状态显示 + */ + private void updateConnectionStatus() { + // 获取实际的连接状态 + boolean actuallyConnected = printerClient.isConnected(); + String currentUrl = printerClient.getCurrentConnectionUrl(); + + if (actuallyConnected) { + connectionStatusLabel.setText(MessageUtils.getMessage("websocket.status.connected")); + connectionStatusLabel.setForeground(Color.GREEN); + reconnectButton.setEnabled(false); + disconnectButton.setEnabled(true); + + // 如果之前是断开状态,现在连接了,更新连接时间 + if (!isConnected) { + lastConnectTime = LocalDateTime.now(); + } + } else { + connectionStatusLabel.setText(MessageUtils.getMessage("websocket.status.disconnected")); + connectionStatusLabel.setForeground(Color.RED); + reconnectButton.setEnabled(true); + disconnectButton.setEnabled(false); + } + + isConnected = actuallyConnected; + connectionUrl = currentUrl != null ? currentUrl : ""; + + connectionUrlLabel.setText(connectionUrl.isEmpty() ? "-" : connectionUrl); + lastConnectTimeLabel.setText(lastConnectTime != null ? lastConnectTime.format(dateFormatter) : "-"); + reconnectCountLabel.setText(String.valueOf(reconnectCount)); + } + + /** + * 重新连接WebSocket + */ + private void reconnectWebSocket() { + try { + printerClient.reconnect(); + reconnectCount++; + lastConnectTime = LocalDateTime.now(); + isConnected = true; // 假设连接成功 + log.info("手动重新连接WebSocket"); + } catch (Exception e) { + log.error("重新连接WebSocket失败", e); + JOptionPane.showMessageDialog(this, + MessageUtils.getMessage("websocket.error.reconnect", new Object[]{e.getMessage()}), + MessageUtils.getMessage("dialog.error"), + JOptionPane.ERROR_MESSAGE); + } + } + + /** + * 断开WebSocket连接 + */ + private void disconnectWebSocket() { + try { + printerClient.disconnect(); + isConnected = false; + log.info("手动断开WebSocket连接"); + } catch (Exception e) { + log.error("断开WebSocket连接失败", e); + JOptionPane.showMessageDialog(this, + MessageUtils.getMessage("websocket.error.disconnect", new Object[]{e.getMessage()}), + MessageUtils.getMessage("dialog.error"), + JOptionPane.ERROR_MESSAGE); + } + } + + @Override + public void onLocaleChanged(Locale newLocale) { + SwingUtilities.invokeLater(() -> { + titleLabel.setText(MessageUtils.getMessage("websocket.status.title")); + reconnectButton.setText(MessageUtils.getMessage("websocket.button.reconnect")); + disconnectButton.setText(MessageUtils.getMessage("websocket.button.disconnect")); + updateConnectionStatus(); + }); + } + + /** + * 清理资源 + */ + public void shutdown() { + if (refreshExecutor != null && !refreshExecutor.isShutdown()) { + refreshExecutor.shutdownNow(); + } + LocaleManager.getInstance().removeLocaleChangeListener(this); + } +} \ No newline at end of file diff --git a/src/main/java/com/goeing/printserver/main/sse/PrinterClient.java b/src/main/java/com/goeing/printserver/main/sse/PrinterClient.java index ea44158..e35082b 100644 --- a/src/main/java/com/goeing/printserver/main/sse/PrinterClient.java +++ b/src/main/java/com/goeing/printserver/main/sse/PrinterClient.java @@ -231,6 +231,41 @@ public class PrinterClient implements ApplicationRunner { isConnecting = false; connect(); } + + /** + * 获取WebSocket连接状态 + */ + public boolean isConnected() { + return session != null && session.isOpen(); + } + + /** + * 获取当前连接的URL + */ + public String getCurrentConnectionUrl() { + if (isConnected()) { + String serverUri = config.getWebsocketUrl(); + String printerId = config.getPrinterId(); + String apiKey = config.getApiKey(); + return serverUri + "?printerId=" + printerId + "&apiKey=" + apiKey; + } + return null; + } + + /** + * 断开WebSocket连接 + */ + public void disconnect() { + if (session != null && session.isOpen()) { + try { + session.close(); + log.info("WebSocket连接已手动断开"); + } catch (Exception e) { + log.error("断开WebSocket连接失败", e); + throw new RuntimeException("断开连接失败", e); + } + } + } @Override public void run(ApplicationArguments args) { diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties index b271995..6489a5d 100644 --- a/src/main/resources/messages_en.properties +++ b/src/main/resources/messages_en.properties @@ -45,6 +45,8 @@ tab.printer.status=Printer Status tab.statistics=Statistics tab.task.search=Task Search tab.settings=Settings +tab.websocket.status=WebSocket Status +tab.system.log=System Log # Table Headers table.fileUrl=File URL @@ -156,4 +158,33 @@ tray.notification.title=Print Server Started tray.notification.message=Print Server is running in the background, click the tray icon to open the main window app.status.running=Running menu.help=Help -menu.help.about=About \ No newline at end of file +menu.help.about=About + +# Tab titles +tab.websocket.status=WebSocket Status +tab.system.log=System Log + +# WebSocket Status Panel +websocket.status.title=WebSocket Status +websocket.status.connected=Connected +websocket.status.disconnected=Disconnected +websocket.status.url=Connection URL: +websocket.status.last.connect=Last Connect Time: +websocket.status.reconnect.count=Reconnect Count: +websocket.button.reconnect=Reconnect +websocket.button.disconnect=Disconnect +websocket.error.reconnect=Failed to reconnect: {0} +websocket.error.disconnect=Failed to disconnect: {0} + +# System Log Panel +log.panel.title=System Log +log.level.all=All +log.level.error=Error +log.level.warn=Warning +log.level.info=Info +log.level.debug=Debug +log.auto.scroll=Auto Scroll +log.button.clear=Clear +log.button.save=Save +log.save.success=Log saved successfully to: {0} +log.save.error=Failed to save log: {0} \ No newline at end of file diff --git a/src/main/resources/messages_zh_CN.properties b/src/main/resources/messages_zh_CN.properties index 9ef82e8..80f9b0b 100644 --- a/src/main/resources/messages_zh_CN.properties +++ b/src/main/resources/messages_zh_CN.properties @@ -43,6 +43,8 @@ tab.current.task=当前任务 tab.queued.tasks=队列任务 tab.printer.status=打印机状态 tab.task.search=任务搜索 +tab.websocket.status=WebSocket状态 +tab.system.log=系统日志 # 表格标题 table.fileUrl=文件URL @@ -154,4 +156,36 @@ tray.open=打开主窗口 tray.exit=退出 tray.notification.title=打印服务器已启动 tray.notification.message=打印服务器正在后台运行,点击托盘图标可打开主窗口 -app.status.running=运行中 \ No newline at end of file +app.status.running=运行中 +system.tray.tooltip=打印服务器 +system.tray.show=显示主窗口 +system.tray.hide=隐藏主窗口 +system.tray.exit=退出应用程序 + +# 选项卡标题 +tab.websocket.status=WebSocket状态 +tab.system.log=系统日志 + +# WebSocket状态面板 +websocket.status.title=WebSocket连接状态 +websocket.status.connection=连接状态: +websocket.status.url=服务器地址: +websocket.status.last.connect=最后连接时间: +websocket.status.reconnect.count=重连次数: +websocket.status.connected=已连接 +websocket.status.disconnected=未连接 +websocket.button.reconnect=重新连接 +websocket.button.disconnect=断开连接 +websocket.error.reconnect=重新连接失败:{0} +websocket.error.disconnect=断开连接失败:{0} + +# 系统日志面板 +log.panel.title=系统日志 +log.level.filter=日志级别: +log.auto.scroll=自动滚动 +log.button.clear=清空 +log.button.save=保存到文件 +log.clear.confirm=确定要清空所有日志吗? +log.save.dialog.title=保存日志文件 +log.save.success=日志文件保存成功:{0} +log.save.error=保存日志文件失败:{0} \ No newline at end of file