Les fenêtres de scan se privaient de l'unique émission d'état

Le manager lazy était bien réveillé — l'avertissement CoreBluetooth sur le
restore identifier le prouve, il ne peut apparaître que si le manager existe.
Et pourtant le publisher restait muet.

La cause est mon découpage en cycles de 4 s. `bleStateSubject` ne publie que sur
CHANGEMENT d'état : le premier cycle réveillait le manager et recevait
`.poweredOn`, mais aux cycles suivants l'état ne changeait plus, donc plus rien
n'était émis. Chaque nouvel abonnement attendait une transition qui avait déjà
eu lieu.

Un seul abonnement, de la durée du timeout, supprime le problème : on reçoit
l'émission là où elle se produit, et le scan tourne sans être relancé. La boucle
d'ouverture de session garde une marge au-delà du scan — couper à la seconde du
timeout ferait échouer la seule montre trouvée.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sylvain Bettinelli
2026-08-31 13:23:31 +00:00
parent 94b1f6243d
commit 01607ed75c

View File

@@ -253,15 +253,25 @@ final class PolarPsFtpWriter {
var sansPsFtp = 0 // aperçus mais ne portant pas le service FEEE
var muettes = 0 // portant FEEE mais dont waitPsFtpReady échoue
// Le scan se fait par fenêtres successives, et non en une seule passe :
// CoreBluetooth peut n'être pas encore `poweredOn` au premier appel le
// scan ne démarre alors jamais, et une fenêtre unique conclurait à tort
// qu'aucun appareil n'existe. Réessayer couvre ce démarrage.
while Date().timeIntervalSince(debut) < timeout {
let sessions = try await sessionsVues(listener, fenetre: 4)
vues = max(vues, sessions.count)
// UN SEUL ABONNEMENT, SUR TOUTE LA DURÉE pas des fenêtres.
//
// Le découpage en cycles de 4 s se privait de l'unique émission d'état :
// `bleStateSubject` ne publie que sur CHANGEMENT. Le premier cycle
// réveillait le manager et recevait `.poweredOn` ; aux cycles suivants
// l'état ne changeait plus, donc plus rien n'était émis et le publisher
// restait muet mesuré le 2026-08-31, y compris après avoir réglé le
// réveil du manager.
//
// Une seule fenêtre, de la durée du timeout, supprime le problème : on
// reçoit l'émission là où elle a lieu, et le scan tourne sans être
// relancé.
do {
let sessions = try await sessionsVues(listener, fenetre: timeout)
vues = sessions.count
for session in sessions {
if Date().timeIntervalSince(debut) > timeout { break }
// Marge au-delà du scan : ouvrir une session prend du temps, et
// couper ici ferait échouer la seule montre trouvée.
if Date().timeIntervalSince(debut) > timeout * 2 { break }
listener.openSessionDirect(session)
guard let client = session.fetchGattClient(BlePsFtpClient.PSFTP_SERVICE)
as? BlePsFtpClient else {