Module commport
[hide private]
[frames] | no frames]

Source Code for Module commport

  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2003-2004 Roger Binns <rogerb@rogerbinns.com> 
  4  ### 
  5  ### This program is free software; you can redistribute it and/or modify 
  6  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
  7  ### 
  8  ### $Id: commport.py 4436 2007-10-30 23:10:47Z djpham $ 
  9   
 10  """Encapsulates the serial port device""" 
 11   
 12  import serial 
 13  import sys 
 14  import common 
 15  import time 
 16  import threading 
 17  import data_recording 
 18   
 19  try: 
 20      import native.usb as usb 
 21  except: 
 22      usb=None 
 23   
 24  # have to work around an annoying bug in LGE drivers 
 25  try: 
 26      import pywintypes 
 27  except: 
 28      pywintypes=None 
 29   
30 -class CommTimeout(Exception):
31 - def __init__(self, str=None, partial=None):
32 Exception.__init__(self, str) 33 self.partial=partial
34
35 -class ATError(Exception):
36 - def __init__(self, str=None, partial=None):
37 Exception.__init__(self, str) 38 self.partial=partial
39
40 -class CommConnection:
41 usbwhine=0
42 - def __init__(self, logtarget, port, baud=115200, timeout=3, hardwareflow=0, 43 softwareflow=0, autolistfunc=None, autolistargs=None, configparameters=None):
44 self._brokennotifications=0 45 self.ser=None 46 self.port=port 47 self.logtarget=logtarget 48 self.clearcounters() 49 if usb is None and self.usbwhine<1: 50 self.log("USB support is not available") 51 self.success=False 52 self.shouldloop=False 53 self.ports=None 54 self.autolistfunc=autolistfunc 55 self.autolistargs=autolistargs 56 self.configparameters=configparameters 57 self.params=(baud,timeout,hardwareflow,softwareflow) 58 self.readahead="" 59 assert port!="auto" or (port=="auto" and autolistfunc is not None) 60 if autolistfunc is not None: 61 self._isauto=True 62 else: 63 self._isauto=False 64 if port=="auto": 65 self.log("Auto detected port requested") 66 self.NextAutoPort() 67 else: 68 self._openport(self.port, *self.params)
69
70 - def IsAuto(self):
71 return self._isauto
72
73 - def close(self):
74 if self.ser is not None: 75 try: 76 # sometimes this gives invalid handles and similar issues 77 self.ser.close() 78 except: 79 pass 80 self.ser=None
81
82 - def _openport(self, port, baud, timeout, hardwareflow, softwareflow, description=None):
83 if data_recording.DR_Play: 84 # we're doing playback, ignore this 85 self.log('Open of comm port ignored') 86 return 87 self.close() 88 self.log("Opening port %s, %d baud, timeout %f, hardwareflow %d, softwareflow %d" % 89 (port, baud, float(timeout), hardwareflow, softwareflow) ) 90 if description is not None: 91 self.log(description) 92 # we try twice since some platforms fail the first time 93 for dummy in range(2): 94 try: 95 self.close() 96 if port.startswith("usb::"): 97 self.ser=self._openusb(port, timeout) 98 else: 99 useport=port 100 if sys.platform=='win32' and port.lower().startswith("com"): useport="\\\\?\\"+port 101 self.ser=serial.Serial(useport, baud, timeout=timeout, rtscts=hardwareflow, xonxoff=softwareflow) 102 self.log("Open of comm port suceeded") 103 self.port=port 104 self.clearcounters() 105 return 106 except serial.serialutil.SerialException,e: 107 if dummy: 108 self.log('Open of comm port failed') 109 raise common.CommsOpenFailure(e.__str__(), port) 110 time.sleep(2)
111
112 - def _openusb(self, name, timeout):
113 self.close() 114 if usb is None: 115 self.log("USB module not available - unable to open "+name) 116 raise Exception("USB module not available - unable to open "+name) 117 _,wantedbus,wanteddev,wantediface=name.split("::") 118 wantediface=int(wantediface) 119 usb.UpdateLists() 120 for bus in usb.AllBusses(): 121 if bus.name()!=wantedbus: 122 continue 123 for device in bus.devices(): 124 if device.name()!=wanteddev: 125 continue 126 for iface in device.interfaces(): 127 if iface.number()!=wantediface: 128 continue 129 return _usbdevicewrapper(iface.openbulk(), timeout) 130 self.log("Failed to find "+name+". You may need to rescan.") 131 raise common.CommsOpenFailure("Failed to find usb device "+name)
132 133
134 - def reset(self):
135 self._openport(self.port, *self.params)
136
137 - def _refreshautoports(self):
138 # ensure we close current port first 139 self.close() 140 self.ports=self.autolistfunc(*self.autolistargs) 141 assert self.ports is not None 142 self.success=False 143 self.portstried=self.ports
144
145 - def NextAutoPort(self):
146 # do we need to refresh list? 147 if (self.ports is None and self.autolistfunc is not None) or \ 148 ( len(self.ports)==0 and (self.success or self.shouldloop)): 149 self._refreshautoports() 150 self.shouldloop=False 151 # have we run out? 152 if len(self.ports)==0: 153 self.ports=None # so user can retry 154 raise common.AutoPortsFailure(map(lambda x: x[0], self.portstried)) 155 # try first in list 156 self.log("Trying next auto port") 157 description=self.ports[0][1]['description'] 158 self.port=self.ports[0][0] 159 self.ports=self.ports[1:] 160 try: 161 self._openport(self.port, *(self.params+(description,))) 162 except common.CommsOpenFailure: 163 self.NextAutoPort()
164
165 - def clearcounters(self):
166 self.readbytes=0 167 self.readrequests=0 168 self.writebytes=0 169 self.writerequests=0
170
171 - def log(self, str):
172 if self.logtarget: 173 self.logtarget.log(self.port+": "+str)
174
175 - def logdata(self, str, data, data_type=None):
176 if self.logtarget: 177 self.logtarget.logdata(self.port+": "+str, data, 178 None, data_type)
179
180 - def setbaudrate(self, rate):
181 """Change to the specified baud rate 182 183 @rtype: Boolean 184 @returns: True on success, False on failure 185 """ 186 try: 187 self.ser.setBaudrate(rate) 188 self.log("Changed port speed to "+`rate`) 189 time.sleep(.5) 190 return True 191 except SilentException: 192 return False 193 except Exception,e: 194 self.log("Port speed "+`rate`+" not supported") 195 return False
196
197 - def setdtr(self, dtr):
198 """Set or Clear DTR 199 200 @rtype: Boolean 201 @returns: True on success, False on failure 202 """ 203 try: 204 self.ser.setDTR(dtr) 205 self.log("DTR set to "+`dtr`) 206 return True 207 except SilentException: 208 return False
209
210 - def setrts(self, rts):
211 """Set or Clear RTS 212 213 @rtype: Boolean 214 @returns: True on success, False on failure 215 """ 216 try: 217 self.ser.setRTS(rts) 218 self.log("RTS set to "+`rts`) 219 return True 220 except SilentException: 221 return False
222
223 - def _write(self, data, log=True):
224 self.writerequests+=1 225 # if we're doing data play back, just ignore this, for now! 226 if data_recording.DR_Play: 227 return 228 if log or data_recording.DR_On: 229 self.logdata("Writing", data, data_recording.DR_Type_Write) 230 self.ser.write(data) 231 self.writebytes+=len(data)
232
233 - def write_thread(self, data, log):
234 try: 235 self._write(data, log) 236 self._write_res=True 237 except Exception,e: 238 self.log('Write Exception: '+str(e))
239 - def write(self, data, log=True):
240 _t=threading.Thread(target=self.write_thread, args=(data, log)) 241 self._write_res=False 242 _t.start() 243 _t.join(self.params[1]+1) 244 if _t.isAlive(): 245 _t._Thread__stop() 246 if not self._write_res: 247 raise CommTimeout()
248
249 - def sendatcommand(self, atcommand, ignoreerror=False, retry=False):
250 #print "sendatcommand: "+atcommand 251 252 if not data_recording.DR_Play: 253 # Flush leftover characters 254 b=self.ser.inWaiting() 255 if b: 256 self.read(b,0) 257 258 fullline="AT"+atcommand 259 self.write(str(fullline+"\r\n")) 260 # Cache response 261 try: 262 self.readatresponse(ignoreerror) 263 except CommTimeout: 264 if retry: 265 # try to read a response 1 more time 266 self.readatresponse(ignoreerror) 267 else: 268 raise 269 270 res=[] 271 272 line=self.getcleanline() 273 if line==fullline: 274 line=self.getcleanline() 275 while line!="OK" and line: 276 if line=="ERROR": 277 if not ignoreerror: 278 raise ATError 279 else: 280 res.append(line) 281 line=self.getcleanline() 282 283 return res
284
285 - def peekline(self):
286 return self.getcleanline(peek=True)
287
288 - def getcleanline(self, peek=False):
289 i=0 290 sbuf=self.readahead 291 if len(sbuf)==0: 292 return "" 293 while sbuf[i]!='\n' and sbuf[i]!='\r': 294 i+=1 295 firstline=sbuf[0:i] 296 if not peek: 297 i+=1 298 while i<len(sbuf): 299 if sbuf[i]!='\n' and sbuf[i]!='\r': 300 break 301 i+=1 302 self.readahead=self.readahead[i:] 303 return firstline
304
305 - def readatresponse(self, ignoreerror, log=True):
306 """Read until OK, ERROR or a timeout""" 307 self.readrequests+=1 308 if data_recording.DR_Play: 309 res=data_recording.get_data(data_recording.DR_Type_Read_ATResponse) 310 if res: 311 self.readahead=res 312 return 313 else: 314 raise CommTimeout() 315 res="" 316 while True: 317 b=self.ser.inWaiting() 318 if b: 319 res=res+self.read(b,0) 320 if res.find("OK\r")>=0 or (res.find("ERROR\r")>=0 and not ignoreerror): 321 break 322 continue 323 r=self.read(1,0) 324 if len(r): 325 res=res+r 326 continue 327 break 328 329 while len(res)>0 and (res[0]=='\n' or res[0]=='\r'): 330 res=res[1:] 331 332 if len(res)==0: 333 self.logdata("Reading remaining data", '', 334 data_recording.DR_Type_Read_ATResponse) 335 raise CommTimeout() 336 337 self.readbytes+=len(res) 338 self.readahead=res 339 if log or data_recording.DR_On: 340 self.logdata("Reading remaining data", res, 341 data_recording.DR_Type_Read_ATResponse) 342 return
343
344 - def readline(self):
345 return self.getcleanline(peek=False)
346
347 - def _isbrokendriver(self, e):
348 if pywintypes is None or not isinstance(e, pywintypes.error) or e[0]!=121: 349 return False 350 return True
351
352 - def _brokendriverread(self, numchars):
353 # we handle timeouts ourselves in this code 354 self._brokennotifications+=1 355 if self._brokennotifications==3: 356 self.log("This driver is broken - enabling workaround") 357 basetime=time.time() 358 while time.time()-basetime<self.params[1]: 359 try: 360 res=self.ser.read(numchars) 361 return res 362 except Exception,e: 363 if not self._isbrokendriver(e): 364 raise 365 raise CommTimeout()
366
367 - def _read(self, numchars=1, log=True):
368 self.readrequests+=1 369 if data_recording.DR_Play: 370 return data_recording.get_data(data_recording.DR_Type_Read) 371 try: 372 res=self.ser.read(numchars) 373 except Exception,e: 374 if not self._isbrokendriver(e): 375 raise 376 res=self._brokendriverread(numchars) 377 if log or data_recording.DR_On: 378 self.logdata("Reading exact data - requested "+`numchars`, res, 379 data_recording.DR_Type_Read) 380 self.readbytes+=len(res) 381 return res
382
383 - def read_thread(self, numchars=1, log=True):
384 try: 385 self._read_res=self._read(numchars, log) 386 except Exception,e: 387 self.log('Read Exception: '+str(e))
388
389 - def read(self, numchars=1, log=True):
390 _t=threading.Thread(target=self.read_thread, args=(numchars, log)) 391 self._read_res=None 392 _t.start() 393 _t.join(self.params[1]+1) 394 if _t.isAlive(): 395 _t._Thread__stop() 396 if self._read_res is None: 397 raise CommTimeout() 398 return self._read_res
399
400 - def readsome(self, log=True, numchars=None):
401 self.readrequests+=1 402 if data_recording.DR_Play: 403 return data_recording.get_data(data_recording.DR_Type_Read_Some) 404 res="" 405 while True: 406 if numchars is not None and len(res)>= numchars: 407 break 408 b=self.ser.inWaiting() 409 if b: 410 res=res+self.read(b,0) 411 continue 412 r=self.read(1,0) 413 if len(r): 414 res=res+r 415 continue 416 break 417 if len(res)==0: 418 raise CommTimeout() 419 self.readbytes+=len(res) 420 if log or data_recording.DR_On: 421 self.logdata("Reading remaining data", res, 422 data_recording.DR_Type_Read_Some) 423 return res
424
425 - def readuntil(self, char, log=True, logsuccess=True, numfailures=0):
426 # Keeps reading until it hits char 427 self.readrequests+=1 428 if data_recording.DR_Play: 429 return data_recording.get_data(data_recording.DR_Type_Read_Until) 430 if False: # don't log this anymore 431 self.logdata("Begin reading until 0x%02x" % (ord(char),), None) 432 433 # set numfailures to non-zero for retries on timeouts 434 res='' 435 while len(res)==0 or res[-1]!=char: 436 if hasattr(self.ser, 'readuntil'): 437 # usb does it directly 438 res2=self.ser.readuntil(char) 439 b=-99999 440 else: 441 b=self.ser.inWaiting() 442 if b<1: b=1 443 res2=self.read(b,0) 444 if len(res2)<1: 445 if numfailures==0: 446 if log: 447 self.log("Timed out waiting for %02x, requested bytes %d - %d bytes read" % 448 (ord(char), b, len(res))) 449 self.logdata("Incomplete read was", res) 450 self.readbytes+=len(res) 451 raise CommTimeout(partial=res) 452 else: 453 numfailures-=1 454 self.log("Timed out - flushing and trying again") 455 res=res+res2 456 457 self.readbytes+=len(res) 458 if logsuccess or data_recording.DR_On: 459 self.logdata("Read completed", res, 460 data_recording.DR_Type_Read_Until) 461 return res
462 463 # these methods here consolidate calls, which makes the BitFling stuff a lot faster due 464 # to fewer roundtrips
465 - def writethenreaduntil(self, data, logwrite, char, logreaduntil=True, logreaduntilsuccess=True, numfailures=1):
466 self.write(data, logwrite) 467 return self.readuntil(char, logreaduntil, logreaduntilsuccess, numfailures)
468
469 -class SilentException(Exception): pass
470
471 -class _usbdevicewrapper:
472
473 - def __init__(self, dev, timeout):
474 self.dev=dev 475 self.timeout=int(timeout*1000)
476
477 - def inWaiting(self):
478 # This will cause one byte at a time reads in the other code. 479 # It isn't really possible to fix until we have built in 480 # buffering for the comm stuff. 481 return 0
482
483 - def read(self, numchars=1):
484 return self.dev.read(numchars, self.timeout)
485
486 - def flushInput(self):
487 self.dev.resetep()
488
489 - def write(self, data):
490 self.dev.write(data, self.timeout)
491
492 - def close(self):
493 self.dev.close()
494
495 - def readuntil(self, char):
496 # try and get it all in one shot 497 try: 498 data=self.dev.read(999999, self.timeout) 499 if len(data) and data[-1]==char: 500 return data 501 except usb.USBException: 502 # ahh, buggy hardware or something 503 self.dev.resetep() 504 data="" 505 # do it the old fashioned way 506 basetime=time.time() 507 while 1000*(time.time()-basetime)<self.timeout: 508 try: 509 more=self.dev.read(99999, max(100,self.timeout-1000*(time.time()-basetime))) 510 data+=more 511 if len(data) and data[-1]==char: 512 return data 513 except usb.USBException: 514 pass 515 # timed out 516 return data
517