fix(#82): skip empty channel slots on RESP_CODE_ERR instead of timing out
The firmware answers RESP_CODE_ERR for an empty/invalid channel index, but _handleErrorFrame never advanced the in-flight channel sync. So every empty slot waited out the 2s timeout + 3 retries (~8s each): a device with a few populated channels out of a large capacity took minutes to sync (~40 slots requested, ~29 empty -> ~4 min) before the real channels (which answer CHANNEL_INFO) finally synced in the last couple seconds. Now an ERR that lands while a channel GET is in flight (and no generic-ack command is waiting -- that one is order-correlated to the ERR first) advances to the next slot immediately, mirroring the CHANNEL_INFO success path minus adding a channel. The decision is pinned as a pure static predicate and tested, matching the connector's existing decision-helper test convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>pull/99/head
parent
31c0674330
commit
6047a02a6e
@ -0,0 +1,62 @@
|
||||
// Channel-sync decision helper (#82).
|
||||
//
|
||||
// The firmware answers RESP_CODE_ERR for an empty/invalid channel index. The
|
||||
// sync must skip that slot the instant the ERR arrives; without this branch it
|
||||
// waits out the 2s timeout + 3 retries on EVERY empty slot, so a device with a
|
||||
// few populated channels out of a large capacity takes minutes to sync (~8s per
|
||||
// empty slot). This pins the decision: advance on ERR only while a channel GET
|
||||
// is in flight AND no generic-ack command is waiting — a pending generic-ack
|
||||
// (a SET / channel-text send) is order-correlated to the ERR first.
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:meshcore_open/connector/meshcore_connector.dart';
|
||||
|
||||
void main() {
|
||||
group('shouldAdvanceChannelSyncOnError', () {
|
||||
test('advances when a channel GET is in flight and nothing else awaits the '
|
||||
'ERR', () {
|
||||
expect(
|
||||
MeshCoreConnector.shouldAdvanceChannelSyncOnError(
|
||||
isSyncingChannels: true,
|
||||
channelSyncInFlight: true,
|
||||
hasPendingGenericAck: false,
|
||||
),
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
||||
test('does not advance when not syncing channels', () {
|
||||
expect(
|
||||
MeshCoreConnector.shouldAdvanceChannelSyncOnError(
|
||||
isSyncingChannels: false,
|
||||
channelSyncInFlight: true,
|
||||
hasPendingGenericAck: false,
|
||||
),
|
||||
isFalse,
|
||||
);
|
||||
});
|
||||
|
||||
test('does not advance when no channel GET is in flight', () {
|
||||
expect(
|
||||
MeshCoreConnector.shouldAdvanceChannelSyncOnError(
|
||||
isSyncingChannels: true,
|
||||
channelSyncInFlight: false,
|
||||
hasPendingGenericAck: false,
|
||||
),
|
||||
isFalse,
|
||||
);
|
||||
});
|
||||
|
||||
test('a pending generic-ack command owns the ERR first (no channel '
|
||||
'advance)', () {
|
||||
expect(
|
||||
MeshCoreConnector.shouldAdvanceChannelSyncOnError(
|
||||
isSyncingChannels: true,
|
||||
channelSyncInFlight: true,
|
||||
hasPendingGenericAck: true,
|
||||
),
|
||||
isFalse,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
Reference in new issue