| | |
| | | import udptcp.UDPPortBReceiver; |
| | | |
| | | public class MessageViewPanel extends JPanel { |
| | | /** |
| | | * |
| | | */ |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | private ResourceBundle messages; // 国际化消息资源包 |
| | | |
| | | // UI组件声明 |
| | | private JButton btnStart, btnPause, btnClear, btnSend; // 控制按钮:开始、暂停、清除、发送 |
| | | private static JComboBox<String> cbDataType; // 下拉框:数据类型、协议、设备ID |
| | | private ResourceBundle messages; |
| | | private JButton btnStart, btnPause, btnClear, btnSend; |
| | | private static JComboBox<String> cbDataType; |
| | | private static JComboBox<String> cbProtocol; |
| | | private static JComboBox<String> cbDeviceId; |
| | | private static JComboBox<String> cbRemoteDevice; // 远程设备下拉框 |
| | | private static JCheckBox chkAutoSave; // 复选框:自动保存、显示来源、十六进制显示、ASCII显示 |
| | | private static JComboBox<String> cbRemoteDevice; |
| | | private static JCheckBox chkAutoSave; |
| | | private static JCheckBox chkShowSource; |
| | | private static JCheckBox chkHexDisplay; |
| | | private static JCheckBox chkAsciiDisplay; |
| | | private static JTextArea txtDataView; // 数据显示区域 |
| | | private JScrollPane scrollPane; // 带滚动条的面板 |
| | | private JTextField txtSendData; // 发送数据文本框 |
| | | private JRadioButton rdoSendHex; // HEX发送单选按钮 |
| | | private JRadioButton rdoSendAscii; // ASCII发送单选按钮 |
| | | private JCheckBox chkAppendNewline; // 回车换行复选框 |
| | | // 添加静态变量跟踪窗口可见状态 |
| | | private static JTextArea txtDataView; |
| | | private JScrollPane scrollPane; |
| | | private JTextField txtSendData; |
| | | private JRadioButton rdoSendHex; |
| | | private JRadioButton rdoSendAscii; |
| | | private JCheckBox chkAppendNewline; |
| | | |
| | | private static final ThreadLocal<StringBuilder> SB = |
| | | ThreadLocal.withInitial(() -> new StringBuilder(256)); |
| | | private static final char[] DIGITS = "0123456789".toCharArray(); |
| | | private static final String COLON = ":"; |
| | | private static final String PREFIX = "["; |
| | | private static final String SUFFIX = "]"; |
| | | |
| | | public static volatile boolean isWindowVisible = false; |
| | | // 状态变量 |
| | | private static boolean isCapturing = false; // 是否正在捕获数据的标志 |
| | | |
| | | // 修改为Map存储设备及其本地端口 |
| | | private static boolean isCapturing = false; |
| | | private static boolean isStarted = false; // 新增:跟踪开始按钮状态 |
| | | |
| | | private static Map<String, Integer> remoteDeviceLocalPortMap = new HashMap<>(); |
| | | // 构造函数 |
| | | public MessageViewPanel(ResourceBundle messages) { |
| | | this.messages = messages; // 初始化消息资源包 |
| | | initializeUI(); // 初始化用户界面 |
| | | } |
| | | |
| | | |
| | | |
| | | public MessageViewPanel(ResourceBundle messages) { |
| | | this.messages = messages; |
| | | initializeUI(); |
| | | } |
| | | |
| | | public void addNotify() { |
| | | super.addNotify(); |
| | | Window window = SwingUtilities.getWindowAncestor(this); |
| | | if (window != null) { |
| | | isWindowVisible = true; // 面板已添加到可见窗口 |
| | | isWindowVisible = true; |
| | | window.addWindowListener(new WindowAdapter() { |
| | | @Override |
| | | public void windowClosing(WindowEvent e) { |
| | |
| | | } |
| | | } |
| | | |
| | | // 初始化UI组件 |
| | | private void initializeUI() { |
| | | setLayout(new GridBagLayout()); // 使用网格袋布局 |
| | | GridBagConstraints gbc = new GridBagConstraints(); // 布局约束对象 |
| | | gbc.insets = new Insets(5, 10, 5, 10); // 设置组件间距(上、左、下、右) |
| | | gbc.anchor = GridBagConstraints.WEST; // 组件靠左对齐 |
| | | gbc.fill = GridBagConstraints.HORIZONTAL; // 水平填充 |
| | | setBackground(new Color(240, 245, 249)); // 设置背景色 |
| | | setLayout(new GridBagLayout()); |
| | | GridBagConstraints gbc = new GridBagConstraints(); |
| | | gbc.insets = new Insets(5, 10, 5, 10); |
| | | gbc.anchor = GridBagConstraints.WEST; |
| | | gbc.fill = GridBagConstraints.HORIZONTAL; |
| | | setBackground(new Color(240, 245, 249)); |
| | | |
| | | // 下拉框区域 |
| | | gbc.gridx = 0; // 起始列 |
| | | gbc.gridy = 0; // 起始行 |
| | | gbc.gridwidth = 1; // 占用1列宽度 |
| | | gbc.weightx = 0.25; // 水平权重 |
| | | gbc.gridx = 0; |
| | | gbc.gridy = 0; |
| | | gbc.gridwidth = 1; |
| | | gbc.weightx = 0.25; |
| | | |
| | | // 数据类型下拉框面板 |
| | | JPanel dataTypePanel = new JPanel(new BorderLayout(5, 0)); // 使用边界布局 |
| | | dataTypePanel.setBackground(new Color(240, 245, 249)); // 设置背景色 |
| | | dataTypePanel.add(createLabel(messages.getString("MESSAGE_VIEW_DATA_TYPE")), BorderLayout.WEST); // 添加标签 |
| | | cbDataType = new JComboBox<>(); // 创建下拉框 |
| | | loadtyps();// 添加"所有类型"选项 |
| | | dataTypePanel.add(cbDataType, BorderLayout.CENTER); // 将下拉框添加到面板中央 |
| | | add(dataTypePanel, gbc); // 将面板添加到主面板 |
| | | JPanel dataTypePanel = new JPanel(new BorderLayout(5, 0)); |
| | | dataTypePanel.setBackground(new Color(240, 245, 249)); |
| | | dataTypePanel.add(createLabel(messages.getString("MESSAGE_VIEW_DATA_TYPE")), BorderLayout.WEST); |
| | | cbDataType = new JComboBox<>(); |
| | | loadtyps(); |
| | | dataTypePanel.add(cbDataType, BorderLayout.CENTER); |
| | | add(dataTypePanel, gbc); |
| | | |
| | | // 协议下拉框 |
| | | gbc.gridx++; // 移动到下一列 |
| | | JPanel protocolPanel = new JPanel(new BorderLayout(5, 0)); // 协议选择面板 |
| | | protocolPanel.setBackground(new Color(240, 245, 249)); // 设置背景色 |
| | | protocolPanel.add(createLabel(messages.getString("MESSAGE_VIEW_PROTOCOL")), BorderLayout.WEST); // 添加协议标签 |
| | | cbProtocol = new JComboBox<>(new String[]{ // 创建协议下拉框并初始化选项 |
| | | messages.getString("MESSAGE_VIEW_ALL_PROTOCOLS"), // 所有协议 |
| | | "UDP", // UDP协议 |
| | | "TCP", // TCP协议 |
| | | "MQTT", // MQTT协议 |
| | | "HTTP" // HTTP协议 |
| | | gbc.gridx++; |
| | | JPanel protocolPanel = new JPanel(new BorderLayout(5, 0)); |
| | | protocolPanel.setBackground(new Color(240, 245, 249)); |
| | | protocolPanel.add(createLabel(messages.getString("MESSAGE_VIEW_PROTOCOL")), BorderLayout.WEST); |
| | | cbProtocol = new JComboBox<>(new String[]{ |
| | | "ALL", |
| | | "55AA01", "55AA02", "55AA03", "55AA05", "55AA07", |
| | | "55AA0A", "55AA0C", "55AA12", "55AA14", "55AA20", |
| | | "GNGGA", "XTB" |
| | | }); |
| | | protocolPanel.add(cbProtocol, BorderLayout.CENTER); // 将下拉框添加到面板中央 |
| | | add(protocolPanel, gbc); // 将面板添加到主面板 |
| | | protocolPanel.add(cbProtocol, BorderLayout.CENTER); |
| | | add(protocolPanel, gbc); |
| | | |
| | | // 设备ID下拉框 |
| | | gbc.gridx++; // 移动到下一列 |
| | | JPanel devicePanel = new JPanel(new BorderLayout(5, 0)); // 设备ID选择面板 |
| | | devicePanel.setBackground(new Color(240, 245, 249)); // 设置背景色 |
| | | devicePanel.add(createLabel(messages.getString("MESSAGE_VIEW_DEVICE_ID")), BorderLayout.WEST); // 添加设备ID标签 |
| | | cbDeviceId = new JComboBox<>(); // 创建设备ID下拉框 |
| | | cbDeviceId.setEditable(true); // 设置为可编辑,允许手动输入设备ID |
| | | cbDeviceId.addItem(messages.getString("MESSAGE_VIEW_ALL_DEVICES")); // 添加"所有设备"选项 |
| | | loadDeviceIds(); // 加载设备ID列表 |
| | | devicePanel.add(cbDeviceId, BorderLayout.CENTER); // 将下拉框添加到面板中央 |
| | | add(devicePanel, gbc); // 将面板添加到主面板 |
| | | gbc.gridx++; |
| | | JPanel devicePanel = new JPanel(new BorderLayout(5, 0)); |
| | | devicePanel.setBackground(new Color(240, 245, 249)); |
| | | devicePanel.add(createLabel(messages.getString("MESSAGE_VIEW_DEVICE_ID")), BorderLayout.WEST); |
| | | cbDeviceId = new JComboBox<>(); |
| | | cbDeviceId.setEditable(true); |
| | | cbDeviceId.addItem("ALL"); |
| | | loadDeviceIds(); |
| | | devicePanel.add(cbDeviceId, BorderLayout.CENTER); |
| | | add(devicePanel, gbc); |
| | | |
| | | // 远程设备下拉框 |
| | | gbc.gridx++; // 移动到下一列 |
| | | JPanel remoteDevicePanel = new JPanel(new BorderLayout(5, 0)); // 远程设备选择面板 |
| | | remoteDevicePanel.setBackground(new Color(240, 245, 249)); // 设置背景色 |
| | | remoteDevicePanel.add(createLabel(messages.getString("MESSAGE_VIEW_REMOTE_DEVICE")), BorderLayout.WEST); // 添加远程设备标签 |
| | | cbRemoteDevice = new JComboBox<>(); // 创建远程设备下拉框 |
| | | cbRemoteDevice.setEditable(true); // 设置为可编辑 |
| | | cbRemoteDevice.addItem(messages.getString("MESSAGE_VIEW_ALL_DEVICES")); // 添加"所有设备"选项 |
| | | remoteDevicePanel.add(cbRemoteDevice, BorderLayout.CENTER); // 将下拉框添加到面板中央 |
| | | add(remoteDevicePanel, gbc); // 将面板添加到主面板 |
| | | gbc.gridx++; |
| | | JPanel remoteDevicePanel = new JPanel(new BorderLayout(5, 0)); |
| | | remoteDevicePanel.setBackground(new Color(240, 245, 249)); |
| | | remoteDevicePanel.add(createLabel(messages.getString("MESSAGE_VIEW_REMOTE_DEVICE")), BorderLayout.WEST); |
| | | cbRemoteDevice = new JComboBox<>(); |
| | | cbRemoteDevice.setEditable(true); |
| | | cbRemoteDevice.addItem(messages.getString("MESSAGE_VIEW_ALL_DEVICES")); |
| | | remoteDevicePanel.add(cbRemoteDevice, BorderLayout.CENTER); |
| | | add(remoteDevicePanel, gbc); |
| | | |
| | | // 复选框区域 |
| | | gbc.gridx = 0; // 重置列位置 |
| | | gbc.gridy++; // 移动到下一行 |
| | | gbc.gridwidth = 4; // 横跨4列 |
| | | gbc.weightx = 1.0; // 水平权重 |
| | | // 复选框区域 - 简化为两个复选框 |
| | | gbc.gridx = 0; |
| | | gbc.gridy++; |
| | | gbc.gridwidth = 4; |
| | | gbc.weightx = 1.0; |
| | | |
| | | JPanel checkboxPanel = new JPanel(new GridLayout(1, 4, 6, 0)); // 创建网格布局面板(1行4列) |
| | | checkboxPanel.setBackground(new Color(240, 245, 249)); // 设置背景色 |
| | | chkAutoSave = createStyledCheckbox(messages.getString("MESSAGE_VIEW_AUTO_SAVE")); // 创建自动保存复选框 |
| | | chkShowSource = createStyledCheckbox(messages.getString("MESSAGE_VIEW_SHOW_SOURCE")); // 创建显示来源复选框 |
| | | chkHexDisplay = createStyledCheckbox(messages.getString("MESSAGE_VIEW_HEX_DISPLAY")); // 创建十六进制显示复选框 |
| | | chkAsciiDisplay = createStyledCheckbox(messages.getString("MESSAGE_VIEW_ASCII_DISPLAY")); // 创建ASCII显示复选框 |
| | | |
| | | // 将复选框添加到面板 |
| | | JPanel checkboxPanel = new JPanel(new GridLayout(1, 2, 6, 0)); // 修改为2列 |
| | | checkboxPanel.setBackground(new Color(240, 245, 249)); |
| | | chkAutoSave = createStyledCheckbox(messages.getString("MESSAGE_VIEW_AUTO_SAVE")); |
| | | chkShowSource = createStyledCheckbox(messages.getString("MESSAGE_VIEW_SHOW_SOURCE")); |
| | | |
| | | // 移除了十六进制和ASCII显示复选框 |
| | | checkboxPanel.add(chkAutoSave); |
| | | checkboxPanel.add(chkShowSource); |
| | | checkboxPanel.add(chkHexDisplay); |
| | | checkboxPanel.add(chkAsciiDisplay); |
| | | |
| | | add(checkboxPanel, gbc); // 将复选框面板添加到主面板 |
| | | add(checkboxPanel, gbc); |
| | | |
| | | // 数据显示区域 |
| | | gbc.gridy++; // 移动到下一行 |
| | | gbc.weighty = 1.0; // 垂直权重 |
| | | gbc.fill = GridBagConstraints.BOTH; // 双向填充 |
| | | gbc.gridy++; |
| | | gbc.weighty = 1.0; |
| | | gbc.fill = GridBagConstraints.BOTH; |
| | | |
| | | txtDataView = new JTextArea(); // 创建文本区域 |
| | | txtDataView.setFont(new Font("Monospaced", Font.PLAIN, 14)); // 设置等宽字体 |
| | | txtDataView.setEditable(false); // 设置为不可编辑 |
| | | scrollPane = new JScrollPane(txtDataView); // 创建带滚动条的面板 |
| | | txtDataView = new JTextArea(); |
| | | txtDataView.setFont(new Font("Monospaced", Font.PLAIN, 14)); |
| | | txtDataView.setEditable(false); |
| | | scrollPane = new JScrollPane(txtDataView); |
| | | scrollPane.setPreferredSize(new Dimension(800, 400)); |
| | | scrollPane.setBorder(BorderFactory.createTitledBorder( // 设置带标题的边框 |
| | | BorderFactory.createLineBorder(new Color(180, 180, 220)), // 边框颜色 |
| | | messages.getString("MESSAGE_VIEW_DATA_PANEL"), // 标题文本 |
| | | TitledBorder.LEFT, // 标题位置 |
| | | TitledBorder.TOP, // 标题位置 |
| | | new Font("微软雅黑", Font.BOLD, 12), // 标题字体 |
| | | new Color(70, 130, 180) // 标题颜色 |
| | | )); |
| | | scrollPane.setBorder(BorderFactory.createTitledBorder( |
| | | BorderFactory.createLineBorder(new Color(180, 180, 220)), |
| | | messages.getString("MESSAGE_VIEW_DATA_PANEL"), |
| | | TitledBorder.LEFT, |
| | | TitledBorder.TOP, |
| | | new Font("微软雅黑", Font.BOLD, 12), |
| | | new Color(70, 130, 180) |
| | | )); |
| | | |
| | | add(scrollPane, gbc); // 将滚动面板添加到主面板 |
| | | add(scrollPane, gbc); |
| | | |
| | | // 按钮面板区域 |
| | | gbc.gridy++; // 移动到下一行 |
| | | gbc.weighty = 0; // 重置垂直权重 |
| | | gbc.fill = GridBagConstraints.HORIZONTAL; // 水平填充 |
| | | gbc.gridy++; |
| | | gbc.weighty = 0; |
| | | gbc.fill = GridBagConstraints.HORIZONTAL; |
| | | |
| | | // 按钮面板 |
| | | JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5)); // 使用流式布局(左对齐) |
| | | buttonPanel.setBackground(new Color(240, 245, 249)); // 设置背景色 |
| | | btnStart = createStyledButton(messages.getString("MESSAGE_VIEW_START"), new Color(70, 130, 180)); // 创建开始按钮(蓝色) |
| | | btnPause = createStyledButton(messages.getString("MESSAGE_VIEW_PAUSE"), new Color(218, 165, 32)); // 创建暂停按钮(金色) |
| | | btnClear = createStyledButton(messages.getString("MESSAGE_VIEW_CLEAR"), new Color(205, 92, 92)); // 创建清除按钮(红色) |
| | | JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5)); |
| | | buttonPanel.setBackground(new Color(240, 245, 249)); |
| | | btnStart = createStyledButton(messages.getString("MESSAGE_VIEW_START"), new Color(70, 130, 180)); |
| | | btnPause = createStyledButton(messages.getString("MESSAGE_VIEW_PAUSE"), new Color(218, 165, 32)); |
| | | btnClear = createStyledButton(messages.getString("MESSAGE_VIEW_CLEAR"), new Color(205, 92, 92)); |
| | | |
| | | // 发送相关控件 |
| | | txtSendData = new JTextField(30); // 发送数据文本框 |
| | | txtSendData.setFont(new Font("微软雅黑", Font.PLAIN, 12)); // 设置字体 |
| | | btnSend = createStyledButton(messages.getString("MESSAGE_VIEW_SEND"), new Color(60, 179, 113)); // 创建发送按钮(绿色) |
| | | txtSendData = new JTextField(30); |
| | | txtSendData.setFont(new Font("微软雅黑", Font.PLAIN, 12)); |
| | | btnSend = createStyledButton(messages.getString("MESSAGE_VIEW_SEND"), new Color(60, 179, 113)); |
| | | |
| | | // 创建发送选项面板 |
| | | JPanel sendOptionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0)); |
| | | sendOptionsPanel.setBackground(new Color(240, 245, 249)); |
| | | |
| | | // 创建单选按钮组 |
| | | ButtonGroup sendFormatGroup = new ButtonGroup(); |
| | | |
| | | // 创建HEX发送单选按钮 |
| | | rdoSendHex = createStyledRadioButton(messages.getString("MESSAGE_VIEW_SEND_HEX")); |
| | | // 创建ASCII发送单选按钮 |
| | | rdoSendAscii = createStyledRadioButton(messages.getString("MESSAGE_VIEW_SEND_ASCII")); |
| | | |
| | | // 将单选按钮添加到按钮组 |
| | | sendFormatGroup.add(rdoSendHex); |
| | | sendFormatGroup.add(rdoSendAscii); |
| | | |
| | | // 默认选中ASCII发送 |
| | | rdoSendAscii.setSelected(true); |
| | | |
| | | // 回车换行复选框 |
| | | chkAppendNewline = createStyledCheckbox(messages.getString("MESSAGE_VIEW_APPEND_NEWLINE")); |
| | | |
| | | sendOptionsPanel.add(rdoSendHex); |
| | | sendOptionsPanel.add(rdoSendAscii); |
| | | sendOptionsPanel.add(chkAppendNewline); |
| | | |
| | | // 将按钮添加到按钮面板 |
| | | buttonPanel.add(btnStart); |
| | | buttonPanel.add(btnPause); |
| | | buttonPanel.add(btnClear); |
| | |
| | | buttonPanel.add(btnSend); |
| | | buttonPanel.add(sendOptionsPanel); |
| | | |
| | | add(buttonPanel, gbc); // 将按钮面板添加到主面板 |
| | | add(buttonPanel, gbc); |
| | | |
| | | // 设置按钮动作 |
| | | setupActions(); |
| | | } |
| | | |
| | | // 创建带样式的标签 |
| | | private JLabel createLabel(String text) { |
| | | JLabel label = new JLabel(text); // 创建标签 |
| | | label.setFont(new Font("微软雅黑", Font.BOLD, 12)); // 设置字体 |
| | | label.setForeground(new Color(70, 70, 70)); // 设置前景色 |
| | | JLabel label = new JLabel(text); |
| | | label.setFont(new Font("微软雅黑", Font.BOLD, 12)); |
| | | label.setForeground(new Color(70, 70, 70)); |
| | | return label; |
| | | } |
| | | |
| | | // 创建带样式的按钮 |
| | | private JButton createStyledButton(String text, Color bgColor) { |
| | | JButton button = new JButton(text); // 创建按钮 |
| | | button.setFont(new Font("微软雅黑", Font.BOLD, 12)); // 设置字体 |
| | | button.setBackground(bgColor); // 设置背景色 |
| | | button.setForeground(Color.WHITE); // 设置前景色(白色) |
| | | button.setFocusPainted(false); // 禁用焦点边框 |
| | | button.setBorder(BorderFactory.createCompoundBorder( // 设置复合边框 |
| | | BorderFactory.createLineBorder(bgColor.darker(), 1), // 外部边框(深色) |
| | | BorderFactory.createEmptyBorder(5, 15, 5, 15) // 内部边距 |
| | | )); |
| | | JButton button = new JButton(text); |
| | | button.setFont(new Font("微软雅黑", Font.BOLD, 12)); |
| | | button.setBackground(bgColor); |
| | | button.setForeground(Color.WHITE); |
| | | button.setFocusPainted(false); |
| | | button.setBorder(BorderFactory.createCompoundBorder( |
| | | BorderFactory.createLineBorder(bgColor.darker(), 1), |
| | | BorderFactory.createEmptyBorder(5, 15, 5, 15) |
| | | )); |
| | | return button; |
| | | } |
| | | |
| | | // 创建带样式的复选框 |
| | | private JCheckBox createStyledCheckbox(String text) { |
| | | JCheckBox checkBox = new JCheckBox(text); // 创建复选框 |
| | | checkBox.setFont(new Font("微软雅黑", Font.PLAIN, 12)); // 设置字体 |
| | | checkBox.setBackground(new Color(240, 245, 249)); // 设置背景色 |
| | | checkBox.setForeground(new Color(70, 70, 70)); // 设置前景色 |
| | | JCheckBox checkBox = new JCheckBox(text); |
| | | checkBox.setFont(new Font("微软雅黑", Font.PLAIN, 12)); |
| | | checkBox.setBackground(new Color(240, 245, 249)); |
| | | checkBox.setForeground(new Color(70, 70, 70)); |
| | | return checkBox; |
| | | } |
| | | |
| | | // 创建带样式的单选按钮 |
| | | private JRadioButton createStyledRadioButton(String text) { |
| | | JRadioButton radioButton = new JRadioButton(text); // 创建单选按钮 |
| | | radioButton.setFont(new Font("微软雅黑", Font.PLAIN, 12)); // 设置字体 |
| | | radioButton.setBackground(new Color(240, 245, 249)); // 设置背景色 |
| | | radioButton.setForeground(new Color(70, 70, 70)); // 设置前景色 |
| | | |
| | | // 美化选中效果 |
| | | JRadioButton radioButton = new JRadioButton(text); |
| | | radioButton.setFont(new Font("微软雅黑", Font.PLAIN, 12)); |
| | | radioButton.setBackground(new Color(240, 245, 249)); |
| | | radioButton.setForeground(new Color(70, 70, 70)); |
| | | radioButton.setIcon(new RadioButtonIcon(false)); |
| | | radioButton.setSelectedIcon(new RadioButtonIcon(true)); |
| | | |
| | | // 添加鼠标悬停效果 |
| | | radioButton.addMouseListener(new java.awt.event.MouseAdapter() { |
| | | public void mouseEntered(java.awt.event.MouseEvent evt) { |
| | | radioButton.setForeground(new Color(30, 144, 255)); |
| | |
| | | radioButton.setForeground(new Color(70, 70, 70)); |
| | | } |
| | | }); |
| | | |
| | | return radioButton; |
| | | } |
| | | |
| | | // 自定义单选按钮图标 |
| | | private static class RadioButtonIcon implements Icon { |
| | | private static final int SIZE = 14; |
| | | private final boolean selected; |
| | |
| | | g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| | | |
| | | if (selected) { |
| | | // 选中状态 - 蓝色填充 |
| | | g2d.setColor(new Color(70, 130, 180)); |
| | | g2d.fillOval(x, y, SIZE, SIZE); |
| | | |
| | | // 内圆 - 白色 |
| | | g2d.setColor(Color.WHITE); |
| | | g2d.fillOval(x + SIZE/4, y + SIZE/4, SIZE/2, SIZE/2); |
| | | } else { |
| | | // 未选中状态 - 灰色边框 |
| | | g2d.setColor(new Color(150, 150, 150)); |
| | | g2d.drawOval(x, y, SIZE-1, SIZE-1); |
| | | } |
| | | |
| | | g2d.dispose(); |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | // 加载数据类型 |
| | | private void loadtyps() { |
| | | cbDataType.removeAllItems(); |
| | | // 添加国际化选项 |
| | | cbDataType.addItem(messages.getString("MESSAGE_VIEW_ALL_TYPES")); |
| | | cbDataType.addItem(messages.getString("MESSAGE_VIEW_UDP_PORT_A")); |
| | | cbDataType.addItem(messages.getString("MESSAGE_VIEW_UDP_PORT_B")); |
| | | cbDataType.addItem(messages.getString("MESSAGE_VIEW_TCP_PORT")); |
| | | cbDataType.addItem(messages.getString("MESSAGE_VIEW_MQTT_PORT")); |
| | | cbDataType.addItem("UDPA"); |
| | | cbDataType.addItem("UDPB"); |
| | | cbDataType.addItem("TCP"); |
| | | } |
| | | |
| | | // 设置按钮动作 |
| | | private void setupActions() { |
| | | btnStart.addActionListener(e -> startCapture()); // 开始按钮绑定开始捕获方法 |
| | | btnPause.addActionListener(e -> pauseCapture()); // 暂停按钮绑定暂停捕获方法 |
| | | btnClear.addActionListener(e -> clearDisplay()); // 清除按钮绑定清除显示方法 |
| | | btnSend.addActionListener(e -> sendData()); // 发送按钮绑定发送方法 |
| | | btnStart.addActionListener(e -> startCapture()); |
| | | btnPause.addActionListener(e -> pauseCapture()); |
| | | btnClear.addActionListener(e -> clearDisplay()); |
| | | btnSend.addActionListener(e -> sendData()); |
| | | } |
| | | |
| | | // 开始捕获数据 |
| | | private void startCapture() { |
| | | if (!isCapturing) { // 如果当前未在捕获状态 |
| | | isCapturing = true; // 设置捕获状态为true |
| | | btnStart.setEnabled(false); // 禁用开始按钮 |
| | | btnPause.setEnabled(true); // 启用暂停按钮 |
| | | // 启动数据捕获线程 |
| | | if (!isCapturing) { |
| | | isStarted = true; // 设置开始标志 |
| | | isCapturing = true; |
| | | btnStart.setEnabled(false); |
| | | btnPause.setEnabled(true); |
| | | } |
| | | } |
| | | |
| | | // 暂停捕获数据 |
| | | private void pauseCapture() { |
| | | isCapturing = false; // 设置捕获状态为false |
| | | btnStart.setEnabled(true); // 启用开始按钮 |
| | | btnPause.setEnabled(false); // 禁用暂停按钮 |
| | | isStarted = false; // 清除开始标志 |
| | | isCapturing = false; |
| | | btnStart.setEnabled(true); |
| | | btnPause.setEnabled(false); |
| | | } |
| | | |
| | | // 清除数据显示 |
| | | private void clearDisplay() { |
| | | txtDataView.setText(""); // 清空文本区域 |
| | | txtDataView.setText(""); |
| | | } |
| | | |
| | | // 发送数据到远程设备 |
| | | private void sendData() { |
| | | // 使用新方法获取当前资源包 |
| | | ResourceBundle currentMessages = Languages.getCurrentMessages(); |
| | | ResourceBundle currentMessages = Languages.getCurrentMessages(); |
| | | String selectedDevice = (String) cbRemoteDevice.getSelectedItem(); |
| | | if (selectedDevice == null || selectedDevice.equals(currentMessages.getString("MESSAGE_VIEW_ALL_DEVICES"))) { |
| | | JOptionPane.showMessageDialog(this, |
| | | currentMessages.getString("MESSAGE_VIEW_SELECT_DEVICE"), |
| | | currentMessages.getString("WARNING"), |
| | | JOptionPane.showMessageDialog(this, |
| | | currentMessages.getString("MESSAGE_VIEW_SELECT_DEVICE"), |
| | | currentMessages.getString("WARNING"), |
| | | JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 获取本地端口 |
| | | Integer localPort = remoteDeviceLocalPortMap.get(selectedDevice); |
| | | if (localPort == null) { |
| | | JOptionPane.showMessageDialog(this, |
| | | JOptionPane.showMessageDialog(this, |
| | | "无法确定设备的接收端口", |
| | | currentMessages.getString("ERROR"), |
| | | currentMessages.getString("ERROR"), |
| | | JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 解析IP和端口 |
| | | String[] parts = selectedDevice.split(":"); |
| | | if (parts.length != 2) { |
| | | JOptionPane.showMessageDialog(this, |
| | | currentMessages.getString("MESSAGE_VIEW_INVALID_DEVICE"), |
| | | currentMessages.getString("ERROR"), |
| | | JOptionPane.showMessageDialog(this, |
| | | currentMessages.getString("MESSAGE_VIEW_INVALID_DEVICE"), |
| | | currentMessages.getString("ERROR"), |
| | | JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | |
| | | try { |
| | | port = Integer.parseInt(parts[1]); |
| | | } catch (NumberFormatException ex) { |
| | | JOptionPane.showMessageDialog(this, |
| | | currentMessages.getString("MESSAGE_VIEW_INVALID_PORT"), |
| | | currentMessages.getString("ERROR"), |
| | | JOptionPane.showMessageDialog(this, |
| | | currentMessages.getString("MESSAGE_VIEW_INVALID_PORT"), |
| | | currentMessages.getString("ERROR"), |
| | | JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | String dataToSend = txtSendData.getText(); |
| | | if (dataToSend.isEmpty()) { |
| | | JOptionPane.showMessageDialog(this, |
| | | currentMessages.getString("MESSAGE_VIEW_EMPTY_DATA"), |
| | | currentMessages.getString("WARNING"), |
| | | JOptionPane.showMessageDialog(this, |
| | | currentMessages.getString("MESSAGE_VIEW_EMPTY_DATA"), |
| | | currentMessages.getString("WARNING"), |
| | | JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 添加回车换行 |
| | | if (chkAppendNewline.isSelected()) { |
| | | dataToSend += "\r\n"; |
| | | } |
| | | |
| | | // 记录发送数据到界面 |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | String timestamp = sdf.format(new Date()); |
| | | |
| | |
| | | displayData.append("[SEND][").append(timestamp).append("][").append(ip).append(":").append(port).append("] "); |
| | | |
| | | if (rdoSendHex.isSelected()) { |
| | | // HEX格式发送 |
| | | String hexData = toHexString(dataToSend); |
| | | displayData.append(hexData); |
| | | if (localPort == UDPPortAReceiver.PORT) { |
| | | // 传入是否HEX发送的标志 |
| | | UDPPortAReceiver.sendData(ip, port, dataToSend, true); |
| | | } else if (localPort == UDPPortBReceiver.PORT) { |
| | | // 传入是否HEX发送的标志 |
| | | UDPPortBReceiver.sendData(ip, port, dataToSend, true); |
| | | } |
| | | |
| | | } else { |
| | | // ASCII格式发送 |
| | | displayData.append(dataToSend); |
| | | // 实际发送ASCII数据(这里需要调用UDP发送方法) |
| | | if (localPort == UDPPortAReceiver.PORT) { |
| | | UDPPortAReceiver.sendData(ip, port, dataToSend, false); |
| | | } else if (localPort == UDPPortBReceiver.PORT) { |
| | |
| | | } |
| | | } |
| | | |
| | | // 在EDT线程中更新UI |
| | | SwingUtilities.invokeLater(() -> { |
| | | txtDataView.append(displayData.toString() + "\n"); |
| | | // 自动滚动到底部 |
| | | txtDataView.setCaretPosition(txtDataView.getDocument().getLength()); |
| | | }); |
| | | } |
| | | |
| | | // 修改添加远程设备方法 |
| | | // 在MessageViewPanel类中修改addRemoteDevice方法 |
| | | public static void addRemoteDevice(String ip, int port, int localPort) { |
| | | String device = ip + ":" + port; |
| | | if (!remoteDeviceLocalPortMap.containsKey(device)) { |
| | | remoteDeviceLocalPortMap.put(device, localPort); |
| | | SwingUtilities.invokeLater(() -> { |
| | | // 检查下拉框中是否已存在该设备 |
| | | boolean exists = false; |
| | | ComboBoxModel<String> model = cbRemoteDevice.getModel(); |
| | | for (int i = 0; i < model.getSize(); i++) { |
| | |
| | | } |
| | | } |
| | | |
| | | // 捕获数据的核心方法 |
| | | // 修改通用数据显示方法 |
| | | public static void showData(String rawData, String ip, int port, int localPort, String deviceId) { |
| | | if (!isWindowVisible) { |
| | | public static void showData(String rawData, String ip, int port, int localPort, |
| | | String DataType, String Protocol, String Id) { |
| | | // 双重检查:窗口可见且已点击开始按钮 |
| | | if (!isWindowVisible || !isStarted) { |
| | | return; |
| | | } |
| | | |
| | | // 获取当前选择 |
| | | |
| | | if (cbDataType == null || cbProtocol == null || cbDeviceId == null) { |
| | | return; |
| | | } |
| | | |
| | | String selectedDataType = (String) cbDataType.getSelectedItem(); |
| | | String selectedProtocol = (String) cbProtocol.getSelectedItem(); |
| | | String selectedDeviceId = (String) cbDeviceId.getSelectedItem(); |
| | | |
| | | // 使用新方法获取当前资源包 |
| | | ResourceBundle messages = Languages.getCurrentMessages(); |
| | | boolean DataTypeboolean = "ALL".equals(selectedDataType) || selectedDataType.equals(DataType); |
| | | boolean Protocolboolean = "ALL".equals(selectedProtocol) || selectedProtocol.equals(Protocol); |
| | | boolean DeviceIdboolean = "ALL".equals(selectedDeviceId) || selectedDeviceId.equals(Id); |
| | | |
| | | String allTypes = messages.getString("MESSAGE_VIEW_ALL_TYPES"); |
| | | String allProtocols = messages.getString("MESSAGE_VIEW_ALL_PROTOCOLS"); |
| | | String allDevices = messages.getString("MESSAGE_VIEW_ALL_DEVICES"); |
| | | String udpPortA = messages.getString("MESSAGE_VIEW_UDP_PORT_A"); |
| | | String udpPortB = messages.getString("MESSAGE_VIEW_UDP_PORT_B"); |
| | | String udpProtocol = "UDP"; // 直接使用协议名称 |
| | | if (DataTypeboolean && Protocolboolean && DeviceIdboolean) { |
| | | StringBuilder sb = SB.get(); |
| | | sb.setLength(0); |
| | | |
| | | // 检查数据类型过滤 |
| | | boolean dataTypeMatch = selectedDataType.equals(allTypes) || |
| | | (localPort == UDPPortAReceiver.PORT && selectedDataType.equals(udpPortA)) || |
| | | (localPort == UDPPortBReceiver.PORT && selectedDataType.equals(udpPortB)); |
| | | if (chkShowSource.isSelected()) { |
| | | sb.append(PREFIX); |
| | | formatTime(sb); |
| | | sb.append(',').append(ip).append(COLON).append(port).append(SUFFIX); |
| | | } |
| | | sb.append(rawData); |
| | | |
| | | // 检查协议过滤 |
| | | boolean protocolMatch = selectedProtocol.equals(allProtocols) || |
| | | (localPort == UDPPortAReceiver.PORT && selectedProtocol.equals(udpProtocol)) || |
| | | (localPort == UDPPortBReceiver.PORT && selectedProtocol.equals(udpProtocol)); |
| | | |
| | | // 检查设备ID过滤 |
| | | boolean deviceIdMatch=false; |
| | | if("1".equals(deviceId)) { |
| | | deviceIdMatch=true; |
| | | }else { |
| | | deviceIdMatch = selectedDeviceId.equals(allDevices) || |
| | | (deviceId != null && deviceId.equals(selectedDeviceId)); |
| | | } |
| | | |
| | | // 只有当所有条件都匹配时才显示 |
| | | if (!(dataTypeMatch && protocolMatch && deviceIdMatch)) { |
| | | return; |
| | | } |
| | | |
| | | // 生成毫秒级时间戳 |
| | | SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); |
| | | String timestamp = sdf.format(new Date()); |
| | | String formattedData = formatData(rawData, ip, port, timestamp); |
| | | |
| | | if (isCapturing) { |
| | | // 添加远程设备到下拉框 |
| | | addRemoteDevice(ip, port, localPort); |
| | | |
| | | // 在EDT线程中更新UI |
| | | final String finalText = sb.toString(); |
| | | SwingUtilities.invokeLater(() -> { |
| | | txtDataView.append(formattedData + "\n"); |
| | | txtDataView.append(finalText); |
| | | txtDataView.append("\n"); |
| | | txtDataView.setCaretPosition(txtDataView.getDocument().getLength()); |
| | | }); |
| | | } |
| | | |
| | | // 如果启用了自动保存,则保存到文件 |
| | | if (chkAutoSave.isSelected()) { |
| | | saveToFile(formattedData); |
| | | if (isCapturing && localPort != 0) { |
| | | addRemoteDevice(ip, port, localPort); |
| | | } |
| | | |
| | | if (chkAutoSave.isSelected()) { |
| | | saveToFile(finalText); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 格式化数据显示 |
| | | // 修改格式显示方法,支持混合格式 |
| | | private static String formatData(String rawData, String ip, int port, String timestamp) { |
| | | StringBuilder formatted = new StringBuilder(); |
| | | // 使用新方法获取当前资源包 |
| | | ResourceBundle messages = Languages.getCurrentMessages(); |
| | | // 显示来源信息 |
| | | if (chkShowSource.isSelected()) { |
| | | formatted.append("[") |
| | | .append(timestamp) |
| | | .append("][") |
| | | .append(ip) |
| | | .append(":") |
| | | .append(port) |
| | | .append("] "); |
| | | } |
| | | |
| | | // 根据显示选项格式化数据 |
| | | if (chkHexDisplay.isSelected() && chkAsciiDisplay.isSelected()) { |
| | | formatted.append(formatHexString(rawData)) |
| | | .append(" | ") |
| | | .append(hexToAscii(rawData)) |
| | | .append(" [") |
| | | .append(messages.getString("MESSAGE_VIEW_MIXED_FORMAT")) |
| | | .append("]"); |
| | | } else if (chkHexDisplay.isSelected()) { |
| | | formatted.append(formatHexString(rawData)) |
| | | .append(" [") |
| | | .append(messages.getString("MESSAGE_VIEW_HEX_FORMAT")) |
| | | .append("]"); |
| | | } else if (chkAsciiDisplay.isSelected()) { |
| | | formatted.append(hexToAscii(rawData)) |
| | | .append(" [") |
| | | .append(messages.getString("MESSAGE_VIEW_ASCII_FORMAT")) |
| | | .append("]"); |
| | | } else { |
| | | formatted.append(hexToAscii(rawData)); |
| | | } |
| | | |
| | | return formatted.toString(); |
| | | } |
| | | |
| | | // 修改设备ID加载方法 |
| | | private void loadDeviceIds() { |
| | | cbDeviceId.removeAllItems(); |
| | | cbDeviceId.addItem(messages.getString("MESSAGE_VIEW_ALL_DEVICES")); |
| | | cbDeviceId.addItem("ALL"); |
| | | |
| | | List<String> tags = Dell_tag.getAlldeviceIds(); |
| | | for (String tag : tags) { |
| | |
| | | } |
| | | } |
| | | |
| | | // 将字符串转换为十六进制表示 |
| | | private static String toHexString(String input) { |
| | | StringBuilder hex = new StringBuilder(); // 使用StringBuilder构建十六进制字符串 |
| | | for (char c : input.toCharArray()) { // 遍历每个字符 |
| | | hex.append(String.format("%02X ", (int) c)); // 格式化为两位十六进制 |
| | | StringBuilder hex = new StringBuilder(); |
| | | for (char c : input.toCharArray()) { |
| | | hex.append(String.format("%02X ", (int) c)); |
| | | } |
| | | return hex.toString().trim(); // 返回并去除末尾空格 |
| | | } |
| | | // 格式化十六进制字符串(每两个字符加空格) |
| | | private static String formatHexString(String hex) { |
| | | StringBuilder formatted = new StringBuilder(); |
| | | for (int i = 0; i < hex.length(); i += 2) { |
| | | if (i > 0) formatted.append(" "); |
| | | formatted.append(hex.substring(i, Math.min(i + 2, hex.length()))); |
| | | } |
| | | return formatted.toString(); |
| | | return hex.toString().trim(); |
| | | } |
| | | |
| | | // 将十六进制字符串转换为ASCII |
| | | private static String hexToAscii(String hexStr) { |
| | | StringBuilder output = new StringBuilder(); |
| | | for (int i = 0; i < hexStr.length(); i += 2) { |
| | | String str = hexStr.substring(i, i + 2); |
| | | output.append((char) Integer.parseInt(str, 16)); |
| | | } |
| | | return output.toString(); |
| | | } |
| | | // 保存数据到文件 |
| | | private static void saveToFile(String data) { |
| | | SimpleDateFormat fileFormat = new SimpleDateFormat("yyyyMMdd"); // 文件日期格式 |
| | | String fileName = "systemfile/baowen/messages_" + fileFormat.format(new Date()) + ".txt"; // 文件名包含日期 |
| | | SimpleDateFormat fileFormat = new SimpleDateFormat("yyyyMMdd"); |
| | | String fileName = "systemfile/baowen/messages_" + fileFormat.format(new Date()) + ".txt"; |
| | | |
| | | try (java.io.FileWriter writer = new java.io.FileWriter(fileName, true)) { // 使用追加模式 |
| | | writer.write(data + "\n"); // 写入数据并换行 |
| | | try (java.io.FileWriter writer = new java.io.FileWriter(fileName, true)) { |
| | | writer.write(data + "\n"); |
| | | } catch (java.io.IOException ex) { |
| | | ex.printStackTrace(); // 打印IO异常 |
| | | ex.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | public static String getSelectedDataType() { |
| | | return cbDataType == null ? "" : String.valueOf(cbDataType.getSelectedItem()); |
| | | } |
| | | |
| | | public static String getSelectedProtocol() { |
| | | return cbProtocol == null ? "" : String.valueOf(cbProtocol.getSelectedItem()); |
| | | } |
| | | |
| | | public static String getSelectedDeviceId() { |
| | | return cbDeviceId == null ? "" : String.valueOf(cbDeviceId.getSelectedItem()); |
| | | } |
| | | |
| | | private static void formatTime(StringBuilder sb) { |
| | | long now = System.currentTimeMillis(); |
| | | long sec = (now / 1000) % 86400; |
| | | int ms = (int) (now % 1000); |
| | | |
| | | int h = (int) (sec / 3600); |
| | | int m = (int) ((sec % 3600) / 60); |
| | | int s = (int) (sec % 60); |
| | | |
| | | sb.append((char) ('0' + (h / 10))) |
| | | .append((char) ('0' + (h % 10))).append(':'); |
| | | sb.append((char) ('0' + (m / 10))) |
| | | .append((char) ('0' + (m % 10))).append(':'); |
| | | sb.append((char) ('0' + (s / 10))) |
| | | .append((char) ('0' + (s % 10))).append('.'); |
| | | sb.append(DIGITS[ms / 100]) |
| | | .append(DIGITS[(ms / 10) % 10]) |
| | | .append(DIGITS[ms % 10]); |
| | | } |
| | | } |