mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Use LogRxRouteDecoder in AppProvider
This commit is contained in:
@@ -29,6 +29,7 @@ import '../utils/image_message_parser.dart';
|
|||||||
import '../utils/media_swarm_protocol.dart';
|
import '../utils/media_swarm_protocol.dart';
|
||||||
import '../utils/message_airtime_estimator.dart';
|
import '../utils/message_airtime_estimator.dart';
|
||||||
import '../utils/fast_gps_packet.dart';
|
import '../utils/fast_gps_packet.dart';
|
||||||
|
import '../utils/log_rx_route_decoder.dart';
|
||||||
|
|
||||||
class _DirectMessageRouteSession {
|
class _DirectMessageRouteSession {
|
||||||
final PathSelection currentSelection;
|
final PathSelection currentSelection;
|
||||||
@@ -2573,12 +2574,10 @@ class AppProvider with ChangeNotifier {
|
|||||||
if (log.responseCode != 0x88) continue;
|
if (log.responseCode != 0x88) continue;
|
||||||
if (log.rawData.length < 6) continue;
|
if (log.rawData.length < 6) continue;
|
||||||
|
|
||||||
final raw = log.rawData;
|
final decoded = LogRxRouteDecoder.decode(log.rawData);
|
||||||
final payloadType = (raw[3] >> 2) & 0x0F;
|
if (decoded == null) continue;
|
||||||
final pathLen = raw[4];
|
if (decoded.payloadType != expectedPayloadType) continue;
|
||||||
if (payloadType != expectedPayloadType) continue;
|
if (decoded.hopCount != message.pathLen) continue;
|
||||||
if (pathLen != message.pathLen) continue;
|
|
||||||
if (raw.length < 5 + pathLen) continue;
|
|
||||||
|
|
||||||
final deltaMs =
|
final deltaMs =
|
||||||
(log.timestamp.difference(message.receivedAt).inMilliseconds).abs();
|
(log.timestamp.difference(message.receivedAt).inMilliseconds).abs();
|
||||||
@@ -2594,11 +2593,9 @@ class AppProvider with ChangeNotifier {
|
|||||||
|
|
||||||
List<int>? _extractPathBytesFromLog(BlePacketLog? log) {
|
List<int>? _extractPathBytesFromLog(BlePacketLog? log) {
|
||||||
if (log == null) return null;
|
if (log == null) return null;
|
||||||
final raw = log.rawData;
|
final decoded = LogRxRouteDecoder.decode(log.rawData);
|
||||||
if (raw.length < 6) return null;
|
if (decoded == null || decoded.pathBytes.isEmpty) return null;
|
||||||
final pathLen = raw[4];
|
return decoded.pathBytes;
|
||||||
if (pathLen <= 0 || raw.length < 5 + pathLen) return null;
|
|
||||||
return raw.sublist(5, 5 + pathLen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removed _syncMessages() - messages are automatically synced via PUSH_CODE_MSG_WAITING events
|
// Removed _syncMessages() - messages are automatically synced via PUSH_CODE_MSG_WAITING events
|
||||||
|
|||||||
@@ -885,6 +885,54 @@ class MessagesProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark unread messages for a specific destination as read.
|
||||||
|
void markDestinationAsRead({
|
||||||
|
required String destinationType,
|
||||||
|
Contact? contact,
|
||||||
|
}) {
|
||||||
|
if (destinationType == 'all') {
|
||||||
|
markAllAsRead();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasChanges = false;
|
||||||
|
for (int i = 0; i < _messages.length; i++) {
|
||||||
|
final message = _messages[i];
|
||||||
|
if (message.isRead || message.isSentMessage || message.isSystemMessage) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final matchesDestination = switch (destinationType) {
|
||||||
|
'channel' => _isChannelMessageForContact(message, contact),
|
||||||
|
'contact' || 'room' => contact != null && _isMessageForDestination(message, contact),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!matchesDestination) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_messages[i] = message.copyWith(isRead: true);
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasChanges) {
|
||||||
|
_persistMessages();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isChannelMessageForContact(Message message, Contact? contact) {
|
||||||
|
if (!message.isChannelMessage) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final selectedChannelIdx = contact != null && contact.publicKey.length > 1
|
||||||
|
? contact.publicKey[1]
|
||||||
|
: 0;
|
||||||
|
return (message.channelIdx ?? 0) == selectedChannelIdx;
|
||||||
|
}
|
||||||
|
|
||||||
/// Mark a specific message as read
|
/// Mark a specific message as read
|
||||||
void markAsRead(String messageId) {
|
void markAsRead(String messageId) {
|
||||||
final index = _messages.indexWhere((m) => m.id == messageId);
|
final index = _messages.indexWhere((m) => m.id == messageId);
|
||||||
|
|||||||
@@ -195,7 +195,6 @@ class _HomeScreenState extends State<HomeScreen>
|
|||||||
|
|
||||||
switch (tab) {
|
switch (tab) {
|
||||||
case _HomeTab.messages:
|
case _HomeTab.messages:
|
||||||
context.read<MessagesProvider>().markAllAsRead();
|
|
||||||
break;
|
break;
|
||||||
case _HomeTab.contacts:
|
case _HomeTab.contacts:
|
||||||
context.read<ContactsProvider>().markAllAsViewed();
|
context.read<ContactsProvider>().markAllAsViewed();
|
||||||
|
|||||||
@@ -89,9 +89,7 @@ class _MessagesTabState extends State<MessagesTab> {
|
|||||||
// Load saved message destination
|
// Load saved message destination
|
||||||
_loadSavedDestination();
|
_loadSavedDestination();
|
||||||
_loadVoiceBitrate();
|
_loadVoiceBitrate();
|
||||||
// Mark all messages as read when tab is opened
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
context.read<MessagesProvider>().markAllAsRead();
|
|
||||||
_checkForNavigationRequest();
|
_checkForNavigationRequest();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -309,6 +307,8 @@ class _MessagesTabState extends State<MessagesTab> {
|
|||||||
_selectedRecipient = recipient;
|
_selectedRecipient = recipient;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_markCurrentDestinationAsRead();
|
||||||
|
|
||||||
_enforceMessageByteLimit();
|
_enforceMessageByteLimit();
|
||||||
|
|
||||||
// Save to preferences
|
// Save to preferences
|
||||||
@@ -465,6 +465,7 @@ class _MessagesTabState extends State<MessagesTab> {
|
|||||||
|
|
||||||
_textController.clear();
|
_textController.clear();
|
||||||
_focusNode.unfocus();
|
_focusNode.unfocus();
|
||||||
|
_markCurrentDestinationAsRead();
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -1641,6 +1642,13 @@ class _MessagesTabState extends State<MessagesTab> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _markCurrentDestinationAsRead() {
|
||||||
|
context.read<MessagesProvider>().markDestinationAsRead(
|
||||||
|
destinationType: _destinationType,
|
||||||
|
contact: _selectedRecipient,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
List<Message> _getFilteredMessages(MessagesProvider messagesProvider) {
|
List<Message> _getFilteredMessages(MessagesProvider messagesProvider) {
|
||||||
// Get all recent messages
|
// Get all recent messages
|
||||||
final allMessages = messagesProvider.getRecentMessages(count: 100);
|
final allMessages = messagesProvider.getRecentMessages(count: 100);
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import '../models/contact.dart';
|
|||||||
|
|
||||||
class DecodedLogRxRoute {
|
class DecodedLogRxRoute {
|
||||||
final int payloadType;
|
final int payloadType;
|
||||||
|
final int pathDescriptor;
|
||||||
final List<int> pathBytes;
|
final List<int> pathBytes;
|
||||||
final int hashSize;
|
final int hashSize;
|
||||||
|
|
||||||
const DecodedLogRxRoute({
|
const DecodedLogRxRoute({
|
||||||
required this.payloadType,
|
required this.payloadType,
|
||||||
|
required this.pathDescriptor,
|
||||||
required this.pathBytes,
|
required this.pathBytes,
|
||||||
required this.hashSize,
|
required this.hashSize,
|
||||||
});
|
});
|
||||||
@@ -63,21 +65,49 @@ class LogRxRouteDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rawPacketData.length <= index) return null;
|
if (rawPacketData.length <= index) return null;
|
||||||
final pathLen = rawPacketData[index++];
|
final pathDescriptor = rawPacketData[index++];
|
||||||
if (rawPacketData.length < index + pathLen) return null;
|
final pathMode = (pathDescriptor & 0xFF) >> 6;
|
||||||
final pathBytes = rawPacketData.sublist(index, index + pathLen);
|
final pathByteLen = pathMode == 0
|
||||||
final hashSize = inferHashSize(
|
? pathDescriptor
|
||||||
pathBytes,
|
: descriptorByteLength(pathDescriptor);
|
||||||
preferredHashSize: preferredHashSize,
|
if (pathByteLen == null || rawPacketData.length < index + pathByteLen) {
|
||||||
);
|
return null;
|
||||||
|
}
|
||||||
|
final pathBytes = rawPacketData.sublist(index, index + pathByteLen);
|
||||||
|
final hashSize = pathMode == 0
|
||||||
|
? inferHashSize(pathBytes, preferredHashSize: preferredHashSize)
|
||||||
|
: (descriptorHashSize(pathDescriptor) ??
|
||||||
|
inferHashSize(pathBytes, preferredHashSize: preferredHashSize));
|
||||||
|
|
||||||
return DecodedLogRxRoute(
|
return DecodedLogRxRoute(
|
||||||
payloadType: payloadType,
|
payloadType: payloadType,
|
||||||
|
pathDescriptor: pathDescriptor,
|
||||||
pathBytes: pathBytes,
|
pathBytes: pathBytes,
|
||||||
hashSize: hashSize,
|
hashSize: hashSize,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int? descriptorHashSize(int pathDescriptor) {
|
||||||
|
final normalized = pathDescriptor & 0xFF;
|
||||||
|
final mode = normalized >> 6;
|
||||||
|
if (mode == 3) return null;
|
||||||
|
return mode + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int? descriptorHopCount(int pathDescriptor) {
|
||||||
|
final hashSize = descriptorHashSize(pathDescriptor);
|
||||||
|
if (hashSize == null) return null;
|
||||||
|
return (pathDescriptor & 0xFF) & 0x3F;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int? descriptorByteLength(int pathDescriptor) {
|
||||||
|
final hashSize = descriptorHashSize(pathDescriptor);
|
||||||
|
final hopCount = descriptorHopCount(pathDescriptor);
|
||||||
|
if (hashSize == null || hopCount == null) return null;
|
||||||
|
final byteLen = hopCount * hashSize;
|
||||||
|
return byteLen <= 64 ? byteLen : null;
|
||||||
|
}
|
||||||
|
|
||||||
static int inferHashSize(List<int> pathBytes, {int? preferredHashSize}) {
|
static int inferHashSize(List<int> pathBytes, {int? preferredHashSize}) {
|
||||||
if (pathBytes.isEmpty) return 1;
|
if (pathBytes.isEmpty) return 1;
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import '../../utils/tictactoe_message_parser.dart';
|
|||||||
import '../../utils/location_formats.dart';
|
import '../../utils/location_formats.dart';
|
||||||
import '../../l10n/app_localizations.dart';
|
import '../../l10n/app_localizations.dart';
|
||||||
import '../../utils/message_extensions.dart';
|
import '../../utils/message_extensions.dart';
|
||||||
|
import '../../utils/log_rx_route_decoder.dart';
|
||||||
import '../../models/message_transfer_details.dart';
|
import '../../models/message_transfer_details.dart';
|
||||||
import 'voice_message_bubble.dart';
|
import 'voice_message_bubble.dart';
|
||||||
import 'image_message_bubble.dart';
|
import 'image_message_bubble.dart';
|
||||||
@@ -1269,12 +1270,10 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
|
|
||||||
// Logged frame format:
|
// Logged frame format:
|
||||||
// [0]=response code 0x88, [1]=snrRaw, [2]=rssi, [3]=packet header, [4]=pathLen
|
// [0]=response code 0x88, [1]=snrRaw, [2]=rssi, [3]=packet header, [4]=pathLen
|
||||||
final raw = log.rawData;
|
final decoded = LogRxRouteDecoder.decode(log.rawData);
|
||||||
final payloadType = (raw[3] >> 2) & 0x0F;
|
if (decoded == null) continue;
|
||||||
final pathLen = raw[4];
|
if (decoded.payloadType != expectedPayloadType) continue;
|
||||||
if (payloadType != expectedPayloadType) continue;
|
if (decoded.hopCount != message.pathLen) continue;
|
||||||
if (pathLen != message.pathLen) continue;
|
|
||||||
if (raw.length < 5 + pathLen) continue;
|
|
||||||
|
|
||||||
final deltaMs =
|
final deltaMs =
|
||||||
(log.timestamp.difference(message.receivedAt).inMilliseconds).abs();
|
(log.timestamp.difference(message.receivedAt).inMilliseconds).abs();
|
||||||
@@ -1290,11 +1289,9 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
|
|
||||||
List<int>? _extractPathBytesFromLog(BlePacketLog? log) {
|
List<int>? _extractPathBytesFromLog(BlePacketLog? log) {
|
||||||
if (log == null) return null;
|
if (log == null) return null;
|
||||||
final raw = log.rawData;
|
final decoded = LogRxRouteDecoder.decode(log.rawData);
|
||||||
if (raw.length < 6) return null;
|
if (decoded == null || decoded.pathBytes.isEmpty) return null;
|
||||||
final pathLen = raw[4];
|
return decoded.pathBytes;
|
||||||
if (pathLen <= 0 || raw.length < 5 + pathLen) return null;
|
|
||||||
return raw.sublist(5, 5 + pathLen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showDeleteConfirmation(BuildContext context) {
|
void _showDeleteConfirmation(BuildContext context) {
|
||||||
|
|||||||
@@ -516,13 +516,10 @@ class _MessageTraceSheetState extends State<MessageTraceSheet> {
|
|||||||
for (final log in logs) {
|
for (final log in logs) {
|
||||||
if (log.responseCode != 0x88) continue; // pushLogRxData
|
if (log.responseCode != 0x88) continue; // pushLogRxData
|
||||||
if (log.rawData.length < 6) continue;
|
if (log.rawData.length < 6) continue;
|
||||||
final raw = log.rawData;
|
final decoded = LogRxRouteDecoder.decode(log.rawData);
|
||||||
final header = raw[3];
|
if (decoded == null) continue;
|
||||||
final payloadType = (header >> 2) & 0x0F;
|
if (decoded.payloadType != expectedPayloadType) continue;
|
||||||
final pathLen = raw[4];
|
if (decoded.hopCount != message.pathLen) continue;
|
||||||
if (payloadType != expectedPayloadType) continue;
|
|
||||||
if (pathLen != message.pathLen) continue;
|
|
||||||
if (raw.length < 5 + pathLen) continue;
|
|
||||||
|
|
||||||
final deltaMs =
|
final deltaMs =
|
||||||
(log.timestamp.difference(message.receivedAt).inMilliseconds).abs();
|
(log.timestamp.difference(message.receivedAt).inMilliseconds).abs();
|
||||||
@@ -533,9 +530,11 @@ class _MessageTraceSheetState extends State<MessageTraceSheet> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bestLog == null || bestDeltaMs > 30000) return null;
|
if (bestLog == null || bestDeltaMs > 30000) return null;
|
||||||
final raw = bestLog.rawData;
|
final decoded = LogRxRouteDecoder.decode(bestLog.rawData);
|
||||||
final pathLen = raw[4];
|
if (decoded == null || decoded.pathBytes.isEmpty) {
|
||||||
return raw.sublist(5, 5 + pathLen);
|
return null;
|
||||||
|
}
|
||||||
|
return decoded.pathBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MeshMapNode?> _matchNodesFromPathHashes({
|
List<MeshMapNode?> _matchNodesFromPathHashes({
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:meshcore_sar_app/models/contact.dart';
|
||||||
import 'package:meshcore_sar_app/models/message.dart';
|
import 'package:meshcore_sar_app/models/message.dart';
|
||||||
import 'package:meshcore_sar_app/models/message_contact_location.dart';
|
import 'package:meshcore_sar_app/models/message_contact_location.dart';
|
||||||
import 'package:meshcore_sar_app/providers/messages_provider.dart';
|
import 'package:meshcore_sar_app/providers/messages_provider.dart';
|
||||||
@@ -11,6 +12,30 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
void main() {
|
void main() {
|
||||||
TestWidgetsFlutterBinding.ensureInitialized();
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
Contact _buildContact({
|
||||||
|
required List<int> prefix,
|
||||||
|
required ContactType type,
|
||||||
|
String name = 'Test Contact',
|
||||||
|
}) {
|
||||||
|
final key = Uint8List.fromList([
|
||||||
|
...prefix,
|
||||||
|
...List<int>.generate(26, (index) => index),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return Contact(
|
||||||
|
publicKey: key,
|
||||||
|
type: type,
|
||||||
|
flags: 0,
|
||||||
|
outPathLen: 0,
|
||||||
|
outPath: Uint8List(64),
|
||||||
|
advName: name,
|
||||||
|
lastAdvert: DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||||
|
advLat: 0,
|
||||||
|
advLon: 0,
|
||||||
|
lastMod: DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
group('MessagesProvider voice detection', () {
|
group('MessagesProvider voice detection', () {
|
||||||
setUp(() {
|
setUp(() {
|
||||||
SharedPreferences.setMockInitialValues({});
|
SharedPreferences.setMockInitialValues({});
|
||||||
@@ -190,5 +215,60 @@ void main() {
|
|||||||
expect(restoredVoice?.downloaders.single.requesterKey6, '112233445566');
|
expect(restoredVoice?.downloaders.single.requesterKey6, '112233445566');
|
||||||
expect(restoredImage?.downloaders.single.requesterName, equals('Bob'));
|
expect(restoredImage?.downloaders.single.requesterName, equals('Bob'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('marks only the selected contact destination as read', () {
|
||||||
|
final provider = MessagesProvider();
|
||||||
|
final alice = _buildContact(
|
||||||
|
prefix: [0x10, 0x11, 0x12, 0x13, 0x14, 0x15],
|
||||||
|
type: ContactType.chat,
|
||||||
|
name: 'Alice',
|
||||||
|
);
|
||||||
|
final bob = _buildContact(
|
||||||
|
prefix: [0x20, 0x21, 0x22, 0x23, 0x24, 0x25],
|
||||||
|
type: ContactType.chat,
|
||||||
|
name: 'Bob',
|
||||||
|
);
|
||||||
|
|
||||||
|
provider.addMessage(
|
||||||
|
Message(
|
||||||
|
id: 'alice-incoming',
|
||||||
|
messageType: MessageType.contact,
|
||||||
|
pathLen: 1,
|
||||||
|
textType: MessageTextType.plain,
|
||||||
|
senderTimestamp: 1700000100,
|
||||||
|
text: 'Alice unread',
|
||||||
|
receivedAt: DateTime.now(),
|
||||||
|
senderPublicKeyPrefix: alice.publicKey.sublist(0, 6),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
provider.addMessage(
|
||||||
|
Message(
|
||||||
|
id: 'bob-incoming',
|
||||||
|
messageType: MessageType.contact,
|
||||||
|
pathLen: 1,
|
||||||
|
textType: MessageTextType.plain,
|
||||||
|
senderTimestamp: 1700000101,
|
||||||
|
text: 'Bob unread',
|
||||||
|
receivedAt: DateTime.now(),
|
||||||
|
senderPublicKeyPrefix: bob.publicKey.sublist(0, 6),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
provider.markDestinationAsRead(
|
||||||
|
destinationType: 'contact',
|
||||||
|
contact: alice,
|
||||||
|
);
|
||||||
|
|
||||||
|
final aliceMessage = provider.messages.firstWhere(
|
||||||
|
(message) => message.id == 'alice-incoming',
|
||||||
|
);
|
||||||
|
final bobMessage = provider.messages.firstWhere(
|
||||||
|
(message) => message.id == 'bob-incoming',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(aliceMessage.isRead, isTrue);
|
||||||
|
expect(bobMessage.isRead, isFalse);
|
||||||
|
expect(provider.unreadCount, equals(1));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,12 +24,37 @@ void main() {
|
|||||||
|
|
||||||
expect(decoded, isNotNull);
|
expect(decoded, isNotNull);
|
||||||
expect(decoded!.payloadType, 0x01);
|
expect(decoded!.payloadType, 0x01);
|
||||||
|
expect(decoded.pathDescriptor, 0x04);
|
||||||
expect(decoded.pathBytes, [0xc2, 0xba, 0x5f, 0xde]);
|
expect(decoded.pathBytes, [0xc2, 0xba, 0x5f, 0xde]);
|
||||||
expect(decoded.hashSize, 2);
|
expect(decoded.hashSize, 2);
|
||||||
expect(decoded.hopHashes, ['c2ba', '5fde']);
|
expect(decoded.hopHashes, ['c2ba', '5fde']);
|
||||||
expect(decoded.originalSenderHashHex, 'c2ba');
|
expect(decoded.originalSenderHashHex, 'c2ba');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('parses encoded descriptor with 2-byte hashes', () {
|
||||||
|
final packet = Uint8List.fromList([
|
||||||
|
0x88,
|
||||||
|
0x37,
|
||||||
|
0xae,
|
||||||
|
0x05,
|
||||||
|
0x42,
|
||||||
|
0xc2,
|
||||||
|
0xba,
|
||||||
|
0x5f,
|
||||||
|
0xde,
|
||||||
|
0x5c,
|
||||||
|
]);
|
||||||
|
|
||||||
|
final decoded = LogRxRouteDecoder.decode(packet);
|
||||||
|
|
||||||
|
expect(decoded, isNotNull);
|
||||||
|
expect(decoded!.pathDescriptor, 0x42);
|
||||||
|
expect(decoded.pathBytes, [0xc2, 0xba, 0x5f, 0xde]);
|
||||||
|
expect(decoded.hashSize, 2);
|
||||||
|
expect(decoded.hopCount, 2);
|
||||||
|
expect(decoded.hopHashes, ['c2ba', '5fde']);
|
||||||
|
});
|
||||||
|
|
||||||
test('uses preferred hash size when packet length is ambiguous', () {
|
test('uses preferred hash size when packet length is ambiguous', () {
|
||||||
final packet = Uint8List.fromList([
|
final packet = Uint8List.fromList([
|
||||||
0x88,
|
0x88,
|
||||||
|
|||||||
Reference in New Issue
Block a user