2024-11-28 14:57:49 +08:00
|
|
|
|
import 'dart:convert';
|
|
|
|
|
|
import 'package:crc32_checksum/crc32_checksum.dart';
|
|
|
|
|
|
import 'package:crypto/crypto.dart';
|
|
|
|
|
|
import 'package:star_lock/app_settings/app_settings.dart';
|
2024-12-02 16:13:07 +08:00
|
|
|
|
import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart';
|
|
|
|
|
|
import 'package:star_lock/talk/startChart/entity/heartbeat_response.dart';
|
2024-11-28 14:57:49 +08:00
|
|
|
|
|
|
|
|
|
|
class ScpMessage {
|
|
|
|
|
|
ScpMessage({
|
|
|
|
|
|
this.ProtocolFlag,
|
|
|
|
|
|
this.MessageType,
|
|
|
|
|
|
this.MessageId,
|
|
|
|
|
|
this.SpTotal,
|
|
|
|
|
|
this.SpIndex,
|
|
|
|
|
|
this.FromPeerId,
|
|
|
|
|
|
this.ToPeerId,
|
|
|
|
|
|
this.PayloadType,
|
|
|
|
|
|
this.PayloadCRC,
|
|
|
|
|
|
this.PayloadLength,
|
|
|
|
|
|
this.Payload,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
String? ProtocolFlag;
|
|
|
|
|
|
int? MessageType;
|
|
|
|
|
|
int? MessageId;
|
|
|
|
|
|
int? SpTotal;
|
|
|
|
|
|
int? SpIndex;
|
|
|
|
|
|
String? FromPeerId;
|
|
|
|
|
|
String? ToPeerId;
|
|
|
|
|
|
int? PayloadType;
|
|
|
|
|
|
int? PayloadCRC;
|
|
|
|
|
|
int? PayloadLength;
|
2024-12-02 16:13:07 +08:00
|
|
|
|
dynamic Payload; // Payload可以是任何类型,这里用dynamic表示
|
2024-11-28 14:57:49 +08:00
|
|
|
|
|
|
|
|
|
|
ScpMessage.fromJson(dynamic json) {
|
|
|
|
|
|
ProtocolFlag = json['ProtocolFlag'];
|
|
|
|
|
|
MessageType = json['MessageType'];
|
|
|
|
|
|
MessageId = json['MessageId'];
|
|
|
|
|
|
SpTotal = json['SpTotal'];
|
|
|
|
|
|
SpIndex = json['SpIndex'];
|
|
|
|
|
|
FromPeerId = json['FromPeerId'];
|
|
|
|
|
|
ToPeerId = json['ToPeerId'];
|
|
|
|
|
|
PayloadType = json['PayloadType'];
|
|
|
|
|
|
PayloadCRC = json['PayloadCRC'];
|
|
|
|
|
|
PayloadLength = json['PayloadLength'];
|
|
|
|
|
|
Payload = json['Payload'];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Map<String, dynamic> toJson() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
'ProtocolFlag': ProtocolFlag,
|
|
|
|
|
|
'MessageType': MessageType,
|
|
|
|
|
|
'MessageId': MessageId,
|
|
|
|
|
|
'SpTotal': SpTotal,
|
|
|
|
|
|
'SpIndex': SpIndex,
|
|
|
|
|
|
'FromPeerId': FromPeerId,
|
|
|
|
|
|
'ToPeerId': ToPeerId,
|
|
|
|
|
|
'PayloadType': PayloadType,
|
|
|
|
|
|
'PayloadCRC': PayloadCRC,
|
|
|
|
|
|
'PayloadLength': PayloadLength,
|
|
|
|
|
|
'Payload': Payload,
|
|
|
|
|
|
};
|
2024-11-30 15:39:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
String toString() {
|
|
|
|
|
|
return 'ScpMessage{ProtocolFlag: $ProtocolFlag, MessageType: $MessageType, MessageId: $MessageId, SpTotal: $SpTotal, SpIndex: $SpIndex, FromPeerId: $FromPeerId, ToPeerId: $ToPeerId, PayloadType: $PayloadType, PayloadCRC: $PayloadCRC, PayloadLength: $PayloadLength, Payload: $Payload}';
|
2024-11-28 14:57:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
String serialize() {
|
|
|
|
|
|
final bytes = <int>[];
|
|
|
|
|
|
|
|
|
|
|
|
// ProtocolFlag (4 bytes)
|
|
|
|
|
|
if (ProtocolFlag != null) {
|
|
|
|
|
|
bytes.addAll(utf8.encode(ProtocolFlag!));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MessageType (1 byte)
|
|
|
|
|
|
if (MessageType != null) {
|
|
|
|
|
|
bytes.add(MessageType!);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MessageId (2 bytes)
|
|
|
|
|
|
if (MessageId != null) {
|
|
|
|
|
|
final highByteMessageId = (MessageId! >> 8) & 0xFF;
|
|
|
|
|
|
final lowByteMessageId = MessageId! & 0xFF;
|
|
|
|
|
|
bytes.add(lowByteMessageId); // 交换位置
|
|
|
|
|
|
bytes.add(highByteMessageId); // 交换位置
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SpTotal (1 byte)
|
|
|
|
|
|
if (SpTotal != null) {
|
|
|
|
|
|
bytes.add(SpTotal!);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SpIndex (1 byte)
|
|
|
|
|
|
if (SpIndex != null) {
|
|
|
|
|
|
bytes.add(SpIndex!);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// FromPeerId (字符串,记录长度)
|
|
|
|
|
|
if (FromPeerId != null) {
|
|
|
|
|
|
bytes.addAll(utf8.encode(FromPeerId!));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ToPeerId (字符串,假设长度固定为32字节)
|
|
|
|
|
|
if (ToPeerId != null) {
|
|
|
|
|
|
bytes.addAll(utf8.encode(ToPeerId!));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// PayloadType (2 bytes)
|
|
|
|
|
|
if (PayloadType != null) {
|
|
|
|
|
|
final highBytePayloadType = (PayloadType! >> 8) & 0xFF;
|
|
|
|
|
|
final lowBytePayloadType = PayloadType! & 0xFF;
|
|
|
|
|
|
bytes.add(lowBytePayloadType); // 交换位置
|
|
|
|
|
|
bytes.add(highBytePayloadType); // 交换位置
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算 PayloadCRC (2 bytes)
|
|
|
|
|
|
if (PayloadCRC != null) {
|
|
|
|
|
|
final highBytePayloadCRC = (PayloadCRC! >> 8) & 0xFF;
|
|
|
|
|
|
final lowBytePayloadCRC = PayloadCRC! & 0xFF;
|
|
|
|
|
|
bytes.add(lowBytePayloadCRC); // 交换位置
|
|
|
|
|
|
bytes.add(highBytePayloadCRC); // 交换位置
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// PayloadLength (4 bytes)
|
|
|
|
|
|
if (PayloadLength != null) {
|
|
|
|
|
|
bytes.add(PayloadLength! & 0xFF);
|
|
|
|
|
|
bytes.add((PayloadLength! >> 8) & 0xFF);
|
|
|
|
|
|
bytes.add((PayloadLength! >> 16) & 0xFF);
|
|
|
|
|
|
bytes.add((PayloadLength! >> 24) & 0xFF);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Payload (字符串,转换为字节)
|
|
|
|
|
|
if (Payload != null) {
|
|
|
|
|
|
bytes.addAll(utf8.encode(Payload!));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 转16进制字符串
|
|
|
|
|
|
final bytesToHexString = bytesToHex(bytes);
|
|
|
|
|
|
|
|
|
|
|
|
return bytesToHexString;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-30 15:39:06 +08:00
|
|
|
|
static ScpMessage deserialize(List<int> bytes) {
|
|
|
|
|
|
final message = ScpMessage();
|
|
|
|
|
|
int offset = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// ProtocolFlag (4 bytes)
|
|
|
|
|
|
if (bytes.length - offset >= 4) {
|
|
|
|
|
|
message.ProtocolFlag = utf8.decode(bytes.sublist(offset, offset + 4));
|
|
|
|
|
|
offset += 4;
|
2024-12-02 15:43:59 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
throw FormatException("Invalid ProtocolFlag length");
|
2024-11-30 15:39:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MessageType (1 byte)
|
|
|
|
|
|
if (bytes.length - offset >= 1) {
|
|
|
|
|
|
message.MessageType = bytes[offset];
|
|
|
|
|
|
offset += 1;
|
2024-12-02 15:43:59 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
throw FormatException("Invalid MessageType length");
|
2024-11-30 15:39:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MessageId (2 bytes, little-endian)
|
|
|
|
|
|
if (bytes.length - offset >= 2) {
|
|
|
|
|
|
message.MessageId = (bytes[offset + 1] << 8) | bytes[offset];
|
|
|
|
|
|
offset += 2;
|
2024-12-02 15:43:59 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
throw FormatException("Invalid MessageId length");
|
2024-11-30 15:39:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SpTotal (1 byte)
|
|
|
|
|
|
if (bytes.length - offset >= 1) {
|
|
|
|
|
|
message.SpTotal = bytes[offset];
|
|
|
|
|
|
offset += 1;
|
2024-12-02 15:43:59 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
throw FormatException("Invalid SpTotal length");
|
2024-11-30 15:39:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SpIndex (1 byte)
|
|
|
|
|
|
if (bytes.length - offset >= 1) {
|
|
|
|
|
|
message.SpIndex = bytes[offset];
|
|
|
|
|
|
offset += 1;
|
2024-12-02 15:43:59 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
throw FormatException("Invalid SpIndex length");
|
2024-11-30 15:39:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// FromPeerId (字符串,长度固定为44字节)
|
|
|
|
|
|
if (bytes.length - offset >= 44) {
|
2024-12-02 16:13:07 +08:00
|
|
|
|
message.FromPeerId =
|
|
|
|
|
|
utf8.decode(bytes.sublist(offset, offset + 44)).trimRight();
|
2024-11-30 15:39:06 +08:00
|
|
|
|
offset += 44;
|
2024-12-02 15:43:59 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
throw FormatException("Invalid FromPeerId length");
|
2024-11-30 15:39:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ToPeerId (字符串,长度固定为44字节)
|
|
|
|
|
|
if (bytes.length - offset >= 44) {
|
2024-12-02 16:13:07 +08:00
|
|
|
|
message.ToPeerId =
|
|
|
|
|
|
utf8.decode(bytes.sublist(offset, offset + 44)).trimRight();
|
2024-11-30 15:39:06 +08:00
|
|
|
|
offset += 44;
|
2024-12-02 15:43:59 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
throw FormatException("Invalid ToPeerId length");
|
2024-11-30 15:39:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// PayloadType (2 bytes, little-endian)
|
|
|
|
|
|
if (bytes.length - offset >= 2) {
|
|
|
|
|
|
message.PayloadType = (bytes[offset + 1] << 8) | bytes[offset];
|
|
|
|
|
|
offset += 2;
|
2024-12-02 15:43:59 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
throw FormatException("Invalid PayloadType length");
|
2024-11-30 15:39:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// PayloadCRC (2 bytes, little-endian)
|
|
|
|
|
|
if (bytes.length - offset >= 2) {
|
|
|
|
|
|
message.PayloadCRC = (bytes[offset + 1] << 8) | bytes[offset];
|
|
|
|
|
|
offset += 2;
|
2024-12-02 15:43:59 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
throw FormatException("Invalid PayloadCRC length");
|
2024-11-30 15:39:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-02 15:43:59 +08:00
|
|
|
|
// PayloadLength (4 bytes, little-endian)
|
2024-11-30 15:39:06 +08:00
|
|
|
|
if (bytes.length - offset >= 4) {
|
2024-12-02 15:43:59 +08:00
|
|
|
|
// 打印PayloadLength对应的4个字节
|
|
|
|
|
|
print('PayloadLength bytes: ${bytes.sublist(offset, offset + 4)}');
|
|
|
|
|
|
|
2024-11-30 15:39:06 +08:00
|
|
|
|
message.PayloadLength = (bytes[offset] |
|
2024-12-02 16:13:07 +08:00
|
|
|
|
(bytes[offset + 1] << 8) |
|
|
|
|
|
|
(bytes[offset + 2] << 16) |
|
|
|
|
|
|
(bytes[offset + 3] << 24)); // 修正为 little-endian
|
2024-11-30 15:39:06 +08:00
|
|
|
|
offset += 4;
|
2024-12-02 15:43:59 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
throw FormatException("Invalid PayloadLength length");
|
2024-11-30 15:39:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Payload (字符串,转换为字节)
|
2024-12-02 17:19:01 +08:00
|
|
|
|
// if (message.PayloadLength != null &&
|
|
|
|
|
|
// bytes.length - offset >= message.PayloadLength!) {
|
|
|
|
|
|
// message.Payload =
|
|
|
|
|
|
// utf8.decode(bytes.sublist(offset, offset + message.PayloadLength!));
|
|
|
|
|
|
// offset += message.PayloadLength!;
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// throw FormatException("Invalid Payload or PayloadLength");
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// 打印解析后的 PayloadLength
|
|
|
|
|
|
print('Parsed PayloadLength: ${message.PayloadLength}');
|
2024-11-30 15:39:06 +08:00
|
|
|
|
|
2024-12-02 16:13:07 +08:00
|
|
|
|
// 解析Payload
|
2024-12-02 17:19:01 +08:00
|
|
|
|
if (message.PayloadType == PayloadTypeConstant.heartbeat) {
|
|
|
|
|
|
// 假设110表示HeartbeatResponse类型
|
|
|
|
|
|
if (message.PayloadLength != null &&
|
|
|
|
|
|
bytes.length - offset >= message.PayloadLength!) {
|
|
|
|
|
|
final payloadBytes =
|
|
|
|
|
|
bytes.sublist(offset, offset + message.PayloadLength!);
|
2024-12-02 16:13:07 +08:00
|
|
|
|
message.Payload = HeartbeatResponse.deserialize(payloadBytes);
|
|
|
|
|
|
offset += message.PayloadLength!;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
throw FormatException("Invalid Payload or PayloadLength");
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 处理其他类型的Payload
|
2024-12-02 17:19:01 +08:00
|
|
|
|
if (message.PayloadLength != null &&
|
|
|
|
|
|
bytes.length - offset >= message.PayloadLength!) {
|
|
|
|
|
|
message.Payload =
|
|
|
|
|
|
utf8.decode(bytes.sublist(offset, offset + message.PayloadLength!));
|
2024-12-02 16:13:07 +08:00
|
|
|
|
offset += message.PayloadLength!;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
throw FormatException("Invalid Payload or PayloadLength");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-02 15:43:59 +08:00
|
|
|
|
// 验证PayloadCRC
|
|
|
|
|
|
// if (message.Payload != null) {
|
|
|
|
|
|
// var crcBytes = List<int>.from(utf8.encode(message.Payload!));
|
|
|
|
|
|
// var calculatedCrc = _calculateCrc16(crcBytes);
|
|
|
|
|
|
// if (calculatedCrc != message.PayloadCRC) {
|
|
|
|
|
|
// throw FormatException("PayloadCRC verification failed. Expected: ${message.PayloadCRC}, Actual: $calculatedCrc");
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
2024-11-30 15:39:06 +08:00
|
|
|
|
return message;
|
|
|
|
|
|
}
|
2024-12-02 16:13:07 +08:00
|
|
|
|
|
2024-12-02 15:43:59 +08:00
|
|
|
|
// CRC-16 计算函数(示例实现,可能需要根据具体协议调整)
|
|
|
|
|
|
static int _calculateCrc16(List<int> data) {
|
|
|
|
|
|
const poly = 0x8005;
|
|
|
|
|
|
int crc = 0xFFFF;
|
|
|
|
|
|
|
|
|
|
|
|
for (final b in data) {
|
|
|
|
|
|
crc ^= b << 8;
|
|
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
|
|
|
if ((crc & 0x8000) != 0) {
|
|
|
|
|
|
crc = (crc << 1) ^ poly;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
crc <<= 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
crc &= 0xFFFF;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return crc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-28 14:57:49 +08:00
|
|
|
|
static String bytesToHex(List<int> bytes) {
|
|
|
|
|
|
return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join('');
|
|
|
|
|
|
}
|
2024-11-30 15:39:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 辅助函数:将16进制字符串转换为字节数组
|
|
|
|
|
|
static List<int> hexToBytes(String hexString) {
|
|
|
|
|
|
final bytes = <int>[];
|
|
|
|
|
|
for (int i = 0; i < hexString.length; i += 2) {
|
|
|
|
|
|
final hexByte = hexString.substring(i, i + 2);
|
|
|
|
|
|
bytes.add(int.parse(hexByte, radix: 16));
|
|
|
|
|
|
}
|
|
|
|
|
|
return bytes;
|
|
|
|
|
|
}
|
2024-11-28 14:57:49 +08:00
|
|
|
|
}
|