@@ -64,6 +64,7 @@ def __init__(
64
64
btr : Optional [str ] = None ,
65
65
sleep_after_open : float = _SLEEP_AFTER_SERIAL_OPEN ,
66
66
rtscts : bool = False ,
67
+ timeout : float = 0.001 ,
67
68
** kwargs : Any ,
68
69
) -> None :
69
70
"""
@@ -82,7 +83,8 @@ def __init__(
82
83
Time to wait in seconds after opening serial connection
83
84
:param rtscts:
84
85
turn hardware handshake (RTS/CTS) on and off
85
-
86
+ :param timeout:
87
+ Timeout for the serial or usb device in seconds (default 0.001)
86
88
:raise ValueError: if both ``bitrate`` and ``btr`` are set or the channel is invalid
87
89
:raise CanInterfaceNotImplementedError: if the serial module is missing
88
90
:raise CanInitializationError: if the underlying serial connection could not be established
@@ -98,7 +100,10 @@ def __init__(
98
100
99
101
with error_check (exception_type = CanInitializationError ):
100
102
self .serialPortOrig = serial .serial_for_url (
101
- channel , baudrate = ttyBaudrate , rtscts = rtscts
103
+ channel ,
104
+ baudrate = ttyBaudrate ,
105
+ rtscts = rtscts ,
106
+ timeout = timeout ,
102
107
)
103
108
104
109
self ._buffer = bytearray ()
@@ -150,46 +155,34 @@ def _write(self, string: str) -> None:
150
155
self .serialPortOrig .flush ()
151
156
152
157
def _read (self , timeout : Optional [float ]) -> Optional [str ]:
158
+ _timeout = serial .Timeout (timeout )
153
159
154
160
with error_check ("Could not read from serial device" ):
155
- # first read what is already in receive buffer
156
- while self .serialPortOrig .in_waiting :
157
- self ._buffer += self .serialPortOrig .read ()
158
- # if we still don't have a complete message, do a blocking read
159
- start = time .time ()
160
- time_left = timeout
161
- while not (
162
- ord (self ._OK ) in self ._buffer or ord (self ._ERROR ) in self ._buffer
163
- ):
164
- self .serialPortOrig .timeout = time_left
165
- byte = self .serialPortOrig .read ()
166
- if byte :
167
- self ._buffer += byte
168
- # if timeout is None, try indefinitely
169
- if timeout is None :
170
- continue
171
- # try next one only if there still is time, and with
172
- # reduced timeout
173
- else :
174
- time_left = timeout - (time .time () - start )
175
- if time_left > 0 :
176
- continue
161
+ while True :
162
+ # Due to accessing `serialPortOrig.in_waiting` too often will reduce the performance.
163
+ # We read the `serialPortOrig.in_waiting` only once here.
164
+ in_waiting = self .serialPortOrig .in_waiting
165
+ for _ in range (max (1 , in_waiting )):
166
+ new_byte = self .serialPortOrig .read (size = 1 )
167
+ if new_byte :
168
+ self ._buffer .extend (new_byte )
177
169
else :
178
- return None
170
+ break
171
+
172
+ if new_byte in (self ._ERROR , self ._OK ):
173
+ string = self ._buffer .decode ()
174
+ self ._buffer .clear ()
175
+ return string
176
+
177
+ if _timeout .expired ():
178
+ break
179
179
180
- # return first message
181
- for i in range (len (self ._buffer )):
182
- if self ._buffer [i ] == ord (self ._OK ) or self ._buffer [i ] == ord (self ._ERROR ):
183
- string = self ._buffer [: i + 1 ].decode ()
184
- del self ._buffer [: i + 1 ]
185
- break
186
- return string
180
+ return None
187
181
188
182
def flush (self ) -> None :
189
- del self ._buffer [:]
183
+ self ._buffer . clear ()
190
184
with error_check ("Could not flush" ):
191
- while self .serialPortOrig .in_waiting :
192
- self .serialPortOrig .read ()
185
+ self .serialPortOrig .reset_input_buffer ()
193
186
194
187
def open (self ) -> None :
195
188
self ._write ("O" )
@@ -204,7 +197,7 @@ def _recv_internal(
204
197
canId = None
205
198
remote = False
206
199
extended = False
207
- frame = []
200
+ data = None
208
201
209
202
string = self ._read (timeout )
210
203
@@ -215,14 +208,12 @@ def _recv_internal(
215
208
canId = int (string [1 :9 ], 16 )
216
209
dlc = int (string [9 ])
217
210
extended = True
218
- for i in range (0 , dlc ):
219
- frame .append (int (string [10 + i * 2 : 12 + i * 2 ], 16 ))
211
+ data = bytearray .fromhex (string [10 : 10 + dlc * 2 ])
220
212
elif string [0 ] == "t" :
221
213
# normal frame
222
214
canId = int (string [1 :4 ], 16 )
223
215
dlc = int (string [4 ])
224
- for i in range (0 , dlc ):
225
- frame .append (int (string [5 + i * 2 : 7 + i * 2 ], 16 ))
216
+ data = bytearray .fromhex (string [5 : 5 + dlc * 2 ])
226
217
elif string [0 ] == "r" :
227
218
# remote frame
228
219
canId = int (string [1 :4 ], 16 )
@@ -242,7 +233,7 @@ def _recv_internal(
242
233
timestamp = time .time (), # Better than nothing...
243
234
is_remote_frame = remote ,
244
235
dlc = dlc ,
245
- data = frame ,
236
+ data = data ,
246
237
)
247
238
return msg , False
248
239
return None , False
@@ -252,15 +243,15 @@ def send(self, msg: Message, timeout: Optional[float] = None) -> None:
252
243
self .serialPortOrig .write_timeout = timeout
253
244
if msg .is_remote_frame :
254
245
if msg .is_extended_id :
255
- sendStr = "R%08X%d" % ( msg .arbitration_id , msg .dlc )
246
+ sendStr = f"R { msg .arbitration_id :08X } { msg .dlc :d } "
256
247
else :
257
- sendStr = "r%03X%d" % ( msg .arbitration_id , msg .dlc )
248
+ sendStr = f"r { msg .arbitration_id :03X } { msg .dlc :d } "
258
249
else :
259
250
if msg .is_extended_id :
260
- sendStr = "T%08X%d" % ( msg .arbitration_id , msg .dlc )
251
+ sendStr = f"T { msg .arbitration_id :08X } { msg .dlc :d } "
261
252
else :
262
- sendStr = "t%03X%d" % ( msg .arbitration_id , msg .dlc )
263
- sendStr += "" . join ([ "%02X" % b for b in msg .data ] )
253
+ sendStr = f"t { msg .arbitration_id :03X } { msg .dlc :d } "
254
+ sendStr += msg .data . hex (). upper ( )
264
255
self ._write (sendStr )
265
256
266
257
def shutdown (self ) -> None :
@@ -295,29 +286,17 @@ def get_version(
295
286
cmd = "V"
296
287
self ._write (cmd )
297
288
298
- start = time .time ()
299
- time_left = timeout
300
- while True :
301
- string = self ._read (time_left )
302
-
303
- if not string :
304
- pass
305
- elif string [0 ] == cmd and len (string ) == 6 :
306
- # convert ASCII coded version
307
- hw_version = int (string [1 :3 ])
308
- sw_version = int (string [3 :5 ])
309
- return hw_version , sw_version
310
- # if timeout is None, try indefinitely
311
- if timeout is None :
312
- continue
313
- # try next one only if there still is time, and with
314
- # reduced timeout
315
- else :
316
- time_left = timeout - (time .time () - start )
317
- if time_left > 0 :
318
- continue
319
- else :
320
- return None , None
289
+ string = self ._read (timeout )
290
+
291
+ if not string :
292
+ pass
293
+ elif string [0 ] == cmd and len (string ) == 6 :
294
+ # convert ASCII coded version
295
+ hw_version = int (string [1 :3 ])
296
+ sw_version = int (string [3 :5 ])
297
+ return hw_version , sw_version
298
+
299
+ return None , None
321
300
322
301
def get_serial_number (self , timeout : Optional [float ]) -> Optional [str ]:
323
302
"""Get serial number of the slcan interface.
@@ -331,24 +310,12 @@ def get_serial_number(self, timeout: Optional[float]) -> Optional[str]:
331
310
cmd = "N"
332
311
self ._write (cmd )
333
312
334
- start = time .time ()
335
- time_left = timeout
336
- while True :
337
- string = self ._read (time_left )
338
-
339
- if not string :
340
- pass
341
- elif string [0 ] == cmd and len (string ) == 6 :
342
- serial_number = string [1 :- 1 ]
343
- return serial_number
344
- # if timeout is None, try indefinitely
345
- if timeout is None :
346
- continue
347
- # try next one only if there still is time, and with
348
- # reduced timeout
349
- else :
350
- time_left = timeout - (time .time () - start )
351
- if time_left > 0 :
352
- continue
353
- else :
354
- return None
313
+ string = self ._read (timeout )
314
+
315
+ if not string :
316
+ pass
317
+ elif string [0 ] == cmd and len (string ) == 6 :
318
+ serial_number = string [1 :- 1 ]
319
+ return serial_number
320
+
321
+ return None
0 commit comments