新增日志面板,ws连接状态
This commit is contained in:
parent
e525e06d7a
commit
b0b6c6dfad
@ -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);
|
||||
|
||||
|
||||
@ -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<ILoggingEvent> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<LogEntry> logQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
// UI组件
|
||||
private JLabel titleLabel;
|
||||
private JTextPane logTextPane;
|
||||
private JScrollPane scrollPane;
|
||||
private JButton clearButton;
|
||||
private JButton saveButton;
|
||||
private JComboBox<LogLevel> 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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
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}
|
||||
@ -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=运行中
|
||||
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}
|
||||
Loading…
Reference in New Issue
Block a user