Parcourir la source

🔨 Abort firmware update on transfer error (#24472)

GHGiampy il y a 1 an
Parent
révision
1d31b6215a
Aucun compte lié à l'adresse e-mail de l'auteur
2 fichiers modifiés avec 112 ajouts et 43 suppressions
  1. 18
    5
      buildroot/share/scripts/MarlinBinaryProtocol.py
  2. 94
    38
      buildroot/share/scripts/upload.py

+ 18
- 5
buildroot/share/scripts/MarlinBinaryProtocol.py Voir le fichier

@@ -376,11 +376,13 @@ class FileTransferProtocol(object):
376 376
         token, data = self.await_response(1000)
377 377
         if token == 'PFT:success':
378 378
             print("File closed")
379
-            return
379
+            return True
380 380
         elif token == 'PFT:ioerror':
381 381
             print("Client storage device IO error")
382
+            return False
382 383
         elif token == 'PFT:invalid':
383 384
             print("No open file")
385
+            return False
384 386
 
385 387
     def abort(self):
386 388
         self.protocol.send(FileTransferProtocol.protocol_id, FileTransferProtocol.Packet.ABORT);
@@ -417,12 +419,23 @@ class FileTransferProtocol(object):
417 419
             self.write(data[start:end])
418 420
             kibs = (( (i+1) * block_size) / 1024) / (millis() + 1 - start_time) * 1000
419 421
             if (i / blocks) >= dump_pctg:
420
-                print("\r{0:2.2f}% {1:4.2f}KiB/s {2} Errors: {3}".format((i / blocks) * 100, kibs, "[{0:4.2f}KiB/s]".format(kibs * cratio) if compression_support else "", self.protocol.errors), end='')
422
+                print("\r{0:2.0f}% {1:4.2f}KiB/s {2} Errors: {3}".format((i / blocks) * 100, kibs, "[{0:4.2f}KiB/s]".format(kibs * cratio) if compression_support else "", self.protocol.errors), end='')
421 423
                 dump_pctg += 0.1
422
-        print("\r{0:2.2f}% {1:4.2f}KiB/s {2} Errors: {3}".format(100, kibs, "[{0:4.2f}KiB/s]".format(kibs * cratio) if compression_support else "", self.protocol.errors)) # no one likes transfers finishing at 99.8%
423
-
424
-        self.close()
424
+            if self.protocol.errors > 0:
425
+                # Dump last status (errors may not be visible)
426
+                print("\r{0:2.0f}% {1:4.2f}KiB/s {2} Errors: {3} - Aborting...".format((i / blocks) * 100, kibs, "[{0:4.2f}KiB/s]".format(kibs * cratio) if compression_support else "", self.protocol.errors), end='')
427
+                print("")   # New line to break the transfer speed line
428
+                self.close()
429
+                print("Transfer aborted due to protocol errors")
430
+                #raise Exception("Transfer aborted due to protocol errors")
431
+                return False;
432
+        print("\r{0:2.0f}% {1:4.2f}KiB/s {2} Errors: {3}".format(100, kibs, "[{0:4.2f}KiB/s]".format(kibs * cratio) if compression_support else "", self.protocol.errors)) # no one likes transfers finishing at 99.8%
433
+
434
+        if not self.close():
435
+            print("Transfer failed")
436
+            return False
425 437
         print("Transfer complete")
438
+        return True
426 439
 
427 440
 
428 441
 class EchoProtocol(object):

+ 94
- 38
buildroot/share/scripts/upload.py Voir le fichier

@@ -20,14 +20,18 @@ Import("env")
20 20
 
21 21
 import MarlinBinaryProtocol
22 22
 
23
-# Internal debug flag
24
-Debug = False
25
-
26 23
 #-----------------#
27 24
 # Upload Callback #
28 25
 #-----------------#
29 26
 def Upload(source, target, env):
30 27
 
28
+    #-------#
29
+    # Debug #
30
+    #-------#
31
+    Debug = False                # Set to True to enable script debug
32
+    def debugPrint(data):
33
+        if Debug: print(f"[Debug]: {data}")
34
+
31 35
     #------------------#
32 36
     # Marlin functions #
33 37
     #------------------#
@@ -39,19 +43,35 @@ def Upload(source, target, env):
39 43
     # Port functions #
40 44
     #----------------#
41 45
     def _GetUploadPort(env):
42
-        if Debug: print('Autodetecting upload port...')
46
+        debugPrint('Autodetecting upload port...')
43 47
         env.AutodetectUploadPort(env)
44
-        port = env.subst('$UPLOAD_PORT')
45
-        if not port:
48
+        portName = env.subst('$UPLOAD_PORT')
49
+        if not portName:
46 50
             raise Exception('Error detecting the upload port.')
47
-        if Debug: print('OK')
48
-        return port
51
+        debugPrint('OK')
52
+        return portName
49 53
 
50 54
     #-------------------------#
51 55
     # Simple serial functions #
52 56
     #-------------------------#
57
+    def _OpenPort():
58
+        # Open serial port
59
+        if port.is_open: return
60
+        debugPrint('Opening upload port...')
61
+        port.open()
62
+        port.reset_input_buffer()
63
+        debugPrint('OK')
64
+
65
+    def _ClosePort():
66
+        # Open serial port
67
+        if port is None: return
68
+        if not port.is_open: return
69
+        debugPrint('Closing upload port...')
70
+        port.close()
71
+        debugPrint('OK')
72
+
53 73
     def _Send(data):
54
-        if Debug: print(f'>> {data}')
74
+        debugPrint(f'>> {data}')
55 75
         strdata = bytearray(data, 'utf8') + b'\n'
56 76
         port.write(strdata)
57 77
         time.sleep(0.010)
@@ -60,37 +80,37 @@ def Upload(source, target, env):
60 80
         clean_responses = []
61 81
         responses = port.readlines()
62 82
         for Resp in responses:
63
-            # Test: suppress invaid chars (coming from debug info)
83
+            # Suppress invalid chars (coming from debug info)
64 84
             try:
65 85
                 clean_response = Resp.decode('utf8').rstrip().lstrip()
66 86
                 clean_responses.append(clean_response)
67 87
             except:
68 88
                 pass
69
-            if Debug: print(f'<< {clean_response}')
89
+            debugPrint(f'<< {clean_response}')
70 90
         return clean_responses
71 91
 
72 92
     #------------------#
73 93
     # SDCard functions #
74 94
     #------------------#
75 95
     def _CheckSDCard():
76
-        if Debug: print('Checking SD card...')
96
+        debugPrint('Checking SD card...')
77 97
         _Send('M21')
78 98
         Responses = _Recv()
79 99
         if len(Responses) < 1 or not any('SD card ok' in r for r in Responses):
80 100
             raise Exception('Error accessing SD card')
81
-        if Debug: print('SD Card OK')
101
+        debugPrint('SD Card OK')
82 102
         return True
83 103
 
84 104
     #----------------#
85 105
     # File functions #
86 106
     #----------------#
87 107
     def _GetFirmwareFiles(UseLongFilenames):
88
-        if Debug: print('Get firmware files...')
108
+        debugPrint('Get firmware files...')
89 109
         _Send(f"M20 F{'L' if UseLongFilenames else ''}")
90 110
         Responses = _Recv()
91 111
         if len(Responses) < 3 or not any('file list' in r for r in Responses):
92 112
             raise Exception('Error getting firmware files')
93
-        if Debug: print('OK')
113
+        debugPrint('OK')
94 114
         return Responses
95 115
 
96 116
     def _FilterFirmwareFiles(FirmwareList, UseLongFilenames):
@@ -114,6 +134,17 @@ def Upload(source, target, env):
114 134
             raise Exception(f"Firmware file '{FirmwareFile}' not removed")
115 135
         return Removed
116 136
 
137
+    def _RollbackUpload(FirmwareFile):
138
+        if not rollback: return
139
+        print(f"Rollback: trying to delete firmware '{FirmwareFile}'...")
140
+        _OpenPort()
141
+        # Wait for SD card release
142
+        time.sleep(1)
143
+        # Remount SD card
144
+        _CheckSDCard()
145
+        print(' OK' if _RemoveFirmwareFile(FirmwareFile) else ' Error!')
146
+        _ClosePort()
147
+
117 148
 
118 149
     #---------------------#
119 150
     # Callback Entrypoint #
@@ -121,6 +152,7 @@ def Upload(source, target, env):
121 152
     port = None
122 153
     protocol = None
123 154
     filetransfer = None
155
+    rollback = False
124 156
 
125 157
     # Get Marlin evironment vars
126 158
     MarlinEnv = env['MARLIN_FEATURES']
@@ -204,9 +236,9 @@ def Upload(source, target, env):
204 236
             if not marlin_custom_firmware_upload:
205 237
                 raise Exception(f"CUSTOM_FIRMWARE_UPLOAD must be enabled in 'Configuration_adv.h' for '{marlin_motherboard}'")
206 238
 
207
-            # Init serial port
239
+            # Init & Open serial port
208 240
             port = serial.Serial(upload_port, baudrate = upload_speed, write_timeout = 0, timeout = 0.1)
209
-            port.reset_input_buffer()
241
+            _OpenPort()
210 242
 
211 243
             # Check SD card status
212 244
             _CheckSDCard()
@@ -228,24 +260,26 @@ def Upload(source, target, env):
228 260
                     print(' OK' if _RemoveFirmwareFile(OldFirmwareFile) else ' Error!')
229 261
 
230 262
             # Close serial
231
-            port.close()
263
+            _ClosePort()
232 264
 
233 265
             # Cleanup completed
234
-            if Debug: print('Cleanup completed')
266
+            debugPrint('Cleanup completed')
235 267
 
236 268
         # WARNING! The serial port must be closed here because the serial transfer that follow needs it!
237 269
 
238 270
         # Upload firmware file
239
-        if Debug: print(f"Copy '{upload_firmware_source_name}' --> '{upload_firmware_target_name}'")
271
+        debugPrint(f"Copy '{upload_firmware_source_name}' --> '{upload_firmware_target_name}'")
240 272
         protocol = MarlinBinaryProtocol.Protocol(upload_port, upload_speed, upload_blocksize, float(upload_error_ratio), int(upload_timeout))
241 273
         #echologger = MarlinBinaryProtocol.EchoProtocol(protocol)
242 274
         protocol.connect()
275
+        # Mark the rollback (delete broken transfer) from this point on
276
+        rollback = True
243 277
         filetransfer = MarlinBinaryProtocol.FileTransferProtocol(protocol)
244
-        filetransfer.copy(upload_firmware_source_name, upload_firmware_target_name, upload_compression, upload_test)
278
+        transferOK = filetransfer.copy(upload_firmware_source_name, upload_firmware_target_name, upload_compression, upload_test)
245 279
         protocol.disconnect()
246 280
 
247 281
         # Notify upload completed
248
-        protocol.send_ascii('M117 Firmware uploaded')
282
+        protocol.send_ascii('M117 Firmware uploaded' if transferOK else 'M117 Firmware upload failed')
249 283
 
250 284
         # Remount SD card
251 285
         print('Wait for SD card release...')
@@ -253,34 +287,56 @@ def Upload(source, target, env):
253 287
         print('Remount SD card')
254 288
         protocol.send_ascii('M21')
255 289
 
256
-        # Trigger firmware update
257
-        if upload_reset:
258
-            print('Trigger firmware update...')
259
-            protocol.send_ascii('M997', True)
290
+        # Transfer failed?
291
+        if not transferOK:
292
+            protocol.shutdown()
293
+            _RollbackUpload(upload_firmware_target_name)
294
+        else:
295
+            # Trigger firmware update
296
+            if upload_reset:
297
+                print('Trigger firmware update...')
298
+                protocol.send_ascii('M997', True)
299
+            protocol.shutdown()
260 300
 
261
-        protocol.shutdown()
262
-        print('Firmware update completed')
301
+        print('Firmware update completed' if transferOK else 'Firmware update failed')
302
+        return 0 if transferOK else -1
263 303
 
264 304
     except KeyboardInterrupt:
265
-        if port: port.close()
305
+        print('Aborted by user')
266 306
         if filetransfer: filetransfer.abort()
267
-        if protocol: protocol.shutdown()
307
+        if protocol: 
308
+            protocol.disconnect()
309
+            protocol.shutdown()
310
+        _RollbackUpload(upload_firmware_target_name)
311
+        _ClosePort()
268 312
         raise
269 313
 
270 314
     except serial.SerialException as se:
271
-        if port: port.close()
272
-        print(f'Serial excepion: {se}')
315
+        # This exception is raised only for send_ascii data (not for binary transfer)
316
+        print(f'Serial excepion: {se}, transfer aborted')
317
+        if protocol: 
318
+            protocol.disconnect()
319
+            protocol.shutdown()
320
+        _RollbackUpload(upload_firmware_target_name)
321
+        _ClosePort()
273 322
         raise Exception(se)
274 323
 
275 324
     except MarlinBinaryProtocol.FatalError:
276
-        if port: port.close()
277
-        if protocol: protocol.shutdown()
278
-        print('Too many retries, Abort')
325
+        print('Too many retries, transfer aborted')
326
+        if protocol: 
327
+            protocol.disconnect()
328
+            protocol.shutdown()
329
+        _RollbackUpload(upload_firmware_target_name)
330
+        _ClosePort()
279 331
         raise
280 332
 
281
-    except:
282
-        if port: port.close()
283
-        if protocol: protocol.shutdown()
333
+    except Exception as ex:
334
+        print(f"\nException: {ex}, transfer aborted")
335
+        if protocol: 
336
+            protocol.disconnect()
337
+            protocol.shutdown()
338
+        _RollbackUpload(upload_firmware_target_name)
339
+        _ClosePort()
284 340
         print('Firmware not updated')
285 341
         raise
286 342
 

Chargement…
Annuler
Enregistrer