package home; import java.io.*; import java.util.Arrays; import java.util.function.Consumer; public class FirmwareUpgrader { private static final int SOH = 0x01; // Start of Header private static final int STX = 0x02; // Start of Text (for 1024-byte blocks) private static final int EOT = 0x04; // End of Transmission private static final int ACK = 0x06; // Acknowledge private static final int NAK = 0x15; // Negative Acknowledge private static final int CAN = 0x18; // Cancel private static final int C = 0x43; // 'C' for CRC mode private final SerialPortService serialService; public FirmwareUpgrader(SerialPortService serialService) { this.serialService = serialService; } public void upgradeFirmware(String filePath, ProgressCallback progressCallback) throws IOException { File file = new File(filePath); if (!file.exists() || !file.isFile()) { throw new IOException("File not found: " + filePath); } // ·¢ËÍ¿ªÊ¼ÐźŠsendStartSignal(); // ·¢ËÍÎļþ sendFile(file, progressCallback); // ·¢ËͽáÊøÐźŠsendEndSignal(); } private void sendStartSignal() throws IOException { // ·¢ËÍ'C'±íʾʹÓÃCRCУÑé serialService.send(new byte[]{C}); // µÈ´ýACK if (!waitForAck()) { throw new IOException("Device not ready for YModem transfer"); } } private void sendFile(File file, ProgressCallback progressCallback) throws IOException { long fileSize = file.length(); String fileName = file.getName(); // ·¢ËÍÎļþÍ·¿é byte[] header = new byte[133]; Arrays.fill(header, (byte)0); header[0] = SOH; header[1] = 0x00; // ¿é±àºÅ header[2] = (byte)0xFF; // ¿é±àºÅ²¹Âë // ÎļþÃûºÍ´óС byte[] fileNameBytes = fileName.getBytes(); System.arraycopy(fileNameBytes, 0, header, 3, Math.min(fileNameBytes.length, 99)); String sizeStr = String.valueOf(fileSize); byte[] sizeBytes = sizeStr.getBytes(); System.arraycopy(sizeBytes, 0, header, 3 + 99, Math.min(sizeBytes.length, 99)); // ¼ÆËãCRC int crc = calculateCRC(header, 3, 128); header[131] = (byte)((crc >> 8) & 0xFF); header[132] = (byte)(crc & 0xFF); serialService.send(header); // µÈ´ýACK if (!waitForAck()) { throw new IOException("Header not acknowledged"); } // ·¢ËÍÎļþÊý¾Ý try (FileInputStream fis = new FileInputStream(file)) { byte[] buffer = new byte[1024]; int bytesRead; int blockNum = 1; long totalSent = 0; while ((bytesRead = fis.read(buffer)) != -1) { // Ìî³ä²»×ãµÄ²¿·Ö if (bytesRead < 1024) { Arrays.fill(buffer, bytesRead, 1024, (byte)0x1A); } // ·¢ËÍÊý¾Ý¿é sendDataBlock(blockNum, buffer); blockNum++; totalSent += bytesRead; // ¸üнø¶È int progress = (int)((totalSent * 100) / fileSize); progressCallback.onProgress(progress); // µÈ´ýACK if (!waitForAck()) { throw new IOException("Data block not acknowledged"); } } } } private void sendDataBlock(int blockNum, byte[] data) throws IOException { byte[] block = new byte[1029]; block[0] = STX; // 1024×Ö½Ú¿é block[1] = (byte)(blockNum & 0xFF); block[2] = (byte)(~blockNum & 0xFF); // ¿½±´Êý¾Ý System.arraycopy(data, 0, block, 3, data.length); // ¼ÆËãCRC int crc = calculateCRC(block, 3, 1024); block[1027] = (byte)((crc >> 8) & 0xFF); block[1028] = (byte)(crc & 0xFF); serialService.send(block); } private void sendEndSignal() throws IOException { // ·¢ËÍEOT serialService.send(new byte[]{EOT}); // µÈ´ýACK if (!waitForAck()) { throw new IOException("EOT not acknowledged"); } // ·¢ËÍ¿ÕÎļþÍ·¿é±íʾ½áÊø byte[] endHeader = new byte[133]; Arrays.fill(endHeader, (byte)0); endHeader[0] = SOH; endHeader[1] = 0x00; // ¿é±àºÅ endHeader[2] = (byte)0xFF; // ¿é±àºÅ²¹Âë // ¼ÆËãCRC int crc = calculateCRC(endHeader, 3, 128); endHeader[131] = (byte)((crc >> 8) & 0xFF); endHeader[132] = (byte)(crc & 0xFF); serialService.send(endHeader); // µÈ´ýACK if (!waitForAck()) { throw new IOException("End header not acknowledged"); } } private boolean waitForAck() { final Object lock = new Object(); final boolean[] result = new boolean[1]; final boolean[] receivedResponse = new boolean[1]; // ´´½¨Ò»¸öÁÙʱÏû·ÑÕßÀ´´¦Àí½ÓÊÕµ½µÄÊý¾Ý Consumer ackConsumer = data -> { if (data.length > 0) { synchronized (lock) { if (data[0] == ACK) { result[0] = true; receivedResponse[0] = true; } else if (data[0] == NAK) { result[0] = false; receivedResponse[0] = true; } else if (data[0] == CAN) { throw new RuntimeException("Transfer canceled by device"); } lock.notifyAll(); } } }; // ×¢²áÁÙʱÏû·ÑÕß serialService.setResponseConsumer(ackConsumer); long startTime = System.currentTimeMillis(); synchronized (lock) { while (System.currentTimeMillis() - startTime < 5000 && !receivedResponse[0]) { try { lock.wait(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } } } // È¡Ïû×¢²áÁÙʱÏû·ÑÕß serialService.setResponseConsumer(null); return receivedResponse[0] && result[0]; } private int calculateCRC(byte[] data, int offset, int length) { int crc = 0; for (int i = 0; i < length; i++) { crc = crc ^ (data[offset + i] << 8); for (int j = 0; j < 8; j++) { if ((crc & 0x8000) != 0) { crc = (crc << 1) ^ 0x1021; } else { crc = crc << 1; } } } return crc & 0xFFFF; } public interface ProgressCallback { void onProgress(int progress); } }