package PublicPannel; import javax.swing.*; import Ymodem.YModem; import home.*; import jiexi.DellTag55AA03; import java.awt.*; import java.awt.event.ActionListener; import java.io.*; import java.net.Socket; public class UpgradePanel extends JPanel { /** * */ private static final long serialVersionUID = 1L; private MainFrame mainFrame; private JButton upgradeBtn; private JTextField filePathField; private JButton selectBinBtn; private JProgressBar progressBar; private JLabel selectFileLabel; private JLabel progressLabel; // 新增:通信方式选择 private JComboBox communicationTypeCombo; private JLabel communicationTypeLabel; // 串口服务实例 private SerialPortService serialService; // 网络通信面板引用 private NetworkCommunicationPanel networkPanel; // 新增:升级相关状态 private volatile boolean upgradeInProgress = false; private volatile boolean cancelUpgrade = false; public UpgradePanel(MainFrame mainFrame) { this.mainFrame = mainFrame; this.serialService = SerialCommunicationPanel.getSerialService(); initializeUI(); } private void initializeUI() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); // 内容面板 JPanel contentPanel = new JPanel(); contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); // 通信方式选择面板 JPanel communicationPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0)); communicationTypeLabel = new JLabel(mainFrame.getString("communication.type")); communicationTypeCombo = new JComboBox<>(new String[]{ mainFrame.getString("serial"), mainFrame.getString("tcp.client"), mainFrame.getString("tcp.server"), mainFrame.getString("udp") }); communicationPanel.add(communicationTypeLabel); communicationPanel.add(communicationTypeCombo); // 文件选择面板 JPanel filePanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0)); filePathField = new JTextField(); filePathField.setEditable(false); filePathField.setPreferredSize(new Dimension(200, 25)); selectBinBtn = ButtonUtils.createBlueButton(mainFrame.getString("select.bin.file")); selectBinBtn.addActionListener(e -> selectBinFile()); selectFileLabel = new JLabel(mainFrame.getString("select.bin.file")); filePanel.add(selectFileLabel); filePanel.add(filePathField); filePanel.add(selectBinBtn); // 进度条和升级按钮面板 JPanel progressButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0)); progressBar = new JProgressBar(0, 100); progressBar.setStringPainted(true); progressBar.setVisible(false); progressBar.setPreferredSize(new Dimension(200, 25)); upgradeBtn = ButtonUtils.createBlueButton(mainFrame.getString("start.upgrade")); upgradeBtn.addActionListener(e -> startUpgrade()); progressLabel = new JLabel(mainFrame.getString("upgrade.progress")); progressButtonPanel.add(progressLabel); progressButtonPanel.add(progressBar); progressButtonPanel.add(upgradeBtn); // 设置固定高度 communicationPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 35)); filePanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 35)); progressButtonPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 35)); // 将所有组件添加到内容面板 contentPanel.add(Box.createVerticalStrut(10)); contentPanel.add(communicationPanel); contentPanel.add(filePanel); contentPanel.add(progressButtonPanel); add(contentPanel); } private void selectBinFile() { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); fileChooser.setDialogTitle(mainFrame.getString("select.bin.file")); fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("BIN files (*.bin)", "bin")); int result = fileChooser.showOpenDialog(this); if (result == JFileChooser.APPROVE_OPTION) { String selectedFile = fileChooser.getSelectedFile().getAbsolutePath(); filePathField.setText(selectedFile); JOptionPane.showMessageDialog(this, mainFrame.getString("selected.file") + ": " + selectedFile, mainFrame.getString("file.selected"), JOptionPane.INFORMATION_MESSAGE); } } private void startUpgrade() { // 检查升级条件 if (filePathField.getText().isEmpty()) { JOptionPane.showMessageDialog(this, mainFrame.getString("please.select.file.first"), mainFrame.getString("warning"), JOptionPane.WARNING_MESSAGE); return; } // 检查通信连接状态 String communicationType = (String) communicationTypeCombo.getSelectedItem(); if (!checkCommunicationStatus(communicationType)) { return; } // 对于网络升级,检查是否选择了客户端 if (!communicationType.equals(mainFrame.getString("serial"))) { if (!checkSelectedClient()) { return; } } // 确认升级 int result = JOptionPane.showConfirmDialog(this, mainFrame.getString("upgrade.confirm.message"), mainFrame.getString("confirm.upgrade"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (result != JOptionPane.YES_OPTION) { return; } upgradeBtn.setEnabled(false); selectBinBtn.setEnabled(false); progressBar.setVisible(true); progressBar.setValue(0); upgradeInProgress = true; cancelUpgrade = false; String filePath = filePathField.getText(); new Thread(() -> { int retryCount = 0; final int maxRetries = 3; try { // 发送升级命令 boolean success = sendUpgradeCommand(communicationType); if (!success) { throw new IOException("Failed to send upgrade command"); } // 等待设备准备升级 Thread.sleep(1000); // 执行升级 if (communicationType.equals(mainFrame.getString("serial"))) { performSerialUpgrade(filePath); } else { performNetworkUpgrade(filePath, communicationType); } } catch (Exception e) { if (retryCount < maxRetries && !cancelUpgrade) { retryCount++; try { Thread.sleep(1000); // 可以在这里添加重试逻辑 } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } else { SwingUtilities.invokeLater(() -> { if (!cancelUpgrade) { JOptionPane.showMessageDialog(this, "升级失败" + ": " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); } restoreUIAfterUpgrade(); }); } } finally { restoreUIAfterUpgrade(); } }).start(); } /** * 检查通信连接状态 */ private boolean checkCommunicationStatus(String communicationType) { if (communicationType.equals(mainFrame.getString("serial"))) { if (!serialService.isOpen()) { JOptionPane.showMessageDialog(this, "请先打开串口连接", "错误", JOptionPane.ERROR_MESSAGE); return false; } } else { // 获取网络通信面板 if (networkPanel == null) { networkPanel = mainFrame.getNetworkCommunicationPanel(); } if (networkPanel == null || !networkPanel.isOpen()) { JOptionPane.showMessageDialog(this, "请先打开网络连接", "错误", JOptionPane.ERROR_MESSAGE); return false; } } return true; } /** * 检查是否选择了客户端(针对网络升级) */ private boolean checkSelectedClient() { if (networkPanel == null) { networkPanel = mainFrame.getNetworkCommunicationPanel(); } // 获取当前选中的客户端 String selectedClient = networkPanel.getSelectedClient(); if (selectedClient == null || selectedClient.isEmpty()) { JOptionPane.showMessageDialog(this, "请先选择要升级的客户端", "错误", JOptionPane.ERROR_MESSAGE); return false; } return true; } /** * 发送升级命令 */ private boolean sendUpgradeCommand(String communicationType) { byte[] upgradeCommand = DellTag55AA03.shengjizhiling(); if (communicationType.equals(mainFrame.getString("serial"))) { // 串口升级期间停止数据捕获 serialService.stopCapture(); return serialService.send(upgradeCommand); } else { // 网络升级 return sendNetworkData(upgradeCommand, "发送升级指令"); } } /** * 执行串口升级 */ private void performSerialUpgrade(String filePath) throws Exception { InputStream inputStream = serialService.getInputStream(); OutputStream outputStream = serialService.getOutputStream(); if (inputStream == null || outputStream == null) { throw new IOException("串口不可用"); } YModem ymodem = new YModem(inputStream, outputStream); File file = new File(filePath); YModem.ProgressCallback progressCallback = new YModem.ProgressCallback() { @Override public void onProgress(int currentBlock, int totalBlocks) { int progress = (int) ((currentBlock * 100.0) / totalBlocks); SwingUtilities.invokeLater(() -> { progressBar.setValue(progress); }); } }; ymodem.send(file.toPath(), progressCallback); SwingUtilities.invokeLater(() -> { JOptionPane.showMessageDialog(this, "升级成功", "成功", JOptionPane.INFORMATION_MESSAGE); }); } /** * 执行网络升级 */ private void performNetworkUpgrade(String filePath, String communicationType) throws Exception { File file = new File(filePath); // 获取选中的客户端信息 String selectedClient = networkPanel.getSelectedClient(); if (selectedClient == null || selectedClient.isEmpty()) { throw new IOException("未选择客户端"); } // 解析客户端地址 String[] clientParts = selectedClient.split(":"); if (clientParts.length != 2) { throw new IOException("客户端地址格式错误: " + selectedClient); } String clientIp = clientParts[0]; int clientPort; try { clientPort = Integer.parseInt(clientParts[1]); } catch (NumberFormatException e) { throw new IOException("客户端端口格式错误: " + clientParts[1]); } // 创建网络输入输出流适配器 NetworkStreamAdapter streamAdapter = new NetworkStreamAdapter(networkPanel, communicationType, clientIp, clientPort); InputStream inputStream = streamAdapter.getInputStream(); OutputStream outputStream = streamAdapter.getOutputStream(); if (inputStream == null || outputStream == null) { throw new IOException("网络流不可用"); } YModem ymodem = new YModem(inputStream, outputStream); YModem.ProgressCallback progressCallback = new YModem.ProgressCallback() { @Override public void onProgress(int currentBlock, int totalBlocks) { int progress = (int) ((currentBlock * 100.0) / totalBlocks); SwingUtilities.invokeLater(() -> { progressBar.setValue(progress); }); } }; try { ymodem.send(file.toPath(), progressCallback); if (!cancelUpgrade) { SwingUtilities.invokeLater(() -> { JOptionPane.showMessageDialog(UpgradePanel.this, "网络升级成功", "成功", JOptionPane.INFORMATION_MESSAGE); }); } } catch (Exception e) { if (!cancelUpgrade) { throw e; } } finally { streamAdapter.close(); } } /** * 发送网络数据 */ private boolean sendNetworkData(byte[] data, String description) { if (networkPanel == null) { networkPanel = mainFrame.getNetworkCommunicationPanel(); } if (networkPanel != null) { // 使用NetworkCommunicationPanel的发送方法 return networkPanel.sendNetworkDataInternal(data, description); } return false; } /** * 恢复UI状态 */ private void restoreUIAfterUpgrade() { SwingUtilities.invokeLater(() -> { upgradeInProgress = false; upgradeBtn.setEnabled(true); selectBinBtn.setEnabled(true); progressBar.setVisible(false); progressBar.setValue(0); // 重新启动串口数据捕获(如果是串口升级) if (serialService != null) { serialService.startCapture(); } }); } /** * 取消升级 */ public void cancelUpgrade() { if (upgradeInProgress) { cancelUpgrade = true; upgradeInProgress = false; } } /** * 设置网络通信面板引用 */ public void setNetworkCommunicationPanel(NetworkCommunicationPanel networkPanel) { this.networkPanel = networkPanel; } public void updateLanguage() { // 更新通信方式选择 communicationTypeLabel.setText(mainFrame.getString("communication.type")); communicationTypeCombo.removeAllItems(); communicationTypeCombo.addItem(mainFrame.getString("serial")); communicationTypeCombo.addItem(mainFrame.getString("tcp.client")); communicationTypeCombo.addItem(mainFrame.getString("tcp.server")); communicationTypeCombo.addItem(mainFrame.getString("udp")); // 更新按钮文本 selectBinBtn.setText(mainFrame.getString("select.bin.file")); upgradeBtn.setText(mainFrame.getString("start.upgrade")); // 更新标签文本 selectFileLabel.setText(mainFrame.getString("select.bin.file")); progressLabel.setText(mainFrame.getString("upgrade.progress")); revalidate(); repaint(); } /** * 网络流适配器 - 内部类 * 用于将网络通信适配为YModem所需的InputStream和OutputStream */ private class NetworkStreamAdapter { private NetworkCommunicationPanel networkPanel; private String communicationType; private String clientIp; private int clientPort; private PipedInputStream inputStream; private PipedOutputStream outputStream; private boolean isConnected = false; // 用于数据接收的管道 private PipedInputStream receiverInputStream; private PipedOutputStream receiverOutputStream; // 保存原始的数据监听器 private NetworkBase.NetworkDataListener originalDataListener; public NetworkStreamAdapter(NetworkCommunicationPanel networkPanel, String communicationType, String clientIp, int clientPort) { this.networkPanel = networkPanel; this.communicationType = communicationType; this.clientIp = clientIp; this.clientPort = clientPort; initializeStreams(); setupNetworkDataListener(); } private void initializeStreams() { try { // 创建管道流用于数据发送 inputStream = new PipedInputStream(8192); // 增加缓冲区大小 outputStream = new PipedOutputStream(inputStream); // 创建管道流用于数据接收 receiverInputStream = new PipedInputStream(8192); receiverOutputStream = new PipedOutputStream(receiverInputStream); isConnected = true; System.out.println("网络流适配器初始化成功"); } catch (Exception e) { System.err.println("初始化网络流失败: " + e.getMessage()); isConnected = false; throw new RuntimeException("网络流初始化失败", e); } } /** * 设置网络数据监听器 */ private void setupNetworkDataListener() { try { // 获取网络通信面板的当前网络实例 NetworkBase currentNetwork = networkPanel.getCurrentNetwork(); if (currentNetwork == null) { System.err.println("网络连接未建立"); return; } // 保存原来的数据监听器 originalDataListener = currentNetwork.getDataListener(); // 创建升级专用的数据监听器 NetworkBase.NetworkDataListener upgradeDataListener = new NetworkBase.NetworkDataListener() { @Override public void onDataReceived(byte[] data, String fromAddress) { // 检查数据是否来自目标客户端 String expectedAddress = clientIp + ":" + clientPort; if (expectedAddress.equals(fromAddress)) { try { // 将接收到的数据写入接收管道,供YModem读取 if (receiverOutputStream != null && !cancelUpgrade) { receiverOutputStream.write(data); receiverOutputStream.flush(); // 调试信息 System.out.println("接收到升级响应数据,长度: " + data.length + ",来自: " + fromAddress); } } catch (IOException e) { System.err.println("写入接收管道失败: " + e.getMessage()); if (!cancelUpgrade) { SwingUtilities.invokeLater(() -> { JOptionPane.showMessageDialog(UpgradePanel.this, "升级数据接收失败: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); }); } } } else { // 非目标客户端的数据,可以记录日志但忽略 System.out.println("忽略非目标客户端数据,来自: " + fromAddress + ",期望: " + expectedAddress); } // 同时调用原来的监听器,保持其他功能正常 if (originalDataListener != null) { originalDataListener.onDataReceived(data, fromAddress); } } @Override public void onStatusChanged(String status, boolean isConnected) { // 传递状态变化给原来的监听器 if (originalDataListener != null) { originalDataListener.onStatusChanged(status, isConnected); } // 如果连接断开且正在升级,需要处理 if (!isConnected && upgradeInProgress) { System.err.println("网络连接断开,升级可能失败"); if (!cancelUpgrade) { SwingUtilities.invokeLater(() -> { JOptionPane.showMessageDialog(UpgradePanel.this, "网络连接已断开,升级失败", "错误", JOptionPane.ERROR_MESSAGE); cancelUpgrade = true; }); } } } @Override public void onError(String errorMessage) { // 传递错误给原来的监听器 if (originalDataListener != null) { originalDataListener.onError(errorMessage); } // 如果正在升级,需要处理错误 if (upgradeInProgress && !cancelUpgrade) { System.err.println("升级过程中发生网络错误: " + errorMessage); SwingUtilities.invokeLater(() -> { JOptionPane.showMessageDialog(UpgradePanel.this, "网络错误: " + errorMessage, "错误", JOptionPane.ERROR_MESSAGE); }); } } }; // 设置新的数据监听器 currentNetwork.setDataListener(upgradeDataListener); System.out.println("升级数据监听器已设置,目标客户端: " + clientIp + ":" + clientPort); } catch (Exception e) { System.err.println("设置网络数据监听器失败: " + e.getMessage()); throw new RuntimeException("网络数据监听器初始化失败", e); } } public InputStream getInputStream() { return receiverInputStream; } public OutputStream getOutputStream() { return new NetworkOutputStream(); } public void close() { try { // 恢复原始的数据监听器 if (networkPanel != null && networkPanel.getCurrentNetwork() != null && originalDataListener != null) { networkPanel.getCurrentNetwork().setDataListener(originalDataListener); System.out.println("已恢复原始数据监听器"); } if (inputStream != null) inputStream.close(); if (outputStream != null) outputStream.close(); if (receiverInputStream != null) receiverInputStream.close(); if (receiverOutputStream != null) receiverOutputStream.close(); System.out.println("网络流适配器已关闭"); } catch (IOException e) { System.err.println("关闭网络流失败: " + e.getMessage()); } } /** * 网络输出流实现 * 将YModem写入的数据通过网络发送到指定客户端 */ private class NetworkOutputStream extends OutputStream { @Override public void write(int b) throws IOException { write(new byte[]{(byte) b}, 0, 1); } @Override public void write(byte[] b, int off, int len) throws IOException { if (cancelUpgrade) { throw new IOException("升级已取消"); } byte[] dataToSend = new byte[len]; System.arraycopy(b, off, dataToSend, 0, len); boolean success = false; if (communicationType.equals(mainFrame.getString("udp"))) { // UDP模式发送 success = networkPanel.sendUdpData(dataToSend, clientIp, clientPort, "升级数据"); } else if (communicationType.equals(mainFrame.getString("tcp.server"))) { // TCP Server模式发送到指定客户端 String clientKey = clientIp + ":" + clientPort; success = networkPanel.sendTcpDataToClient(clientKey, dataToSend, "升级数据"); } if (!success) { throw new IOException("网络发送失败"); } // 调试信息 System.out.println("发送升级数据,长度: " + len); } @Override public void flush() throws IOException { // 网络发送立即生效,无需特殊处理 } @Override public void close() throws IOException { // 不关闭底层网络连接 } } } }