PyXR

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



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2006 Joe Pham <djpham@bitpim.org>
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: data_recording.py 4624 2008-06-30 03:55:35Z djpham $
0009 
0010 """
0011 Handle data recording stuff
0012 """
0013 
0014 # System modules
0015 import cPickle
0016 import struct
0017 import threading
0018 import time
0019 
0020 # wx modules
0021 
0022 # BitPim modules
0023 import common
0024 import pubsub
0025 
0026 # constants
0027 DR_Version=(0, 0, 1, 0) # 00.10
0028 DR_Signature='BitPimDR'
0029 DR_Rec_Marker='<-->'
0030 DR_Type_All=0xff
0031 DR_Type_Note=0x01
0032 DR_Type_Write=0x02
0033 DR_Type_Read=0x04
0034 DR_Type_Read_ATResponse=0x08
0035 DR_Type_Read_Some=0x10
0036 DR_Type_Read_Until=0x20
0037 DR_Type_Data=0x40
0038 DR_Type_Name={
0039     DR_Type_Note: 'Note',
0040     DR_Type_Write: 'Write',
0041     DR_Type_Read: 'Read',
0042     DR_Type_Read_ATResponse: 'Read ATResponse',
0043     DR_Type_Read_Some: 'Read Some',
0044     DR_Type_Read_Until: 'Read Until',
0045     DR_Type_Data: 'Data',
0046     }
0047 
0048 # exceptions
0049 class DataRecordingError(Exception):
0050     def __init__(self, value):
0051         Exception.__init__(self, value)
0052 
0053 # global varaibales
0054 DR_On=False
0055 DR_Play=False
0056 _the_recorder=None
0057 _the_player=None
0058 
0059 # public routines
0060 
0061 def record_to_file(file_name, append=False):
0062     "start a data recording session into the specified file"
0063     global DR_On, _the_recorder
0064     if DR_On and _the_recorder:
0065         _the_recorder.stop()
0066     _rec=DR_Rec_File(file_name, append)
0067     _rec.start()
0068     pubsub.publish(pubsub.DR_RECORD, data=_rec)
0069 
0070 def playback_from_file(file_name):
0071     "start a playback session from the specified file"
0072     global DR_Play, _the_player
0073     if DR_Play and _the_player:
0074         _the_player.stop()
0075     _player=DR_Read_File(file_name)
0076     _player.start()
0077     pubsub.publish(pubsub.DR_PLAY, data=_player)
0078 
0079 def stop():
0080     "stop a recording and/or playback session"
0081     global DR_Play, DR_On, _the_recorder, _the_player
0082     if DR_On and _the_recorder:
0083         _the_recorder.stop()
0084     if DR_Play and _the_player:
0085         _the_player.stop()
0086     pubsub.publish(pubsub.DR_STOP)
0087 
0088 def register(start_recording=None, start_playback=None, stop=None):
0089     if start_recording:
0090         pubsub.subscribe(start_recording, pubsub.DR_RECORD)
0091     if start_playback:
0092         pubsub.subscribe(start_playback, pubsub.DR_PLAY)
0093     if stop:
0094         pubsub.subscribe(stop, pubsub.DR_STOP)
0095 
0096 def unregister(start_recording=None, start_playback=None, stop=None):
0097     if start_recording:
0098         pubsub.unsubscribe(start_recording)
0099     if start_playback:
0100         pubsub.unsubscribe(start_playback)
0101     if stop:
0102         pubsub.unsubscribe(stop)
0103 
0104 def get_data(data_type):
0105     global DR_Play, _the_player
0106     # return the next data packet of the type data_type
0107     if DR_Play and _the_player:
0108         return _the_player.get_data(data_type)
0109     raise DataRecordingError('Data Playback not active')
0110 
0111 def get_headers():
0112     # return a list of headers used for setting the start point
0113     global DR_Play, _the_player
0114     if DR_Play and _the_player:
0115         return _the_player.get_headers()
0116     raise DataRecordingError('Data Playback not active')
0117 
0118 def set_start(start):
0119     # set/reset the start point of the recording file
0120     global DR_Play, _the_player
0121     if DR_Play and _the_player:
0122         _the_player.set_start(start)
0123     else:
0124         raise DataRecordingError('Data Playback not active')
0125 
0126 def record(dr_type, dr_data, dr_class=None):
0127     global DR_On, _the_recorder
0128     if DR_On and _the_recorder:
0129         _the_recorder.record(dr_type, dr_data, dr_class)
0130     elif __debug__:
0131         raise DataRecordingError('Data Recording not active')
0132 
0133 def filename():
0134     "return the current file name being used for recording or playback"
0135     if _the_recorder:
0136         return _the_recorder._file_name
0137     elif _the_player:
0138         return _the_player._file_name
0139 
0140 #-------------------------------------------------------------------------------
0141 class DR_Record(object):
0142     def __init__(self, dr_type, dr_data, klass=None):
0143         self._type=dr_type
0144         self._data=dr_data
0145         self._time=time.time()
0146         if klass:
0147             try:
0148                 self._class_module=klass.__module__
0149                 self._class_name=klass.__name__
0150             except:
0151                 klass=klass.__class__
0152                 self._class_module=klass.__module__
0153                 self._class_name=klass.__name__
0154         else:
0155             self._class_module=None
0156             self._class_name=None
0157 
0158     def get(self):
0159         return self._data
0160     def set(self, data):
0161         self._data=data
0162     def __repr__(self):
0163         # return a string rep suitable for log, protocol log, and analyzer
0164         # display
0165         t=time.localtime(self._time)
0166         _s="%d:%02d:%02d.%03d " % (t[3], t[4], t[5],
0167                                    int((self._time-int(self._time))*1000))
0168         if self._type==DR_Type_Note:
0169             _s+=self._data
0170         else:
0171             # data
0172             _s+=" %s - %d bytes\n" % (DR_Type_Name.get(self._type, 'Data'),
0173                                       len(self._data))
0174             if self._class_module and self._class_name:
0175                 _s+="<#! %s.%s !#>\n" % (self._class_module, self._class_name)
0176             _s+=common.datatohexstring(self._data)
0177         _s+='\n'
0178         return _s
0179     def summary(self):
0180         # return a summary string of this record
0181         global DR_Type_Name
0182         t=time.localtime(self._time)
0183         _s="%d:%02d:%02d.%03d " % (t[3], t[4], t[5],
0184                                    int((self._time-int(self._time))*1000))
0185         if self._type==DR_Type_Note:
0186             _s+=self._data
0187         else:
0188             _s+=DR_Type_Name.get(self._type, '<Unknown>')
0189         if len(_s)>80:
0190             _s=_s[:75]+'<...>'
0191         return _s
0192 
0193 #-------------------------------------------------------------------------------
0194 class DR_File(object):
0195     def __init__(self, file_name):
0196         self._file_name=file_name
0197         self._file=None
0198         self._valid=False
0199 
0200     def _check_header(self):
0201         # check to ensure that this file has the right header
0202         try:
0203             return file(self._file_name, 'rb').read(len(DR_Signature))==DR_Signature
0204         except IOError:
0205             return False
0206         except:
0207             if __debug__:
0208                 raise
0209             return False
0210 
0211     def stop(self):
0212         global DR_On
0213         self._file.close()
0214         DR_On=False
0215         
0216 #-------------------------------------------------------------------------------
0217 class DR_Rec_File(DR_File):
0218     def __init__(self, file_name, append=False):
0219         super(DR_Rec_File, self).__init__(file_name)
0220         self._pause=None
0221         if not append:
0222             self._file=file(self._file_name, 'wb')
0223             self._write_header()
0224             self._valid=True
0225         else:
0226             if self._check_header():
0227                 self._file=file(self._file_name, 'ab')
0228                 self._valid=True
0229             else:
0230                 self._valid=False
0231 
0232     def _write_header(self):
0233         # write the header to this file
0234         self._file.write(DR_Signature)
0235         _s=''
0236         for e in DR_Version:
0237             _s+=struct.pack('B', e)
0238         self._file.write(_s)
0239 
0240     def can_record(self):
0241         return bool(self._valid and self._file)
0242 
0243 
0244     def record(self, dr_type, dr_data, dr_class=None):
0245         if self._pause and (self._pause & dr_type):
0246             # not recording this type
0247             return
0248         if self._file:
0249             _t=threading.Thread(target=self._write_record,
0250                                 args=(dr_type, dr_data, dr_class))
0251             _t.start()
0252 
0253     def _write_record(self, dr_type, dr_data, dr_class=None):
0254         _rec=DR_Record(dr_type, dr_data, dr_class)
0255         _s=cPickle.dumps(_rec)
0256         self._file.write(DR_Rec_Marker+struct.pack('<L', len(_s))+_s)
0257 
0258     def stop(self):
0259         global DR_On, _the_recorder
0260         self._file.close()
0261         self._file=None
0262         self._valid=False
0263         DR_On=False
0264         _the_recorder=None
0265 
0266     def start(self):
0267         global DR_On, _the_recorder
0268         if self._file is None and self._file_name:
0269             self._file=file(self._file_name, 'ab')
0270             self._valid=True
0271         DR_On=True
0272         _the_recorder=self
0273 
0274     def pause(self, data_type=DR_Type_All):
0275         self._pause_type=data_type
0276     def unpause(self):
0277         self._pause=None
0278 
0279 #-------------------------------------------------------------------------------
0280 class DR_Read_File(DR_File):
0281     def __init__(self, file_name):
0282         super(DR_Read_File, self).__init__(file_name)
0283         if self._check_header():
0284             self._valid=True
0285             self._file=file(self._file_name, 'rb')
0286         else:
0287             self._valid=False
0288         self._data=[]
0289         self._read_data()
0290         self._start_index=0
0291         self._end_index=len(self._data)
0292         self._current_index=0
0293 
0294     def _read_rec(self):
0295         # read one DR record and return
0296         _marker=self._file.read(len(DR_Rec_Marker))
0297         if _marker!=DR_Rec_Marker:
0298             # marker not found
0299             return None
0300         try:
0301             _slen=self._file.read(struct.calcsize('L'))
0302             _data_len=struct.unpack('<L', _slen)[0]
0303             _sdata=self._file.read(_data_len)
0304             return cPickle.loads(_sdata)
0305         except (MemoryError, cPickle.UnpicklingError):
0306             return None
0307         except:
0308             if __debug__:
0309                 raise
0310             return None
0311 
0312     def _read_data(self):
0313         # read from the file and recontruct the data
0314         # first,get pass the header
0315         self._file.seek(len(DR_Signature)+len(DR_Version))
0316         _rec=self._read_rec()
0317         while _rec:
0318             self._data.append(_rec)
0319             _rec=self._read_rec()
0320         self._file.close()
0321 
0322     def get_string_data(self):
0323         # return all the data as a big string for display purposes
0324         _s=''
0325         for e in self._data:
0326             _s+=`e`
0327         return _s
0328     def get_headers(self):
0329         # return a list of headers
0330         return [x.summary() for x in self._data]
0331 
0332     def set_start(self, start_idx):
0333         self._start_index=start_idx
0334         self._current_index=start_idx
0335 
0336     def start(self):
0337         global DR_Play, _the_player
0338         self._current_index=self._start_index
0339         DR_Play=True
0340         _the_player=self
0341 
0342     def stop(self):
0343         global DR_Play, _the_player
0344         DR_Play=False
0345         _the_player=None
0346 
0347     def get_data(self, data_type):
0348         # return the data of the type 'data_type'
0349         if self._current_index>=self._end_index:
0350             # end of data
0351             raise DataRecordingError('No more data available for playback')
0352         _idx=self._current_index
0353         for i,e in enumerate(self._data[_idx:]):
0354             if e._type==data_type:
0355                 self._current_index+=i+1
0356                 return e.get()
0357         # no such data
0358         raise DataRecordingError('Failed to find playback data type')
0359 

Generated by PyXR 0.9.4