88import argparse
99import binascii
1010import sys
11+ import threading
1112import time
1213
1314from rich .live import Live
@@ -41,7 +42,7 @@ class SubCommand(InfuseCommand):
4142 DESCRIPTION = "Automatically OTA upgrade observed devices"
4243
4344 def __init__ (self , args ):
44- self ._client = LocalClient (args . server_sock , 1.0 )
45+ self ._clients = [ LocalClient (addr , 1.0 ) for addr in args . server_sock ]
4546 self ._conn_timeout = args .conn_timeout
4647 self ._min_rssi : int | None = args .rssi
4748 self ._explicit_ids : list [int ] = []
@@ -113,6 +114,7 @@ def __init__(self, args):
113114 with args .list .open ("r" ) as f :
114115 for line in f .readlines ():
115116 self ._explicit_ids .append (int (line .strip (), 0 ))
117+ self .end = False
116118
117119 @classmethod
118120 def add_parser (cls , parser ):
@@ -129,7 +131,7 @@ def add_parser(cls, parser):
129131 explicit .add_argument ("--id" , type = InfuseDeviceId , help = "Single device to upgrade" )
130132 explicit .add_argument ("--list" , type = ValidFile , help = "File containing a list of IDs to upgrade" )
131133
132- add_server_port_parser (parser )
134+ add_server_port_parser (parser , multi_port = True )
133135
134136 @property
135137 def _actioning (self ) -> set [int ]:
@@ -261,6 +263,9 @@ def run_file_copy(self, live: Live, mtu: int, source: HopReceived, client: Local
261263
262264 def run_thread (self , live : Live , client : LocalClient ):
263265 for source , announce in client .observe_announce ():
266+ if self .end :
267+ return
268+
264269 live .update (self .progress_table ())
265270 if len (self ._explicit_ids ):
266271 if source .infuse_id not in self ._explicit_ids :
@@ -368,14 +373,61 @@ def run_thread(self, live: Live, client: LocalClient):
368373 live .update (self .progress_table ())
369374
370375 def run (self ):
371- if not self ._client .comms_check ():
372- sys .exit ("No communications gateway detected (infuse gateway/bt_native)" )
376+ # Check Gateways are available
377+ unavailable : list [LocalClient ] = []
378+ for client in self ._clients :
379+ if not client .comms_check ():
380+ unavailable .append (client )
381+ if len (unavailable ) != 0 :
382+ print (
383+ f"Warning: Could not use { len (unavailable )} gateways on port(s)"
384+ f" { [x ._input_sock .getsockname () for x in unavailable ]} ."
385+ )
373386
387+ # If requested, load single diff onto gateways.
374388 if self ._single_diff :
375- try :
376- self .gateway_diff_load (self ._client )
377- except RuntimeError as e :
378- sys .exit (str (e ))
389+ for client in unavailable :
390+ self ._clients .remove (client )
391+ for client in self ._clients :
392+ try :
393+ self .gateway_diff_load (client )
394+ except RuntimeError as e :
395+ unavailable .append (client )
396+ port_name = client ._input_sock .getsockname ()
397+ print (f"Skipping Gateway on port { port_name } : { '' .join (e .args )} ." )
398+
399+ # Ensure there is at least one operational gateway available
400+ if len (unavailable ) == len (self ._clients ):
401+ sys .exit ("No communications gateway detected (infuse gateway/bt_native)" )
402+ for client in unavailable :
403+ self ._clients .remove (client )
404+
405+ if len (self ._clients ) > 1 :
406+ print (
407+ f"running on { len (self ._clients )} gateways "
408+ f"{ [x ._input_sock .getsockname () for x in self ._clients ]} "
409+ )
379410
411+ threads : list [threading .Thread ] = []
380412 with Live (self .progress_table (), refresh_per_second = 4 ) as live :
381- self .run_thread (live , self ._client )
413+ for client in self ._clients :
414+ socket = client ._input_sock .getsockname ()
415+ t = threading .Thread (
416+ target = self .run_thread ,
417+ args = (live , client ),
418+ name = f"OTA Upgrade { socket } " ,
419+ )
420+ threads .append (t )
421+ if len (threads ) > 1 :
422+ try :
423+ for t in threads :
424+ t .start ()
425+ for t in threads :
426+ t .join ()
427+ except KeyboardInterrupt :
428+ self .end = True
429+ self .state_update (live , "Shutting down..." )
430+ for t in threads :
431+ t .join ()
432+ else :
433+ threads [0 ].run ()
0 commit comments