PyXR

c:\projects\bitpim\src \ commport.py



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2003-2004 Roger Binns <rogerb@rogerbinns.com>
0004 ###
0005 ### This program is free software; you can redistribute it and/or modify
0006 ### it under the terms of the BitPim license as detailed in the LICENSE file.
0007 ###
0008 ### $Id: commport.py 4436 2007-10-30 23:10:47Z djpham $
0009 
0010 """Encapsulates the serial port device"""
0011 
0012 import serial
0013 import sys
0014 import common
0015 import time
0016 import threading
0017 import data_recording
0018 
0019 try:
0020     import native.usb as usb
0021 except:
0022     usb=None
0023 
0024 # have to work around an annoying bug in LGE drivers
0025 try:
0026     import pywintypes
0027 except:
0028     pywintypes=None
0029 
0030 class CommTimeout(Exception):
0031     def __init__(self, str=None, partial=None):
0032         Exception.__init__(self, str)
0033         self.partial=partial
0034 
0035 class ATError(Exception):
0036     def __init__(self, str=None, partial=None):
0037         Exception.__init__(self, str)
0038         self.partial=partial
0039 
0040 class CommConnection:
0041     usbwhine=0
0042     def __init__(self, logtarget, port, baud=115200, timeout=3, hardwareflow=0,
0043                  softwareflow=0, autolistfunc=None, autolistargs=None, configparameters=None):
0044         self._brokennotifications=0
0045         self.ser=None
0046         self.port=port
0047         self.logtarget=logtarget
0048         self.clearcounters()
0049         if usb is None and self.usbwhine<1:
0050             self.log("USB support is not available")
0051         self.success=False
0052         self.shouldloop=False
0053         self.ports=None
0054         self.autolistfunc=autolistfunc
0055         self.autolistargs=autolistargs
0056         self.configparameters=configparameters
0057         self.params=(baud,timeout,hardwareflow,softwareflow)
0058         self.readahead=""
0059         assert port!="auto" or (port=="auto" and autolistfunc is not None)
0060         if autolistfunc is not None:
0061             self._isauto=True
0062         else:
0063             self._isauto=False
0064         if port=="auto":
0065             self.log("Auto detected port requested")
0066             self.NextAutoPort()
0067         else:
0068             self._openport(self.port, *self.params)
0069 
0070     def IsAuto(self):
0071         return self._isauto
0072 
0073     def close(self):
0074         if self.ser is not None:
0075             try:
0076                 # sometimes this gives invalid handles and similar issues
0077                 self.ser.close()
0078             except:
0079                 pass
0080             self.ser=None
0081 
0082     def _openport(self, port, baud, timeout, hardwareflow, softwareflow, description=None):
0083         if data_recording.DR_Play:
0084             # we're doing playback, ignore this
0085             self.log('Open of comm port ignored')
0086             return
0087         self.close()
0088         self.log("Opening port %s, %d baud, timeout %f, hardwareflow %d, softwareflow %d" %
0089                  (port, baud, float(timeout), hardwareflow, softwareflow) )
0090         if description is not None:
0091             self.log(description)
0092         # we try twice since some platforms fail the first time
0093         for dummy in range(2):
0094             try:
0095                 self.close()
0096                 if port.startswith("usb::"):
0097                     self.ser=self._openusb(port, timeout)
0098                 else:
0099                     useport=port
0100                     if sys.platform=='win32' and port.lower().startswith("com"): useport="\\\\?\\"+port
0101                     self.ser=serial.Serial(useport, baud, timeout=timeout, rtscts=hardwareflow, xonxoff=softwareflow)
0102                 self.log("Open of comm port suceeded")
0103                 self.port=port
0104                 self.clearcounters()
0105                 return
0106             except serial.serialutil.SerialException,e:
0107                 if dummy:
0108                     self.log('Open of comm port failed')
0109                     raise common.CommsOpenFailure(e.__str__(), port)
0110                 time.sleep(2)
0111 
0112     def _openusb(self, name, timeout):
0113         self.close()
0114         if usb is None:
0115             self.log("USB module not available - unable to open "+name)
0116             raise Exception("USB module not available - unable to open "+name)
0117         _,wantedbus,wanteddev,wantediface=name.split("::")
0118         wantediface=int(wantediface)
0119         usb.UpdateLists()
0120         for bus in usb.AllBusses():
0121             if bus.name()!=wantedbus:
0122                 continue
0123             for device in bus.devices():
0124                 if device.name()!=wanteddev:
0125                     continue
0126                 for iface in device.interfaces():
0127                     if iface.number()!=wantediface:
0128                         continue
0129                     return _usbdevicewrapper(iface.openbulk(), timeout)
0130         self.log("Failed to find "+name+".  You may need to rescan.")
0131         raise common.CommsOpenFailure("Failed to find usb device "+name)
0132         
0133 
0134     def reset(self):
0135         self._openport(self.port, *self.params)
0136         
0137     def _refreshautoports(self):
0138         # ensure we close current port first
0139         self.close()
0140         self.ports=self.autolistfunc(*self.autolistargs)
0141         assert self.ports is not None
0142         self.success=False
0143         self.portstried=self.ports
0144 
0145     def NextAutoPort(self):
0146         # do we need to refresh list?
0147         if (self.ports is None and self.autolistfunc is not None) or \
0148            ( len(self.ports)==0 and (self.success or self.shouldloop)):
0149             self._refreshautoports()
0150             self.shouldloop=False
0151         # have we run out?
0152         if len(self.ports)==0:
0153             self.ports=None # so user can retry
0154             raise common.AutoPortsFailure(map(lambda x: x[0], self.portstried))
0155         # try first in list
0156         self.log("Trying next auto port")
0157         description=self.ports[0][1]['description']
0158         self.port=self.ports[0][0]
0159         self.ports=self.ports[1:]
0160         try:
0161             self._openport(self.port, *(self.params+(description,)))
0162         except common.CommsOpenFailure:
0163             self.NextAutoPort()
0164             
0165     def clearcounters(self):
0166         self.readbytes=0
0167         self.readrequests=0
0168         self.writebytes=0
0169         self.writerequests=0
0170 
0171     def log(self, str):
0172         if self.logtarget:
0173             self.logtarget.log(self.port+": "+str)
0174 
0175     def logdata(self, str, data, data_type=None):
0176         if self.logtarget:
0177             self.logtarget.logdata(self.port+": "+str, data,
0178                                    None, data_type)
0179 
0180     def setbaudrate(self, rate):
0181         """Change to the specified baud rate
0182 
0183         @rtype: Boolean
0184         @returns: True on success, False on failure
0185         """
0186         try:
0187             self.ser.setBaudrate(rate)
0188             self.log("Changed port speed to "+`rate`)
0189             time.sleep(.5)
0190             return True
0191         except SilentException:
0192             return False
0193         except Exception,e:
0194             self.log("Port speed "+`rate`+" not supported")
0195             return False
0196 
0197     def setdtr(self, dtr):
0198         """Set or Clear DTR
0199 
0200         @rtype: Boolean
0201         @returns: True on success, False on failure
0202         """
0203         try:
0204             self.ser.setDTR(dtr)
0205             self.log("DTR set to "+`dtr`)
0206             return True
0207         except SilentException:
0208             return False
0209         
0210     def setrts(self, rts):
0211         """Set or Clear RTS
0212 
0213         @rtype: Boolean
0214         @returns: True on success, False on failure
0215         """
0216         try:
0217             self.ser.setRTS(rts)
0218             self.log("RTS set to "+`rts`)
0219             return True
0220         except SilentException:
0221             return False
0222 
0223     def _write(self, data, log=True):
0224         self.writerequests+=1
0225         # if we're doing data play back, just ignore this, for now!
0226         if data_recording.DR_Play:
0227             return
0228         if log or data_recording.DR_On:
0229             self.logdata("Writing", data, data_recording.DR_Type_Write)
0230         self.ser.write(data)
0231         self.writebytes+=len(data)
0232 
0233     def write_thread(self, data, log):
0234         try:
0235             self._write(data, log)
0236             self._write_res=True
0237         except Exception,e:
0238             self.log('Write Exception: '+str(e))
0239     def write(self, data, log=True):
0240         _t=threading.Thread(target=self.write_thread, args=(data, log))
0241         self._write_res=False
0242         _t.start()
0243         _t.join(self.params[1]+1)
0244         if _t.isAlive():
0245             _t._Thread__stop()
0246         if not self._write_res:
0247             raise CommTimeout()
0248 
0249     def sendatcommand(self, atcommand, ignoreerror=False, retry=False):
0250         #print "sendatcommand: "+atcommand
0251 
0252         if not data_recording.DR_Play:
0253             # Flush leftover characters
0254             b=self.ser.inWaiting()
0255             if b:
0256                 self.read(b,0)
0257 
0258         fullline="AT"+atcommand
0259         self.write(str(fullline+"\r\n"))
0260         # Cache response
0261         try:
0262             self.readatresponse(ignoreerror)
0263         except CommTimeout:
0264             if retry:
0265                 # try to read a response 1 more time
0266                 self.readatresponse(ignoreerror)
0267             else:
0268                 raise
0269 
0270         res=[]
0271         
0272         line=self.getcleanline()
0273         if line==fullline:
0274             line=self.getcleanline()
0275         while line!="OK" and line:
0276             if line=="ERROR":
0277                 if not ignoreerror:
0278                     raise ATError
0279             else:
0280                 res.append(line)
0281             line=self.getcleanline()
0282             
0283         return res
0284 
0285     def peekline(self):
0286         return self.getcleanline(peek=True)
0287 
0288     def getcleanline(self, peek=False):
0289         i=0
0290         sbuf=self.readahead
0291         if len(sbuf)==0:
0292             return ""
0293         while sbuf[i]!='\n' and sbuf[i]!='\r':
0294             i+=1
0295         firstline=sbuf[0:i]
0296         if not peek:
0297             i+=1
0298             while i<len(sbuf):
0299                 if sbuf[i]!='\n' and sbuf[i]!='\r':
0300                     break
0301                 i+=1
0302             self.readahead=self.readahead[i:]
0303         return firstline
0304         
0305     def readatresponse(self, ignoreerror, log=True):
0306         """Read until OK, ERROR or a timeout"""
0307         self.readrequests+=1
0308         if data_recording.DR_Play:
0309             res=data_recording.get_data(data_recording.DR_Type_Read_ATResponse)
0310             if res:
0311                 self.readahead=res
0312                 return
0313             else:
0314                 raise CommTimeout()
0315         res=""
0316         while True:
0317             b=self.ser.inWaiting()
0318             if b:
0319                 res=res+self.read(b,0)
0320                 if res.find("OK\r")>=0 or (res.find("ERROR\r")>=0 and not ignoreerror):
0321                     break
0322                 continue
0323             r=self.read(1,0)
0324             if len(r):
0325                 res=res+r
0326                 continue
0327             break
0328 
0329         while len(res)>0 and (res[0]=='\n' or res[0]=='\r'):
0330             res=res[1:]
0331 
0332         if len(res)==0:
0333             self.logdata("Reading remaining data", '',
0334                          data_recording.DR_Type_Read_ATResponse)
0335             raise CommTimeout()
0336 
0337         self.readbytes+=len(res)
0338         self.readahead=res
0339         if log or data_recording.DR_On:
0340             self.logdata("Reading remaining data", res,
0341                          data_recording.DR_Type_Read_ATResponse)
0342         return
0343 
0344     def readline(self):
0345         return self.getcleanline(peek=False)
0346         
0347     def _isbrokendriver(self, e):
0348         if pywintypes is None or not isinstance(e, pywintypes.error) or e[0]!=121:
0349             return False
0350         return True
0351 
0352     def _brokendriverread(self, numchars):
0353         # we handle timeouts ourselves in this code
0354         self._brokennotifications+=1
0355         if self._brokennotifications==3:
0356             self.log("This driver is broken - enabling workaround")
0357         basetime=time.time()
0358         while time.time()-basetime<self.params[1]:
0359             try:
0360                 res=self.ser.read(numchars)
0361                 return res
0362             except Exception,e:
0363                 if not self._isbrokendriver(e):
0364                     raise
0365         raise CommTimeout()
0366 
0367     def _read(self, numchars=1, log=True):
0368         self.readrequests+=1
0369         if data_recording.DR_Play:
0370             return data_recording.get_data(data_recording.DR_Type_Read)
0371         try:
0372             res=self.ser.read(numchars)
0373         except Exception,e:
0374             if not self._isbrokendriver(e):
0375                 raise
0376             res=self._brokendriverread(numchars)
0377         if log or data_recording.DR_On:
0378             self.logdata("Reading exact data - requested "+`numchars`, res,
0379                          data_recording.DR_Type_Read)
0380         self.readbytes+=len(res)
0381         return res
0382 
0383     def read_thread(self, numchars=1, log=True):
0384         try:
0385             self._read_res=self._read(numchars, log)
0386         except Exception,e:
0387             self.log('Read Exception: '+str(e))
0388 
0389     def read(self, numchars=1, log=True):
0390         _t=threading.Thread(target=self.read_thread, args=(numchars, log))
0391         self._read_res=None
0392         _t.start()
0393         _t.join(self.params[1]+1)
0394         if _t.isAlive():
0395             _t._Thread__stop()
0396         if self._read_res is None:
0397             raise CommTimeout()
0398         return self._read_res
0399 
0400     def readsome(self, log=True, numchars=None):
0401         self.readrequests+=1
0402         if data_recording.DR_Play:
0403             return data_recording.get_data(data_recording.DR_Type_Read_Some)
0404         res=""
0405         while True:
0406             if numchars is not None and len(res)>= numchars:
0407                 break
0408             b=self.ser.inWaiting()
0409             if b:
0410                 res=res+self.read(b,0)
0411                 continue
0412             r=self.read(1,0)
0413             if len(r):
0414                 res=res+r
0415                 continue
0416             break
0417         if len(res)==0:
0418             raise CommTimeout()
0419         self.readbytes+=len(res)
0420         if log or data_recording.DR_On:
0421             self.logdata("Reading remaining data", res,
0422                          data_recording.DR_Type_Read_Some)
0423         return res
0424 
0425     def readuntil(self, char, log=True, logsuccess=True, numfailures=0):
0426         # Keeps reading until it hits char
0427         self.readrequests+=1
0428         if data_recording.DR_Play:
0429             return data_recording.get_data(data_recording.DR_Type_Read_Until)
0430         if False: # don't log this anymore
0431             self.logdata("Begin reading until 0x%02x" % (ord(char),), None)
0432 
0433         # set numfailures to non-zero for retries on timeouts
0434         res=''
0435         while len(res)==0 or res[-1]!=char:
0436             if hasattr(self.ser, 'readuntil'):
0437                 # usb does it directly
0438                 res2=self.ser.readuntil(char)
0439                 b=-99999
0440             else:
0441                 b=self.ser.inWaiting()
0442                 if b<1: b=1
0443                 res2=self.read(b,0)
0444             if len(res2)<1:
0445                 if numfailures==0:
0446                     if log:
0447                         self.log("Timed out waiting for %02x, requested bytes %d  - %d bytes read" % 
0448                                  (ord(char), b, len(res)))
0449                         self.logdata("Incomplete read was", res)
0450                     self.readbytes+=len(res)
0451                     raise CommTimeout(partial=res)
0452                 else:
0453                     numfailures-=1
0454                     self.log("Timed out - flushing and trying again")
0455             res=res+res2
0456 
0457         self.readbytes+=len(res)
0458         if logsuccess or data_recording.DR_On:
0459             self.logdata("Read completed", res,
0460                          data_recording.DR_Type_Read_Until)
0461         return res
0462 
0463     # these methods here consolidate calls, which makes the BitFling stuff a lot faster due
0464     # to fewer roundtrips
0465     def writethenreaduntil(self, data, logwrite, char, logreaduntil=True, logreaduntilsuccess=True, numfailures=1):
0466         self.write(data, logwrite)
0467         return self.readuntil(char, logreaduntil, logreaduntilsuccess, numfailures)
0468 
0469 class SilentException(Exception): pass
0470         
0471 class _usbdevicewrapper:
0472 
0473     def __init__(self, dev, timeout):
0474         self.dev=dev
0475         self.timeout=int(timeout*1000)
0476 
0477     def inWaiting(self):
0478         # This will cause one byte at a time reads in the other code.
0479         # It isn't really possible to fix until we have built in
0480         # buffering for the comm stuff.
0481         return 0
0482 
0483     def read(self, numchars=1):
0484         return self.dev.read(numchars, self.timeout)
0485 
0486     def flushInput(self):
0487         self.dev.resetep()
0488     
0489     def write(self, data):
0490         self.dev.write(data, self.timeout)
0491 
0492     def close(self):
0493         self.dev.close()
0494 
0495     def readuntil(self, char):
0496         # try and get it all in one shot
0497         try:
0498             data=self.dev.read(999999, self.timeout)
0499             if len(data) and data[-1]==char:
0500                 return data
0501         except usb.USBException:
0502             # ahh, buggy hardware or something
0503             self.dev.resetep()
0504             data=""
0505         # do it the old fashioned way
0506         basetime=time.time()
0507         while 1000*(time.time()-basetime)<self.timeout:
0508             try:
0509                 more=self.dev.read(99999, max(100,self.timeout-1000*(time.time()-basetime)))
0510                 data+=more
0511                 if len(data) and data[-1]==char:
0512                     return data
0513             except usb.USBException:
0514                 pass
0515         # timed out
0516         return data
0517 

Generated by PyXR 0.9.4