99import struct
1010import subprocess
1111import time
12- from collections .abc import Callable
1312from functools import wraps
13+ from typing import Callable
14+ from typing import Optional
15+ from typing import Tuple
1416
1517import netifaces
1618import pexpect
1921
2022
2123def extract_address (
22- command : str , pattern : str , default_return : str = ''
24+ command : str ,
25+ pattern : str ,
26+ default_return : str = '' ,
27+ retries : int = 3 ,
28+ delay : int = 2 ,
2329) -> Callable [[Callable [[str ], str ]], Callable [[IdfDut ], str ]]:
2430 def decorator (func : Callable [[str ], str ]) -> Callable [[IdfDut ], str ]:
2531 @wraps (func )
2632 def wrapper (dut : IdfDut ) -> str :
27- clean_buffer (dut )
28- execute_command (dut , command )
29- try :
30- result = dut .expect (pattern , timeout = 5 )[1 ].decode ()
31- except Exception as e :
32- logging .error (f'Error: { e } ' )
33- return default_return
34- return func (result )
33+ # requires Python3.10
34+ # last_exception: Exception | None = None
35+ last_exception : Optional [Exception ] = None
36+ for attempt in range (1 , retries + 1 ):
37+ try :
38+ clean_buffer (dut )
39+ execute_command (dut , command )
40+ result = dut .expect (pattern , timeout = 5 )[1 ].decode ()
41+ return func (result )
42+ except Exception as e :
43+ logging .exception (f'[{ command } ] Attempt { attempt } /{ retries } failed: { e } ' )
44+ last_exception = e
45+ if attempt < retries :
46+ time .sleep (delay )
47+
48+ if last_exception :
49+ logging .exception (f'[{ command } ] Giving up after { retries } retries.' )
50+ return default_return
3551
3652 return wrapper
3753
@@ -133,7 +149,7 @@ def wait_for_join(dut: IdfDut, role: str) -> bool:
133149 return False
134150
135151
136- def joinWiFiNetwork (dut : IdfDut , wifi : wifi_parameter ) -> tuple [str , int ]:
152+ def joinWiFiNetwork (dut : IdfDut , wifi : wifi_parameter ) -> Tuple [str , int ]:
137153 clean_buffer (dut )
138154 ip_address = ''
139155 for order in range (1 , wifi .retry_times ):
@@ -173,35 +189,41 @@ def init_thread(dut: IdfDut) -> None:
173189 reset_thread (dut )
174190
175191
192+ def stop_thread (dut : IdfDut ) -> None :
193+ execute_command (dut , 'thread stop' )
194+ dut .expect ('disabled' , timeout = 20 )
195+ reset_thread (dut )
196+
197+
176198def reset_thread (dut : IdfDut ) -> None :
177199 execute_command (dut , 'factoryreset' )
178200 dut .expect ('OpenThread attached to netif' , timeout = 20 )
179201 wait (dut , 3 )
180202 clean_buffer (dut )
181203
182204
205+ def hardreset_dut (dut : IdfDut ) -> None :
206+ dut .serial .hard_reset ()
207+ time .sleep (5 )
208+ execute_command (dut , 'factoryreset' )
209+
210+
183211# get the mleid address of the thread
184- def get_mleid_addr (dut : IdfDut ) -> str :
185- dut_adress = ''
186- execute_command (dut , 'ipaddr mleid' )
187- dut_adress = dut .expect (r'\n((?:\w+:){7}\w+)\r' , timeout = 5 )[1 ].decode ()
188- return str (dut_adress )
212+ @extract_address ('ipaddr mleid' , r'\n((?:\w+:){7}\w+)\r' )
213+ def get_mleid_addr (addr : str ) -> str :
214+ return addr
189215
190216
191217# get the rloc address of the thread
192- def get_rloc_addr (dut : IdfDut ) -> str :
193- dut_adress = ''
194- execute_command (dut , 'ipaddr rloc' )
195- dut_adress = dut .expect (r'\n((?:\w+:){7}\w+)\r' , timeout = 5 )[1 ].decode ()
196- return str (dut_adress )
218+ @extract_address ('ipaddr rloc' , r'\n((?:\w+:){7}\w+)\r' )
219+ def get_rloc_addr (addr : str ) -> str :
220+ return addr
197221
198222
199223# get the linklocal address of the thread
200- def get_linklocal_addr (dut : IdfDut ) -> str :
201- dut_adress = ''
202- execute_command (dut , 'ipaddr linklocal' )
203- dut_adress = dut .expect (r'\n((?:\w+:){7}\w+)\r' , timeout = 5 )[1 ].decode ()
204- return str (dut_adress )
224+ @extract_address ('ipaddr linklocal' , r'\n((?:\w+:){7}\w+)\r' )
225+ def get_linklocal_addr (addr : str ) -> str :
226+ return addr
205227
206228
207229# get the global unicast address of the thread:
@@ -222,7 +244,7 @@ def get_rloc16_addr(rloc16: str) -> str:
222244# ping of thread
223245def ot_ping (
224246 dut : IdfDut , target : str , timeout : int = 5 , count : int = 1 , size : int = 56 , interval : int = 1 , hoplimit : int = 64
225- ) -> tuple [int , int ]:
247+ ) -> Tuple [int , int ]:
226248 command = f'ping { str (target )} { size } { count } { interval } { hoplimit } { str (timeout )} '
227249 execute_command (dut , command )
228250 transmitted = dut .expect (r'(\d+) packets transmitted' , timeout = 60 )[1 ].decode ()
@@ -619,22 +641,19 @@ def decimal_to_hex(decimal_str: str) -> str:
619641 return hex_str
620642
621643
622- def get_omrprefix (br : IdfDut ) -> str :
623- execute_command (br , 'br omrprefix' )
624- omrprefix = br .expect (r'Local: ((?:\w+:){4}):/\d+\r' , timeout = 5 )[1 ].decode ()
625- return str (omrprefix )
644+ @extract_address ('br omrprefix' , r'Local: ((?:\w+:){4}):/\d+\r' )
645+ def get_omrprefix (addr : str ) -> str :
646+ return addr
626647
627648
628- def get_onlinkprefix (br : IdfDut ) -> str :
629- execute_command (br , 'br onlinkprefix' )
630- onlinkprefix = br .expect (r'Local: ((?:\w+:){4}):/\d+\r' , timeout = 5 )[1 ].decode ()
631- return str (onlinkprefix )
649+ @extract_address ('br onlinkprefix' , r'Local: ((?:\w+:){4}):/\d+\r' )
650+ def get_onlinkprefix (addr : str ) -> str :
651+ return addr
632652
633653
634- def get_nat64prefix (br : IdfDut ) -> str :
635- execute_command (br , 'br nat64prefix' )
636- nat64prefix = br .expect (r'Local: ((?:\w+:){6}):/\d+' , timeout = 5 )[1 ].decode ()
637- return str (nat64prefix )
654+ @extract_address ('br nat64prefix' , r'Local: ((?:\w+:){6}):/\d+' )
655+ def get_nat64prefix (addr : str ) -> str :
656+ return addr
638657
639658
640659def execute_command (dut : IdfDut , command : str , prefix : str = 'ot ' ) -> None :
@@ -647,3 +666,17 @@ def get_ouput_string(dut: IdfDut, command: str, wait_time: int) -> str:
647666 tmp = dut .expect (pexpect .TIMEOUT , timeout = wait_time )
648667 clean_buffer (dut )
649668 return str (tmp )
669+
670+
671+ def wait_for_host_network (host : str = '8.8.8.8' , retries : int = 6 , interval : int = 10 ) -> None :
672+ for attempt in range (1 , retries + 1 ):
673+ try :
674+ subprocess .run (['ping' , '-c' , '1' , '-W' , '2' , host ], check = True )
675+ logging .info (f'Host network reachable on attempt { attempt } ' )
676+ return
677+ except subprocess .CalledProcessError :
678+ logging .info (f'Ping attempt { attempt } failed, retrying in { interval } seconds...' )
679+ if attempt < retries :
680+ time .sleep (interval )
681+ else :
682+ raise RuntimeError (f'Host network is not reachable after { retries } attempts.' )
0 commit comments