Got the basic path tracing working.

This commit is contained in:
Winston Lowe 2026-01-24 20:36:14 -08:00
parent 2089613696
commit fcf741b20a
3 changed files with 104 additions and 2 deletions

View file

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:ffi';
import 'dart:typed_data';
// Buffer Reader - sequential binary data reader with pointer tracking
@ -18,6 +19,10 @@ class BufferReader {
return data;
}
void skipBytes(int count) {
_pointer += count;
}
Uint8List readRemainingBytes() => readBytes(remaining);
String readString() =>

View file

@ -1,6 +1,9 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:meshcore_open/widgets/path_trace_dialog.dart';
import 'package:provider/provider.dart';
import '../connector/meshcore_connector.dart';
@ -51,11 +54,14 @@ class _ContactsScreenState extends State<ContactsScreen>
final ContactGroupStore _groupStore = ContactGroupStore();
List<ContactGroup> _groups = [];
Timer? _searchDebounce;
StreamSubscription<Uint8List>? _frameSubscription;
Uint8List _tagData = Uint8List(4);
@override
void initState() {
super.initState();
_loadGroups();
_setupFrameListener();
}
@override
@ -65,6 +71,44 @@ class _ContactsScreenState extends State<ContactsScreen>
super.dispose();
}
void _setupFrameListener() {
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
// Listen for incoming text messages from the repeater
_frameSubscription = connector.receivedFrames.listen((frame) {
if (frame.isEmpty) return;
if (frame[0] == respCodeSent) {
_tagData = frame.sublist(2, 6);
print("Stored tag data: $_tagData");
}
// Check if it's a binary response
if (frame[0] == pushCodeTraceData && listEquals(frame.sublist(4, 8), _tagData)) {
if (!mounted) return;
_handleTraceResponse(frame);
}
});
}
Future<void> _handleTraceResponse(Uint8List frame)async {
final buffer = BufferReader(frame);
buffer.skipBytes(2); // Skip push code and reserved byte
int pathLength = buffer.readUInt8();
buffer.skipBytes(5); // Skip Flag byte and tag data
buffer.skipBytes(4); // Skip auth code
Uint8List pathData = buffer.readBytes(pathLength);
Uint8List snrData = buffer.readRemainingBytes();
print("Received path data length: $pathLength, SNR data length: ${snrData.length}");
showDialog(
context: context,
builder: (context) => PathTraceDialog(
pathData: pathData,
snrData: snrData,
),
);
}
Future<void> _loadGroups() async {
final groups = await _groupStore.loadGroups();
if (!mounted) return;
@ -757,11 +801,12 @@ class _ContactsScreenState extends State<ContactsScreen>
leading: const Icon(Icons.radar, color: Colors.green),
title: Text("Ping"),
onTap: () async {
Navigator.pop(sheetContext);
final frame = buildTraceReq(
DateTime.now().millisecondsSinceEpoch ~/ 1000,
0,
0,
payload: contact.publicKey.sublist(0,1),
payload: Uint8List.fromList([0x85,0x91,0x07,0x91,0x85]) //contact.publicKey.sublist(0,1),
);
await connector.sendFrame(frame);
}

View file

@ -0,0 +1,52 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:meshcore_open/widgets/snr_indicator.dart';
class PathTraceDialog extends StatefulWidget {
const PathTraceDialog({
super.key,
required this.pathData,
required this.snrData,
});
final Uint8List pathData;
final Uint8List snrData;
@override
State<PathTraceDialog> createState() => _PathTraceDialogState();
}
class _PathTraceDialogState extends State<PathTraceDialog> {
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Path Trace'),
content: SizedBox(
width: double.maxFinite,
child: ListView.builder(
itemCount: widget.snrData.length,
itemBuilder: (context, index) {
return ListTile(
leading: index >= widget.snrData.length / 2 ? Icon(Icons.arrow_circle_left) : Icon(Icons.arrow_circle_right),
title: index == 0 || index == widget.snrData.length - 1 ? ( index == 0 ? Text('You to 0x${widget.pathData[0].toRadixString(16).toUpperCase()}') : Text('0x${widget.pathData[widget.pathData.length - 1].toRadixString(16).toUpperCase()} to You')) : Text('0x${widget.pathData[index-1].toRadixString(16).toUpperCase()} to 0x${widget.pathData[index].toRadixString(16).toUpperCase()}'),
trailing: SNRIcon(snr: widget.snrData[index] / 4.0),
onTap: () {
// Handle item tap
},
);
},
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Close'),
),
],
);
}
}