|
@@ -6,8 +6,10 @@ import java.time.Duration;
|
|
|
import java.time.LocalDateTime;
|
|
import java.time.LocalDateTime;
|
|
|
import java.time.ZoneOffset;
|
|
import java.time.ZoneOffset;
|
|
|
import java.util.Arrays;
|
|
import java.util.Arrays;
|
|
|
|
|
+import java.util.logging.Logger;
|
|
|
|
|
|
|
|
public class ProtocolUtils {
|
|
public class ProtocolUtils {
|
|
|
|
|
+ private static final Logger logger = Logger.getLogger(ProtocolUtils.class.getName());
|
|
|
/**
|
|
/**
|
|
|
* 将2000年起始的时间戳转换为LocalDateTime
|
|
* 将2000年起始的时间戳转换为LocalDateTime
|
|
|
*/
|
|
*/
|
|
@@ -116,23 +118,24 @@ public class ProtocolUtils {
|
|
|
* 12 34 56 00 16 80 34 0B 13 81 23 45 67 80 0B 00 00 00 00 00 10 D1
|
|
* 12 34 56 00 16 80 34 0B 13 81 23 45 67 80 0B 00 00 00 00 00 10 D1
|
|
|
*/
|
|
*/
|
|
|
public static byte[] buildShutdownAckPacket(byte[] originalFrame) {
|
|
public static byte[] buildShutdownAckPacket(byte[] originalFrame) {
|
|
|
- // 提取源、目的地址
|
|
|
|
|
- int srcLenIndex = 7;
|
|
|
|
|
- int srcLen = originalFrame[srcLenIndex] & 0xFF;
|
|
|
|
|
- int srcByteLen = (srcLen + 1) / 2;
|
|
|
|
|
- int srcStart = srcLenIndex + 1;
|
|
|
|
|
- int srcEnd = srcStart + srcByteLen;
|
|
|
|
|
-
|
|
|
|
|
- int dstLenIndex = srcEnd;
|
|
|
|
|
- int dstLen = originalFrame[dstLenIndex] & 0xFF;
|
|
|
|
|
- int dstByteLen = (dstLen + 1) / 2;
|
|
|
|
|
- int dstStart = dstLenIndex + 1;
|
|
|
|
|
- int dstEnd = dstStart + dstByteLen;
|
|
|
|
|
-
|
|
|
|
|
- // 结束通讯帧包序号固定 0x80
|
|
|
|
|
- byte packetSeq = (byte) 0x80;
|
|
|
|
|
-
|
|
|
|
|
- byte[] response = new byte[3 + 2 + 1 + 1 + 1 + dstByteLen + 1 + srcByteLen + 1];
|
|
|
|
|
|
|
+ // 步骤1:动态提取地址信息(对齐validateMessage)
|
|
|
|
|
+ int[] addrInfo = extractAddrInfo(originalFrame);
|
|
|
|
|
+ int srcLenFlag = addrInfo[0];
|
|
|
|
|
+ int srcAddrByteLen = addrInfo[1];
|
|
|
|
|
+ int srcAddrStart = addrInfo[2];
|
|
|
|
|
+ int srcAddrEnd = addrInfo[3];
|
|
|
|
|
+ int destLenFlag = addrInfo[4];
|
|
|
|
|
+ int destAddrByteLen = addrInfo[5];
|
|
|
|
|
+ int destAddrStart = addrInfo[6];
|
|
|
|
|
+ int destAddrEnd = addrInfo[7];
|
|
|
|
|
+
|
|
|
|
|
+ // 步骤2:提取地址字节
|
|
|
|
|
+ byte[] srcAddr = Arrays.copyOfRange(originalFrame, srcAddrStart, srcAddrEnd);
|
|
|
|
|
+ byte[] destAddr = Arrays.copyOfRange(originalFrame, destAddrStart, destAddrEnd);
|
|
|
|
|
+
|
|
|
|
|
+ // 步骤3:构造应答帧
|
|
|
|
|
+ int totalLen = 3 + 2 + 1 + 1 + 1 + destAddrByteLen + 1 + srcAddrByteLen + 1;
|
|
|
|
|
+ byte[] response = new byte[totalLen];
|
|
|
int idx = 0;
|
|
int idx = 0;
|
|
|
|
|
|
|
|
// 系统识别码
|
|
// 系统识别码
|
|
@@ -141,25 +144,22 @@ public class ProtocolUtils {
|
|
|
response[idx++] = 0x56;
|
|
response[idx++] = 0x56;
|
|
|
|
|
|
|
|
// 长度
|
|
// 长度
|
|
|
- int totalLen = response.length;
|
|
|
|
|
response[idx++] = (byte) ((totalLen >> 8) & 0xFF);
|
|
response[idx++] = (byte) ((totalLen >> 8) & 0xFF);
|
|
|
response[idx++] = (byte) (totalLen & 0xFF);
|
|
response[idx++] = (byte) (totalLen & 0xFF);
|
|
|
|
|
|
|
|
- // 包序号
|
|
|
|
|
- response[idx++] = packetSeq;
|
|
|
|
|
-
|
|
|
|
|
- // 帧类型 0x34
|
|
|
|
|
|
|
+ // 包序号0x80 + 帧类型0x34
|
|
|
|
|
+ response[idx++] = (byte) 0x80;
|
|
|
response[idx++] = 0x34;
|
|
response[idx++] = 0x34;
|
|
|
|
|
|
|
|
- // 源地址 = 原帧目的地址
|
|
|
|
|
- response[idx++] = (byte) dstLen;
|
|
|
|
|
- System.arraycopy(originalFrame, dstStart, response, idx, dstByteLen);
|
|
|
|
|
- idx += dstByteLen;
|
|
|
|
|
|
|
+ // 源地址=原目的地址(动态)
|
|
|
|
|
+ response[idx++] = (byte) destLenFlag;
|
|
|
|
|
+ System.arraycopy(destAddr, 0, response, idx, destAddrByteLen);
|
|
|
|
|
+ idx += destAddrByteLen;
|
|
|
|
|
|
|
|
- // 目的地址 = 原帧源地址
|
|
|
|
|
- response[idx++] = (byte) srcLen;
|
|
|
|
|
- System.arraycopy(originalFrame, srcStart, response, idx, srcByteLen);
|
|
|
|
|
- idx += srcByteLen;
|
|
|
|
|
|
|
+ // 目的地址=原源地址(动态)
|
|
|
|
|
+ response[idx++] = (byte) srcLenFlag;
|
|
|
|
|
+ System.arraycopy(srcAddr, 0, response, idx, srcAddrByteLen);
|
|
|
|
|
+ idx += srcAddrByteLen;
|
|
|
|
|
|
|
|
// 异或校验
|
|
// 异或校验
|
|
|
byte xor = calculateXorCheck(Arrays.copyOfRange(response, 0, idx));
|
|
byte xor = calculateXorCheck(Arrays.copyOfRange(response, 0, idx));
|
|
@@ -167,89 +167,77 @@ public class ProtocolUtils {
|
|
|
|
|
|
|
|
return response;
|
|
return response;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- public static byte[] buildErrorResponse(String msg) {
|
|
|
|
|
- byte[] text = msg.getBytes();
|
|
|
|
|
- byte[] data = new byte[3 + 2 + 1 + text.length + 1];
|
|
|
|
|
- int idx = 0;
|
|
|
|
|
- data[idx++] = 0x12;
|
|
|
|
|
- data[idx++] = 0x34;
|
|
|
|
|
- data[idx++] = 0x56;
|
|
|
|
|
-
|
|
|
|
|
- int len = data.length;
|
|
|
|
|
- data[idx++] = (byte) ((len >> 8) & 0xFF);
|
|
|
|
|
- data[idx++] = (byte) (len & 0xFF);
|
|
|
|
|
-
|
|
|
|
|
- data[idx++] = 0x7F; // 错误类型标识
|
|
|
|
|
- System.arraycopy(text, 0, data, idx, text.length);
|
|
|
|
|
- idx += text.length;
|
|
|
|
|
-
|
|
|
|
|
- data[idx++] = calculateXorCheck(Arrays.copyOfRange(data, 0, idx - 1));
|
|
|
|
|
- return data;
|
|
|
|
|
- }
|
|
|
|
|
public static byte[] buildCustomReplyFrame(byte[] request) throws IOException {
|
|
public static byte[] buildCustomReplyFrame(byte[] request) throws IOException {
|
|
|
- // 校验长度至少20个字节
|
|
|
|
|
- if (request.length < 20) {
|
|
|
|
|
- throw new IllegalArgumentException("请求帧长度不足,无法构造应答");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 提取关键字段
|
|
|
|
|
- byte[] header = Arrays.copyOfRange(request, 0, 4); // 系统识别码
|
|
|
|
|
- byte packetSeq = request[5]; // 包序号
|
|
|
|
|
- byte frameType = request[6]; // 帧类型(0x31)
|
|
|
|
|
- byte addrLen = request[7]; // 源地址长度(0B)
|
|
|
|
|
- int srcAddrStart = 8;
|
|
|
|
|
- int srcAddrEnd = srcAddrStart + 6; // 实际取6字节源地址
|
|
|
|
|
- int destAddrStart = srcAddrEnd + 1; // 跳过目的地址长度0B
|
|
|
|
|
- int destAddrEnd = destAddrStart + 6;
|
|
|
|
|
-
|
|
|
|
|
|
|
+ // 步骤1:动态提取地址信息(对齐validateMessage)
|
|
|
|
|
+ int[] addrInfo = extractAddrInfo(request);
|
|
|
|
|
+ int srcLenFlag = addrInfo[0];
|
|
|
|
|
+ int srcAddrByteLen = addrInfo[1]; // 原源地址长度 m
|
|
|
|
|
+ int srcAddrStart = addrInfo[2];
|
|
|
|
|
+ int srcAddrEnd = addrInfo[3];
|
|
|
|
|
+ int destLenFlag = addrInfo[4];
|
|
|
|
|
+ int destAddrByteLen = addrInfo[5]; // 原目的地址长度 n
|
|
|
|
|
+ int destAddrStart = addrInfo[6];
|
|
|
|
|
+ int destAddrEnd = addrInfo[7];
|
|
|
|
|
+
|
|
|
|
|
+ // 步骤2:提取地址字节
|
|
|
byte[] srcAddr = Arrays.copyOfRange(request, srcAddrStart, srcAddrEnd);
|
|
byte[] srcAddr = Arrays.copyOfRange(request, srcAddrStart, srcAddrEnd);
|
|
|
byte[] destAddr = Arrays.copyOfRange(request, destAddrStart, destAddrEnd);
|
|
byte[] destAddr = Arrays.copyOfRange(request, destAddrStart, destAddrEnd);
|
|
|
|
|
|
|
|
- // --- 构造应答帧体 ---
|
|
|
|
|
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
|
|
|
|
|
+ // 步骤3:固定字段提取
|
|
|
|
|
+ byte[] sysId = Arrays.copyOfRange(request, 0, 3); // 系统识别码(3字节)
|
|
|
|
|
+ byte packetSeq = request[5]; // 包序号(1字节)
|
|
|
|
|
+ byte frameType = request[6]; // 帧类型0x31(1字节)
|
|
|
|
|
|
|
|
- // 前四字节
|
|
|
|
|
- bos.write(header, 0, 4);
|
|
|
|
|
- // 帧长度 0x16
|
|
|
|
|
- bos.write(new byte[]{0x16});
|
|
|
|
|
- // 包序号、帧类型
|
|
|
|
|
- bos.write(packetSeq);
|
|
|
|
|
- bos.write(frameType);
|
|
|
|
|
- bos.write(addrLen);
|
|
|
|
|
-
|
|
|
|
|
- // 交换地址段:目的 → 源
|
|
|
|
|
- bos.write(destAddr); // 目的地址放前
|
|
|
|
|
- bos.write(addrLen);
|
|
|
|
|
- bos.write(srcAddr); // 源地址放后
|
|
|
|
|
-
|
|
|
|
|
- // 计算异或校验码
|
|
|
|
|
- byte[] withoutCheck = bos.toByteArray();
|
|
|
|
|
- byte xor = 0x00;
|
|
|
|
|
- for (byte b : withoutCheck) xor ^= b;
|
|
|
|
|
- bos.write(xor);
|
|
|
|
|
|
|
+ // 帧长度字段值 = 系统识别码(3字节)+2(自身) + 1(包序号) + 1(帧类型) + 1(源地址标识) + n(源地址) + 1(目的地址标识) + m(目的地址)
|
|
|
|
|
+ int frameLenValue = 3 + 2 + 1 + 1 + 1 + destAddrByteLen + 1 + srcAddrByteLen+1;
|
|
|
|
|
|
|
|
- byte[] result = bos.toByteArray();
|
|
|
|
|
- return result;
|
|
|
|
|
|
|
+ // 步骤4:构造帧体(直接写入正确长度,无需占位回填)
|
|
|
|
|
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
|
|
|
+ bos.write(sysId); // 系统识别码(3字节)
|
|
|
|
|
+ // 写入帧长度字段(包含自身2字节,高字节在前)
|
|
|
|
|
+ bos.write((byte) ((frameLenValue >> 8) & 0xFF));
|
|
|
|
|
+ bos.write((byte) (frameLenValue & 0xFF));
|
|
|
|
|
+
|
|
|
|
|
+ // 步骤5:地址交换(动态长度)
|
|
|
|
|
+ bos.write(packetSeq); // 包序号
|
|
|
|
|
+ bos.write(frameType); // 帧类型
|
|
|
|
|
+ bos.write(destLenFlag); // 应答源地址标识=原目的标识
|
|
|
|
|
+ bos.write(destAddr); // 应答源地址=原目的地址(n字节)
|
|
|
|
|
+ bos.write(srcLenFlag); // 应答目的地址标识=原源标识
|
|
|
|
|
+ bos.write(srcAddr); // 应答目的地址=原源地址(m字节)
|
|
|
|
|
+
|
|
|
|
|
+ // 步骤6:计算异或校验(仅计算不含校验位的帧体)
|
|
|
|
|
+ byte[] tempFrame = bos.toByteArray();
|
|
|
|
|
+ byte xor = calculateXorCheck(tempFrame);
|
|
|
|
|
+
|
|
|
|
|
+ // 步骤7:拼接最终帧(帧体 + 异或校验位)
|
|
|
|
|
+ ByteArrayOutputStream finalBos = new ByteArrayOutputStream();
|
|
|
|
|
+ finalBos.write(tempFrame);
|
|
|
|
|
+ finalBos.write(xor);
|
|
|
|
|
+
|
|
|
|
|
+ return finalBos.toByteArray();
|
|
|
}
|
|
}
|
|
|
public static byte[] buildEndReplyFrame(byte[] request) throws IOException {
|
|
public static byte[] buildEndReplyFrame(byte[] request) throws IOException {
|
|
|
- if (request == null || request.length < 25) {
|
|
|
|
|
- throw new IllegalArgumentException("请求帧长度不足,无法构造结束应答");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 1 系统识别码
|
|
|
|
|
- byte[] sysId = Arrays.copyOfRange(request, 0, 3); // 12 34 56
|
|
|
|
|
-
|
|
|
|
|
- // 2 包序号
|
|
|
|
|
- byte packetSeq = request[5]; // 81
|
|
|
|
|
|
|
+ // 步骤1:动态提取地址信息(对齐validateMessage)
|
|
|
|
|
+ int[] addrInfo = extractAddrInfo(request);
|
|
|
|
|
+ int srcLenFlag = addrInfo[0];
|
|
|
|
|
+ int srcAddrByteLen = addrInfo[1]; // 源地址长度 n
|
|
|
|
|
+ int srcAddrStart = addrInfo[2];
|
|
|
|
|
+ int srcAddrEnd = addrInfo[3];
|
|
|
|
|
+ int destLenFlag = addrInfo[4];
|
|
|
|
|
+ int destAddrByteLen = addrInfo[5]; // 目的地址长度 m
|
|
|
|
|
+ int destAddrStart = addrInfo[6];
|
|
|
|
|
+ int destAddrEnd = addrInfo[7];
|
|
|
|
|
+
|
|
|
|
|
+ // 步骤2:提取地址字节
|
|
|
|
|
+ byte[] srcAddr = Arrays.copyOfRange(request, srcAddrStart, srcAddrEnd);
|
|
|
|
|
+ byte[] destAddr = Arrays.copyOfRange(request, destAddrStart, destAddrEnd);
|
|
|
|
|
|
|
|
- // 3 源/目的地址
|
|
|
|
|
- byte srcLen = request[7];
|
|
|
|
|
- byte[] srcAddr = Arrays.copyOfRange(request, 8, 14);
|
|
|
|
|
- byte destLen = request[14];
|
|
|
|
|
- byte[] destAddr = Arrays.copyOfRange(request, 15, 21);
|
|
|
|
|
|
|
+ // 步骤3:固定字段提取
|
|
|
|
|
+ byte[] sysId = Arrays.copyOfRange(request, 0, 3); // 系统识别码(3字节)
|
|
|
|
|
+ byte packetSeq = request[5]; // 包序号(1字节)
|
|
|
|
|
|
|
|
- // 4 当前时间转换为从 2000-01-01 开始的秒数
|
|
|
|
|
|
|
+ // 步骤4:时间戳(4字节)
|
|
|
long secondsSince2000 = getSecondsSince2000();
|
|
long secondsSince2000 = getSecondsSince2000();
|
|
|
byte[] timestamp = new byte[4];
|
|
byte[] timestamp = new byte[4];
|
|
|
timestamp[0] = (byte) ((secondsSince2000 >> 24) & 0xFF);
|
|
timestamp[0] = (byte) ((secondsSince2000 >> 24) & 0xFF);
|
|
@@ -257,37 +245,37 @@ public class ProtocolUtils {
|
|
|
timestamp[2] = (byte) ((secondsSince2000 >> 8) & 0xFF);
|
|
timestamp[2] = (byte) ((secondsSince2000 >> 8) & 0xFF);
|
|
|
timestamp[3] = (byte) (secondsSince2000 & 0xFF);
|
|
timestamp[3] = (byte) (secondsSince2000 & 0xFF);
|
|
|
|
|
|
|
|
- // 5 构造内容区(从包序号开始,到时间戳结束)
|
|
|
|
|
|
|
+ // ========== 核心修正:计算包含自身2字节的帧长度字段值 ==========
|
|
|
|
|
+ // 帧长度字段值 = 2(自身) + 1(包序号) + 1(帧类型) + 1(源地址标识) + n(源地址) + 1(目的地址标识) + m(目的地址) + 4(时间戳)
|
|
|
|
|
+ int frameLenValue =3+ 2 + 1 + 1 + 1 + srcAddrByteLen + 1 + destAddrByteLen + 4+1;
|
|
|
|
|
+ // 拆分高低字节
|
|
|
|
|
+ byte lenHigh = (byte) ((frameLenValue >> 8) & 0xFF);
|
|
|
|
|
+ byte lenLow = (byte) (frameLenValue & 0xFF);
|
|
|
|
|
+
|
|
|
|
|
+ // 步骤5:构造内容区(包序号到时间戳,不含长度字段)
|
|
|
ByteArrayOutputStream content = new ByteArrayOutputStream();
|
|
ByteArrayOutputStream content = new ByteArrayOutputStream();
|
|
|
- content.write(packetSeq); // 包序号
|
|
|
|
|
- content.write(0x34); // 帧类型(替换成 0x34)
|
|
|
|
|
- content.write(srcLen); // 源地址长度
|
|
|
|
|
- content.write(srcAddr); // 源地址
|
|
|
|
|
- content.write(destLen); // 目的地址长度
|
|
|
|
|
- content.write(destAddr); // 目的地址
|
|
|
|
|
- content.write(timestamp); // 当前时间戳
|
|
|
|
|
|
|
+ content.write(0X80); // 包序号(1字节)
|
|
|
|
|
+ content.write(0x34); // 帧类型0x34(1字节)
|
|
|
|
|
+ content.write(destLenFlag);// 目的地址标识(1字节)
|
|
|
|
|
+ content.write(destAddr); // 目的地址(m字节)
|
|
|
|
|
+ content.write(srcLenFlag); // 源地址标识(1字节)srcLenFlag
|
|
|
|
|
+ content.write(srcAddr); // 源地址(n字节)
|
|
|
|
|
+ content.write(timestamp); // 时间戳(4字节)
|
|
|
byte[] contentBytes = content.toByteArray();
|
|
byte[] contentBytes = content.toByteArray();
|
|
|
|
|
|
|
|
- // 6 计算帧长度(从包序号到时间戳的长度)
|
|
|
|
|
- int len = contentBytes.length;
|
|
|
|
|
- byte lenHigh = (byte) ((len >> 8) & 0xFF);
|
|
|
|
|
- byte lenLow = (byte) (len & 0xFF);
|
|
|
|
|
-
|
|
|
|
|
- // 7 拼完整帧(不含校验)
|
|
|
|
|
|
|
+ // 步骤6:拼帧(系统识别码 + 长度字段 + 内容区)
|
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
|
- bos.write(sysId); // 系统识别码
|
|
|
|
|
- bos.write(lenHigh); // 帧长度高
|
|
|
|
|
- bos.write(lenLow); // 帧长度低
|
|
|
|
|
|
|
+ bos.write(sysId); // 系统识别码(3字节)
|
|
|
|
|
+ bos.write(lenHigh); // 长度高字节(包含自身2字节的总长度)
|
|
|
|
|
+ bos.write(lenLow); // 长度低字节
|
|
|
bos.write(contentBytes);
|
|
bos.write(contentBytes);
|
|
|
|
|
|
|
|
- // 8 计算异或校验
|
|
|
|
|
|
|
+ // 步骤7:计算并添加异或校验(仅计算不含校验位的帧体)
|
|
|
byte[] noCheck = bos.toByteArray();
|
|
byte[] noCheck = bos.toByteArray();
|
|
|
- byte xor = 0x00;
|
|
|
|
|
- for (byte b : noCheck) xor ^= b;
|
|
|
|
|
- bos.write(xor); // 校验位
|
|
|
|
|
|
|
+ byte xor = calculateXorCheck(noCheck);
|
|
|
|
|
+ bos.write(xor);
|
|
|
|
|
|
|
|
byte[] result = bos.toByteArray();
|
|
byte[] result = bos.toByteArray();
|
|
|
-
|
|
|
|
|
System.out.println("结束帧应答: " + printHex(result));
|
|
System.out.println("结束帧应答: " + printHex(result));
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
@@ -302,4 +290,72 @@ public class ProtocolUtils {
|
|
|
for (byte b : bytes) sb.append(String.format("%02X ", b));
|
|
for (byte b : bytes) sb.append(String.format("%02X ", b));
|
|
|
return sb.toString().trim();
|
|
return sb.toString().trim();
|
|
|
}
|
|
}
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 核心工具方法:根据地址标识计算地址字节长度
|
|
|
|
|
+ * 规则:标识值转10进制 → 奇数+1 → 除以2
|
|
|
|
|
+ */
|
|
|
|
|
+ private static int calculateAddrByteLength(int lenFlag) {
|
|
|
|
|
+ int temp = lenFlag;
|
|
|
|
|
+ // 奇数则+1
|
|
|
|
|
+ if (temp % 2 != 0) {
|
|
|
|
|
+ temp += 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 除以2得到字节长度
|
|
|
|
|
+ return temp / 2;
|
|
|
|
|
+ }
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 核心工具:正向解析源/目的地址信息(贴合实际报文结构)
|
|
|
|
|
+ * 逻辑:
|
|
|
|
|
+ * 1. 正向定位源地址标识(索引7)、源地址、目的地址标识、目的地址
|
|
|
|
|
+ * 2. 找 0x01 2C 验证地址段结束位置,确保解析合法
|
|
|
|
|
+ * @return 数组格式:[srcLenFlag, srcAddrByteLen, srcAddrStart, srcAddrEnd, destLenFlag, destAddrByteLen, destAddrStart, destAddrEnd]
|
|
|
|
|
+ */
|
|
|
|
|
+ private static int[] extractAddrInfo(byte[] request) {
|
|
|
|
|
+ if (request == null || request.length < 25) { // 适配实际报文最小长度(82字节)
|
|
|
|
|
+ throw new IllegalArgumentException("请求帧长度不足,无法解析地址信息,长度=" + (request == null ? 0 : request.length));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 步骤1:正向定位核心索引(贴合协议固定结构) ==========
|
|
|
|
|
+ int srcLenIndex = 7; // 源地址标识固定在索引7
|
|
|
|
|
+ int srcLenFlag = request[srcLenIndex] & 0xFF;
|
|
|
|
|
+ int srcAddrByteLen = calculateAddrByteLength(srcLenFlag);
|
|
|
|
|
+ int srcAddrStart = srcLenIndex + 1;
|
|
|
|
|
+ int srcAddrEnd = srcAddrStart + srcAddrByteLen;
|
|
|
|
|
+
|
|
|
|
|
+ // 目的地址标识 = 源地址结束索引
|
|
|
|
|
+ int destLenIndex = srcAddrEnd;
|
|
|
|
|
+ int destLenFlag = request[destLenIndex] & 0xFF;
|
|
|
|
|
+ int destAddrByteLen = calculateAddrByteLength(destLenFlag);
|
|
|
|
|
+ int destAddrStart = destLenIndex + 1;
|
|
|
|
|
+ int destAddrEnd = destAddrStart + destAddrByteLen;
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 步骤2:验证地址段结束位置(找 0x01 2C) ==========
|
|
|
|
|
+ int deviceCodeIndex = destAddrEnd;
|
|
|
|
|
+ // 校验设备编码+功能码是否为 0x01 2C(地址段后必须紧跟)
|
|
|
|
|
+ if (deviceCodeIndex + 1 >= request.length
|
|
|
|
|
+ || request[deviceCodeIndex] != 0x01
|
|
|
|
|
+ || request[deviceCodeIndex + 1] != 0x2C) {
|
|
|
|
|
+ throw new IllegalArgumentException("地址段结束后未找到 0x01 2C 序列,地址解析异常:设备编码索引=" + deviceCodeIndex);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 步骤3:范围最终校验 ==========
|
|
|
|
|
+ // 源地址范围校验
|
|
|
|
|
+ if (srcAddrStart < 8 || srcAddrEnd > destLenIndex) {
|
|
|
|
|
+ throw new IllegalArgumentException(
|
|
|
|
|
+ String.format("源地址范围异常:标识索引=%d 标识值=%d 计算长度=%d 起始=%d 结束=%d",
|
|
|
|
|
+ srcLenIndex, srcLenFlag, srcAddrByteLen, srcAddrStart, srcAddrEnd));
|
|
|
|
|
+ }
|
|
|
|
|
+ // 目的地址范围校验
|
|
|
|
|
+ if (destAddrStart < (destLenIndex + 1) || destAddrEnd > deviceCodeIndex) {
|
|
|
|
|
+ throw new IllegalArgumentException(
|
|
|
|
|
+ String.format("目的地址范围异常:标识索引=%d 标识值=%d 计算长度=%d 起始=%d 结束=%d",
|
|
|
|
|
+ destLenIndex, destLenFlag, destAddrByteLen, destAddrStart, destAddrEnd));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return new int[]{
|
|
|
|
|
+ srcLenFlag, srcAddrByteLen, srcAddrStart, srcAddrEnd,
|
|
|
|
|
+ destLenFlag, destAddrByteLen, destAddrStart, destAddrEnd
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
}
|
|
}
|