66
77import argparse
88import os
9+ import time
910
1011from runners .core import ZephyrBinaryRunner , RunnerCaps
1112
1920# Default Python-CAN context to use, see python-can documentation for details
2021DEFAULT_CAN_CONTEXT = 'default'
2122
23+ # Default program number
24+ DEFAULT_PROGRAM_NUMBER = 1
25+
26+ # Default timeouts and retries
27+ DEFAULT_TIMEOUT = 10.0 # seconds
28+ DEFAULT_SDO_TIMEOUT = 0.3 # seconds
29+ DEFAULT_SDO_RETRIES = 1
30+
2231# Object dictionary indexes
2332H1F50_PROGRAM_DATA = 0x1F50
2433H1F51_PROGRAM_CTRL = 0x1F51
@@ -40,8 +49,9 @@ def __call__(self, parser, namespace, values, option_string=None):
4049class CANopenBinaryRunner (ZephyrBinaryRunner ):
4150 '''Runner front-end for CANopen.'''
4251 def __init__ (self , cfg , node_id , can_context = DEFAULT_CAN_CONTEXT ,
43- program_number = 1 , confirm = True ,
44- confirm_only = True , timeout = 10 ):
52+ program_number = DEFAULT_PROGRAM_NUMBER , confirm = True ,
53+ confirm_only = True , timeout = DEFAULT_TIMEOUT ,
54+ sdo_retries = DEFAULT_SDO_RETRIES , sdo_timeout = DEFAULT_SDO_TIMEOUT ):
4555 if MISSING_REQUIREMENTS :
4656 raise RuntimeError ('one or more Python dependencies were missing; '
4757 "see the getting started guide for details on "
@@ -55,7 +65,9 @@ def __init__(self, cfg, node_id, can_context=DEFAULT_CAN_CONTEXT,
5565 self .downloader = CANopenProgramDownloader (logger = self .logger ,
5666 node_id = node_id ,
5767 can_context = can_context ,
58- program_number = program_number )
68+ program_number = program_number ,
69+ sdo_retries = sdo_retries ,
70+ sdo_timeout = sdo_timeout )
5971
6072 @classmethod
6173 def name (cls ):
@@ -72,28 +84,35 @@ def do_add_parser(cls, parser):
7284
7385 # Optional:
7486 parser .add_argument ('--can-context' , default = DEFAULT_CAN_CONTEXT ,
75- help = 'Custom Python-CAN context to use' )
76- parser .add_argument ('--program-number' , default = 1 ,
77- help = 'program number, default is 1 ' )
87+ help = f' Python-CAN context to use (default: { DEFAULT_CAN_CONTEXT } ) ' )
88+ parser .add_argument ('--program-number' , type = int , default = DEFAULT_PROGRAM_NUMBER ,
89+ help = f 'program number ( default: { DEFAULT_PROGRAM_NUMBER } ) ' )
7890 parser .add_argument ('--confirm' , '--no-confirm' ,
7991 dest = 'confirm' , nargs = 0 ,
8092 action = ToggleAction ,
8193 help = 'confirm after starting? (default: yes)' )
8294 parser .add_argument ('--confirm-only' , default = False , action = 'store_true' ,
8395 help = 'confirm only, no program download (default: no)' )
84- parser .add_argument ('--timeout' , default = 10 ,
85- help = 'boot-up timeout, default is 10 seconds' )
96+ parser .add_argument ('--timeout' , type = float , default = DEFAULT_TIMEOUT ,
97+ help = f'Timeout in seconds (default: { DEFAULT_TIMEOUT } )' )
98+ parser .add_argument ('--sdo-retries' , type = int , default = DEFAULT_SDO_RETRIES ,
99+ help = f'CANopen SDO request retries (default: { DEFAULT_SDO_RETRIES } )' )
100+ parser .add_argument ('--sdo-timeout' , type = float , default = DEFAULT_SDO_TIMEOUT ,
101+ help = f'''CANopen SDO response timeout in seconds
102+ (default: { DEFAULT_SDO_TIMEOUT } )''' )
86103
87104 parser .set_defaults (confirm = True )
88105
89106 @classmethod
90107 def do_create (cls , cfg , args ):
91108 return CANopenBinaryRunner (cfg , int (args .node_id ),
92109 can_context = args .can_context ,
93- program_number = int ( args .program_number ) ,
110+ program_number = args .program_number ,
94111 confirm = args .confirm ,
95112 confirm_only = args .confirm_only ,
96- timeout = int (args .timeout ))
113+ timeout = args .timeout ,
114+ sdo_retries = args .sdo_retries ,
115+ sdo_timeout = args .sdo_timeout )
97116
98117 def do_run (self , command , ** kwargs ):
99118 if command == 'flash' :
@@ -107,7 +126,7 @@ def flash(self, **kwargs):
107126 self .downloader .program_number )
108127
109128 self .downloader .connect ()
110- status = self .downloader .flash_status ( )
129+ status = self .downloader .wait_for_flash_status_ok ( self . timeout )
111130 if status == 0 :
112131 self .downloader .swid ()
113132 else :
@@ -126,13 +145,15 @@ def flash(self, **kwargs):
126145
127146 self .downloader .stop_program ()
128147 self .downloader .clear_program ()
148+ self .downloader .wait_for_flash_status_ok (self .timeout )
129149 self .downloader .download (self .bin_file )
130150
131- status = self .downloader .flash_status ( )
151+ status = self .downloader .wait_for_flash_status_ok ( self . timeout )
132152 if status != 0 :
133153 raise ValueError ('Program download failed: '
134154 'flash status 0x{:02x}' .format (status ))
135155
156+ self .downloader .swid ()
136157 self .downloader .start_program ()
137158 self .downloader .wait_for_bootup (self .timeout )
138159 self .downloader .swid ()
@@ -146,7 +167,8 @@ def flash(self, **kwargs):
146167class CANopenProgramDownloader (object ):
147168 '''CANopen program downloader'''
148169 def __init__ (self , logger , node_id , can_context = DEFAULT_CAN_CONTEXT ,
149- program_number = 1 ):
170+ program_number = DEFAULT_PROGRAM_NUMBER ,
171+ sdo_retries = DEFAULT_SDO_RETRIES , sdo_timeout = DEFAULT_SDO_TIMEOUT ):
150172 super (CANopenProgramDownloader , self ).__init__ ()
151173 self .logger = logger
152174 self .node_id = node_id
@@ -160,6 +182,9 @@ def __init__(self, logger, node_id, can_context=DEFAULT_CAN_CONTEXT,
160182 self .swid_sdo = self .node .sdo [H1F56_PROGRAM_SWID ][self .program_number ]
161183 self .flash_sdo = self .node .sdo [H1F57_FLASH_STATUS ][self .program_number ]
162184
185+ self .node .sdo .MAX_RETRIES = sdo_retries
186+ self .node .sdo .RESPONSE_TIMEOUT = sdo_timeout
187+
163188 def connect (self ):
164189 '''Connect to CAN network'''
165190 try :
@@ -238,20 +263,36 @@ def download(self, bin_file):
238263 break
239264 outfile .write (chunk )
240265 progress .next (n = len (chunk ))
266+ except :
267+ raise ValueError ('Failed to download program' )
268+ finally :
241269 progress .finish ()
242270 infile .close ()
243271 outfile .close ()
244- except :
245- raise ValueError ('Failed to download program' )
246272
247- def wait_for_bootup (self , timeout = 10 ):
273+ def wait_for_bootup (self , timeout = DEFAULT_TIMEOUT ):
248274 '''Wait for boot-up message reception'''
249275 self .logger .info ('Waiting for boot-up message...' )
250276 try :
251277 self .node .nmt .wait_for_bootup (timeout = timeout )
252278 except :
253279 raise ValueError ('Timeout waiting for boot-up message' )
254280
281+ def wait_for_flash_status_ok (self , timeout = DEFAULT_TIMEOUT ):
282+ '''Wait for flash status ok'''
283+ self .logger .info ('Waiting for flash status ok' )
284+ end_time = time .time () + timeout
285+ while True :
286+ now = time .time ()
287+ status = self .flash_status ()
288+ if status == 0 :
289+ break
290+
291+ if now > end_time :
292+ return status
293+
294+ return status
295+
255296 @staticmethod
256297 def create_object_dictionary ():
257298 '''Create a synthetic CANopen object dictionary for program download'''
0 commit comments