Warn channels for delayed log rx

This commit is contained in:
Janez T
2026-03-13 20:32:07 +01:00
parent 9f31f50e61
commit e7a481f97b
8 changed files with 174 additions and 14 deletions

View File

@@ -489,7 +489,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 113;
CURRENT_PROJECT_VERSION = 115;
DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@@ -511,7 +511,7 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 113;
CURRENT_PROJECT_VERSION = 115;
DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
@@ -530,7 +530,7 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 113;
CURRENT_PROJECT_VERSION = 115;
DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
@@ -547,7 +547,7 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 113;
CURRENT_PROJECT_VERSION = 115;
DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
@@ -679,7 +679,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 113;
CURRENT_PROJECT_VERSION = 115;
DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@@ -702,7 +702,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 113;
CURRENT_PROJECT_VERSION = 115;
DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;

View File

@@ -43,7 +43,7 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>113</string>
<string>115</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>ITSAppUsesNonExemptEncryption</key>

View File

@@ -5,22 +5,24 @@
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000231">
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.001473">
</testcase>
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.67336">
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.569758">
</testcase>
<testcase classname="fastlane.lanes" name="2: build_app" time="113.383916">
<testcase classname="fastlane.lanes" name="2: build_app" time="95.271262">
</testcase>
<testcase classname="fastlane.lanes" name="3: upload_to_app_store" time="221.37558">
<testcase classname="fastlane.lanes" name="3: upload_to_app_store" time="91.795184">
<failure message="/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/actions/actions_helper.rb:67:in &apos;Fastlane::Actions.execute_action&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/runner.rb:255:in &apos;block in Fastlane::Runner#execute_action&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/runner.rb:229:in &apos;Dir.chdir&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/runner.rb:229:in &apos;Fastlane::Runner#execute_action&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/runner.rb:157:in &apos;Fastlane::Runner#trigger_action_by_name&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/fast_file.rb:159:in &apos;Fastlane::FastFile#method_missing&apos;&#10;Fastfile:23:in &apos;block (2 levels) in Fastlane::FastFile#parsing_binding&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/lane.rb:41:in &apos;Fastlane::Lane#call&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/runner.rb:49:in &apos;block in Fastlane::Runner#execute&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/runner.rb:45:in &apos;Dir.chdir&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/runner.rb:45:in &apos;Fastlane::Runner#execute&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/lane_manager.rb:46:in &apos;Fastlane::LaneManager.cruise_lane&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/command_line_handler.rb:34:in &apos;Fastlane::CommandLineHandler.handle&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/commands_generator.rb:110:in &apos;block (2 levels) in Fastlane::CommandsGenerator#run&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/commander-4.6.0/lib/commander/command.rb:187:in &apos;Commander::Command#call&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/commander-4.6.0/lib/commander/command.rb:157:in &apos;Commander::Command#run&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/commander-4.6.0/lib/commander/runner.rb:444:in &apos;Commander::Runner#run_active_command&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb:124:in &apos;Commander::Runner#run!&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/commander-4.6.0/lib/commander/delegates.rb:18:in &apos;Commander::Delegates#run!&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/commands_generator.rb:363:in &apos;Fastlane::CommandsGenerator#run&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/commands_generator.rb:43:in &apos;Fastlane::CommandsGenerator.start&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/fastlane/lib/fastlane/cli_tools_distributor.rb:132:in &apos;Fastlane::CLIToolsDistributor.take_off&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/gems/fastlane-2.231.1/bin/fastlane:23:in &apos;&lt;top (required)&gt;&apos;&#10;/opt/homebrew/Cellar/ruby/4.0.1/lib/ruby/4.0.0/rubygems.rb:303:in &apos;Kernel#load&apos;&#10;/opt/homebrew/Cellar/ruby/4.0.1/lib/ruby/4.0.0/rubygems.rb:303:in &apos;Gem.activate_and_load_bin_path&apos;&#10;/opt/homebrew/Cellar/fastlane/2.231.1/libexec/bin/fastlane:25:in &apos;&lt;main&gt;&apos;&#10;&#10;Invalid username and password combination. Used &apos;hey@dz0n.dev&apos; as the username." />
</testcase>

View File

@@ -108,6 +108,87 @@ class TraceNodeResolver {
);
}
static List<ResolvedTraceNode> alignPathSelections({
required List<ResolvedTraceNode> nodes,
MeshMapNode? startNode,
MeshMapNode? endNode,
}) {
if (nodes.isEmpty || nodes.any((node) => node.candidates.isEmpty)) {
return nodes;
}
final candidateCosts = List.generate(
nodes.length,
(_) => <double>[],
growable: false,
);
final previousChoice = List.generate(
nodes.length,
(_) => <int>[],
growable: false,
);
for (var i = 0; i < nodes.length; i++) {
final currentCandidates = nodes[i].candidates;
candidateCosts[i] = List<double>.filled(
currentCandidates.length,
double.infinity,
);
previousChoice[i] = List<int>.filled(currentCandidates.length, -1);
for (var j = 0; j < currentCandidates.length; j++) {
final current = currentCandidates[j];
if (i == 0) {
candidateCosts[i][j] = startNode == null
? 0
: _distanceBetweenNodes(startNode, current);
continue;
}
final previousCandidates = nodes[i - 1].candidates;
for (var k = 0; k < previousCandidates.length; k++) {
final candidateCost =
candidateCosts[i - 1][k] +
_distanceBetweenNodes(previousCandidates[k], current);
if (candidateCost < candidateCosts[i][j]) {
candidateCosts[i][j] = candidateCost;
previousChoice[i][j] = k;
}
}
}
}
var bestLastIndex = 0;
var bestLastCost = double.infinity;
final lastCandidates = nodes.last.candidates;
for (var i = 0; i < lastCandidates.length; i++) {
final endCost = endNode == null
? 0
: _distanceBetweenNodes(lastCandidates[i], endNode);
final totalCost = candidateCosts.last[i] + endCost;
if (totalCost < bestLastCost) {
bestLastCost = totalCost;
bestLastIndex = i;
}
}
final selectedIndices = List<int>.filled(nodes.length, 0);
selectedIndices[nodes.length - 1] = bestLastIndex;
for (var i = nodes.length - 1; i > 0; i--) {
selectedIndices[i - 1] = previousChoice[i][selectedIndices[i]];
}
return List<ResolvedTraceNode>.generate(nodes.length, (index) {
final resolved = nodes[index];
return ResolvedTraceNode(
candidates: resolved.candidates,
matchCount: resolved.matchCount,
usedOnlineFallback: resolved.usedOnlineFallback,
selectedIndex: selectedIndices[index],
);
}, growable: false);
}
static double _scoreNode(
MeshMapNode node, {
LatLng? referenceA,
@@ -126,6 +207,14 @@ class TraceNodeResolver {
return double.maxFinite;
}
static double _distanceBetweenNodes(MeshMapNode a, MeshMapNode b) {
return _distance.as(
LengthUnit.Meter,
LatLng(a.latitude, a.longitude),
LatLng(b.latitude, b.longitude),
);
}
static double _distanceToSegmentMeters(LatLng p, LatLng a, LatLng b) {
final ax = a.longitude;
final ay = a.latitude;

View File

@@ -420,12 +420,17 @@ class _ContactTraceSheetState extends State<ContactTraceSheet> {
),
)
.toList();
final alignedRelayNodes = TraceNodeResolver.alignPathSelections(
nodes: matchedRelayNodes,
startNode: senderNode.node,
endNode: recipientNode.node,
);
return _ContactTraceResult(
sender: senderNode,
recipient: recipientNode,
routeHashes: routeHashes,
matchedRelayNodes: matchedRelayNodes,
matchedRelayNodes: alignedRelayNodes,
);
}

View File

@@ -531,12 +531,17 @@ class _MessageTraceSheetState extends State<MessageTraceSheet> {
senderLatLng: senderLatLng,
recipientLatLng: recipientLatLng,
);
final alignedMatched = TraceNodeResolver.alignPathSelections(
nodes: matched,
startNode: senderNode.node,
endNode: recipientNode.node,
);
return _TraceResult(
mode: TraceMode.packetPath,
sender: senderNode,
recipient: recipientNode,
pathHashes: hopHashes,
matchedPathNodes: matched,
matchedPathNodes: alignedMatched,
);
}

View File

@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 2026.0313.1+28
version: 2026.0313.2+29
environment:
sdk: ^3.9.2

View File

@@ -88,6 +88,65 @@ void main() {
expect(resolved.cycle().node?.name, 'First Match');
expect(resolved.cycle().cycle().node?.name, 'Second Match');
});
test('aligns ambiguous hops to the closest continuous path', () {
final start = _node(
name: 'Start',
publicKey: 'start00',
latitude: 46.000,
longitude: 14.000,
);
final end = _node(
name: 'End',
publicKey: 'end000',
latitude: 46.300,
longitude: 14.300,
);
final hop1Near = _node(
name: 'Hop 1 Near',
publicKey: 'aa1100',
latitude: 46.100,
longitude: 14.100,
);
final hop1Far = _node(
name: 'Hop 1 Far',
publicKey: 'aa11ff',
latitude: 46.250,
longitude: 14.000,
);
final hop2Near = _node(
name: 'Hop 2 Near',
publicKey: 'bb2200',
latitude: 46.200,
longitude: 14.200,
);
final hop2Far = _node(
name: 'Hop 2 Far',
publicKey: 'bb22ff',
latitude: 46.050,
longitude: 14.280,
);
final aligned = TraceNodeResolver.alignPathSelections(
nodes: [
TraceNodeResolver.resolveBest(
nodes: [hop1Far, hop1Near],
localPublicKeys: {hop1Near.publicKey, hop1Far.publicKey},
prefixHex: 'aa11',
),
TraceNodeResolver.resolveBest(
nodes: [hop2Far, hop2Near],
localPublicKeys: {hop2Near.publicKey, hop2Far.publicKey},
prefixHex: 'bb22',
),
],
startNode: start,
endNode: end,
);
expect(aligned[0].node?.name, 'Hop 1 Near');
expect(aligned[1].node?.name, 'Hop 2 Near');
});
}
MeshMapNode _node({