00001
00002
00003
00004
00005 '''rtshell
00006
00007 Copyright (C) 2009-2014
00008 Geoffrey Biggs
00009 RT-Synthesis Research Group
00010 Intelligent Systems Research Institute,
00011 National Institute of Advanced Industrial Science and Technology (AIST),
00012 Japan
00013 All rights reserved.
00014 Licensed under the Eclipse Public License -v 1.0 (EPL)
00015 http://www.opensource.org/licenses/eclipse-1.0.txt
00016
00017 Log interface.
00018
00019 '''
00020
00021
00022 import sys
00023
00024
00025
00026
00027
00028 class EndOfLogError(EOFError):
00029 '''The end of the log has been reached while reading.'''
00030 pass
00031
00032
00033 class InvalidIndexError(EOFError):
00034 '''An invalid index was requested.'''
00035 pass
00036
00037
00038
00039
00040
00041 class EntryTS(object):
00042 def __init__(self, sec=0, nsec=0, time=None):
00043 super(EntryTS, self).__init__()
00044 if time is not None:
00045 self._sec, self._nsec = self._get_values(time)
00046 else:
00047 self._sec = sec
00048 self._nsec = nsec
00049
00050 def __repr__(self):
00051 return 'EntryTS(_sec={0}, _nsec={1})'.format(self._sec, self._nsec)
00052
00053 def __str__(self):
00054 return '{0}.{1:09}'.format(self._sec, self._nsec)
00055
00056 def __lt__(self, other):
00057 sec, nsec = self._get_values(other)
00058 if self._sec < sec:
00059 return True
00060 elif self._sec == sec:
00061 if self._nsec < nsec:
00062 return True
00063 return False
00064
00065 def __le__(self, other):
00066 sec, nsec = self._get_values(other)
00067 if self._sec < sec:
00068 return True
00069 elif self._sec == sec:
00070 if self._nsec <= nsec:
00071 return True
00072 return False
00073
00074 def __eq__(self, other):
00075 sec, nsec = self._get_values(other)
00076 if self._sec == sec and self._nsec == nsec:
00077 return True
00078 return False
00079
00080 def __ne__(self, other):
00081 sec, nsec = self._get_values(other)
00082 if self._sec != sec or self._nsec != nsec:
00083 return True
00084 return False
00085
00086 def __gt__(self, other):
00087 sec, nsec = self._get_values(other)
00088 if self._sec > sec:
00089 return True
00090 elif self._sec == sec:
00091 if self._nsec > nsec:
00092 return True
00093 return False
00094
00095 def __ge__(self, other):
00096 sec, nsec = self._get_values(other)
00097 if self._sec > sec:
00098 return True
00099 elif self._sec == sec:
00100 if self._nsec >= nsec:
00101 return True
00102 return False
00103
00104 @property
00105 def float(self):
00106 '''Get the time value as a float.'''
00107 return float(self._sec) + float(self._nsec) / 1e9
00108
00109 @property
00110 def sec(self):
00111 return self._sec
00112
00113 @sec.setter
00114 def sec(self, sec):
00115 self._sec = sec
00116
00117 @property
00118 def nsec(self):
00119 return self._nsec
00120
00121 @nsec.setter
00122 def nsec(self, nsec):
00123 self._nsec = nsec
00124
00125 def _get_values(self, other):
00126 if type(other) == EntryTS:
00127 return other.sec, other.nsec
00128 else:
00129 return int(other), int((other * 1000000000) % 1000000000)
00130
00131
00132
00133
00134
00135 class Log(object):
00136 def __init__(self, mode='r', meta=None, verbose=False, *args, **kwargs):
00137 '''Base constructor.
00138
00139 The log will be opened on construction. It should be closed manually,
00140 as Python does not guarantee that __del__() will be called.
00141
00142 @param mode Permissions. Specificy 'r' for read, 'w' for write or 'rw'
00143 for read/write. Not all logs support all permissions.
00144 Read/write permissions are particularly uncommon.
00145 @param meta A block of data to write into the log. Implementations
00146 are free to deal with this any way they wish, as long as it
00147 can be retrieved in read mode. However, there is no
00148 requirement that it can be changed after opening the log
00149 for writing. Users should set it before opening the log.
00150 @param verbose Print verbose output to stderr.
00151
00152 '''
00153 super(Log, self).__init__()
00154 self._mode = mode
00155 self._meta = meta
00156 self._vb = verbose
00157 self.open()
00158
00159 def __del__(self):
00160 self.close()
00161
00162 def __enter__(self):
00163 return self
00164
00165 def __exit__(self, exc_type, exc_val, exc_tb):
00166 if exc_type == None:
00167 self.close(finalise=True)
00168 else:
00169 self.close(finalise=False)
00170 return False
00171
00172 def __iter__(self):
00173 return self
00174
00175 def next(self):
00176 d = self.read()
00177 if not d:
00178 raise StopIteration
00179 return d[0]
00180
00181 def __str__(self):
00182 return 'Log interface object.'
00183
00184 @property
00185 def end(self):
00186 '''The position of the final entry in the log.
00187
00188 A tuple of (index, timestamp).
00189
00190 '''
00191 return self._get_end()
00192
00193 @property
00194 def eof(self):
00195 '''True if the log has reached the end.'''
00196 return self._eof()
00197
00198 @property
00199 def metadata(self):
00200 '''Return the metadata from the log (if any).'''
00201 return self._meta
00202
00203 @metadata.setter
00204 def metadata(self, metadata):
00205 self._meta = metadata
00206
00207 @property
00208 def mode(self):
00209 '''The mode of the log.'''
00210 return self._mode
00211
00212 @property
00213 def name(self):
00214 '''The name of the log.'''
00215 return self._name
00216
00217 @property
00218 def pos(self):
00219 '''The current position in the log.
00220
00221 A tuple of (index, timestamp).
00222
00223 This points at the current position - i.e. the entry most recently
00224 read.
00225
00226 '''
00227 return self._get_cur_pos()
00228
00229 @property
00230 def start(self):
00231 '''The position of the first entry in the log.
00232
00233 A tuple of (index, timestamp).
00234
00235 '''
00236 return self._get_start()
00237
00238 def open(self):
00239 '''Opens the log.'''
00240 self._open()
00241
00242 def finalise(self):
00243 '''Prepare the log to be closed.
00244
00245 Any cleaning up that needs to be done before closing the log should
00246 be done here.
00247
00248 This function is called just before closing the log by @ref close.
00249 If using the context manager statement (the 'with' statement), it will
00250 be called automatically, unless an exception has occured.
00251
00252 It is not required for objects implementing the Log interface to
00253 implement this function.
00254
00255 '''
00256 self._finalise()
00257
00258 def close(self, finalise=True):
00259 '''Closes the log.
00260
00261 @param finalise Whether the log should be finalised before closing.
00262 Defaults to True.
00263
00264 '''
00265 if finalise:
00266 self.finalise()
00267 self._close()
00268
00269 def write(self, timestamp, data):
00270 '''Writes an entry to the log.
00271
00272 The timestamp is necessary to allow reading back data at the
00273 same rate as it was recorded. It must be an object that
00274 supports comparisons using <, <=, =, >= and >. ilog.EntryTS
00275 provides a suitable object.
00276
00277 '''
00278 raise NotImplementedError
00279
00280 def read(self, timestamp=None, number=None):
00281 '''Read entries from the log.
00282
00283 If a time limit is given, all entries until that time limit is
00284 reached will be read.
00285
00286 If a number is given, that number of entries will be returned.
00287 This option overrides the time limit option.
00288
00289 If EOF is hit before the requested entries are read, what was
00290 read will be returned and @ref eof will return True.
00291
00292 If neither option is given, the next value will be returned.
00293
00294 Returns a list of tuples, [(index, timestamp, data), ...].
00295
00296 '''
00297 raise NotImplementedError
00298
00299 def rewind(self):
00300 '''Rewind the log to the first entry.'''
00301 raise NotImplementedError
00302
00303 def seek(self, timestamp=None, index=None):
00304 '''Rewind or fast-forward the log.
00305
00306 If the timestamp or index is earlier than the current position, the log
00307 will be rewound. If it is later, the log will be fast-forwarded.
00308
00309 When the log is rewound, it will go back to the first entry before
00310 the given timestamp or index. If an entry exactly matches the given
00311 timestamp or index, the log position will be at that entry, meaning
00312 that the next value read will be that entry.
00313
00314 When a log is fast-forwarded, it will go to the first entry after the
00315 given timestamp or index. If an entry exactly matches the given
00316 timestamp or index, the log position will be at that entry, meaning
00317 that the next value read will be that entry.
00318
00319 '''
00320 raise NotImplementedError
00321
00322 def _close(self):
00323 raise NotImplementedError
00324
00325 def _eof(self):
00326 return True
00327
00328 def _finalise(self):
00329 pass
00330
00331 def _get_cur_pos(self):
00332 '''Get the current position in the log.
00333
00334 Should be implemented by implementation objects. Called by the
00335 @ref position property.
00336
00337 '''
00338 raise NotImplementedError
00339
00340 def _get_start(self):
00341 '''Get the position of the first entry in the log.
00342
00343 Should be implemented by implementation objects. Called by the
00344 @ref start property.
00345
00346 '''
00347 raise NotImplementedError
00348
00349 def _get_end(self):
00350 '''Get the position of the last entry in the log.
00351
00352 Should be implemented by implementation objects. Called by the
00353 @ref end property.
00354
00355 '''
00356 raise NotImplementedError
00357
00358 def _open(self):
00359 raise NotImplementedError
00360
00361 def _vb_print(self, string):
00362 '''Print verbose information when self._vb is True.'''
00363 if self._vb:
00364 print >>sys.stderr, string
00365