mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Add packet filter stats and hex
This commit is contained in:
147
test/services/live_traffic_summary_test.dart
Normal file
147
test/services/live_traffic_summary_test.dart
Normal file
@@ -0,0 +1,147 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:meshcore_sar_app/models/ble_packet_log.dart';
|
||||
import 'package:meshcore_sar_app/services/live_traffic_summary.dart';
|
||||
|
||||
BlePacketLog _log({
|
||||
required DateTime timestamp,
|
||||
required PacketDirection direction,
|
||||
required List<int> rawData,
|
||||
int? responseCode,
|
||||
double? snrDb,
|
||||
int? rssiDbm,
|
||||
}) {
|
||||
return BlePacketLog(
|
||||
timestamp: timestamp,
|
||||
rawData: Uint8List.fromList(rawData),
|
||||
direction: direction,
|
||||
responseCode: responseCode ?? (rawData.isEmpty ? null : rawData.first),
|
||||
logRxDataInfo: snrDb == null && rssiDbm == null
|
||||
? null
|
||||
: LogRxDataInfo(
|
||||
entropy: 0,
|
||||
isLikelyEncrypted: false,
|
||||
snrDb: snrDb,
|
||||
rssiDbm: rssiDbm,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<int> _multiHopRaw({
|
||||
required List<int> hops,
|
||||
int payloadType = 0x01,
|
||||
int hashSize = 2,
|
||||
}) {
|
||||
final hopCount = hops.length ~/ hashSize;
|
||||
final pathDescriptor = ((hashSize - 1) << 6) | hopCount;
|
||||
return [
|
||||
0x88,
|
||||
0x00,
|
||||
0x00,
|
||||
payloadType << 2,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
pathDescriptor,
|
||||
...hops,
|
||||
];
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('LiveTrafficSummary', () {
|
||||
test('uses only the rolling 60-second window', () {
|
||||
final now = DateTime(2026, 3, 12, 12, 0, 0);
|
||||
final snapshot = LiveTrafficSummary.fromLogs([
|
||||
_log(
|
||||
timestamp: now.subtract(const Duration(seconds: 61)),
|
||||
direction: PacketDirection.rx,
|
||||
rawData: [0x88, 0x00, 0x00],
|
||||
responseCode: 0x88,
|
||||
),
|
||||
_log(
|
||||
timestamp: now.subtract(const Duration(seconds: 20)),
|
||||
direction: PacketDirection.rx,
|
||||
rawData: [0x88, 0x00, 0x00],
|
||||
responseCode: 0x88,
|
||||
),
|
||||
_log(
|
||||
timestamp: now.subtract(const Duration(seconds: 10)),
|
||||
direction: PacketDirection.tx,
|
||||
rawData: [0x01, 0x02],
|
||||
responseCode: 0x88,
|
||||
),
|
||||
_log(
|
||||
timestamp: now.subtract(const Duration(seconds: 5)),
|
||||
direction: PacketDirection.rx,
|
||||
rawData: [0x01, 0x02],
|
||||
responseCode: 0x01,
|
||||
),
|
||||
], now: now);
|
||||
|
||||
expect(snapshot.totalCount, 1);
|
||||
expect(snapshot.rxCount, 1);
|
||||
expect(snapshot.txCount, 0);
|
||||
expect(snapshot.packetsPerMinute, 1);
|
||||
});
|
||||
|
||||
test('aggregates RSSI, SNR, and multi-hop route metrics', () {
|
||||
final now = DateTime(2026, 3, 12, 12, 0, 0);
|
||||
final snapshot = LiveTrafficSummary.fromLogs([
|
||||
_log(
|
||||
timestamp: now.subtract(const Duration(seconds: 30)),
|
||||
direction: PacketDirection.rx,
|
||||
rawData: _multiHopRaw(hops: [0xC0, 0x10, 0x63, 0x01, 0x68, 0xD9]),
|
||||
responseCode: 0x88,
|
||||
snrDb: 12.0,
|
||||
rssiDbm: -84,
|
||||
),
|
||||
_log(
|
||||
timestamp: now.subtract(const Duration(seconds: 15)),
|
||||
direction: PacketDirection.rx,
|
||||
rawData: _multiHopRaw(hops: [0xDE, 0xAD, 0xBE, 0xEF]),
|
||||
responseCode: 0x88,
|
||||
snrDb: 6.0,
|
||||
rssiDbm: -90,
|
||||
),
|
||||
_log(
|
||||
timestamp: now.subtract(const Duration(seconds: 5)),
|
||||
direction: PacketDirection.tx,
|
||||
rawData: [0x03, 0x04],
|
||||
responseCode: 0x88,
|
||||
),
|
||||
], now: now);
|
||||
|
||||
expect(snapshot.latestRssiDbm, -90);
|
||||
expect(snapshot.latestSnrDb, 6.0);
|
||||
expect(snapshot.avgRssiDbm, closeTo(-87.0, 0.01));
|
||||
expect(snapshot.avgSnrDb, closeTo(9.0, 0.01));
|
||||
expect(snapshot.multiHopCount, 2);
|
||||
expect(snapshot.avgHopCount, closeTo(2.5, 0.01));
|
||||
expect(snapshot.busyness, LiveTrafficBusyness.quiet);
|
||||
});
|
||||
|
||||
test('supports clearing the live view without mutating source logs', () {
|
||||
final now = DateTime(2026, 3, 12, 12, 0, 0);
|
||||
final clearAt = now.subtract(const Duration(seconds: 8));
|
||||
final snapshot = LiveTrafficSummary.fromLogs([
|
||||
_log(
|
||||
timestamp: now.subtract(const Duration(seconds: 10)),
|
||||
direction: PacketDirection.rx,
|
||||
rawData: [0x88, 0x00, 0x00],
|
||||
responseCode: 0x88,
|
||||
),
|
||||
_log(
|
||||
timestamp: now.subtract(const Duration(seconds: 4)),
|
||||
direction: PacketDirection.rx,
|
||||
rawData: [0x88, 0x00, 0x00],
|
||||
responseCode: 0x88,
|
||||
),
|
||||
], now: now, clearedAt: clearAt);
|
||||
|
||||
expect(snapshot.totalCount, 1);
|
||||
expect(snapshot.visibleEntries, hasLength(1));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'package:meshcore_sar_app/models/contact.dart';
|
||||
import 'package:meshcore_sar_app/models/path_history.dart';
|
||||
import 'package:meshcore_sar_app/models/path_selection.dart';
|
||||
import 'package:meshcore_sar_app/services/path_history_service.dart';
|
||||
|
||||
@@ -178,6 +179,31 @@ void main() {
|
||||
expect(history.directPaths.single.pathBytes, [0x03, 0x04, 0x01, 0x02]);
|
||||
expect(history.directPaths.single.hashSize, 2);
|
||||
expect(history.directPaths.single.hopCount, 2);
|
||||
expect(history.directPaths.single.source, PathRecordSource.observed);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'learned paths stay marked as observed after being seen on-air',
|
||||
() async {
|
||||
final service = PathHistoryService();
|
||||
final contact = _buildContact(
|
||||
seed: 3,
|
||||
pathBytes: [0xAA, 0xBB],
|
||||
hopCount: 2,
|
||||
hashSize: 1,
|
||||
);
|
||||
|
||||
await service.initialize();
|
||||
await service.recordReceivedBytePath(contact.publicKeyHex, [
|
||||
0xBB,
|
||||
0xAA,
|
||||
], 1);
|
||||
await service.recordLearnedPath(contact);
|
||||
|
||||
final history = service.historyFor(contact.publicKeyHex);
|
||||
expect(history.directPaths, hasLength(1));
|
||||
expect(history.directPaths.single.source, PathRecordSource.observed);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user