@@ -58,7 +58,11 @@ def __init__(self, parent=None, nCallbacks=10):
5858 """
5959 self ._parent = parent
6060 self .nCallbacks = nCallbacks
61-
61+
62+ # Threading event: set to True to request cancellation of any running
63+ # streaming upload.
64+ self ._cancel_event = threading .Event ()
65+
6266 # Initialize callback functions for different types of OTA events
6367 self .init_callback_functions (self .nCallbacks )
6468
@@ -294,6 +298,15 @@ def get_platformio_upload_command(self, can_id, project_path="."):
294298 # MD5) and wait for ``{"ota_status":"success"|"error"}``.
295299 # ========================================================================
296300
301+ def cancel_streaming_ota (self ):
302+ """Request cancellation of any running streaming upload.
303+
304+ The upload worker checks this flag at every chunk boundary and will
305+ abort cleanly (restoring the parent serial connection) on the next
306+ iteration.
307+ """
308+ self ._cancel_event .set ()
309+
297310 def start_can_streaming_ota (self , can_id : int , firmware_path : str ,
298311 progress_callback = None , status_callback = None ,
299312 port : str = None , baud : int = STREAMING_BAUD ):
@@ -349,6 +362,8 @@ def start_can_streaming_ota(self, can_id: int, firmware_path: str,
349362 crc32 , num_chunks , progress_callback , status_callback )
350363 )
351364 upload_thread .daemon = True
365+ # Reset any previous cancellation before starting the new upload
366+ self ._cancel_event .clear ()
352367 upload_thread .start ()
353368
354369 return upload_thread
@@ -434,6 +449,12 @@ def _streaming_upload_worker(self, can_id, port, baud, firmware_data, firmware_s
434449 chunk_no = 0
435450
436451 while sent < firmware_size :
452+ # Honour cancellation requests between chunks
453+ if self ._cancel_event .is_set ():
454+ if status_callback :
455+ status_callback ("Upload cancelled by user" , False )
456+ return
457+
437458 end = min (sent + CHUNK_SIZE , firmware_size )
438459 payload = firmware_data [sent :end ]
439460 chunk_no += 1
@@ -445,6 +466,11 @@ def _streaming_upload_worker(self, can_id, port, baud, firmware_data, firmware_s
445466 # Block until the master ACKs at least ``sent`` bytes.
446467 deadline = time .time () + ACK_TIMEOUT_S
447468 while last_ack < sent :
469+ # Check for cancellation inside the ACK wait loop as well
470+ if self ._cancel_event .is_set ():
471+ if status_callback :
472+ status_callback ("Upload cancelled by user" , False )
473+ return
448474 last_ack , err = self ._drain_acks (ser , ack_buf , last_ack )
449475 if err is not None :
450476 if status_callback :
@@ -545,6 +571,8 @@ def _wait_for_status(self, ser, deadline, key_values=None,
545571 buf = bytearray ()
546572 while time .time () < deadline :
547573 chunk = ser .read (ser .in_waiting or 1 )
574+ # debug print of chunks
575+ # self._parent.logger.debug(f"Received chunk: {chunk}")
548576 if chunk :
549577 buf .extend (chunk )
550578 obj = self ._try_extract_json_status (bytes (buf ))
0 commit comments