Browse Source

🔨 Abort firmware update on transfer error (#24472)

GHGiampy 2 years ago
parent
commit
1d31b6215a
No account linked to committer's email address
2 changed files with 112 additions and 43 deletions
  1. 18
    5
      buildroot/share/scripts/MarlinBinaryProtocol.py
  2. 94
    38
      buildroot/share/scripts/upload.py

+ 18
- 5
buildroot/share/scripts/MarlinBinaryProtocol.py View File

376
         token, data = self.await_response(1000)
376
         token, data = self.await_response(1000)
377
         if token == 'PFT:success':
377
         if token == 'PFT:success':
378
             print("File closed")
378
             print("File closed")
379
-            return
379
+            return True
380
         elif token == 'PFT:ioerror':
380
         elif token == 'PFT:ioerror':
381
             print("Client storage device IO error")
381
             print("Client storage device IO error")
382
+            return False
382
         elif token == 'PFT:invalid':
383
         elif token == 'PFT:invalid':
383
             print("No open file")
384
             print("No open file")
385
+            return False
384
 
386
 
385
     def abort(self):
387
     def abort(self):
386
         self.protocol.send(FileTransferProtocol.protocol_id, FileTransferProtocol.Packet.ABORT);
388
         self.protocol.send(FileTransferProtocol.protocol_id, FileTransferProtocol.Packet.ABORT);
417
             self.write(data[start:end])
419
             self.write(data[start:end])
418
             kibs = (( (i+1) * block_size) / 1024) / (millis() + 1 - start_time) * 1000
420
             kibs = (( (i+1) * block_size) / 1024) / (millis() + 1 - start_time) * 1000
419
             if (i / blocks) >= dump_pctg:
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
                 dump_pctg += 0.1
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
         print("Transfer complete")
437
         print("Transfer complete")
438
+        return True
426
 
439
 
427
 
440
 
428
 class EchoProtocol(object):
441
 class EchoProtocol(object):

+ 94
- 38
buildroot/share/scripts/upload.py View File

20
 
20
 
21
 import MarlinBinaryProtocol
21
 import MarlinBinaryProtocol
22
 
22
 
23
-# Internal debug flag
24
-Debug = False
25
-
26
 #-----------------#
23
 #-----------------#
27
 # Upload Callback #
24
 # Upload Callback #
28
 #-----------------#
25
 #-----------------#
29
 def Upload(source, target, env):
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
     # Marlin functions #
36
     # Marlin functions #
33
     #------------------#
37
     #------------------#
39
     # Port functions #
43
     # Port functions #
40
     #----------------#
44
     #----------------#
41
     def _GetUploadPort(env):
45
     def _GetUploadPort(env):
42
-        if Debug: print('Autodetecting upload port...')
46
+        debugPrint('Autodetecting upload port...')
43
         env.AutodetectUploadPort(env)
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
             raise Exception('Error detecting the upload port.')
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
     # Simple serial functions #
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
     def _Send(data):
73
     def _Send(data):
54
-        if Debug: print(f'>> {data}')
74
+        debugPrint(f'>> {data}')
55
         strdata = bytearray(data, 'utf8') + b'\n'
75
         strdata = bytearray(data, 'utf8') + b'\n'
56
         port.write(strdata)
76
         port.write(strdata)
57
         time.sleep(0.010)
77
         time.sleep(0.010)
60
         clean_responses = []
80
         clean_responses = []
61
         responses = port.readlines()
81
         responses = port.readlines()
62
         for Resp in responses:
82
         for Resp in responses:
63
-            # Test: suppress invaid chars (coming from debug info)
83
+            # Suppress invalid chars (coming from debug info)
64
             try:
84
             try:
65
                 clean_response = Resp.decode('utf8').rstrip().lstrip()
85
                 clean_response = Resp.decode('utf8').rstrip().lstrip()
66
                 clean_responses.append(clean_response)
86
                 clean_responses.append(clean_response)
67
             except:
87
             except:
68
                 pass
88
                 pass
69
-            if Debug: print(f'<< {clean_response}')
89
+            debugPrint(f'<< {clean_response}')
70
         return clean_responses
90
         return clean_responses
71
 
91
 
72
     #------------------#
92
     #------------------#
73
     # SDCard functions #
93
     # SDCard functions #
74
     #------------------#
94
     #------------------#
75
     def _CheckSDCard():
95
     def _CheckSDCard():
76
-        if Debug: print('Checking SD card...')
96
+        debugPrint('Checking SD card...')
77
         _Send('M21')
97
         _Send('M21')
78
         Responses = _Recv()
98
         Responses = _Recv()
79
         if len(Responses) < 1 or not any('SD card ok' in r for r in Responses):
99
         if len(Responses) < 1 or not any('SD card ok' in r for r in Responses):
80
             raise Exception('Error accessing SD card')
100
             raise Exception('Error accessing SD card')
81
-        if Debug: print('SD Card OK')
101
+        debugPrint('SD Card OK')
82
         return True
102
         return True
83
 
103
 
84
     #----------------#
104
     #----------------#
85
     # File functions #
105
     # File functions #
86
     #----------------#
106
     #----------------#
87
     def _GetFirmwareFiles(UseLongFilenames):
107
     def _GetFirmwareFiles(UseLongFilenames):
88
-        if Debug: print('Get firmware files...')
108
+        debugPrint('Get firmware files...')
89
         _Send(f"M20 F{'L' if UseLongFilenames else ''}")
109
         _Send(f"M20 F{'L' if UseLongFilenames else ''}")
90
         Responses = _Recv()
110
         Responses = _Recv()
91
         if len(Responses) < 3 or not any('file list' in r for r in Responses):
111
         if len(Responses) < 3 or not any('file list' in r for r in Responses):
92
             raise Exception('Error getting firmware files')
112
             raise Exception('Error getting firmware files')
93
-        if Debug: print('OK')
113
+        debugPrint('OK')
94
         return Responses
114
         return Responses
95
 
115
 
96
     def _FilterFirmwareFiles(FirmwareList, UseLongFilenames):
116
     def _FilterFirmwareFiles(FirmwareList, UseLongFilenames):
114
             raise Exception(f"Firmware file '{FirmwareFile}' not removed")
134
             raise Exception(f"Firmware file '{FirmwareFile}' not removed")
115
         return Removed
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
     # Callback Entrypoint #
150
     # Callback Entrypoint #
121
     port = None
152
     port = None
122
     protocol = None
153
     protocol = None
123
     filetransfer = None
154
     filetransfer = None
155
+    rollback = False
124
 
156
 
125
     # Get Marlin evironment vars
157
     # Get Marlin evironment vars
126
     MarlinEnv = env['MARLIN_FEATURES']
158
     MarlinEnv = env['MARLIN_FEATURES']
204
             if not marlin_custom_firmware_upload:
236
             if not marlin_custom_firmware_upload:
205
                 raise Exception(f"CUSTOM_FIRMWARE_UPLOAD must be enabled in 'Configuration_adv.h' for '{marlin_motherboard}'")
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
             port = serial.Serial(upload_port, baudrate = upload_speed, write_timeout = 0, timeout = 0.1)
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
             # Check SD card status
243
             # Check SD card status
212
             _CheckSDCard()
244
             _CheckSDCard()
228
                     print(' OK' if _RemoveFirmwareFile(OldFirmwareFile) else ' Error!')
260
                     print(' OK' if _RemoveFirmwareFile(OldFirmwareFile) else ' Error!')
229
 
261
 
230
             # Close serial
262
             # Close serial
231
-            port.close()
263
+            _ClosePort()
232
 
264
 
233
             # Cleanup completed
265
             # Cleanup completed
234
-            if Debug: print('Cleanup completed')
266
+            debugPrint('Cleanup completed')
235
 
267
 
236
         # WARNING! The serial port must be closed here because the serial transfer that follow needs it!
268
         # WARNING! The serial port must be closed here because the serial transfer that follow needs it!
237
 
269
 
238
         # Upload firmware file
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
         protocol = MarlinBinaryProtocol.Protocol(upload_port, upload_speed, upload_blocksize, float(upload_error_ratio), int(upload_timeout))
272
         protocol = MarlinBinaryProtocol.Protocol(upload_port, upload_speed, upload_blocksize, float(upload_error_ratio), int(upload_timeout))
241
         #echologger = MarlinBinaryProtocol.EchoProtocol(protocol)
273
         #echologger = MarlinBinaryProtocol.EchoProtocol(protocol)
242
         protocol.connect()
274
         protocol.connect()
275
+        # Mark the rollback (delete broken transfer) from this point on
276
+        rollback = True
243
         filetransfer = MarlinBinaryProtocol.FileTransferProtocol(protocol)
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
         protocol.disconnect()
279
         protocol.disconnect()
246
 
280
 
247
         # Notify upload completed
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
         # Remount SD card
284
         # Remount SD card
251
         print('Wait for SD card release...')
285
         print('Wait for SD card release...')
253
         print('Remount SD card')
287
         print('Remount SD card')
254
         protocol.send_ascii('M21')
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
     except KeyboardInterrupt:
304
     except KeyboardInterrupt:
265
-        if port: port.close()
305
+        print('Aborted by user')
266
         if filetransfer: filetransfer.abort()
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
         raise
312
         raise
269
 
313
 
270
     except serial.SerialException as se:
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
         raise Exception(se)
322
         raise Exception(se)
274
 
323
 
275
     except MarlinBinaryProtocol.FatalError:
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
         raise
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
         print('Firmware not updated')
340
         print('Firmware not updated')
285
         raise
341
         raise
286
 
342
 

Loading…
Cancel
Save