mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-08-04 06:52:59 +10:00
Refactor ranking calculation for direct repeaters and update path handling in channel message screens
This commit is contained in:
@@ -59,13 +59,12 @@ class DirectRepeater {
|
|||||||
return -1; // Stale repeaters get lowest rank
|
return -1; // Stale repeaters get lowest rank
|
||||||
}
|
}
|
||||||
// Higher SNR gets higher rank and recency within maxAgeMinutes breaks ties.
|
// Higher SNR gets higher rank and recency within maxAgeMinutes breaks ties.
|
||||||
final snrOffset = snr + 31.75;
|
|
||||||
final ageMs =
|
final ageMs =
|
||||||
DateTime.now().millisecondsSinceEpoch -
|
DateTime.now().millisecondsSinceEpoch -
|
||||||
lastUpdated.millisecondsSinceEpoch;
|
lastUpdated.millisecondsSinceEpoch;
|
||||||
final maxAgeMs = maxAgeMinutes * 60 * 1000;
|
final maxAgeMs = maxAgeMinutes * 60 * 1000;
|
||||||
final recencyScore = (maxAgeMs - ageMs).clamp(0, maxAgeMs);
|
final recencyScore = (maxAgeMs - ageMs).clamp(0, maxAgeMs);
|
||||||
return (snrOffset * 1000).round() + recencyScore;
|
return (snr * snr).round() + recencyScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isStale() {
|
bool isStale() {
|
||||||
|
|||||||
@@ -901,7 +901,8 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => ChannelMessagePathScreen(message: message),
|
builder: (context) =>
|
||||||
|
ChannelMessagePathScreen(message: message, channelMessage: true),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,18 +17,27 @@ import '../models/contact.dart';
|
|||||||
|
|
||||||
class ChannelMessagePathScreen extends StatelessWidget {
|
class ChannelMessagePathScreen extends StatelessWidget {
|
||||||
final ChannelMessage message;
|
final ChannelMessage message;
|
||||||
|
final bool channelMessage;
|
||||||
const ChannelMessagePathScreen({super.key, required this.message});
|
const ChannelMessagePathScreen({
|
||||||
|
super.key,
|
||||||
|
required this.message,
|
||||||
|
this.channelMessage = false,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<MeshCoreConnector>(
|
return Consumer<MeshCoreConnector>(
|
||||||
builder: (context, connector, _) {
|
builder: (context, connector, _) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final primaryPath = _selectPrimaryPath(
|
final primaryPathTmp = _selectPrimaryPath(
|
||||||
message.pathBytes,
|
message.pathBytes,
|
||||||
message.pathVariants,
|
message.pathVariants,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final primaryPath = !channelMessage && !message.isOutgoing
|
||||||
|
? Uint8List.fromList(primaryPathTmp.reversed.toList())
|
||||||
|
: primaryPathTmp;
|
||||||
|
|
||||||
final hops = _buildPathHops(primaryPath, connector.contacts, l10n);
|
final hops = _buildPathHops(primaryPath, connector.contacts, l10n);
|
||||||
final hasHopDetails = primaryPath.isNotEmpty;
|
final hasHopDetails = primaryPath.isNotEmpty;
|
||||||
final observedLabel = _formatObservedHops(
|
final observedLabel = _formatObservedHops(
|
||||||
@@ -37,7 +46,6 @@ class ChannelMessagePathScreen extends StatelessWidget {
|
|||||||
l10n,
|
l10n,
|
||||||
);
|
);
|
||||||
final extraPaths = _otherPaths(primaryPath, message.pathVariants);
|
final extraPaths = _otherPaths(primaryPath, message.pathVariants);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(l10n.channelPath_title),
|
title: Text(l10n.channelPath_title),
|
||||||
@@ -50,9 +58,9 @@ class ChannelMessagePathScreen extends StatelessWidget {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => PathTraceMapScreen(
|
builder: (context) => PathTraceMapScreen(
|
||||||
title: context.l10n.contacts_repeaterPathTrace,
|
title: context.l10n.contacts_repeaterPathTrace,
|
||||||
path: Uint8List.fromList(primaryPath),
|
path: primaryPath,
|
||||||
flipPathRound: true,
|
flipPathRound: true,
|
||||||
reversePathRound: true,
|
reversePathRound: !message.isOutgoing,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -62,7 +70,7 @@ class ChannelMessagePathScreen extends StatelessWidget {
|
|||||||
tooltip: l10n.channelPath_viewMap,
|
tooltip: l10n.channelPath_viewMap,
|
||||||
onPressed: hasHopDetails
|
onPressed: hasHopDetails
|
||||||
? () {
|
? () {
|
||||||
_openPathMap(context);
|
_openPathMap(context, channelMessage: channelMessage);
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
@@ -248,13 +256,18 @@ class ChannelMessagePathScreen extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _openPathMap(BuildContext context, {Uint8List? initialPath}) {
|
void _openPathMap(
|
||||||
|
BuildContext context, {
|
||||||
|
Uint8List? initialPath,
|
||||||
|
bool channelMessage = false,
|
||||||
|
}) {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => ChannelMessagePathMapScreen(
|
builder: (context) => ChannelMessagePathMapScreen(
|
||||||
message: message,
|
message: message,
|
||||||
initialPath: initialPath,
|
initialPath: initialPath,
|
||||||
|
channelMessage: channelMessage,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -264,11 +277,13 @@ class ChannelMessagePathScreen extends StatelessWidget {
|
|||||||
class ChannelMessagePathMapScreen extends StatefulWidget {
|
class ChannelMessagePathMapScreen extends StatefulWidget {
|
||||||
final ChannelMessage message;
|
final ChannelMessage message;
|
||||||
final Uint8List? initialPath;
|
final Uint8List? initialPath;
|
||||||
|
final bool channelMessage;
|
||||||
|
|
||||||
const ChannelMessagePathMapScreen({
|
const ChannelMessagePathMapScreen({
|
||||||
super.key,
|
super.key,
|
||||||
required this.message,
|
required this.message,
|
||||||
this.initialPath,
|
this.initialPath,
|
||||||
|
this.channelMessage = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -323,11 +338,16 @@ class _ChannelMessagePathMapScreenState
|
|||||||
primaryPath,
|
primaryPath,
|
||||||
widget.message.pathVariants,
|
widget.message.pathVariants,
|
||||||
);
|
);
|
||||||
final selectedPath = _resolveSelectedPath(
|
final selectedPathTmp = _resolveSelectedPath(
|
||||||
_selectedPath,
|
_selectedPath,
|
||||||
observedPaths,
|
observedPaths,
|
||||||
primaryPath,
|
primaryPath,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final selectedPath = !widget.channelMessage && widget.message.isOutgoing
|
||||||
|
? Uint8List.fromList(selectedPathTmp.reversed.toList())
|
||||||
|
: selectedPathTmp;
|
||||||
|
|
||||||
final selectedIndex = _indexForPath(selectedPath, observedPaths);
|
final selectedIndex = _indexForPath(selectedPath, observedPaths);
|
||||||
final hops = _buildPathHops(
|
final hops = _buildPathHops(
|
||||||
selectedPath,
|
selectedPath,
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
double _zoomFromStdDev(double latStdDev, double lonStdDev) {
|
double _zoomFromStdDev(double latStdDev, double lonStdDev) {
|
||||||
final maxSpread = max(latStdDev, lonStdDev);
|
final maxSpread = max(latStdDev, lonStdDev);
|
||||||
if (maxSpread <= 0) return 13.0;
|
if (maxSpread <= 0) return 13.0;
|
||||||
// Approzimate: each zoom level halves the visible area
|
// Approximate: each zoom level halves the visible area
|
||||||
// ~0.01 degrees spread -> zoom 13, ~0.1 -> zoom 10, ~1.0 -> zoom 7
|
// ~0.01 degrees spread -> zoom 13, ~0.1 -> zoom 10, ~1.0 -> zoom 7
|
||||||
final zoom = 10.0 - log(maxSpread * 10 + 1) / ln10 * 3;
|
final zoom = 10.0 - log(maxSpread * 10 + 1) / ln10 * 3;
|
||||||
return zoom.clamp(4.0, 15.0);
|
return zoom.clamp(4.0, 15.0);
|
||||||
@@ -825,7 +825,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
color: _getNodeColor(contact.type),
|
color: _getNodeColor(contact.type),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(child: Text(contact.name)),
|
Expanded(child: SelectableText(contact.name)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
content: Column(
|
content: Column(
|
||||||
@@ -996,7 +996,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Text(value, style: const TextStyle(fontSize: 14)),
|
SelectableText(value, style: const TextStyle(fontSize: 14)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class PathTraceData {
|
|||||||
class PathTraceMapScreen extends StatefulWidget {
|
class PathTraceMapScreen extends StatefulWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final Uint8List path;
|
final Uint8List path;
|
||||||
|
final int? repeaterId;
|
||||||
final bool flipPathRound;
|
final bool flipPathRound;
|
||||||
final bool reversePathRound;
|
final bool reversePathRound;
|
||||||
|
|
||||||
@@ -53,6 +54,7 @@ class PathTraceMapScreen extends StatefulWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.path,
|
required this.path,
|
||||||
|
this.repeaterId,
|
||||||
this.flipPathRound = false,
|
this.flipPathRound = false,
|
||||||
this.reversePathRound = false,
|
this.reversePathRound = false,
|
||||||
});
|
});
|
||||||
@@ -97,7 +99,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List addReturnpath(Uint8List pathBytes) {
|
Uint8List addReturnPath(Uint8List pathBytes) {
|
||||||
Uint8List? traceBytes;
|
Uint8List? traceBytes;
|
||||||
final len = (pathBytes.length + pathBytes.length - 1);
|
final len = (pathBytes.length + pathBytes.length - 1);
|
||||||
traceBytes = Uint8List(len);
|
traceBytes = Uint8List(len);
|
||||||
@@ -125,11 +127,13 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
: widget.path;
|
: widget.path;
|
||||||
|
|
||||||
if (widget.flipPathRound) {
|
if (widget.flipPathRound) {
|
||||||
path = addReturnpath(pathTmp);
|
path = addReturnPath(pathTmp);
|
||||||
} else {
|
} else {
|
||||||
path = pathTmp;
|
path = pathTmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print('Initiating path trace with path: ${_formatPathPrefixes(path)}');
|
||||||
|
|
||||||
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
|
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
|
||||||
final frame = buildTraceReq(
|
final frame = buildTraceReq(
|
||||||
DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||||
|
|||||||
Reference in New Issue
Block a user