periodic_funs.py
Go to the documentation of this file.
00001 ##
00002 # Periodically logs the output of a callback function by calling it at a certain
00003 # rate and gathering up the results into a list
00004 class PeriodicLogger():
00005     ##
00006     # initializes the logger but doesn't start it
00007     #
00008     # @param callback the function to be called each time
00009     # @param rate the rate in seconds at which to call the callback
00010     # @param args the function arguments to pass into the callback
00011     def __init__(self, callback, rate=0.01, args=None):
00012         self.ret = []
00013         self.cb = callback
00014         self.rate = rate
00015         self.args = args
00016         self.is_running = False
00017         self.max_calls = None
00018         self.num_calls = 0
00019         self.beg_time = 0.
00020         self.thread = None
00021 
00022     ##
00023     # begins the logger 
00024     # @param max_calls the maximum number of times to call the callback
00025     def start(self, max_calls=None):
00026         if self.is_running:
00027             return
00028         self.max_calls = max_calls
00029         self.is_running = True
00030         self.num_calls = 0
00031         self.beg_time = rospy.Time.now().to_sec()
00032         self.thread = threading.Timer(self.rate, self._run)
00033         self.thread.start()
00034 
00035     def _run(self):
00036         if not self.is_running:
00037             return
00038 
00039         act_time = self.beg_time + self.rate * (self.num_calls + 2)
00040         interval = act_time - rospy.Time.now().to_sec()
00041         self.thread = threading.Timer(interval, self._run)
00042         self.thread.start()
00043 
00044         if self.args is None:
00045             retval = self.cb()
00046         else:
00047             retval = self.cb(*self.args)
00048         self.ret += [retval]
00049 
00050         self.num_calls += 1
00051         # break if we have called the sufficent number of times
00052         if self.max_calls is not None:
00053             if self.num_calls == self.max_calls:
00054                 self.is_running = False
00055                 return
00056 
00057     ##
00058     # stops the monitor
00059     # @return the result of the monitor
00060     def stop(self):
00061         self.thread.cancel()
00062         if not self.is_running:
00063             return None
00064         self.is_running = False
00065         return self.ret
00066 
00067     ##
00068     # If max_calls sets to automatically terminate, return the ret vals
00069     def get_ret_vals(self):
00070         if self.is_running:
00071             return None
00072         return self.ret
00073 
00074 ##
00075 # Periodically monitors the output of a callback function by calling it at a certain
00076 # rate and compares it with a provided model to insure the value doesn't vary greatly
00077 # within a degree of tolerance provided by the variance function
00078 class PeriodicMonitor():
00079     ##
00080     # initializes the monitor but doesn't start it
00081     #
00082     # @param callback the function to be called each time
00083     # @param rate the rate in seconds at which to call the callback
00084     # @param args the function arguments to pass into the callback
00085     def __init__(self, callback, rate=0.01, args=None):
00086         self.ret = []
00087         self.cb = callback
00088         self.rate = rate
00089         self.args = args
00090         self.is_running = False
00091         self.num_calls = 0
00092         self.beg_time = 0.
00093         self.thread = None
00094         self.mean_model = None
00095         self.variance_model = None
00096         self.std_devs = 0.
00097         self.failure = False
00098 
00099     ##
00100     # begins the monitor
00101     # TODO DOCS
00102     # @param max_calls the maximum number of times to call the callback
00103     def start(self, mean_model, variance_model, std_devs=2.5, max_calls=None, 
00104                                                 contingency=None, contingency_args=None):
00105         if len(mean_model) != len(variance_model):
00106             log("Models must be of same length")
00107             return
00108         if self.is_running:
00109             return
00110         self.is_running = True
00111         self.mean_model = mean_model
00112         self.variance_model = variance_model
00113         self.std_devs = std_devs
00114         self.max_calls = max_calls
00115         self.contingency = contingency
00116         self.contincency_args = contingency_args
00117         self.model_index = 0
00118         self.failure = False
00119             
00120         self.num_calls = 0
00121         self.beg_time = rospy.Time.now().to_sec()
00122         self.thread = threading.Timer(self.rate, self._run)
00123         self.thread.start()
00124 
00125     def _run(self):
00126         if not self.is_running:
00127             return
00128 
00129         act_time = self.beg_time + self.rate * (self.num_calls + 2)
00130         interval = act_time - rospy.Time.now().to_sec()
00131         self.thread = threading.Timer(interval, self._run)
00132         self.thread.start()
00133 
00134         if self.args is None:
00135             retval = self.cb()
00136         else:
00137             retval = self.cb(*self.args)
00138 
00139         # go through each coordinate in the vector
00140         for coord_i in len(retval[1]):
00141             diff = abs(retval[1][coord_i] - self.mean_model[self.model_index][coord_i])
00142             deviation = np.sqrt(self.variance_model[self.model_index][coord_i])
00143             if diff > self.std_devs * deviation:
00144                 # signal is outside allowable range
00145                 self.failure = True
00146                 self.is_running = False
00147                 # call contingency function
00148                 if contingency_args is None:
00149                     self.contingency()
00150                 else:
00151                     self.contingency(*contingency_args)
00152                 return
00153         self.ret += [retval]
00154         self.model_index += 1
00155         if self.model_index == len(self.mean_model):
00156             self.is_running = False
00157             return
00158 
00159         # break if we have called the sufficent number of times
00160         if not self.max_calls is None:
00161             self.max_calls -= 1
00162             if self.max_calls == 0:
00163                 self.is_running = False
00164                 return
00165 
00166     ##
00167     # stops the monitor
00168     # @return the result of the monitor
00169     def stop(self):
00170         self.thread.cancel()
00171         if not self.is_running:
00172             return None
00173         self.is_running = False
00174         return self.ret
00175 
00176     ##
00177     # If max_calls sets to automatically terminate, return the ret vals
00178     def get_ret_vals(self):
00179         if self.is_running:
00180             return None
00181         return self.ret
00182 
00183     # TODO DOCS
00184     def has_failed():
00185         return self.failure
00186 
00187     # TODO DOCS
00188     def wait_for_completion(rate=0.01):
00189         while self.is_running and not rospy.is_shutdown():
00190             rospy.sleep(rate)
00191         return not self.failure


kelsey_sandbox
Author(s): kelsey
autogenerated on Wed Nov 27 2013 11:52:04