Escalade DSLR recovery : uhubctl power cycle + backoff 60s après 10 échecs
Niveaux : ioctl reset (2) -> unbind/rebind (3-5) -> uhubctl 5V cut (6+) -> backoff 60s (10+) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -151,12 +151,49 @@ def _usb_rebind_canon():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _usb_power_cycle_canon():
|
||||||
|
"""Coupe et retablit l'alimentation 5V du port USB Canon via uhubctl.
|
||||||
|
Necessite un hub USB compatible power switching (Realtek, etc.)."""
|
||||||
|
import glob, subprocess, time
|
||||||
|
try:
|
||||||
|
for f in glob.glob("/sys/bus/usb/devices/*/idVendor"):
|
||||||
|
if open(f).read().strip() == "04a9":
|
||||||
|
dev_id = __import__("os.path", fromlist=["basename"]).basename(
|
||||||
|
__import__("os.path", fromlist=["dirname"]).dirname(f)
|
||||||
|
)
|
||||||
|
if "." in dev_id:
|
||||||
|
hub = dev_id.rsplit(".", 1)[0]
|
||||||
|
port = dev_id.rsplit(".", 1)[1]
|
||||||
|
else:
|
||||||
|
hub = dev_id.rsplit("-", 1)[0]
|
||||||
|
port = dev_id.rsplit("-", 1)[1]
|
||||||
|
log.info(f"uhubctl power OFF: hub={hub} port={port}")
|
||||||
|
subprocess.run(
|
||||||
|
["sudo", "uhubctl", "-l", hub, "-p", port, "-a", "off"],
|
||||||
|
timeout=10, capture_output=True,
|
||||||
|
)
|
||||||
|
time.sleep(8)
|
||||||
|
subprocess.run(
|
||||||
|
["sudo", "uhubctl", "-l", hub, "-p", port, "-a", "on"],
|
||||||
|
timeout=10, capture_output=True,
|
||||||
|
)
|
||||||
|
log.info(f"uhubctl power ON: hub={hub} port={port}")
|
||||||
|
time.sleep(8)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
log.warning(f"_usb_power_cycle_canon: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
async def surveiller_dslr():
|
async def surveiller_dslr():
|
||||||
"""Surveille le DSLR en continu : detecte les deconnexions et reconnecte automatiquement.
|
"""Surveille le DSLR en continu : detecte les deconnexions et reconnecte automatiquement.
|
||||||
Apres plusieurs echecs, fait un reset USB ioctl pour debloquer le port PTP."""
|
Escalade : ioctl reset -> unbind/rebind -> uhubctl power cycle -> backoff long."""
|
||||||
global _dslr_erreurs
|
global _dslr_erreurs
|
||||||
_echecs_connexion = 0
|
_echecs_connexion = 0
|
||||||
while True:
|
while True:
|
||||||
|
if _echecs_connexion >= 10:
|
||||||
|
await asyncio.sleep(60)
|
||||||
|
else:
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
if capture_en_cours:
|
if capture_en_cours:
|
||||||
continue
|
continue
|
||||||
@@ -175,7 +212,6 @@ async def surveiller_dslr():
|
|||||||
_echecs_connexion += 1
|
_echecs_connexion += 1
|
||||||
await _reconnecter_dslr_avec_reset(_echecs_connexion)
|
await _reconnecter_dslr_avec_reset(_echecs_connexion)
|
||||||
else:
|
else:
|
||||||
# Non connecte : chercher et connecter un DSLR disponible
|
|
||||||
dslrs = gp.Camera.autodetect()
|
dslrs = gp.Camera.autodetect()
|
||||||
if len(dslrs) > 0:
|
if len(dslrs) > 0:
|
||||||
_echecs_connexion += 1
|
_echecs_connexion += 1
|
||||||
@@ -187,13 +223,19 @@ async def surveiller_dslr():
|
|||||||
|
|
||||||
async def _reconnecter_dslr_avec_reset(echecs: int):
|
async def _reconnecter_dslr_avec_reset(echecs: int):
|
||||||
"""Reconnexion DSLR avec escalade progressive :
|
"""Reconnexion DSLR avec escalade progressive :
|
||||||
1-2 : ioctl reset
|
1 : simple reconnexion
|
||||||
3+ : unbind/rebind USB (simule debranchement physique)
|
2 : ioctl reset
|
||||||
|
3-5 : unbind/rebind USB
|
||||||
|
6+ : uhubctl power cycle (coupe 5V du port)
|
||||||
|
10+ : backoff 60s entre tentatives
|
||||||
"""
|
"""
|
||||||
camera.deconnecter()
|
camera.deconnecter()
|
||||||
await diffuser_ws({"type": "camera_erreur", "message": "Appareil photo deconnecte"})
|
await diffuser_ws({"type": "camera_erreur", "message": "Appareil photo deconnecte"})
|
||||||
|
|
||||||
if echecs >= 3:
|
if echecs >= 6:
|
||||||
|
log.info(f"USB power cycle Canon (echec #{echecs})...")
|
||||||
|
await asyncio.get_event_loop().run_in_executor(None, _usb_power_cycle_canon)
|
||||||
|
elif echecs >= 3:
|
||||||
log.info(f"USB rebind Canon (echec #{echecs})...")
|
log.info(f"USB rebind Canon (echec #{echecs})...")
|
||||||
ok = await asyncio.get_event_loop().run_in_executor(None, _usb_rebind_canon)
|
ok = await asyncio.get_event_loop().run_in_executor(None, _usb_rebind_canon)
|
||||||
if not ok:
|
if not ok:
|
||||||
|
|||||||
Reference in New Issue
Block a user