ieee802154_io.py

Go to the documentation of this file.
00001 #   Copyright (c) 2008 Axel Wachtler
00002 #   All rights reserved.
00003 #
00004 #   Redistribution and use in source and binary forms, with or without
00005 #   modification, are permitted provided that the following conditions
00006 #   are met:
00007 #
00008 #   * Redistributions of source code must retain the above copyright
00009 #     notice, this list of conditions and the following disclaimer.
00010 #   * Redistributions in binary form must reproduce the above copyright
00011 #     notice, this list of conditions and the following disclaimer in the
00012 #     documentation and/or other materials provided with the distribution.
00013 #   * Neither the name of the authors nor the names of its contributors
00014 #     may be used to endorse or promote products derived from this software
00015 #     without specific prior written permission.
00016 #
00017 #   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018 #   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019 #   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020 #   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021 #   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022 #   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023 #   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024 #   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025 #   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026 #   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027 #   POSSIBILITY OF SUCH DAMAGE.
00028 
00029 # $Id: ieee802154_io.py,v 1.17 2010/02/22 20:44:32 awachtler Exp $
00030 ##
00031 # @file
00032 # @ingroup grpContribCapture
00033 # @brief File/Serial port io related classes.
00034 #
00035 
00036 # === import ==================================================================
00037 import sys, serial, time, threading, Queue, struct, os
00038 from ieee802154_base import PcapBase, UtilBase
00039 
00040 # === globals =================================================================
00041 
00042 # === functions ===============================================================
00043 
00044 # === classes =================================================================
00045 ##
00046 # @brief Capture file reader.
00047 #
00048 # This class reads packets from a capture file,
00049 # that was previously stored with tshark/wireshark.
00050 #
00051 # @todo This class reads the entire file (@ref open()) at once.
00052 # So it would be a good idea, to read the file block by block.
00053 #
00054 class FileIn(PcapBase, UtilBase):
00055 
00056     ## Open and read a capture file.
00057     # @param fname name of the file
00058     def open(self, fname):
00059         self.fname = fname
00060         self.fh = open(fname,'r')
00061         self.FCNT = 0
00062         d = self.fh.read()
00063         self.HEADER,self.frames = self.pcap_parse_data(d)
00064         print "nb frames", len(self.frames)
00065         self.frameidx = 0
00066 
00067     ## close the file handle
00068     def close(self):
00069         self.fh.close()
00070 
00071     ## return file header
00072     def pcap_get_header(self):
00073         return self.HEADER
00074 
00075     def info(self):
00076         afn = os.path.abspath(self.fh.name)
00077         ret = {'type'   : 'file',
00078                'file'   : os.path.basename(afn),
00079                'dir'    : os.path.dirname(afn),
00080                'frames' : self.FCNT,
00081                 }
00082         return ret
00083 
00084     ##
00085     # Setting of a channel is is unsupported for a file
00086     def set_channel(self, channel):
00087         self.error("can not set channel on a file")
00088 
00089     ## read a frame f
00090     def read_packet(self):
00091         try:
00092             #print "read frame %d" % self.frameidx
00093             ret = self.frames[self.frameidx]
00094             self.frameidx += 1
00095             self.FCNT += 1
00096             print map(hex,map(ord,ret))
00097         except IndexError:
00098             ret = None
00099         except:
00100             import traceback
00101             traceback.print_exc()
00102             ret = None
00103         return ret
00104 
00105 
00106 # special handling for windows
00107 RxErrors = [Exception]
00108 try:    
00109     import pywintypes
00110     RxErrors.append(pywintypes.error)
00111 except:
00112     pass
00113 RxErrors = tuple(RxErrors)
00114 
00115 
00116 ##
00117 # @brief Serial Interface Reader.
00118 #
00119 class PortIn(PcapBase, UtilBase):
00120     TMO = 1
00121     FCNT = 0
00122     UNSYNC = 0
00123     SYNCED = 1
00124     IDXERR = 0
00125     BAUDRATE = 38400
00126     def __init__(self):
00127         self.RxThread = None
00128         self.RxQueue = Queue.Queue()
00129         self.TextQueue = Queue.Queue()
00130         self.channel = None
00131         self.crate = None
00132         self.clist = []
00133         self.state = self.UNSYNC
00134         self.maxpackets = -1
00135     ## @brief Open the serial port.
00136     def open(self, fname):
00137         if self.RxThread:
00138             self.RxThread.join()
00139         try:
00140             port,baud = fname.split(":")
00141         except:
00142             port = fname
00143         else:
00144             self.BAUDRATE = eval(baud)
00145         self.fname = port
00146         self.sport = serial.Serial(self.fname, self.BAUDRATE, timeout=10)
00147         self.sport.open()
00148         self.RxThread=threading.Thread(target = self.__rx__)
00149         self.RxThread.setDaemon(1)
00150         self.RxThread.start()
00151         self.init()
00152 
00153     def reset(self):
00154         self.sport.write("\n\nidle\n")
00155 
00156     def init(self):
00157         self.sport.write("\n\nidle\n")
00158         self.update_hw_status()
00159         t = int(time.time())
00160         self.sport.write("timeset %s\n" % t)
00161         self.timebase = t
00162         self.sport.write("sniff\n")
00163 
00164     def update_hw_status(self):
00165         self.sport.write("\n\nidle\n")
00166         self.sport.write("parms\n")
00167         time.sleep(.5)
00168         for l in self.get_text_queue():
00169             if l.find("PLATFORM")>=0:
00170                 self.platform = l.split(":")[1].strip()
00171             if l.find("SUPP_CMSK")>=0:
00172                 self.clist = self.get_channels(eval(l.split(":")[1]))
00173             if l.find("CURR_CHAN")>=0:
00174                 self.channel = eval(l.split(":")[1])
00175             if l.find("TIMER_SCALE")>=0:
00176                 self.tscale = eval(l.split(":")[1])
00177             if l.find("TICK_NUMBER")>=0:
00178                 self.ticknb = eval(l.split(":")[1])
00179             if l.find("CURR_RATE")>=0:
00180                 self.crate = l.split(":")[1].strip()
00181             if l.find("SUPP_RATES")>=0:
00182                 self.rates = l.split(":")[1].strip()
00183 
00184 
00185     def get_text_queue(self):
00186         ret = ""
00187         while not self.TextQueue.empty():
00188             ret += self.TextQueue.get()
00189         ret = ret.replace('\x0b',"")
00190         return ret.split("\n")
00191 
00192     def get_channels(self,cmask):
00193         ret = []
00194         cidx = 0
00195         while cmask != 0:
00196             if cmask & 1:
00197                 ret.append(cidx)
00198             cidx += 1
00199             cmask /=2
00200         return ret
00201 
00202     def close(self):
00203         if self.RxThread != None:
00204             self.RxThread.join(self.TMO)
00205             self.RxThread = None
00206         self.sport.close()
00207 
00208     def info(self):
00209         self.update_hw_status()
00210         ret = {'type'   : 'port',
00211                'chan'   : self.channel,
00212                'port'   : self.sport.port,
00213                'clist'  : self.clist,
00214                'tscale' : self.tscale,
00215                'ticknb' : self.ticknb,
00216                'crate'  : self.crate,
00217                'rates'  : self.rates,
00218                'platform' : self.platform,
00219                 }
00220         self.sport.write("sniff\n")
00221         return ret
00222 
00223     def __rx__(self):
00224         frm = ""
00225         while 1:
00226             try:
00227                 sdata = self.sport.read(1)
00228                 n = self.sport.inWaiting()
00229                 if n:
00230                     sdata += self.sport.read(n)
00231             except RxErrors:
00232                 break
00233             except:
00234                 traceback.print_exc()
00235                 break
00236             frm += sdata
00237             if self.state == self.UNSYNC:
00238                 self.TextQueue.put(sdata)
00239                 p = self.sync_search(frm)
00240                 if type(p) != None:
00241                     frm = frm[p:]
00242                     self.state = self.SYNCED
00243             if self.state == self.SYNCED:
00244                 self.state,frm = self.packetizer(frm)
00245                 self.message(2,"state sync after packetizer(), state=%d, len_frm=%d",self.state, len(frm))
00246 
00247     def sync_search(self,frm):
00248         ret = None
00249         p = 0
00250         nbstartpos = frm.count('\x01')
00251         dlen = len(frm)
00252         if nbstartpos:
00253             self.message(2,"syncsearch : dlen=%d, nbstarts=%d" ,dlen,nbstartpos)
00254             for idx in range(nbstartpos):
00255                 try:
00256                     p += frm[p:].index('\x01')
00257                     plen = ord(frm[p+1])
00258                     pe = p + plen + 2
00259                     self.message(2,"syncing : idx=%d, packet=%d:%d, plen=%d dlen=%d", idx,p,pe,plen,dlen)
00260 
00261                     if pe <= dlen:
00262                         self.message(2,"packet : %s " , str(map(lambda i,f=frm: hex(ord(f[i])), (p,p+1,pe-1,pe) )))
00263                         if(frm[pe] == '\x04'):
00264                             ret = p
00265                             self.message(1,"synced : idx=%d, packet=%d:%d, plen=%d dlen=%d", idx,p,pe,plen,dlen)
00266                             raise Exception, "Synced"
00267                     p += 1
00268                 except IndexError:
00269                     # this catches the blind access in line "l = ord(frm[p+1])"
00270                     break
00271                 except Exception:
00272                     break
00273                 except:
00274                     self.exc_handler("sync_search")
00275                     break
00276         return ret
00277 
00278     def packetizer(self,frm):
00279         state = self.SYNCED
00280         while 1:
00281             frmlen = len(frm)
00282             if len(frm) < 3:
00283                 # incomplete data
00284                 break
00285             if frm[0] != '\x01':
00286                 state = self.UNSYNC
00287                 break
00288             pktlen = ord(frm[1])
00289             if (pktlen+3) > frmlen:
00290                 # incomplete data
00291                 break
00292             if frm[pktlen+2] != '\x04':
00293                 state = self.UNSYNC
00294                 break
00295             packet,frm = frm[:pktlen+3],frm[pktlen+3:]
00296 
00297             ## XXX refactor this hack
00298             # convert frame from serial line to pcap format
00299             # u8   length
00300             # u64  ts
00301             # u8[] frm
00302             #
00303             # packet data is prefixed with byte STX (1) + length (1)
00304             # and suffixed with EOT char (4)
00305             fl = pktlen - 8
00306             ticks,pkt = packet[2:10], packet[10:-1]
00307             ticks = struct.unpack('LL',ticks)
00308             tstamp = ((ticks[0]*self.ticknb) + ticks[1]) * self.tscale
00309             t_sec = int(tstamp)
00310             t_usec = int((tstamp-t_sec)*1.0e6)
00311             ts = struct.pack('LL',(t_sec+self.timebase),t_usec)
00312             lenfield = struct.pack("LL",fl,fl)
00313             packet = ts+lenfield+pkt
00314 
00315             if self.FCNT < self.maxpackets or self.maxpackets == 0:
00316                 self.RxQueue.put(packet)
00317                 self.FCNT += 1
00318                 self.message(1,"Found Packet l=%d qsize: %d:\npkt: %s",
00319                         pktlen, self.RxQueue.qsize(),
00320                         " ".join(map(lambda s: '%02x' %s,map(ord,packet))))
00321             else:
00322                 self.message(1,"Discard Packet l=%d qsize: %d", pktlen,self.RxQueue.qsize())
00323         return state,frm
00324 
00325     def set_channel(self, channel):
00326         if channel in self.clist:
00327             self.channel = channel
00328             time.sleep(0.1) # this sleep is somehow needed for rzusb stick.
00329             self.sport.write("chan %d\n" % channel)
00330         else:
00331             print "Unsupported channel %d not in %s" % (channel,self.clist)
00332 
00333     def set_rate(self, rate):
00334         if rate in self.rates:
00335             self.rate = rate
00336             self.sport.write("\nidle\ndrate %s\nsniff\n" % self.rate)
00337         else:
00338             print "Unsupported data rate %s not in %s" % (rate, self.rates)
00339 
00340     def read_packet(self):
00341         if self.RxQueue.empty():
00342             ret = None
00343         else:
00344             ret = self.RxQueue.get()
00345         return ret
00346 
00347 # === init ====================================================================

This documentation for µracoli was generated on Wed Feb 2 2011 by  doxygen 1.7.1