From 28a5913ba32f61a93a6c29790435a3c40ff794f6 Mon Sep 17 00:00:00 2001 From: ericz Date: Mon, 18 May 2026 19:00:07 +0200 Subject: [PATCH 1/5] multibyte for snr_indicator and map_filtering. --- lib/connector/meshcore_connector.dart | 54 ++++++++++++++++++++----- lib/helpers/path_helper.dart | 28 ++++++------- lib/screens/chat_screen.dart | 18 ++++++--- lib/screens/contacts_screen.dart | 14 ++++++- lib/screens/map_screen.dart | 17 +++++++- lib/widgets/path_management_dialog.dart | 18 ++++++--- lib/widgets/snr_indicator.dart | 25 +++++++----- 7 files changed, 121 insertions(+), 53 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index d341feeb..f2d8040c 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -49,15 +49,33 @@ import 'meshcore_protocol.dart'; class DirectRepeater { static const int maxAgeMinutes = 30; // Max age for direct repeater info - final int pubkeyFirstByte; + final List pubkeyPrefix; double snr; DateTime lastUpdated; DirectRepeater({ - required this.pubkeyFirstByte, + required List pubkeyPrefix, required this.snr, DateTime? lastUpdated, - }) : lastUpdated = lastUpdated ?? DateTime.now(); + }) : pubkeyPrefix = List.unmodifiable(pubkeyPrefix), + lastUpdated = lastUpdated ?? DateTime.now(); + + String get pubkeyPrefixHex => pubkeyPrefix + .map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase()) + .join(); + + bool matchesPrefix(List prefix) { + return pubkeyPrefix.length == prefix.length && + listEquals(pubkeyPrefix, prefix); + } + + bool matchesPathStart(List pathBytes, int hashByteWidth) { + final width = hashByteWidth.clamp(1, 4).toInt(); + if (pathBytes.length < width || pubkeyPrefix.length != width) { + return false; + } + return listEquals(pubkeyPrefix, pathBytes.sublist(0, width)); + } void update(double newSNR) { snr = newSNR; @@ -3853,12 +3871,16 @@ class MeshCoreConnector extends ChangeNotifier { _clientRepeat = frame[80] != 0; } // Path hash mode v10+ (byte 81): width = mode + 1 byte(s) per hop + final previousPathHashByteWidth = _pathHashByteWidth; if (frame.length >= 82) { final mode = (frame[81] & 0xFF).clamp(0, 3); _pathHashByteWidth = mode + 1; } else { _pathHashByteWidth = 1; } + if (_pathHashByteWidth != previousPathHashByteWidth) { + _directRepeaters.clear(); + } // Firmware reports MAX_CONTACTS / 2 for v3+ device info. final reportedContacts = frame[2]; @@ -4298,7 +4320,11 @@ class MeshCoreConnector extends ChangeNotifier { for (var i = 0; i < _contacts.length; i++) { final contact = _contacts[i]; if (contact.type != advTypeChat) continue; - if (_pathMatchesContact(pathBytes, contact.publicKey, pathHashWidth: pathHashWidth)) { + if (_pathMatchesContact( + pathBytes, + contact.publicKey, + pathHashWidth: pathHashWidth, + )) { matches.add(i); } } @@ -4315,7 +4341,11 @@ class MeshCoreConnector extends ChangeNotifier { } } - bool _pathMatchesContact(Uint8List pathBytes, Uint8List publicKey, {int? pathHashWidth}) { + bool _pathMatchesContact( + Uint8List pathBytes, + Uint8List publicKey, { + int? pathHashWidth, + }) { final w = pathHashWidth ?? _pathHashByteWidth; if (pathBytes.isEmpty || publicKey.length < w) return false; for (int i = 0; i + w <= pathBytes.length; i += w) { @@ -6170,9 +6200,13 @@ class MeshCoreConnector extends ChangeNotifier { } void _updateDirectRepeater(Contact contact, double snr, Uint8List path) { - final pubkeyFirstByte = path.isNotEmpty - ? path.last - : contact.publicKey.first; + final hashWidth = _pathHashByteWidth.clamp(1, 4).toInt(); + final pubkeyPrefix = path.isNotEmpty + ? path.sublist(math.max(0, path.length - hashWidth)) + : contact.publicKey.sublist( + 0, + math.min(hashWidth, contact.publicKey.length), + ); _directRepeaters.removeWhere((r) => r.isStale()); @@ -6184,7 +6218,7 @@ class MeshCoreConnector extends ChangeNotifier { } final isTracked = _directRepeaters.where( - (r) => r.pubkeyFirstByte == pubkeyFirstByte, + (r) => r.matchesPrefix(pubkeyPrefix), ); final sortedRepeaters = List.from(_directRepeaters) @@ -6204,7 +6238,7 @@ class MeshCoreConnector extends ChangeNotifier { repeater.update(snr); } else if (_directRepeaters.length < 5) { _directRepeaters.add( - DirectRepeater(pubkeyFirstByte: pubkeyFirstByte, snr: snr), + DirectRepeater(pubkeyPrefix: pubkeyPrefix, snr: snr), ); } notifyListeners(); diff --git a/lib/helpers/path_helper.dart b/lib/helpers/path_helper.dart index 3109de90..1971060c 100644 --- a/lib/helpers/path_helper.dart +++ b/lib/helpers/path_helper.dart @@ -1,5 +1,3 @@ -import 'dart:typed_data'; - import 'package:flutter/foundation.dart'; import '../models/contact.dart'; import '../connector/meshcore_protocol.dart'; @@ -17,7 +15,10 @@ class PathHelper { .join(); } - static List splitPathBytes(List pathBytes, int hashByteWidth) { + static List splitPathBytes( + List pathBytes, + int hashByteWidth, + ) { if (pathBytes.isEmpty) return const []; final width = hashByteWidth.clamp(1, 4); @@ -51,18 +52,13 @@ class PathHelper { final hex = formatHopHex(hopBytes); // Find matching contacts by comparing public key prefix - final matches = allContacts - .where((c) { - if (c.publicKey.length < hopBytes.length) return false; - if (c.type != advTypeRepeater && c.type != advTypeRoom) return false; - // Compare bytes using listEquals for multi-byte support - return listEquals( - c.publicKey.sublist(0, hopBytes.length), - hopBytes, - ); - }) - .toList(); - + final matches = allContacts.where((c) { + if (c.publicKey.length < hopBytes.length) return false; + if (c.type != advTypeRepeater && c.type != advTypeRoom) return false; + // Compare bytes using listEquals for multi-byte support + return listEquals(c.publicKey.sublist(0, hopBytes.length), hopBytes); + }).toList(); + if (matches.isEmpty) { parts.add(hex); } else if (matches.length == 1) { @@ -71,7 +67,7 @@ class PathHelper { parts.add(matches.map((c) => c.name).join(' | ')); } } - + return parts.join(' \u2192 '); } } diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart index f6a0c1c5..00e0c7d2 100644 --- a/lib/screens/chat_screen.dart +++ b/lib/screens/chat_screen.dart @@ -805,16 +805,22 @@ class _ChatScreenState extends State { pathsWithRepeaters = paths.map((path) { final isDirectRepeater = directRepeater != null && - path.pathBytes.isNotEmpty && - directRepeater.pubkeyFirstByte == path.pathBytes.first; + directRepeater.matchesPathStart( + path.pathBytes, + connector.pathHashByteWidth, + ); final isSecondDirectRepeater = secondDirectRepeater != null && - path.pathBytes.isNotEmpty && - secondDirectRepeater.pubkeyFirstByte == path.pathBytes.first; + secondDirectRepeater.matchesPathStart( + path.pathBytes, + connector.pathHashByteWidth, + ); final isThirdDirectRepeater = thirdDirectRepeater != null && - path.pathBytes.isNotEmpty && - thirdDirectRepeater.pubkeyFirstByte == path.pathBytes.first; + thirdDirectRepeater.matchesPathStart( + path.pathBytes, + connector.pathHashByteWidth, + ); int ranking = -1; Color color = Colors.grey; diff --git a/lib/screens/contacts_screen.dart b/lib/screens/contacts_screen.dart index a4cc35ca..d320df11 100644 --- a/lib/screens/contacts_screen.dart +++ b/lib/screens/contacts_screen.dart @@ -1266,7 +1266,7 @@ class _ContactsScreenState extends State MaterialPageRoute( builder: (context) => PathTraceMapScreen( title: context.l10n.contacts_repeaterPing, - path: Uint8List.fromList([contact.publicKey.first]), + path: _contactPathPrefix(contact, hw), targetContact: contact, pathHashByteWidth: hw, ), @@ -1299,7 +1299,7 @@ class _ContactsScreenState extends State : context.l10n.contacts_roomPing, path: contact.pathBytesForDisplay.isNotEmpty ? contact.pathBytesForDisplay - : Uint8List.fromList([contact.publicKey.first]), + : _contactPathPrefix(contact, hw), flipPathAround: contact.pathBytesForDisplay.isNotEmpty, targetContact: contact, pathHashByteWidth: hw, @@ -1416,6 +1416,16 @@ class _ContactsScreenState extends State ); } + Uint8List _contactPathPrefix(Contact contact, int hashByteWidth) { + if (contact.publicKey.isEmpty) return Uint8List(0); + final width = hashByteWidth + .clamp(1, pubKeySize) + .toInt() + .clamp(1, contact.publicKey.length) + .toInt(); + return Uint8List.fromList(contact.publicKey.sublist(0, width)); + } + void _confirmDelete( BuildContext context, MeshCoreConnector connector, diff --git a/lib/screens/map_screen.dart b/lib/screens/map_screen.dart index f9581f91..d11b8ab8 100644 --- a/lib/screens/map_screen.dart +++ b/lib/screens/map_screen.dart @@ -728,7 +728,10 @@ class _MapScreenState extends State { for (final repeater in withLocation) { if (repeater.type != advTypeRepeater) continue; if (repeater.publicKey.length < lastHop.length) continue; - if (!listEquals(repeater.publicKey.sublist(0, lastHop.length), lastHop)) { + if (!listEquals( + repeater.publicKey.sublist(0, lastHop.length), + lastHop, + )) { continue; } anchorSet.add(LatLng(repeater.latitude!, repeater.longitude!)); @@ -986,11 +989,21 @@ class _MapScreenState extends State { } if (settings.mapShowOverlaps) { + final hopWidth = context + .read() + .pathHashByteWidth + .clamp(1, pubKeySize) + .toInt(); final hasOverlap = contacts .where( (c) => c.publicKeyHex != contact.publicKeyHex && - c.publicKey.first == contact.publicKey.first && + c.publicKey.length >= hopWidth && + contact.publicKey.length >= hopWidth && + listEquals( + c.publicKey.sublist(0, hopWidth), + contact.publicKey.sublist(0, hopWidth), + ) && (c.type == advTypeRepeater || c.type == advTypeRoom) && (contact.type == advTypeRepeater || contact.type == advTypeRoom), diff --git a/lib/widgets/path_management_dialog.dart b/lib/widgets/path_management_dialog.dart index aa877369..a110b176 100644 --- a/lib/widgets/path_management_dialog.dart +++ b/lib/widgets/path_management_dialog.dart @@ -202,16 +202,22 @@ class _PathManagementDialogState extends State<_PathManagementDialog> { paths.map((path) { final isDirectRepeater = directRepeater != null && - path.pathBytes.isNotEmpty && - directRepeater.pubkeyFirstByte == path.pathBytes.first; + directRepeater.matchesPathStart( + path.pathBytes, + connector.pathHashByteWidth, + ); final isSecondDirectRepeater = secondDirectRepeater != null && - path.pathBytes.isNotEmpty && - secondDirectRepeater.pubkeyFirstByte == path.pathBytes.first; + secondDirectRepeater.matchesPathStart( + path.pathBytes, + connector.pathHashByteWidth, + ); final isThirdDirectRepeater = thirdDirectRepeater != null && - path.pathBytes.isNotEmpty && - thirdDirectRepeater.pubkeyFirstByte == path.pathBytes.first; + thirdDirectRepeater.matchesPathStart( + path.pathBytes, + connector.pathHashByteWidth, + ); int ranking = -1; Color color = Colors.grey; diff --git a/lib/widgets/snr_indicator.dart b/lib/widgets/snr_indicator.dart index 99f20539..4850393f 100644 --- a/lib/widgets/snr_indicator.dart +++ b/lib/widgets/snr_indicator.dart @@ -1,23 +1,28 @@ import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; import 'package:latlong2/latlong.dart'; import '../connector/meshcore_connector.dart'; import '../connector/meshcore_protocol.dart'; +import '../helpers/path_helper.dart'; import '../l10n/l10n.dart'; import '../models/contact.dart'; import 'signal_ui.dart'; Contact? _getRepeaterPrefixMatchNearLocation( List contacts, - int pubkeyFirstByte, { + List pubkeyPrefix, { LatLng? searchPoint, bool preferFavorites = false, }) { final candidates = contacts .where( (c) => - c.publicKey.isNotEmpty && - c.publicKey.first == pubkeyFirstByte && + c.publicKey.length >= pubkeyPrefix.length && + listEquals( + c.publicKey.sublist(0, pubkeyPrefix.length), + pubkeyPrefix, + ) && (c.type == advTypeRepeater || c.type == advTypeRoom), ) .toList(); @@ -162,7 +167,7 @@ class _SNRIndicatorState extends State { ), if (directRepeater != null) Text( - '${directRepeaters.length}: ${directRepeater.pubkeyFirstByte.toRadixString(16).padLeft(2, '0')}: ${_formatLastUpdated(directRepeater.lastUpdated)}', + '${directRepeaters.length}: ${directRepeater.pubkeyPrefixHex}: ${_formatLastUpdated(directRepeater.lastUpdated)}', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, @@ -236,23 +241,21 @@ class _SNRIndicatorState extends State { final contact = _getRepeaterPrefixMatchNearLocation( allContacts, - repeater.pubkeyFirstByte, + repeater.pubkeyPrefix, searchPoint: selfPoint, preferFavorites: true, ); final name = contact?.name; + final prefixLabel = PathHelper.formatHopHex( + repeater.pubkeyPrefix, + ); return Column( children: [ ListTile( leading: Icon(snrUi.icon, color: snrUi.color), - title: Text( - name ?? - repeater.pubkeyFirstByte - .toRadixString(16) - .padLeft(2, '0'), - ), + title: Text(name ?? prefixLabel), subtitle: Text( 'SNR: ${repeater.snr.toStringAsFixed(1)} dB\n${l10n.snrIndicator_lastSeen}: ${_formatLastUpdated(repeater.lastUpdated)}', ), From 73e5b80232bdb8eda7b99e614b35ddd4a5288e54 Mon Sep 17 00:00:00 2001 From: ericz Date: Mon, 18 May 2026 19:17:53 +0200 Subject: [PATCH 2/5] add HashMode Setting --- lib/connector/meshcore_connector.dart | 9 ++- lib/screens/settings_screen.dart | 84 +++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index f2d8040c..59ef4e6f 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -2571,7 +2571,14 @@ class MeshCoreConnector extends ChangeNotifier { Future setPathHashMode(int mode) async { if (!isConnected) return; - await sendFrame(buildSetPathHashModeFrame(mode.clamp(0, 2))); + final clampedMode = mode.clamp(0, 2).toInt(); + await sendFrame(buildSetPathHashModeFrame(clampedMode)); + final nextWidth = clampedMode + 1; + if (_pathHashByteWidth != nextWidth) { + _pathHashByteWidth = nextWidth; + _directRepeaters.clear(); + notifyListeners(); + } } Future refreshDeviceInfo() async { diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 5679f757..3dd8c812 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -298,6 +298,15 @@ class _SettingsScreenState extends State { onTap: () => pushCompanionRadioStatsScreen(context), ), const Divider(height: 1), + ListTile( + leading: const Icon(Icons.route_outlined), + title: Text(l10n.repeater_pathHashMode), + subtitle: Text(_pathHashModeSubtitle(connector.pathHashByteWidth)), + trailing: const Icon(Icons.chevron_right), + enabled: connector.isConnected, + onTap: () => _editPathHashMode(context, connector), + ), + const Divider(height: 1), ListTile( leading: const Icon(Icons.location_on_outlined), title: Text(l10n.settings_location), @@ -338,6 +347,13 @@ class _SettingsScreenState extends State { ); } + String _pathHashModeSubtitle(int pathHashByteWidth) { + final width = pathHashByteWidth.clamp(1, 3).toInt(); + final mode = width - 1; + final unit = width == 1 ? 'byte' : 'bytes'; + return 'Mode $mode - $width $unit per hop'; + } + Widget _buildActionsCard(BuildContext context, MeshCoreConnector connector) { final l10n = context.l10n; return Card( @@ -545,6 +561,74 @@ class _SettingsScreenState extends State { ); } + void _editPathHashMode(BuildContext context, MeshCoreConnector connector) { + final l10n = context.l10n; + var selectedMode = (connector.pathHashByteWidth - 1).clamp(0, 2).toInt(); + + showDialog( + context: context, + builder: (dialogContext) => StatefulBuilder( + builder: (context, setDialogState) => AlertDialog( + title: Text(l10n.repeater_pathHashMode), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DropdownButtonFormField( + initialValue: selectedMode, + decoration: InputDecoration( + labelText: l10n.repeater_pathHashMode, + border: const OutlineInputBorder(), + ), + items: const [ + DropdownMenuItem(value: 0, child: Text('0 - 1 byte')), + DropdownMenuItem(value: 1, child: Text('1 - 2 bytes')), + DropdownMenuItem(value: 2, child: Text('2 - 3 bytes')), + ], + onChanged: (value) { + if (value == null) return; + setDialogState(() => selectedMode = value); + }, + ), + const SizedBox(height: 12), + Text( + l10n.repeater_pathHashModeHelper, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(dialogContext), + child: Text(l10n.common_cancel), + ), + FilledButton( + onPressed: () async { + Navigator.pop(dialogContext); + try { + await connector.setPathHashMode(selectedMode); + await connector.refreshDeviceInfo(); + if (!context.mounted) return; + showDismissibleSnackBar( + context, + content: Text(l10n.repeater_settingsSaved), + ); + } catch (e) { + if (!context.mounted) return; + showDismissibleSnackBar( + context, + content: Text(l10n.settings_error(e.toString())), + ); + } + }, + child: Text(l10n.common_save), + ), + ], + ), + ), + ); + } + void _editLocation(BuildContext context, MeshCoreConnector connector) { final l10n = context.l10n; final latController = TextEditingController(); From 87708e268483a2702dba273ed17acb04a0d4b5d8 Mon Sep 17 00:00:00 2001 From: ericz Date: Mon, 18 May 2026 20:42:40 +0200 Subject: [PATCH 3/5] fixes direct repeater mixing 1 byte paths with 2 byte Indicator, and wrong description. --- lib/connector/meshcore_connector.dart | 51 ++++++++++++++++++++++----- lib/l10n/app_bg.arb | 2 +- lib/l10n/app_de.arb | 2 +- lib/l10n/app_en.arb | 2 +- lib/l10n/app_es.arb | 2 +- lib/l10n/app_fr.arb | 2 +- lib/l10n/app_hu.arb | 2 +- lib/l10n/app_it.arb | 2 +- lib/l10n/app_ja.arb | 2 +- lib/l10n/app_ko.arb | 2 +- lib/l10n/app_localizations.dart | 2 +- lib/l10n/app_localizations_bg.dart | 2 +- lib/l10n/app_localizations_de.dart | 2 +- lib/l10n/app_localizations_en.dart | 2 +- lib/l10n/app_localizations_es.dart | 2 +- lib/l10n/app_localizations_fr.dart | 2 +- lib/l10n/app_localizations_hu.dart | 2 +- lib/l10n/app_localizations_it.dart | 2 +- lib/l10n/app_localizations_ja.dart | 2 +- lib/l10n/app_localizations_ko.dart | 2 +- lib/l10n/app_localizations_nl.dart | 2 +- lib/l10n/app_localizations_pl.dart | 2 +- lib/l10n/app_localizations_pt.dart | 2 +- lib/l10n/app_localizations_ru.dart | 2 +- lib/l10n/app_localizations_sk.dart | 2 +- lib/l10n/app_localizations_sl.dart | 2 +- lib/l10n/app_localizations_sv.dart | 2 +- lib/l10n/app_localizations_uk.dart | 2 +- lib/l10n/app_localizations_zh.dart | 2 +- lib/l10n/app_nl.arb | 2 +- lib/l10n/app_pl.arb | 2 +- lib/l10n/app_pt.arb | 2 +- lib/l10n/app_ru.arb | 2 +- lib/l10n/app_sk.arb | 2 +- lib/l10n/app_sl.arb | 2 +- lib/l10n/app_sv.arb | 2 +- lib/l10n/app_uk.arb | 2 +- lib/l10n/app_zh.arb | 2 +- lib/screens/channel_chat_screen.dart | 18 +++++++--- 39 files changed, 92 insertions(+), 51 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 59ef4e6f..ed7d6f12 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -50,11 +50,13 @@ import 'meshcore_protocol.dart'; class DirectRepeater { static const int maxAgeMinutes = 30; // Max age for direct repeater info final List pubkeyPrefix; + final int pathHashWidth; double snr; DateTime lastUpdated; DirectRepeater({ required List pubkeyPrefix, + required this.pathHashWidth, required this.snr, DateTime? lastUpdated, }) : pubkeyPrefix = List.unmodifiable(pubkeyPrefix), @@ -69,8 +71,8 @@ class DirectRepeater { listEquals(pubkeyPrefix, prefix); } - bool matchesPathStart(List pathBytes, int hashByteWidth) { - final width = hashByteWidth.clamp(1, 4).toInt(); + bool matchesPathStart(List pathBytes, int pathHashByteWidth) { + final width = pathHashByteWidth.clamp(1, 4).toInt(); if (pathBytes.length < width || pubkeyPrefix.length != width) { return false; } @@ -5566,6 +5568,7 @@ class MeshCoreConnector extends ChangeNotifier { repeats: message.repeats, repeatCount: message.repeatCount, pathLength: message.pathLength, + pathHashWidth: message.pathHashWidth, pathBytes: message.pathBytes, pathVariants: message.pathVariants, channelIndex: message.channelIndex, @@ -5602,6 +5605,7 @@ class MeshCoreConnector extends ChangeNotifier { messages[existingIndex] = existing.copyWith( repeatCount: newRepeatCount, pathLength: mergedPathLength, + pathHashWidth: existing.pathHashWidth ?? processedMessage.pathHashWidth, pathBytes: mergedPathBytes, pathVariants: mergedPathVariants, packetHash: existing.packetHash ?? processedMessage.packetHash, @@ -5966,6 +5970,7 @@ class MeshCoreConnector extends ChangeNotifier { //final payloadVer = (header >> 6) & 0x03; final pathLenRaw = packet.readByte(); final pathByteLen = _decodePathByteLen(pathLenRaw); + final pathHashWidth = _decodePathHashWidth(pathLenRaw); final pathBytes = packet.readBytes(pathByteLen); final payload = packet.readBytes(packet.remaining); @@ -5976,6 +5981,7 @@ class MeshCoreConnector extends ChangeNotifier { rawPacket, payload, pathBytes, + pathHashWidth, routeType, snr, ); @@ -6079,6 +6085,7 @@ class MeshCoreConnector extends ChangeNotifier { Uint8List rawPacket, Uint8List payload, Uint8List path, + int pathHashWidth, int routeType, double snr, ) { @@ -6159,7 +6166,12 @@ class MeshCoreConnector extends ChangeNotifier { } else { _handleDiscovery(newContact, rawPacket); } - _updateDirectRepeater(newContact, snr, path); + _updateDirectRepeater( + newContact, + snr, + path, + pathHashWidth: pathHashWidth, + ); return; } @@ -6197,7 +6209,12 @@ class MeshCoreConnector extends ChangeNotifier { _pathHistoryService!.handlePathUpdated(_contacts[existingIndex]); } - _updateDirectRepeater(_contacts[existingIndex], snr, path); + _updateDirectRepeater( + _contacts[existingIndex], + snr, + path, + pathHashWidth: pathHashWidth, + ); appLogger.info( 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_contacts[existingIndex].pathLength}', @@ -6206,8 +6223,16 @@ class MeshCoreConnector extends ChangeNotifier { } } - void _updateDirectRepeater(Contact contact, double snr, Uint8List path) { - final hashWidth = _pathHashByteWidth.clamp(1, 4).toInt(); + void _updateDirectRepeater( + Contact contact, + double snr, + Uint8List path, { + required int pathHashWidth, + }) { + final hashWidth = pathHashWidth.clamp(1, 4).toInt(); + if (path.isNotEmpty && path.length < hashWidth) { + return; + } final pubkeyPrefix = path.isNotEmpty ? path.sublist(math.max(0, path.length - hashWidth)) : contact.publicKey.sublist( @@ -6225,7 +6250,7 @@ class MeshCoreConnector extends ChangeNotifier { } final isTracked = _directRepeaters.where( - (r) => r.matchesPrefix(pubkeyPrefix), + (r) => r.pathHashWidth == hashWidth && r.matchesPrefix(pubkeyPrefix), ); final sortedRepeaters = List.from(_directRepeaters) @@ -6245,7 +6270,11 @@ class MeshCoreConnector extends ChangeNotifier { repeater.update(snr); } else if (_directRepeaters.length < 5) { _directRepeaters.add( - DirectRepeater(pubkeyPrefix: pubkeyPrefix, snr: snr), + DirectRepeater( + pubkeyPrefix: pubkeyPrefix, + pathHashWidth: hashWidth, + snr: snr, + ), ); } notifyListeners(); @@ -6376,10 +6405,14 @@ const int _cipherMacSize = 2; /// Bits 0-5: hash count (0-63), Bits 6-7: hash size code (0=1byte, 1=2bytes, 2=3bytes). int _decodePathByteLen(int pathLenRaw) { final hashCount = pathLenRaw & 63; - final hashSize = ((pathLenRaw >> 6) & 0x03) + 1; + final hashSize = _decodePathHashWidth(pathLenRaw); return hashCount * hashSize; } +int _decodePathHashWidth(int pathLenRaw) { + return ((pathLenRaw >> 6) & 0x03) + 1; +} + class _RawPacket { final int header; final int routeType; diff --git a/lib/l10n/app_bg.arb b/lib/l10n/app_bg.arb index 45536cf6..7ea19982 100644 --- a/lib/l10n/app_bg.arb +++ b/lib/l10n/app_bg.arb @@ -1105,7 +1105,7 @@ "repeater_advancedSettings": "Напреднал", "repeater_advancedSettingsSubtitle": "Регулаторни копчета за опитни оператори", "repeater_pathHashMode": "Режим за хеширане на пътища", - "repeater_pathHashModeHelper": "Байтовете, използвани за кодиране на идентификатора на този репитер в таговете за откриване на потоци/цикли, са: 0=1 байт (256 идентификатора, до 64 скача), 1=2 байта (65 000 идентификатора, до 32 скача), 2=3 байта (16 милиона идентификатора, до 21 скача). Версии 1.13 и по-стари версии на фърмуера използват многобайтови пътища – само след като мрежата е актуализирана до версия 1.14 или по-нова.", + "repeater_pathHashModeHelper": "Байтовете, използвани за кодиране на идентификатора на този репитер в таговете за flood път/откриване на цикли. 0=1 байт (256 идентификатора, до 64 скока), 1=2 байта (65 000 идентификатора, до 32 скока), 2=3 байта (16 милиона идентификатора, до 21 скока). Фърмуер преди v1.14 винаги използва 1-байтови пътища; v1.14 и по-нови версии могат да се конфигурират за 2- или 3-байтови пътища.", "repeater_txDelay": "Забавяне на проекта \"Flood TX\"", "repeater_txDelayHelper": "Предавайте разстоянието между пакетите за трафик при наводнения, като множител на времето за пренос на пакета (0-2, по подразбиране 0.5). По-висока стойност означава по-малко сблъсъци, но по-бавно предаване.", "repeater_directTxDelay": "Директно забавяне на сигнала", diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 7f491d4f..fd9ab886 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -1105,7 +1105,7 @@ "repeater_advancedSettings": "Fortgeschritten", "repeater_advancedSettingsSubtitle": "Regler für erfahrene Bediener", "repeater_pathHashMode": "Hash-Modus für Pfade", - "repeater_pathHashModeHelper": "Bytes, die zur Kodierung der ID dieses Repeaters in Flood-Pfad-/Schleifen-Erkennung-Tags verwendet werden. 0 = 1 Byte (256 IDs, bis zu 64 Hops), 1 = 2 Bytes (65.000 IDs, bis zu 32 Hops), 2 = 3 Bytes (16 Millionen IDs, bis zu 21 Hops). Firmware-Versionen 1.13 und älter verwenden mehrstellige Pfade – ab Version 1.14+ wird nur ein Pfad erstellt, sobald das Netzwerk aktiv ist.", + "repeater_pathHashModeHelper": "Bytes, die zur Kodierung der ID dieses Repeaters in Flood-Pfad-/Schleifen-Erkennung-Tags verwendet werden. 0 = 1 Byte (256 IDs, bis zu 64 Hops), 1 = 2 Bytes (65.000 IDs, bis zu 32 Hops), 2 = 3 Bytes (16 Millionen IDs, bis zu 21 Hops). Firmware vor v1.14 verwendet immer 1-Byte-Pfade; ab v1.14 können 2- oder 3-Byte-Pfade eingestellt werden.", "repeater_txDelay": "Verzögerung bei Flood TX", "repeater_txDelayHelper": "Wiederholung des Abstands für Hochwasser-Verkehr, als Multiplikator der Übertragungszeit des Pakets (0-2, Standardwert 0,5). Höherer Wert = weniger Kollisionen, aber langsamere Übertragung.", "repeater_directTxDelay": "Direkter TX-Verzögerung", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 456fab56..c030fa41 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1361,7 +1361,7 @@ "repeater_advancedSettings": "Advanced", "repeater_advancedSettingsSubtitle": "Tuning knobs for experienced operators", "repeater_pathHashMode": "Path hash mode", - "repeater_pathHashModeHelper": "Bytes used to encode this repeater's ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). v1.13 and older firmware drops multi-byte paths — only raise once your network is on v1.14+.", + "repeater_pathHashModeHelper": "Bytes used to encode this repeater's ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). Firmware before v1.14 always used 1-byte paths; v1.14 and newer can be configured for 2- or 3-byte paths.", "repeater_txDelay": "Flood TX delay", "repeater_txDelayHelper": "Retransmit spacing for flood traffic, as a multiplier of the packet's airtime (0-2, default 0.5). Higher = fewer collisions but slower delivery.", "repeater_directTxDelay": "Direct TX delay", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index d4cf27da..697cefa7 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -1105,7 +1105,7 @@ "repeater_advancedSettings": "Avanzado", "repeater_advancedSettingsSubtitle": "Perillas de ajuste para operadores experimentados", "repeater_pathHashMode": "Modo de hash de ruta", - "repeater_pathHashModeHelper": "Bytes utilizados para codificar el ID de este repetidor en las etiquetas de ruta/detección de bucles. 0=1 byte (256 IDs, hasta 64 saltos), 1=2 bytes (65.000 IDs, hasta 32 saltos), 2=3 bytes (16 millones de IDs, hasta 21 saltos). Las versiones 1.13 y anteriores de firmware eliminan rutas de múltiples bytes; solo se detectan una vez que la red está activa en la versión 1.14 o posterior.", + "repeater_pathHashModeHelper": "Bytes utilizados para codificar el ID de este repetidor en las etiquetas de ruta flood/detección de bucles. 0=1 byte (256 IDs, hasta 64 saltos), 1=2 bytes (65.000 IDs, hasta 32 saltos), 2=3 bytes (16 millones de IDs, hasta 21 saltos). El firmware anterior a v1.14 siempre usaba rutas de 1 byte; v1.14 y posteriores pueden configurarse para rutas de 2 o 3 bytes.", "repeater_txDelay": "Retraso en Flood, TX", "repeater_txDelayHelper": "Ajuste de retransmisión para el tráfico de inundación, como un multiplicador del tiempo de transmisión del paquete (0-2, valor predeterminado 0.5). Un valor más alto significa menos colisiones, pero una entrega más lenta.", "repeater_directTxDelay": "Retraso directo en TX", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 7817c20d..5bc12755 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -1105,7 +1105,7 @@ "repeater_advancedSettings": "Avancé", "repeater_advancedSettingsSubtitle": "Molettes de réglage pour les opérateurs expérimentés", "repeater_pathHashMode": "Mode de hachage de chemin", - "repeater_pathHashModeHelper": "Octets utilisés pour encoder l'ID de ce routeur dans les balises de détection de flux/boucles. 0 = 1 octet (256 ID, jusqu'à 64 sauts), 1 = 2 octets (65 000 ID, jusqu'à 32 sauts), 2 = 3 octets (16 millions d'ID, jusqu'à 21 sauts). Les versions 1.13 et antérieures utilisent des chemins multi-octets ; à partir de la version 1.14, cela n'est plus nécessaire.", + "repeater_pathHashModeHelper": "Octets utilisés pour encoder l'ID de ce répéteur dans les balises de chemin flood/détection de boucle. 0=1 octet (256 ID, jusqu'à 64 sauts), 1=2 octets (65 000 ID, jusqu'à 32 sauts), 2=3 octets (16 millions d'ID, jusqu'à 21 sauts). Le firmware antérieur à v1.14 utilisait toujours des chemins d'1 octet; v1.14 et les versions ultérieures peuvent être configurées pour des chemins de 2 ou 3 octets.", "repeater_txDelay": "Retard dû aux inondations à Texas", "repeater_txDelayHelper": "Rétransmettre l'espacement pour le trafic de secours en cas de inondation, en multipliant le temps d'émission du paquet (0-2, valeur par défaut : 0,5). Une valeur plus élevée signifie moins de collisions, mais une vitesse de transmission plus lente.", "repeater_directTxDelay": "Retard de transmission direct", diff --git a/lib/l10n/app_hu.arb b/lib/l10n/app_hu.arb index 97426183..e3081f87 100644 --- a/lib/l10n/app_hu.arb +++ b/lib/l10n/app_hu.arb @@ -1291,7 +1291,7 @@ "repeater_advancedSettings": "Haladó", "repeater_advancedSettingsSubtitle": "Erkélő kapcsolók tapasztalt kezelők számára", "repeater_pathHashMode": "Út-hash mód", - "repeater_pathHashModeHelper": "A byte-ok, amelyek az alábbi repeater-ek azonosítójának kódolására szolgálnak a flood-útvonal/ciklus-észlelő címkékben. 0=1 byte (256 azonosító, akár 64 útvonal), 1=2 byte (65 000 azonosító, akár 32 útvonal), 2=3 byte (16 millió azonosító, akár 21 útvonal). A v1.13-as verziótól kezdődően és az azt követő verziókban a több byte-os útvonalak megszűntek – csak egyetlen útvonal létesül, miután a hálózat a v1.14-es verzióra vagy az azt követő verzióra frissült.", + "repeater_pathHashModeHelper": "A repeater azonosítójának kódolására használt byte-ok száma a flood-útvonal/ciklusészlelési címkékben. 0=1 byte (256 azonosító, legfeljebb 64 ugrás), 1=2 byte (65 000 azonosító, legfeljebb 32 ugrás), 2=3 byte (16 millió azonosító, legfeljebb 21 ugrás). A v1.14 előtti firmware mindig 1 byte-os útvonalakat használt; a v1.14 és újabb verziók 2 vagy 3 byte-os útvonalakra is konfigurálhatók.", "repeater_txDelay": "Flood TX késés", "repeater_txDelayHelper": "Újraküldési intervallum árvíz esetén, amely a csomag átviteli idejének (0-2, alapérték 0,5) szorzata. Minél nagyobb az érték, annál kevesebb ütközés, de lassabb a továbbítás.", "repeater_directTxDelay": "Közvetlen TX késés", diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 664bceef..dac5f78c 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -1105,7 +1105,7 @@ "repeater_advancedSettings": "Avanzato", "repeater_advancedSettingsSubtitle": "Manopole di regolazione per operatori esperti", "repeater_pathHashMode": "Modalità di hashing del percorso", - "repeater_pathHashModeHelper": "Byte utilizzati per codificare l'ID di questo ripetitore nei tag per il rilevamento del percorso/loop. 0=1 byte (256 ID, fino a 64 salti), 1=2 byte (65.000 ID, fino a 32 salti), 2=3 byte (16 milioni di ID, fino a 21 salti). Le versioni 1.13 e precedenti utilizzano percorsi multi-byte: è necessario attivare la rete prima di utilizzare questa funzionalità (a partire dalla versione 1.14).", + "repeater_pathHashModeHelper": "Byte utilizzati per codificare l'ID di questo ripetitore nei tag di percorso flood/rilevamento loop. 0=1 byte (256 ID, fino a 64 salti), 1=2 byte (65.000 ID, fino a 32 salti), 2=3 byte (16 milioni di ID, fino a 21 salti). Il firmware precedente alla v1.14 usava sempre percorsi a 1 byte; v1.14 e versioni successive possono essere configurate per percorsi a 2 o 3 byte.", "repeater_txDelay": "Ritardo a Flood, TX", "repeater_txDelayHelper": "Riassegnare lo spazio tra i pacchetti per gestire il traffico intenso, come un moltiplicatore del tempo di trasmissione (da 0 a 2, valore predefinito 0,5). Un valore più alto significa meno collisioni, ma una trasmissione più lenta.", "repeater_directTxDelay": "Ritardo diretto TX", diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index 9d2cef21..f2037360 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -1291,7 +1291,7 @@ "repeater_advancedSettings": "高度な", "repeater_advancedSettingsSubtitle": "経験豊富なオペレーター向けの調整ノブ", "repeater_pathHashMode": "パスハッシュモード", - "repeater_pathHashModeHelper": "このリピーターのIDをフローパス/ループ検出タグにエンコードするために使用されるバイト数。 0=1バイト (256個のID、最大64ホップ)、1=2バイト (65,000個のID、最大32ホップ)、2=3バイト (160万個のID、最大21ホップ)。 v1.13およびそれ以前のファームウェアでは、マルチバイトパスがサポートされていません。 v1.14以降のバージョンでは、一度ネットワークが起動されると、パスが一度だけ検出されます。", + "repeater_pathHashModeHelper": "このリピーターのIDをフラッド経路/ループ検出タグにエンコードするために使用するバイト数。0=1バイト (256 ID、最大64ホップ)、1=2バイト (65,000 ID、最大32ホップ)、2=3バイト (1,600万 ID、最大21ホップ)。v1.14より前のファームウェアは常に1バイト経路を使用していました。v1.14以降は2バイトまたは3バイト経路に設定できます。", "repeater_txDelay": "フロイド・TXでの遅延", "repeater_txDelayHelper": "洪水時の交通量に対応するための再送信間隔を、パケットの通信時間を掛けた値(0~2、デフォルト0.5)で設定します。値を大きくすると衝突が減りますが、通信速度が遅くなります。", "repeater_directTxDelay": "直接的なTX遅延", diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb index d778429b..27e7c1ee 100644 --- a/lib/l10n/app_ko.arb +++ b/lib/l10n/app_ko.arb @@ -1291,7 +1291,7 @@ "repeater_advancedSettings": "고급", "repeater_advancedSettingsSubtitle": "숙련된 운영자를 위한 조절 노브", "repeater_pathHashMode": "패스 해시 모드", - "repeater_pathHashModeHelper": "이 리피터의 ID를 플러드 경로/루프 감지 태그에 인코딩하는 데 사용되는 바이트 수: 0=1 바이트 (256개의 ID, 최대 64개의 홉), 1=2 바이트 (65,000개의 ID, 최대 32개의 홉), 2=3 바이트 (16백만 개의 ID, 최대 21개의 홉). v1.13 및 이전 버전의 펌웨어는 다중 바이트 경로를 지원하지 않으며, 네트워크가 v1.14 이상으로 업그레이드되면 한 번만 감지합니다.", + "repeater_pathHashModeHelper": "이 리피터의 ID를 플러드 경로/루프 감지 태그에 인코딩하는 데 사용되는 바이트 수입니다. 0=1바이트(256개 ID, 최대 64홉), 1=2바이트(65,000개 ID, 최대 32홉), 2=3바이트(1,600만 개 ID, 최대 21홉). v1.14 이전 펌웨어는 항상 1바이트 경로를 사용했으며, v1.14 이상은 2바이트 또는 3바이트 경로로 설정할 수 있습니다.", "repeater_txDelay": "플러드 TX 지연", "repeater_txDelayHelper": "홍수 시 교통량에 맞춰 재전송 간격을 설정합니다. 이는 패킷의 전송 시간을 곱한 값 (0-2, 기본값 0.5)으로 설정합니다. 값이 클수록 충돌이 줄어들지만 전송 속도가 느려집니다.", "repeater_directTxDelay": "직접적인 TX 지연", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 159d755c..93fb9181 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -4493,7 +4493,7 @@ abstract class AppLocalizations { /// No description provided for @repeater_pathHashModeHelper. /// /// In en, this message translates to: - /// **'Bytes used to encode this repeater\'s ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). v1.13 and older firmware drops multi-byte paths — only raise once your network is on v1.14+.'** + /// **'Bytes used to encode this repeater\'s ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). Firmware before v1.14 always used 1-byte paths; v1.14 and newer can be configured for 2- or 3-byte paths.'** String get repeater_pathHashModeHelper; /// No description provided for @repeater_txDelay. diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index 409803b2..544fdbe8 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -2524,7 +2524,7 @@ class AppLocalizationsBg extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Байтовете, използвани за кодиране на идентификатора на този репитер в таговете за откриване на потоци/цикли, са: 0=1 байт (256 идентификатора, до 64 скача), 1=2 байта (65 000 идентификатора, до 32 скача), 2=3 байта (16 милиона идентификатора, до 21 скача). Версии 1.13 и по-стари версии на фърмуера използват многобайтови пътища – само след като мрежата е актуализирана до версия 1.14 или по-нова.'; + 'Байтовете, използвани за кодиране на идентификатора на този репитер в таговете за flood път/откриване на цикли. 0=1 байт (256 идентификатора, до 64 скока), 1=2 байта (65 000 идентификатора, до 32 скока), 2=3 байта (16 милиона идентификатора, до 21 скока). Фърмуер преди v1.14 винаги използва 1-байтови пътища; v1.14 и по-нови версии могат да се конфигурират за 2- или 3-байтови пътища.'; @override String get repeater_txDelay => 'Забавяне на проекта \"Flood TX\"'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 9f06906d..a504e5ba 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -2523,7 +2523,7 @@ class AppLocalizationsDe extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Bytes, die zur Kodierung der ID dieses Repeaters in Flood-Pfad-/Schleifen-Erkennung-Tags verwendet werden. 0 = 1 Byte (256 IDs, bis zu 64 Hops), 1 = 2 Bytes (65.000 IDs, bis zu 32 Hops), 2 = 3 Bytes (16 Millionen IDs, bis zu 21 Hops). Firmware-Versionen 1.13 und älter verwenden mehrstellige Pfade – ab Version 1.14+ wird nur ein Pfad erstellt, sobald das Netzwerk aktiv ist.'; + 'Bytes, die zur Kodierung der ID dieses Repeaters in Flood-Pfad-/Schleifen-Erkennung-Tags verwendet werden. 0 = 1 Byte (256 IDs, bis zu 64 Hops), 1 = 2 Bytes (65.000 IDs, bis zu 32 Hops), 2 = 3 Bytes (16 Millionen IDs, bis zu 21 Hops). Firmware vor v1.14 verwendet immer 1-Byte-Pfade; ab v1.14 können 2- oder 3-Byte-Pfade eingestellt werden.'; @override String get repeater_txDelay => 'Verzögerung bei Flood TX'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 6def06b7..7cf14fe3 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -2470,7 +2470,7 @@ class AppLocalizationsEn extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Bytes used to encode this repeater\'s ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). v1.13 and older firmware drops multi-byte paths — only raise once your network is on v1.14+.'; + 'Bytes used to encode this repeater\'s ID in flood path/loop-detect tags. 0=1 byte (256 IDs, up to 64 hops), 1=2 bytes (65K IDs, up to 32 hops), 2=3 bytes (16M IDs, up to 21 hops). Firmware before v1.14 always used 1-byte paths; v1.14 and newer can be configured for 2- or 3-byte paths.'; @override String get repeater_txDelay => 'Flood TX delay'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index bb4f40e8..bbfed65c 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -2519,7 +2519,7 @@ class AppLocalizationsEs extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Bytes utilizados para codificar el ID de este repetidor en las etiquetas de ruta/detección de bucles. 0=1 byte (256 IDs, hasta 64 saltos), 1=2 bytes (65.000 IDs, hasta 32 saltos), 2=3 bytes (16 millones de IDs, hasta 21 saltos). Las versiones 1.13 y anteriores de firmware eliminan rutas de múltiples bytes; solo se detectan una vez que la red está activa en la versión 1.14 o posterior.'; + 'Bytes utilizados para codificar el ID de este repetidor en las etiquetas de ruta flood/detección de bucles. 0=1 byte (256 IDs, hasta 64 saltos), 1=2 bytes (65.000 IDs, hasta 32 saltos), 2=3 bytes (16 millones de IDs, hasta 21 saltos). El firmware anterior a v1.14 siempre usaba rutas de 1 byte; v1.14 y posteriores pueden configurarse para rutas de 2 o 3 bytes.'; @override String get repeater_txDelay => 'Retraso en Flood, TX'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 44e40657..e1223cf3 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -2537,7 +2537,7 @@ class AppLocalizationsFr extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Octets utilisés pour encoder l\'ID de ce routeur dans les balises de détection de flux/boucles. 0 = 1 octet (256 ID, jusqu\'à 64 sauts), 1 = 2 octets (65 000 ID, jusqu\'à 32 sauts), 2 = 3 octets (16 millions d\'ID, jusqu\'à 21 sauts). Les versions 1.13 et antérieures utilisent des chemins multi-octets ; à partir de la version 1.14, cela n\'est plus nécessaire.'; + 'Octets utilisés pour encoder l\'ID de ce répéteur dans les balises de chemin flood/détection de boucle. 0=1 octet (256 ID, jusqu\'à 64 sauts), 1=2 octets (65 000 ID, jusqu\'à 32 sauts), 2=3 octets (16 millions d\'ID, jusqu\'à 21 sauts). Le firmware antérieur à v1.14 utilisait toujours des chemins d\'1 octet; v1.14 et les versions ultérieures peuvent être configurées pour des chemins de 2 ou 3 octets.'; @override String get repeater_txDelay => 'Retard dû aux inondations à Texas'; diff --git a/lib/l10n/app_localizations_hu.dart b/lib/l10n/app_localizations_hu.dart index 298a6fd6..b7b5cf28 100644 --- a/lib/l10n/app_localizations_hu.dart +++ b/lib/l10n/app_localizations_hu.dart @@ -2530,7 +2530,7 @@ class AppLocalizationsHu extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'A byte-ok, amelyek az alábbi repeater-ek azonosítójának kódolására szolgálnak a flood-útvonal/ciklus-észlelő címkékben. 0=1 byte (256 azonosító, akár 64 útvonal), 1=2 byte (65 000 azonosító, akár 32 útvonal), 2=3 byte (16 millió azonosító, akár 21 útvonal). A v1.13-as verziótól kezdődően és az azt követő verziókban a több byte-os útvonalak megszűntek – csak egyetlen útvonal létesül, miután a hálózat a v1.14-es verzióra vagy az azt követő verzióra frissült.'; + 'A repeater azonosítójának kódolására használt byte-ok száma a flood-útvonal/ciklusészlelési címkékben. 0=1 byte (256 azonosító, legfeljebb 64 ugrás), 1=2 byte (65 000 azonosító, legfeljebb 32 ugrás), 2=3 byte (16 millió azonosító, legfeljebb 21 ugrás). A v1.14 előtti firmware mindig 1 byte-os útvonalakat használt; a v1.14 és újabb verziók 2 vagy 3 byte-os útvonalakra is konfigurálhatók.'; @override String get repeater_txDelay => 'Flood TX késés'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index e3f7ee58..4bc50054 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -2523,7 +2523,7 @@ class AppLocalizationsIt extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Byte utilizzati per codificare l\'ID di questo ripetitore nei tag per il rilevamento del percorso/loop. 0=1 byte (256 ID, fino a 64 salti), 1=2 byte (65.000 ID, fino a 32 salti), 2=3 byte (16 milioni di ID, fino a 21 salti). Le versioni 1.13 e precedenti utilizzano percorsi multi-byte: è necessario attivare la rete prima di utilizzare questa funzionalità (a partire dalla versione 1.14).'; + 'Byte utilizzati per codificare l\'ID di questo ripetitore nei tag di percorso flood/rilevamento loop. 0=1 byte (256 ID, fino a 64 salti), 1=2 byte (65.000 ID, fino a 32 salti), 2=3 byte (16 milioni di ID, fino a 21 salti). Il firmware precedente alla v1.14 usava sempre percorsi a 1 byte; v1.14 e versioni successive possono essere configurate per percorsi a 2 o 3 byte.'; @override String get repeater_txDelay => 'Ritardo a Flood, TX'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index 263a906e..60bf4ed0 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -2408,7 +2408,7 @@ class AppLocalizationsJa extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'このリピーターのIDをフローパス/ループ検出タグにエンコードするために使用されるバイト数。 0=1バイト (256個のID、最大64ホップ)、1=2バイト (65,000個のID、最大32ホップ)、2=3バイト (160万個のID、最大21ホップ)。 v1.13およびそれ以前のファームウェアでは、マルチバイトパスがサポートされていません。 v1.14以降のバージョンでは、一度ネットワークが起動されると、パスが一度だけ検出されます。'; + 'このリピーターのIDをフラッド経路/ループ検出タグにエンコードするために使用するバイト数。0=1バイト (256 ID、最大64ホップ)、1=2バイト (65,000 ID、最大32ホップ)、2=3バイト (1,600万 ID、最大21ホップ)。v1.14より前のファームウェアは常に1バイト経路を使用していました。v1.14以降は2バイトまたは3バイト経路に設定できます。'; @override String get repeater_txDelay => 'フロイド・TXでの遅延'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index f9c0d760..954b1470 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -2407,7 +2407,7 @@ class AppLocalizationsKo extends AppLocalizations { @override String get repeater_pathHashModeHelper => - '이 리피터의 ID를 플러드 경로/루프 감지 태그에 인코딩하는 데 사용되는 바이트 수: 0=1 바이트 (256개의 ID, 최대 64개의 홉), 1=2 바이트 (65,000개의 ID, 최대 32개의 홉), 2=3 바이트 (16백만 개의 ID, 최대 21개의 홉). v1.13 및 이전 버전의 펌웨어는 다중 바이트 경로를 지원하지 않으며, 네트워크가 v1.14 이상으로 업그레이드되면 한 번만 감지합니다.'; + '이 리피터의 ID를 플러드 경로/루프 감지 태그에 인코딩하는 데 사용되는 바이트 수입니다. 0=1바이트(256개 ID, 최대 64홉), 1=2바이트(65,000개 ID, 최대 32홉), 2=3바이트(1,600만 개 ID, 최대 21홉). v1.14 이전 펌웨어는 항상 1바이트 경로를 사용했으며, v1.14 이상은 2바이트 또는 3바이트 경로로 설정할 수 있습니다.'; @override String get repeater_txDelay => '플러드 TX 지연'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index ad140bf3..76bd03b2 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -2502,7 +2502,7 @@ class AppLocalizationsNl extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Bytes die gebruikt worden om de ID van deze repeater te coderen in flood-pad/lusdetectietags. 0=1 byte (256 ID\'s, tot 64 hops), 1=2 bytes (65.000 ID\'s, tot 32 hops), 2=3 bytes (16 miljoen ID\'s, tot 21 hops). Versies 1.13 en ouder gebruiken multi-byte paden – alleen na het activeren van het netwerk.'; + 'Bytes die gebruikt worden om de ID van deze repeater te coderen in flood-pad/lusdetectietags. 0=1 byte (256 ID\'s, tot 64 hops), 1=2 bytes (65.000 ID\'s, tot 32 hops), 2=3 bytes (16 miljoen ID\'s, tot 21 hops). Firmware vóór v1.14 gebruikte altijd 1-byte paden; v1.14 en nieuwer kunnen worden ingesteld op 2- of 3-byte paden.'; @override String get repeater_txDelay => 'Vertraging bij Flood TX'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 6c7c3696..225d0aee 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -2534,7 +2534,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Bity wykorzystywane do kodowania identyfikatora tego urządzenia w tagach ścieżek/detekcji pętli. 0=1 bity (256 identyfikatorów, do 64 skoków), 1=2 bity (65 000 identyfikatorów, do 32 skoków), 2=3 bity (1 600 000 identyfikatorów, do 21 skoków). Wersje 1.13 i wcześniejsze nie obsługują ścieżek wielobitowych – wykrywają tylko jedną, gdy sieć jest w wersji 1.14 lub nowszej.'; + 'Bajty używane do kodowania identyfikatora tego repeatera w tagach ścieżki flood/wykrywania pętli. 0=1 bajt (256 identyfikatorów, do 64 skoków), 1=2 bajty (65 000 identyfikatorów, do 32 skoków), 2=3 bajty (16 milionów identyfikatorów, do 21 skoków). Firmware sprzed v1.14 zawsze używał ścieżek 1-bajtowych; v1.14 i nowsze można skonfigurować na ścieżki 2- lub 3-bajtowe.'; @override String get repeater_txDelay => 'Opóźnienie w Flood, TX'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index e6fd49e3..9084607b 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -2516,7 +2516,7 @@ class AppLocalizationsPt extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Bytes utilizados para codificar o ID deste repetidor nas tags de caminho/detecção de loop. 0=1 byte (256 IDs, até 64 saltos), 1=2 bytes (65.000 IDs, até 32 saltos), 2=3 bytes (16 milhões de IDs, até 21 saltos). As versões 1.13 e anteriores do firmware não suportam caminhos multi-byte — apenas funcionam uma vez após a ativação da rede (a partir da versão 1.14+).'; + 'Bytes usados para codificar o ID deste repetidor nas tags de caminho flood/detecção de loop. 0=1 byte (256 IDs, até 64 saltos), 1=2 bytes (65.000 IDs, até 32 saltos), 2=3 bytes (16 milhões de IDs, até 21 saltos). O firmware anterior à v1.14 sempre usava caminhos de 1 byte; v1.14 e versões mais recentes podem ser configuradas para caminhos de 2 ou 3 bytes.'; @override String get repeater_txDelay => 'Atraso na entrega em Flood, TX'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 25452003..bf3f7453 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -2523,7 +2523,7 @@ class AppLocalizationsRu extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Байты, используемые для кодирования идентификатора этого ретранслятора в тегах для обнаружения потоков/циклов. 0 = 1 байт (256 идентификаторов, до 64 переходов), 1 = 2 байта (65 000 идентификаторов, до 32 переходов), 2 = 3 байта (1 600 000 идентификаторов, до 21 перехода). Версии прошивки v1.13 и более ранние версии не поддерживают многобайтовые пути — они поднимаются только после того, как ваша сеть будет обновлена до версии v1.14 и выше.'; + 'Байты, используемые для кодирования идентификатора этого ретранслятора в тегах flood-маршрута/обнаружения циклов. 0 = 1 байт (256 идентификаторов, до 64 переходов), 1 = 2 байта (65 000 идентификаторов, до 32 переходов), 2 = 3 байта (16 миллионов идентификаторов, до 21 перехода). Прошивки до v1.14 всегда использовали 1-байтовые маршруты; v1.14 и новее можно настроить на 2- или 3-байтовые маршруты.'; @override String get repeater_txDelay => 'Задержка в работе системы Flood TX'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index 91461385..16cc82b3 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -2503,7 +2503,7 @@ class AppLocalizationsSk extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Byty použité na zakódovanie ID tohto opakovača v tagoch pre trasu/detekciu slučky. 0 = 1 bytu (256 ID, až 64 skokov), 1 = 2 byty (65 000 ID, až 32 skokov), 2 = 3 byty (16 miliónov ID, až 21 skokov). Verzie 1.13 a staršie nepodporujú viacbytové trasy – fungujú len, keď je sieť aktivovaná.'; + 'Bajty použité na zakódovanie ID tohto opakovača v tagoch flood trasy/detekcie slučky. 0=1 bajt (256 ID, až 64 skokov), 1=2 bajty (65 000 ID, až 32 skokov), 2=3 bajty (16 miliónov ID, až 21 skokov). Firmvér pred v1.14 vždy používal 1-bajtové trasy; v1.14 a novšie možno nakonfigurovať na 2- alebo 3-bajtové trasy.'; @override String get repeater_txDelay => 'Zpoždanie v Flood, TX'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index 48103647..d39d11a5 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -2498,7 +2498,7 @@ class AppLocalizationsSl extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Biti, ki so bila uporabljena za kodiranje ID-ja tega releja v oznakah za zaznavanje pot/kroga, imajo naslednje velikosti: 0=1 bit (256 ID-jev, do 64 skokov), 1=2 biti (65.000 ID-jev, do 32 skokov), 2=3 biti (16 milijonov ID-jev, do 21 skokov). V različicah 1.13 in starejših se ustvarjajo večbitne poti – vendar se to zgodi šele, ko je omrežje vklopljeno v različicah 1.14 in kasnejših.'; + 'Bajti, uporabljeni za kodiranje ID-ja tega repetitorja v oznakah flood poti/zaznavanja zank. 0=1 bajt (256 ID-jev, do 64 skokov), 1=2 bajta (65.000 ID-jev, do 32 skokov), 2=3 bajti (16 milijonov ID-jev, do 21 skokov). Vdelana programska oprema pred v1.14 je vedno uporabljala 1-bajtne poti; v1.14 in novejše je mogoče nastaviti na 2- ali 3-bajtne poti.'; @override String get repeater_txDelay => 'Zatemnitevanje zaradi poplav v Texasu'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index 7453c18f..2e20e2f7 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -2487,7 +2487,7 @@ class AppLocalizationsSv extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Byte används för att koda denna repeaters ID i taggar för att upptäcka loopar/flödesvägar. 0=1 byte (256 ID:n, upp till 64 hopp), 1=2 byte (65 000 ID:n, upp till 32 hopp), 2=3 byte (16 miljoner ID:n, upp till 21 hopp). Versioner 1.13 och äldre har stöd för multi-byte-vägar – endast en gång när nätverket är aktiverat (från och med version 1.14).'; + 'Byte som används för att koda denna repeaters ID i taggar för flood-väg/loopdetektering. 0=1 byte (256 ID:n, upp till 64 hopp), 1=2 byte (65 000 ID:n, upp till 32 hopp), 2=3 byte (16 miljoner ID:n, upp till 21 hopp). Firmware före v1.14 använde alltid 1-byte-vägar; v1.14 och nyare kan konfigureras för 2- eller 3-byte-vägar.'; @override String get repeater_txDelay => 'Försening i Flood TX'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 9c30fd8e..ee6a7d52 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -2522,7 +2522,7 @@ class AppLocalizationsUk extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Байти, що використовуються для кодування ідентифікатора цього ретранслятора в тегах для виявлення потоків/петлі. 0=1 байт (256 ідентифікаторів, до 64 перехідів), 1=2 байти (65 000 ідентифікаторів, до 32 перехідів), 2=3 байти (16 мільйонів ідентифікаторів, до 21 переходу). Версії 1.13 та старіші не підтримують багатобайтні шляхи — вони активуються лише після того, як мережа буде оновлена до версії 1.14+.'; + 'Байти, що використовуються для кодування ідентифікатора цього ретранслятора в тегах flood-шляху/виявлення петель. 0=1 байт (256 ідентифікаторів, до 64 переходів), 1=2 байти (65 000 ідентифікаторів, до 32 переходів), 2=3 байти (16 мільйонів ідентифікаторів, до 21 переходу). Прошивки до v1.14 завжди використовували 1-байтові шляхи; v1.14 і новіші можна налаштувати на 2- або 3-байтові шляхи.'; @override String get repeater_txDelay => 'Затримка у Flood, штат Техас'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index d2304a66..37ee452f 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -2362,7 +2362,7 @@ class AppLocalizationsZh extends AppLocalizations { @override String get repeater_pathHashModeHelper => - '用于编码此复用器的 ID 的字节数,在“洪流路径/环检测”标签中使用。 0=1 字节(256 个 ID,最多 64 个跳跃),1=2 字节(65K 个 ID,最多 32 个跳跃),2=3 字节(16M 个 ID,最多 21 个跳跃)。 v1.13 及更早版本的固件会使用多字节路径——只有在您的网络升级到 v1.14 或更高版本后才会生效。'; + '用于在洪泛路径/环路检测标签中编码此中继器 ID 的字节数。0=1 字节(256 个 ID,最多 64 跳),1=2 字节(65K 个 ID,最多 32 跳),2=3 字节(16M 个 ID,最多 21 跳)。v1.14 之前的固件始终使用 1 字节路径;v1.14 及更新版本可配置为 2 或 3 字节路径。'; @override String get repeater_txDelay => '洪水(德克萨斯州)延误'; diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 7e83215b..072b2d51 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -1105,7 +1105,7 @@ "repeater_advancedSettings": "Geavanceerd", "repeater_advancedSettingsSubtitle": "Regelhendels voor ervaren gebruikers", "repeater_pathHashMode": "Hash-modus voor paden", - "repeater_pathHashModeHelper": "Bytes die gebruikt worden om de ID van deze repeater te coderen in flood-pad/lusdetectietags. 0=1 byte (256 ID's, tot 64 hops), 1=2 bytes (65.000 ID's, tot 32 hops), 2=3 bytes (16 miljoen ID's, tot 21 hops). Versies 1.13 en ouder gebruiken multi-byte paden – alleen na het activeren van het netwerk.", + "repeater_pathHashModeHelper": "Bytes die gebruikt worden om de ID van deze repeater te coderen in flood-pad/lusdetectietags. 0=1 byte (256 ID's, tot 64 hops), 1=2 bytes (65.000 ID's, tot 32 hops), 2=3 bytes (16 miljoen ID's, tot 21 hops). Firmware vóór v1.14 gebruikte altijd 1-byte paden; v1.14 en nieuwer kunnen worden ingesteld op 2- of 3-byte paden.", "repeater_txDelay": "Vertraging bij Flood TX", "repeater_txDelayHelper": "Herzendinterval voor verkeer tijdens overstromingen, als een veelvoud van de tijd die het pakket nodig heeft (0-2, standaard 0.5). Een hoger getal betekent minder botsingen, maar ook een langere leveringstijd.", "repeater_directTxDelay": "Directe vertraging", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 4366e273..afb88e57 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -1115,7 +1115,7 @@ "repeater_advancedSettings": "Zaawansowany", "repeater_advancedSettingsSubtitle": "Regulowane pokrętła dla doświadczonych operatorów", "repeater_pathHashMode": "Tryb haszujący ścieżkę", - "repeater_pathHashModeHelper": "Bity wykorzystywane do kodowania identyfikatora tego urządzenia w tagach ścieżek/detekcji pętli. 0=1 bity (256 identyfikatorów, do 64 skoków), 1=2 bity (65 000 identyfikatorów, do 32 skoków), 2=3 bity (1 600 000 identyfikatorów, do 21 skoków). Wersje 1.13 i wcześniejsze nie obsługują ścieżek wielobitowych – wykrywają tylko jedną, gdy sieć jest w wersji 1.14 lub nowszej.", + "repeater_pathHashModeHelper": "Bajty używane do kodowania identyfikatora tego repeatera w tagach ścieżki flood/wykrywania pętli. 0=1 bajt (256 identyfikatorów, do 64 skoków), 1=2 bajty (65 000 identyfikatorów, do 32 skoków), 2=3 bajty (16 milionów identyfikatorów, do 21 skoków). Firmware sprzed v1.14 zawsze używał ścieżek 1-bajtowych; v1.14 i nowsze można skonfigurować na ścieżki 2- lub 3-bajtowe.", "repeater_txDelay": "Opóźnienie w Flood, TX", "repeater_txDelayHelper": "Ustawienie odstępu dla ruchu związanego z powodzią, jako mnożnik czasu przesyłania pakietu (0-2, domyślnie 0,5). Wyższe wartości oznaczają mniejszą liczbę kolizji, ale wolniejszą prędkość przesyłania.", "repeater_directTxDelay": "Bezpośrednie opóźnienie sygnału TX", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 6b8abbbc..01243468 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -1105,7 +1105,7 @@ "repeater_advancedSettings": "Avançado", "repeater_advancedSettingsSubtitle": "Controles de ajuste para operadores experientes", "repeater_pathHashMode": "Modo de hash de caminho", - "repeater_pathHashModeHelper": "Bytes utilizados para codificar o ID deste repetidor nas tags de caminho/detecção de loop. 0=1 byte (256 IDs, até 64 saltos), 1=2 bytes (65.000 IDs, até 32 saltos), 2=3 bytes (16 milhões de IDs, até 21 saltos). As versões 1.13 e anteriores do firmware não suportam caminhos multi-byte — apenas funcionam uma vez após a ativação da rede (a partir da versão 1.14+).", + "repeater_pathHashModeHelper": "Bytes usados para codificar o ID deste repetidor nas tags de caminho flood/detecção de loop. 0=1 byte (256 IDs, até 64 saltos), 1=2 bytes (65.000 IDs, até 32 saltos), 2=3 bytes (16 milhões de IDs, até 21 saltos). O firmware anterior à v1.14 sempre usava caminhos de 1 byte; v1.14 e versões mais recentes podem ser configuradas para caminhos de 2 ou 3 bytes.", "repeater_txDelay": "Atraso na entrega em Flood, TX", "repeater_txDelayHelper": "Ajuste de espaçamento para tráfego de inundações, como um multiplicador do tempo de transmissão (0-2, padrão 0,5). Quanto maior, menos colisões, mas uma entrega mais lenta.", "repeater_directTxDelay": "Atraso direto no sinal TX", diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index 5d5bcfc3..0dd6d497 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -1436,7 +1436,7 @@ "repeater_advancedSettings": "Продвинутый", "repeater_advancedSettingsSubtitle": "Регуляторы для опытных операторов", "repeater_pathHashMode": "Режим хеширования пути", - "repeater_pathHashModeHelper": "Байты, используемые для кодирования идентификатора этого ретранслятора в тегах для обнаружения потоков/циклов. 0 = 1 байт (256 идентификаторов, до 64 переходов), 1 = 2 байта (65 000 идентификаторов, до 32 переходов), 2 = 3 байта (1 600 000 идентификаторов, до 21 перехода). Версии прошивки v1.13 и более ранние версии не поддерживают многобайтовые пути — они поднимаются только после того, как ваша сеть будет обновлена до версии v1.14 и выше.", + "repeater_pathHashModeHelper": "Байты, используемые для кодирования идентификатора этого ретранслятора в тегах flood-маршрута/обнаружения циклов. 0 = 1 байт (256 идентификаторов, до 64 переходов), 1 = 2 байта (65 000 идентификаторов, до 32 переходов), 2 = 3 байта (16 миллионов идентификаторов, до 21 перехода). Прошивки до v1.14 всегда использовали 1-байтовые маршруты; v1.14 и новее можно настроить на 2- или 3-байтовые маршруты.", "repeater_txDelay": "Задержка в работе системы Flood TX", "repeater_txDelayHelper": "Передача с увеличенным интервалом для трафика во время наводнения, в качестве коэффициента, умножающего время передачи пакета (от 0 до 2, по умолчанию 0,5). Более высокое значение означает меньшее количество столкновений, но более медленную передачу.", "repeater_directTxDelay": "Прямая задержка сигнала TX", diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index 713c3976..1393d22b 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -1105,7 +1105,7 @@ "repeater_advancedSettings": "Pokročilé", "repeater_advancedSettingsSubtitle": "Ovládacie knopy pre skúsených operátorov", "repeater_pathHashMode": "Režim hashovania cesty", - "repeater_pathHashModeHelper": "Byty použité na zakódovanie ID tohto opakovača v tagoch pre trasu/detekciu slučky. 0 = 1 bytu (256 ID, až 64 skokov), 1 = 2 byty (65 000 ID, až 32 skokov), 2 = 3 byty (16 miliónov ID, až 21 skokov). Verzie 1.13 a staršie nepodporujú viacbytové trasy – fungujú len, keď je sieť aktivovaná.", + "repeater_pathHashModeHelper": "Bajty použité na zakódovanie ID tohto opakovača v tagoch flood trasy/detekcie slučky. 0=1 bajt (256 ID, až 64 skokov), 1=2 bajty (65 000 ID, až 32 skokov), 2=3 bajty (16 miliónov ID, až 21 skokov). Firmvér pred v1.14 vždy používal 1-bajtové trasy; v1.14 a novšie možno nakonfigurovať na 2- alebo 3-bajtové trasy.", "repeater_txDelay": "Zpoždanie v Flood, TX", "repeater_txDelayHelper": "Nastavenie pre opakované vysielanie pre dopravu počas povodní, ako násobok času, ktorý paket využije (0-2, výchoce hodnota 0,5). Vyššia hodnota znamená menej kolízii, ale pomalšie doručovanie.", "repeater_directTxDelay": "Priame oneskorenie TX", diff --git a/lib/l10n/app_sl.arb b/lib/l10n/app_sl.arb index 00930c85..e64fee4c 100644 --- a/lib/l10n/app_sl.arb +++ b/lib/l10n/app_sl.arb @@ -1105,7 +1105,7 @@ "repeater_advancedSettings": "Napredno", "repeater_advancedSettingsSubtitle": "Gumbi za nastavljanje za izkušene uporabnike", "repeater_pathHashMode": "Način ustvarjanja hash-a poti", - "repeater_pathHashModeHelper": "Biti, ki so bila uporabljena za kodiranje ID-ja tega releja v oznakah za zaznavanje pot/kroga, imajo naslednje velikosti: 0=1 bit (256 ID-jev, do 64 skokov), 1=2 biti (65.000 ID-jev, do 32 skokov), 2=3 biti (16 milijonov ID-jev, do 21 skokov). V različicah 1.13 in starejših se ustvarjajo večbitne poti – vendar se to zgodi šele, ko je omrežje vklopljeno v različicah 1.14 in kasnejših.", + "repeater_pathHashModeHelper": "Bajti, uporabljeni za kodiranje ID-ja tega repetitorja v oznakah flood poti/zaznavanja zank. 0=1 bajt (256 ID-jev, do 64 skokov), 1=2 bajta (65.000 ID-jev, do 32 skokov), 2=3 bajti (16 milijonov ID-jev, do 21 skokov). Vdelana programska oprema pred v1.14 je vedno uporabljala 1-bajtne poti; v1.14 in novejše je mogoče nastaviti na 2- ali 3-bajtne poti.", "repeater_txDelay": "Zatemnitevanje zaradi poplav v Texasu", "repeater_txDelayHelper": "Uporaba intervalov za ponovno pošiljanje v primeru prometa zaradi poplav, kot pomnožnik časovne trajanje paketa (0-2, privzeto 0,5). Veje vrednost = manjše kolizije, vendar počasnejše dostavo.", "repeater_directTxDelay": "Neposredni časovno odlašanje", diff --git a/lib/l10n/app_sv.arb b/lib/l10n/app_sv.arb index 00fd3b08..e8f1b230 100644 --- a/lib/l10n/app_sv.arb +++ b/lib/l10n/app_sv.arb @@ -1105,7 +1105,7 @@ "repeater_advancedSettings": "Avancerad", "repeater_advancedSettingsSubtitle": "Ställjusteringsknappar för erfarna användare", "repeater_pathHashMode": "Hash-läge för sökväg", - "repeater_pathHashModeHelper": "Byte används för att koda denna repeaters ID i taggar för att upptäcka loopar/flödesvägar. 0=1 byte (256 ID:n, upp till 64 hopp), 1=2 byte (65 000 ID:n, upp till 32 hopp), 2=3 byte (16 miljoner ID:n, upp till 21 hopp). Versioner 1.13 och äldre har stöd för multi-byte-vägar – endast en gång när nätverket är aktiverat (från och med version 1.14).", + "repeater_pathHashModeHelper": "Byte som används för att koda denna repeaters ID i taggar för flood-väg/loopdetektering. 0=1 byte (256 ID:n, upp till 64 hopp), 1=2 byte (65 000 ID:n, upp till 32 hopp), 2=3 byte (16 miljoner ID:n, upp till 21 hopp). Firmware före v1.14 använde alltid 1-byte-vägar; v1.14 och nyare kan konfigureras för 2- eller 3-byte-vägar.", "repeater_txDelay": "Försening i Flood TX", "repeater_txDelayHelper": "Återöverföringsintervall för trafik under perioder med hög belastning, som en multiplikator av paketets överföringstid (0-2, standard 0,5). Högre värde = färre kollisioner, men långsammare leverans.", "repeater_directTxDelay": "Direkt TX-fördröjning", diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index 8330d364..10025137 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -1115,7 +1115,7 @@ "repeater_advancedSettings": "Просунутий", "repeater_advancedSettingsSubtitle": "Регулювальні ручки для досвідчених операторів", "repeater_pathHashMode": "Режим хешування шляху", - "repeater_pathHashModeHelper": "Байти, що використовуються для кодування ідентифікатора цього ретранслятора в тегах для виявлення потоків/петлі. 0=1 байт (256 ідентифікаторів, до 64 перехідів), 1=2 байти (65 000 ідентифікаторів, до 32 перехідів), 2=3 байти (16 мільйонів ідентифікаторів, до 21 переходу). Версії 1.13 та старіші не підтримують багатобайтні шляхи — вони активуються лише після того, як мережа буде оновлена до версії 1.14+.", + "repeater_pathHashModeHelper": "Байти, що використовуються для кодування ідентифікатора цього ретранслятора в тегах flood-шляху/виявлення петель. 0=1 байт (256 ідентифікаторів, до 64 переходів), 1=2 байти (65 000 ідентифікаторів, до 32 переходів), 2=3 байти (16 мільйонів ідентифікаторів, до 21 переходу). Прошивки до v1.14 завжди використовували 1-байтові шляхи; v1.14 і новіші можна налаштувати на 2- або 3-байтові шляхи.", "repeater_txDelay": "Затримка у Flood, штат Техас", "repeater_txDelayHelper": "Повторне надсилання з урахуванням навантаження від потоків транспорту, як множник від часу передачі пакета (0-2, за замовчуванням 0,5). Чим вище значення, тим менше конфліктів, але повільніше передавання.", "repeater_directTxDelay": "Пряме затримка TX", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 1e289315..d4743425 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -1135,7 +1135,7 @@ "repeater_advancedSettings": "高级", "repeater_advancedSettingsSubtitle": "高级操作员使用的调节旋钮", "repeater_pathHashMode": "路径哈希模式", - "repeater_pathHashModeHelper": "用于编码此复用器的 ID 的字节数,在“洪流路径/环检测”标签中使用。 0=1 字节(256 个 ID,最多 64 个跳跃),1=2 字节(65K 个 ID,最多 32 个跳跃),2=3 字节(16M 个 ID,最多 21 个跳跃)。 v1.13 及更早版本的固件会使用多字节路径——只有在您的网络升级到 v1.14 或更高版本后才会生效。", + "repeater_pathHashModeHelper": "用于在洪泛路径/环路检测标签中编码此中继器 ID 的字节数。0=1 字节(256 个 ID,最多 64 跳),1=2 字节(65K 个 ID,最多 32 跳),2=3 字节(16M 个 ID,最多 21 跳)。v1.14 之前的固件始终使用 1 字节路径;v1.14 及更新版本可配置为 2 或 3 字节路径。", "repeater_txDelay": "洪水(德克萨斯州)延误", "repeater_txDelayHelper": "对于洪水流量,重新传输间隔应设置为包的传输时间(0-2,默认值为0.5)的倍数。 较高的值意味着更少的冲突,但传输速度会变慢。", "repeater_directTxDelay": "直接的 TX 延迟", diff --git a/lib/screens/channel_chat_screen.dart b/lib/screens/channel_chat_screen.dart index fcd50861..92903521 100644 --- a/lib/screens/channel_chat_screen.dart +++ b/lib/screens/channel_chat_screen.dart @@ -13,6 +13,7 @@ import '../helpers/chat_scroll_controller.dart'; import '../connector/meshcore_protocol.dart'; import '../helpers/cyr2lat.dart'; import '../helpers/gif_helper.dart'; +import '../helpers/path_helper.dart'; import '../helpers/reaction_helper.dart'; import '../helpers/snack_bar_builder.dart'; import '../l10n/l10n.dart'; @@ -422,6 +423,9 @@ class _ChannelChatScreenState extends State { : (message.pathVariants.isNotEmpty ? message.pathVariants.first : Uint8List(0)); + final displayPathHashWidth = + message.pathHashWidth ?? + context.read().pathHashByteWidth; const maxSwipeOffset = 64.0; const replySwipeThreshold = 64.0; @@ -608,7 +612,10 @@ class _ChannelChatScreenState extends State { : EdgeInsets.zero, child: Text( context.l10n.channels_via( - _formatPathPrefixes(displayPath), + _formatPathPrefixes( + displayPath, + displayPathHashWidth, + ), ), style: TextStyle( fontSize: 11, @@ -1432,10 +1439,11 @@ class _ChannelChatScreenState extends State { ); } - String _formatPathPrefixes(Uint8List pathBytes) { - return pathBytes - .map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase()) - .join(','); + String _formatPathPrefixes(Uint8List pathBytes, int pathHashByteWidth) { + return PathHelper.splitPathBytes( + pathBytes, + pathHashByteWidth, + ).map(PathHelper.formatHopHex).join(','); } } From 91759f531a88ea1aa3f8c8bd7195a37fc2247f0e Mon Sep 17 00:00:00 2001 From: ericz Date: Mon, 18 May 2026 21:30:37 +0200 Subject: [PATCH 4/5] fix double entry due to mixed 1byte and 2byte paths --- lib/connector/meshcore_connector.dart | 113 ++++++++++++++++++++++++-- lib/widgets/snr_indicator.dart | 10 +++ 2 files changed, 116 insertions(+), 7 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index ed7d6f12..08cdd121 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -49,14 +49,16 @@ import 'meshcore_protocol.dart'; class DirectRepeater { static const int maxAgeMinutes = 30; // Max age for direct repeater info - final List pubkeyPrefix; - final int pathHashWidth; + List pubkeyPrefix; + int pathHashWidth; + String? contactKeyHex; double snr; DateTime lastUpdated; DirectRepeater({ required List pubkeyPrefix, required this.pathHashWidth, + this.contactKeyHex, required this.snr, DateTime? lastUpdated, }) : pubkeyPrefix = List.unmodifiable(pubkeyPrefix), @@ -79,8 +81,21 @@ class DirectRepeater { return listEquals(pubkeyPrefix, pathBytes.sublist(0, width)); } - void update(double newSNR) { + void update( + double newSNR, { + List? pubkeyPrefix, + int? pathHashWidth, + String? contactKeyHex, + }) { snr = newSNR; + if (pubkeyPrefix != null && + pubkeyPrefix.length >= this.pubkeyPrefix.length) { + this.pubkeyPrefix = List.unmodifiable(pubkeyPrefix); + } + if (pathHashWidth != null && pathHashWidth >= this.pathHashWidth) { + this.pathHashWidth = pathHashWidth; + } + this.contactKeyHex ??= contactKeyHex; lastUpdated = DateTime.now(); } @@ -6239,6 +6254,25 @@ class MeshCoreConnector extends ChangeNotifier { 0, math.min(hashWidth, contact.publicKey.length), ); + final contactKeyHex = _resolveDirectRepeaterContactKeyHex( + contact, + pubkeyPrefix, + path.isEmpty, + ); + final knownRepeaters = contactKeyHex == null + ? _directRepeaters + .where( + (r) => + r.contactKeyHex != null && + _contactKeyMatchesPrefix(r.contactKeyHex!, pubkeyPrefix), + ) + .toList() + : const []; + final knownRepeater = knownRepeaters.length == 1 + ? knownRepeaters.first + : null; + final effectiveContactKeyHex = + contactKeyHex ?? knownRepeater?.contactKeyHex; _directRepeaters.removeWhere((r) => r.isStale()); @@ -6249,9 +6283,25 @@ class MeshCoreConnector extends ChangeNotifier { return; } - final isTracked = _directRepeaters.where( - (r) => r.pathHashWidth == hashWidth && r.matchesPrefix(pubkeyPrefix), - ); + final isTracked = _directRepeaters.where((r) { + if (knownRepeater != null) { + return identical(r, knownRepeater); + } + if (effectiveContactKeyHex != null) { + return r.contactKeyHex == effectiveContactKeyHex || + (r.contactKeyHex == null && + _contactKeyMatchesPrefix( + effectiveContactKeyHex, + r.pubkeyPrefix, + )); + } + if (r.contactKeyHex == null && + r.pathHashWidth == hashWidth && + r.matchesPrefix(pubkeyPrefix)) { + return true; + } + return false; + }).toList(); final sortedRepeaters = List.from(_directRepeaters) ..sort((a, b) => b.snr.compareTo(a.snr)); @@ -6267,12 +6317,30 @@ class MeshCoreConnector extends ChangeNotifier { if (isTracked.isNotEmpty) { final repeater = isTracked.first; - repeater.update(snr); + repeater.update( + snr, + pubkeyPrefix: pubkeyPrefix, + pathHashWidth: hashWidth, + contactKeyHex: effectiveContactKeyHex, + ); + if (effectiveContactKeyHex != null) { + _directRepeaters.removeWhere( + (r) => + !identical(r, repeater) && + (r.contactKeyHex == effectiveContactKeyHex || + (r.contactKeyHex == null && + _contactKeyMatchesPrefix( + effectiveContactKeyHex, + r.pubkeyPrefix, + ))), + ); + } } else if (_directRepeaters.length < 5) { _directRepeaters.add( DirectRepeater( pubkeyPrefix: pubkeyPrefix, pathHashWidth: hashWidth, + contactKeyHex: effectiveContactKeyHex, snr: snr, ), ); @@ -6280,6 +6348,37 @@ class MeshCoreConnector extends ChangeNotifier { notifyListeners(); } + String? _resolveDirectRepeaterContactKeyHex( + Contact contact, + List pubkeyPrefix, + bool pathIsEmpty, + ) { + if (pathIsEmpty && + (contact.type == advTypeRepeater || contact.type == advTypeRoom)) { + return contact.publicKeyHex; + } + + final prefixMatches = allContacts + .where( + (c) => + (c.type == advTypeRepeater || c.type == advTypeRoom) && + _contactKeyMatchesPrefix(c.publicKeyHex, pubkeyPrefix), + ) + .toList(); + if (prefixMatches.length == 1) { + return prefixMatches.first.publicKeyHex; + } + + return null; + } + + bool _contactKeyMatchesPrefix(String contactKeyHex, List pubkeyPrefix) { + final prefixHex = pubkeyPrefix + .map((b) => b.toRadixString(16).padLeft(2, '0')) + .join(); + return contactKeyHex.startsWith(prefixHex); + } + void _handleAutoAddConfig(Uint8List frame) { final reader = BufferReader(frame); try { diff --git a/lib/widgets/snr_indicator.dart b/lib/widgets/snr_indicator.dart index 4850393f..ca8e55ce 100644 --- a/lib/widgets/snr_indicator.dart +++ b/lib/widgets/snr_indicator.dart @@ -12,9 +12,18 @@ import 'signal_ui.dart'; Contact? _getRepeaterPrefixMatchNearLocation( List contacts, List pubkeyPrefix, { + String? contactKeyHex, LatLng? searchPoint, bool preferFavorites = false, }) { + if (contactKeyHex != null) { + for (final c in contacts) { + if (c.publicKeyHex == contactKeyHex) { + return c; + } + } + } + final candidates = contacts .where( (c) => @@ -242,6 +251,7 @@ class _SNRIndicatorState extends State { final contact = _getRepeaterPrefixMatchNearLocation( allContacts, repeater.pubkeyPrefix, + contactKeyHex: repeater.contactKeyHex, searchPoint: selfPoint, preferFavorites: true, ); From 3f1bd54889f7a2c1d4370346570c32cdb7eb5541 Mon Sep 17 00:00:00 2001 From: ericz Date: Mon, 18 May 2026 22:49:56 +0200 Subject: [PATCH 5/5] fix wrong hop count in channel_message_path_screen observed path chooser. --- lib/screens/channel_message_path_screen.dart | 42 ++++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/screens/channel_message_path_screen.dart b/lib/screens/channel_message_path_screen.dart index df20f2b7..6001d39e 100644 --- a/lib/screens/channel_message_path_screen.dart +++ b/lib/screens/channel_message_path_screen.dart @@ -490,11 +490,17 @@ class _ChannelMessagePathMapScreenState ? Uint8List.fromList(selectedPathTmp.reversed.toList()) : selectedPathTmp; - final selectedIndex = _indexForPath(selectedPath, observedPaths); - final width = (widget.message.pathHashWidth ?? connector.pathHashByteWidth) - .clamp(1, 4) - .toInt(); - final hops = _buildPathHops(selectedPath, connector, context.l10n, width); + final width = + (widget.message.pathHashWidth ?? connector.pathHashByteWidth) + .clamp(1, 4) + .toInt(); + final selectedIndex = _indexForPath(selectedPathTmp, observedPaths); + final hops = _buildPathHops( + selectedPath, + connector, + context.l10n, + width, + ); final points = []; @@ -612,14 +618,18 @@ class _ChannelMessagePathMapScreenState bounds: bounds, ), if (observedPaths.length > 1) - _buildPathSelector(context, observedPaths, selectedIndex, ( - index, - ) { - setState(() { - _selectedPath = observedPaths[index].pathBytes; - _focusedHopIndex = null; - }); - }), + _buildPathSelector( + context, + observedPaths, + selectedIndex, + width, + (index) { + setState(() { + _selectedPath = observedPaths[index].pathBytes; + _focusedHopIndex = null; + }); + }, + ), if (points.isEmpty) Center( child: Card( @@ -645,13 +655,11 @@ class _ChannelMessagePathMapScreenState BuildContext context, List<_ObservedPath> paths, int selectedIndex, + int hashByteWidth, ValueChanged onSelected, ) { final l10n = context.l10n; - final width = context.read().pathHashByteWidth.clamp( - 1, - 4, - ); + final width = hashByteWidth.clamp(1, 4); final selectedPath = paths[selectedIndex]; final label = selectedPath.isPrimary ? l10n.channelPath_primaryPath(selectedIndex + 1)