def __init__(self): self.__alarm_queue = Queue() self.__current_thread = None self.__lock = Lock() self._init_default_attribute_values() Client.__init__(self) return
def setUp(self): DefaultTestFixture.setUp(self) self.lock = Lock() self.pool = ThreadPool(3) self.queue = Queue() self.simple_action_counter = 0 return
def __init__(self): ServiceNode.__init__(self) EventConsumerMixin.__init__(self, self.handle_alarm, \ self.handle_exception) self.__running = 0 self.__queue = Queue() self._id_manager = None self._dynamic_alarms = {} return
def __init__(self): self.__server = None self.__sched_lock = Lock() self.__sched_thread = None self.__schedules = Queue() self.__pv = None super(LightingGroup, self).__init__() EventConsumerMixin.__init__(self, self._sched_update, self._sched_exception) return
def __init__(self): AVRNode.__init__(self) AutoDiscoveredNode.__init__(self) self._lock = Lock() self.conversion_list = {} self._queue = Queue() self.debug = 0 self.running = 0 self._start_called = 0 self.devices = '' self.device_addresses = [] self._been_discovered = 0
def _startmanager(self): self._queue = queue = Queue() self._stopflag = stopflag = Flag() self._thread = thread = ImmortalThread(name=self.url, target=self._runmanager, args=(stopflag, queue)) thread.start()
def _proxy_start_active_mode(self): if self.link: try: if self._proxy_sid is None: #have not started subscription service yet if self.proxy_direction == GET_ONLY: self._proxy_active_source = self._proxy_linked_node() if self._proxy_active_source is None: raise ENotStarted() self._proxy_active_destination = self else: #SET_ONLY self._proxy_active_source = self self._proxy_active_destination = self._proxy_linked_node( ) if self._proxy_active_destination is None: raise ENotStarted() self._proxy_active_queue = Queue() self._proxy_sid = SM.create_delivered( self, {1: self._proxy_active_source}) if self.debug: print 'Active proxy %s started successfully' % ( self.name) except: #it didn't work. Setup schedule to try again in x seconds. if self._retry_win_high < 90: self._retry_win_high += 1 retry_in = randint(int(self._retry_win_high * .66), self._retry_win_high) scheduler.seconds_from_now_do(retry_in, self._proxy_start_active_mode) #raise #took this out since it mostly just served to force the scheduler tread to restart if self.debug: msglog.exception()
class EWebConnectAlarmClient(Client): _batch_mode_default = 0 _host_default = REQUIRED _port_default = 4546 _timeout_default = 60 def _init_default_attribute_values(self): self.batch_mode = self._batch_mode_default self.host = self._host_default self.port = self._port_default self.timeout = self._timeout_default return def __init__(self): self.__alarm_queue = Queue() self.__current_thread = None self.__lock = Lock() self._init_default_attribute_values() Client.__init__(self) return def configure(self, config): Client.configure(self, config) set_attribute(self, 'batch_mode', self._batch_mode_default, config, int) set_attribute(self, 'host', self._host_default, config, str) set_attribute(self, 'port', self._port_default, config, int) set_attribute(self, 'timeout', self._timeout_default, config, int) return def configuration(self): config = Client.configuration(self) get_attribute(self, 'batch_mode', config, int) get_attribute(self, 'host', config, str) get_attribute(self, 'port', config, int) get_attribute(self, 'timeout', config, int) return config def start(self): self.__lock.acquire() try: self.__running = 1 self.register_event(NewAlarmsEvent, self._new_alarms) finally: self.__lock.release() Client.start(self) self.debug = 1 def stop(self): self.__lock.acquire() try: self.unregister_event(NewAlarmsEvent) self.__running = 0 finally: self.__lock.release() Client.stop(self) def is_running(self): return self.__running def message_log(self, message, message_type=msglog.types.DB): if message_type != msglog.types.DB or self.debug: msglog.log('EWebConnect Alarm Client', message_type, message) ## # Event handler for the NewAlarmsEvent # # Queues each new alarm for processing and then schedules # _prime_process_alarm_queue() on a thread pool to ensure that alarms # are processed. def _new_alarms(self, event): self.__lock.acquire() try: if not self.is_running(): raise ENotStarted('%s' % self.as_node_url()) finally: self.__lock.release() for alarm in event: self.__alarm_queue.put(alarm.as_dictionary()) self.message_log('New Alarms Event, queuing action') LOW.queue_noresult(self._prime_process_alarm_queue) return ## # If no thread is actively processing the alarm queue, then set this thread # as the current alarm queue processor and invoke _process_alarm_queue(). # # @note This method is in invoked as an action queued on a thread pool and # should never be called directly when processing an event. def _prime_process_alarm_queue(self): # @todo Save queue in a PDO? thread = currentThread() self.__current_thread = thread self._process_alarm_queue(thread) ## # Process all alarms on the alarm queue. # # @note This method is in invoked indirectly as an action queued on a # thread pool and should never be called directly when processing an # event. def _process_alarm_queue(self, my_thread): self.message_log('Processing Alarm Queue...') while my_thread == self.__current_thread: alarm_dict = self.__alarm_queue.get(0) if alarm_dict is NOTHING: break try: self._send_alarm_dict(alarm_dict) except: self.message_log('Failed to send alarm:\n %r' % alarm_dict, msglog.types.ERR) msglog.exception() else: self.message_log( 'New alarm process coincided with running process') self.message_log('Finished Processing Alarm Queue') ## # Format the Alarm described by alarm_dict as an eWebConnect Alarm # message and send it to the eWebConnect server. # @note This method always succeeds to format a message, any field that # is not valid for any reason is set to "N/A". Furthermore, # this method does not intercept any networking failures as it # is the caller's responsibility to handle retries, etc... def _send_alarm_dict(self, alarm_dict): if self.host is None: self.message_log( 'Failed to send alarm; host address is None:\n %r' % alarm_dict, msglog.types.INFO) return # nowhere to send it! ewebconnect_text = ( "%(timestamp)s, %(TZ)s, %(host)s, %(what)s, %(code)s:" " %(type)s %(text)s") % { "timestamp": self._alarm_timestamp(alarm_dict), "TZ": self._alarm_tz(alarm_dict), "host": self._alarm_host(alarm_dict), "what": self._alarm_what(alarm_dict), "code": self._alarm_code(alarm_dict), "type": self._alarm_type(alarm_dict), "text": self._alarm_text(alarm_dict), } self.message_log('Sending Alarm: %s' % ewebconnect_text) server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: # @fixme Try block only exists because the normal desctructor # invokation does not appear to work on mpx.lib.socket # sockets which is a big deal if there is an exception. # - mevans server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.connect((self.host, self.port), self.timeout) server_socket.sendall(ewebconnect_text, self.timeout) finally: # A finally block is used because the normal desctructor invokation # does not appear to work on mpx.lib.socket socket's. - mevans # @fixme Figure out why! server_socket.close() # Could this hang? Should I use shutdown? self.message_log('Alarm Sent') return ## # Convert an Alarm's time-stamp to a string in eWebConnect's format. # # @param alarm_dict The dictionary representation of an Alarm, presumably # returned by the Alarm's as_dictionary() method. # @return A string representing the the time-stamp in eWebConnect's format. # @note Any failure during the conversion results in the string "N/A" and # the exception is logged to the msglog. This is to help ensure that # the alarm is still delivered with as much useful information as # possible. def _alarm_timestamp(self, alarm_dict): result = "N/A" try: localtime = time.localtime(alarm_dict['timestamp']) result = time.strftime("%m/%d/%Y %H:%M:%S", localtime) except: msglog.exception() return result ## # @see _alarm_timestamp(). def _alarm_tz(self, alarm_dict): result = "N/A" try: is_dst = time.localtime(alarm_dict['timestamp']).tm_isdst result = time.tzname[is_dst] except: msglog.exception() return result ## # @see _alarm_timestamp(). def _alarm_host(self, alarm_dict): result = "N/A" try: # @fixme F/W should have this as a 'system attribute' (aka # property) and should support COV on properties. result = socket.gethostbyaddr(socket.gethostname())[0] except: msglog.exception() return result ## # @see _alarm_timestamp(). def _alarm_code(self, alarm_dict): result = "N/A" try: result = alarm_dict['state'] except: msglog.exception() return result ## # @see _alarm_timestamp(). def _alarm_what(self, alarm_dict): result = "ALARM" return result ## # @see _alarm_timestamp(). def _alarm_type(self, alarm_dict): result = "N/A" try: result = alarm_dict['type'] except: msglog.exception() return result ## # @see _alarm_timestamp(). def _alarm_text(self, alarm_dict): result = "N/A" try: result = alarm_dict['data'] except: msglog.exception() return result
class EWebConnectAlarmClient(Client): _batch_mode_default = 0 _host_default = REQUIRED _port_default = 4546 _timeout_default = 60 def _init_default_attribute_values(self): self.batch_mode = self._batch_mode_default self.host = self._host_default self.port = self._port_default self.timeout = self._timeout_default return def __init__(self): self.__alarm_queue = Queue() self.__current_thread = None self.__lock = Lock() self._init_default_attribute_values() Client.__init__(self) return def configure(self,config): Client.configure(self,config) set_attribute(self,'batch_mode', self._batch_mode_default, config, int) set_attribute(self,'host', self._host_default, config, str) set_attribute(self,'port', self._port_default, config, int) set_attribute(self,'timeout', self._timeout_default, config, int) return def configuration(self): config = Client.configuration(self) get_attribute(self, 'batch_mode', config, int) get_attribute(self, 'host', config, str) get_attribute(self, 'port', config, int) get_attribute(self, 'timeout', config, int) return config def start(self): self.__lock.acquire() try: self.__running = 1 self.register_event(NewAlarmsEvent,self._new_alarms) finally: self.__lock.release() Client.start(self) self.debug = 1 def stop(self): self.__lock.acquire() try: self.unregister_event(NewAlarmsEvent) self.__running = 0 finally: self.__lock.release() Client.stop(self) def is_running(self): return self.__running def message_log(self,message,message_type=msglog.types.DB): if message_type != msglog.types.DB or self.debug: msglog.log('EWebConnect Alarm Client',message_type,message) ## # Event handler for the NewAlarmsEvent # # Queues each new alarm for processing and then schedules # _prime_process_alarm_queue() on a thread pool to ensure that alarms # are processed. def _new_alarms(self, event): self.__lock.acquire() try: if not self.is_running(): raise ENotStarted('%s' % self.as_node_url()) finally: self.__lock.release() for alarm in event: self.__alarm_queue.put(alarm.as_dictionary()) self.message_log('New Alarms Event, queuing action') LOW.queue_noresult(self._prime_process_alarm_queue) return ## # If no thread is actively processing the alarm queue, then set this thread # as the current alarm queue processor and invoke _process_alarm_queue(). # # @note This method is in invoked as an action queued on a thread pool and # should never be called directly when processing an event. def _prime_process_alarm_queue(self): # @todo Save queue in a PDO? thread = currentThread() self.__current_thread = thread self._process_alarm_queue(thread) ## # Process all alarms on the alarm queue. # # @note This method is in invoked indirectly as an action queued on a # thread pool and should never be called directly when processing an # event. def _process_alarm_queue(self,my_thread): self.message_log('Processing Alarm Queue...') while my_thread == self.__current_thread: alarm_dict = self.__alarm_queue.get(0) if alarm_dict is NOTHING: break try: self._send_alarm_dict(alarm_dict) except: self.message_log('Failed to send alarm:\n %r' % alarm_dict, msglog.types.ERR) msglog.exception() else: self.message_log('New alarm process coincided with running process') self.message_log('Finished Processing Alarm Queue') ## # Format the Alarm described by alarm_dict as an eWebConnect Alarm # message and send it to the eWebConnect server. # @note This method always succeeds to format a message, any field that # is not valid for any reason is set to "N/A". Furthermore, # this method does not intercept any networking failures as it # is the caller's responsibility to handle retries, etc... def _send_alarm_dict(self, alarm_dict): if self.host is None: self.message_log('Failed to send alarm; host address is None:\n %r' % alarm_dict, msglog.types.INFO) return # nowhere to send it! ewebconnect_text = ( "%(timestamp)s, %(TZ)s, %(host)s, %(what)s, %(code)s:" " %(type)s %(text)s" ) % { "timestamp":self._alarm_timestamp(alarm_dict), "TZ":self._alarm_tz(alarm_dict), "host":self._alarm_host(alarm_dict), "what":self._alarm_what(alarm_dict), "code":self._alarm_code(alarm_dict), "type":self._alarm_type(alarm_dict), "text":self._alarm_text(alarm_dict), } self.message_log('Sending Alarm: %s' % ewebconnect_text) server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) try: # @fixme Try block only exists because the normal desctructor # invokation does not appear to work on mpx.lib.socket # sockets which is a big deal if there is an exception. # - mevans server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) server_socket.connect((self.host, self.port), self.timeout) server_socket.sendall(ewebconnect_text, self.timeout) finally: # A finally block is used because the normal desctructor invokation # does not appear to work on mpx.lib.socket socket's. - mevans # @fixme Figure out why! server_socket.close() # Could this hang? Should I use shutdown? self.message_log('Alarm Sent') return ## # Convert an Alarm's time-stamp to a string in eWebConnect's format. # # @param alarm_dict The dictionary representation of an Alarm, presumably # returned by the Alarm's as_dictionary() method. # @return A string representing the the time-stamp in eWebConnect's format. # @note Any failure during the conversion results in the string "N/A" and # the exception is logged to the msglog. This is to help ensure that # the alarm is still delivered with as much useful information as # possible. def _alarm_timestamp(self, alarm_dict): result = "N/A" try: localtime = time.localtime(alarm_dict['timestamp']) result = time.strftime("%m/%d/%Y %H:%M:%S", localtime) except: msglog.exception() return result ## # @see _alarm_timestamp(). def _alarm_tz(self, alarm_dict): result = "N/A" try: is_dst = time.localtime(alarm_dict['timestamp']).tm_isdst result = time.tzname[is_dst] except: msglog.exception() return result ## # @see _alarm_timestamp(). def _alarm_host(self, alarm_dict): result = "N/A" try: # @fixme F/W should have this as a 'system attribute' (aka # property) and should support COV on properties. result = socket.gethostbyaddr(socket.gethostname())[0] except: msglog.exception() return result ## # @see _alarm_timestamp(). def _alarm_code(self, alarm_dict): result = "N/A" try: result = alarm_dict['state'] except: msglog.exception() return result ## # @see _alarm_timestamp(). def _alarm_what(self, alarm_dict): result = "ALARM" return result ## # @see _alarm_timestamp(). def _alarm_type(self, alarm_dict): result = "N/A" try: result = alarm_dict['type'] except: msglog.exception() return result ## # @see _alarm_timestamp(). def _alarm_text(self, alarm_dict): result = "N/A" try: result = alarm_dict['data'] except: msglog.exception() return result
class AlarmManager(ServiceNode,EventConsumerMixin): def __init__(self): ServiceNode.__init__(self) EventConsumerMixin.__init__(self, self.handle_alarm, \ self.handle_exception) self.__running = 0 self.__queue = Queue() self._id_manager = None self._dynamic_alarms = {} return def configure(self, config): ServiceNode.configure(self, config) if self._id_manager is None: self._id_manager = UniqueID(self) return def unique_id(self): return self._id_manager.allocate_id() def active(self): children = [] for child in self.chidlren_nodes(): if child.active(): children.append(child) return children def acknowledged(self): children = [] for child in self.children_nodes(): if child.active() and child.acknowledged(): children.append(child) return children def unacknowledged(self): children = [] for child in self.children_nodes(): if child.active() and not child.acknowledged(): children.append(child) return children def add_dynamic_alarm(self, dynamic_alarm): dynamic_alarm_id = id(dynamic_alarm) if not self._dynamic_alarms.has_key(dynamic_alarm_id): self._dynamic_alarms[dynamic_alarm_id] = dynamic_alarm dynamic_alarm.event_subscribe(self, AlarmTriggerEvent) dynamic_alarm.event_subscribe(self, AlarmClearEvent) return def start(self): if not self.__running: known_alarms = self.get_child('alarms') for child in known_alarms.children_nodes(): child.event_subscribe(self, AlarmTriggerEvent) child.event_subscribe(self, AlarmClearEvent) self.__running = 1 self.__thread = ImmortalThread(name=self.name,target=self.__run) self.__thread.start() ServiceNode.start(self) return def stop(self): self.__running = 0 self.__thread = None self.__queue.put(None) try: # @fixme try/except/else is a hack to survive testcases use # of prune. Really should revisit... alarms = self.get_child('alarms') except: pass else: for child in alarms.children_nodes(): child.cancel(self, AlarmTriggerEvent) child.cancel(self, AlarmClearEvent) return ServiceNode.stop(self) def queue_alarm_check(self,alarm): self.__queue.put(alarm) def __run(self): while self.__running: alarm = self.__queue.get() if alarm: alarm.check_condition() else: self.__thread.should_die() return ## # Both AlarmClear and AlarmTrigger events come here, # separate the two and send to the appropriate handler. def handle_alarm(self, alarm): alarm.source.caught(alarm) if self.debug: msglog.log('broadway',msglog.types.ALARM,str(alarm)) if alarm.__class__ == AlarmTriggerEvent: return self._handle_trigger(alarm) elif alarm.__class__ == AlarmClearEvent: return self._handle_clear(alarm) msglog.log('broadway', msglog.types.WARN,'Alarm manager %s got ' + 'an event that it does not recoginize, ignoring.' % self.name) def _handle_trigger(self, alarm): export_thread = Thread(name=alarm.source.name, target=self._run_exports, args=(alarm,)) export_thread.start() def _run_exports(self,alarm): if self.has_child('exporters'): exporters = self.get_child('exporters').children_nodes() exceptions = [] for exporter in exporters: try: exporter.export(alarm) except Exception, e: msglog.exception() exceptions.append(e) if exceptions: alarm.source.fail(alarm) else: alarm.source.success(alarm) else:
class TestCase(DefaultTestFixture): def setUp(self): DefaultTestFixture.setUp(self) self.lock = Lock() self.pool = ThreadPool(3) self.queue = Queue() self.simple_action_counter = 0 return def tearDown(self): self.pool._unload() DefaultTestFixture.tearDown(self) return def simple_action(self, object): # @note It appears that even the '+= 1' operation is not # guaranteed to be atomic. self.lock.acquire() self.simple_action_counter += 1 self.lock.release() return 'simple_action_result' def slow_action(self, object): time.sleep(1.0) return 'slow_action_result' def simple_queue_action(self, object): self.queue.put(object) return def test_simple_queue(self): self.pool.queue(self.simple_queue_action, self) result = self.queue.get(1.0) if result is not self: raise "Queue returned %r instead of self, %r." % (result, self) return def test_result(self): t1 = time.time() pending_result = self.pool.queue(self.simple_action, self) result = pending_result.result(10.0) t2 = time.time() if result != 'simple_action_result': raise ("pending_result.result() returned the wrong value (%s)." % result) if (t2 - t1) >= 10.0: raise "pending_result.result() blocked for no reason." return def test_pending_reasult(self): t1 = time.time() pending_result = PendingResult(None, None, self.simple_action, self) pending_result_two = self.pool.queue_pending_result(pending_result) if pending_result_two is not pending_result: raise "pending_result_two is NOT pending_result" result = pending_result.result(10.0) t2 = time.time() if result != 'simple_action_result': raise ("pending_result.result() returned the wrong value (%s)." % result) if (t2 - t1) >= 10.0: raise "pending_result.result() blocked for no reason." return def test_pending_action(self): pending_action = PendingAction(self.simple_queue_action, self) self.pool.queue_pending_action(pending_action) result = self.queue.get(1.0) if result is not self: raise "Queue returned %r instead of self, %r." % (result, self) return return def test_result_timeout(self): t1 = time.time() pending_result = self.pool.queue(self.slow_action, self) result = pending_result.result(0.25) t2 = time.time() if (t2 - t2) >= 1.0: raise "Blocked 1 second when a 1/4 second timeout." if result != NORESULT: raise "Got a result (%s) when none was expected." return def test_1000_actions(self): for i in xrange(0, 1000): self.pool.queue(self.simple_action, self) time.sleep(0.1) t1 = time.time() while self.simple_action_counter < 1000: tn = time.time() if (tn - t1) > 3.0: raise ( "Taking ridiculously long to process 1000 queued actions.") time.sleep(0.1) return def test_HIGH_pool_1(self): t1 = time.time() pending_result = HIGH.queue(self.simple_action, self) result = pending_result.result(10.0) t2 = time.time() if result != 'simple_action_result': raise ("pending_result.result() returned the wrong value (%s)." % result) if (t2 - t1) >= 10.0: raise "pending_result.result() blocked for no reason." return def test_HIGH_pool_2(self): self.test_HIGH_pool_1() return def test_HIGH_pool_resize_1(self): HIGH.resize(1) if HIGH.size() != 1: raise "Resize to 1 thread failed." for i in xrange(0, 100): HIGH.queue(self.simple_action, self) t1 = time.time() while self.simple_action_counter < 100: tn = time.time() if (tn - t1) > 3.0: raise ( "Taking ridiculously long to process 100 queued actions.") time.sleep(0.1) return def test_HIGH_pool_resize_20(self): HIGH.resize(20) if HIGH.size() != 20: raise "Resize to 20 threads failed." for i in xrange(0, 100): HIGH.queue(self.simple_action, self) t1 = time.time() while self.simple_action_counter < 100: tn = time.time() if (tn - t1) > 3.0: raise ( "Taking ridiculously long to process 100 queued actions.") time.sleep(0.1) return
class DallasBus(AVRNode, AutoDiscoveredNode): # Cached commands that are independant of the bus. getkey_cmd = '\x15\x00\x01\xb0' def __init__(self): AVRNode.__init__(self) AutoDiscoveredNode.__init__(self) self._lock = Lock() self.conversion_list = {} self._queue = Queue() self.debug = 0 self.running = 0 self._start_called = 0 self.devices = '' self.device_addresses = [] self._been_discovered = 0 def lock(self): self._lock.acquire() def unlock(self): self._lock.release() ## # @see node.AVRNode#configure # def configure(self,config): AVRNode.configure(self,config) id_chr = chr(self.id) # Commands cached in their entirety self.reset_cmd = '\x15\x00\x02\x10' + id_chr self.scan_cmd = '\x15\x00\x02\xc0' + id_chr self.unscan_cmd = '\x15\x00\x02\xd0' + id_chr self.convert_cmd = '\x15\x01\x04\x70' + id_chr + '\x01\x44' self.readscratch_cmd = '\x15\x00\x04\x70' + id_chr + '\x01\xbe' self.readrom_cmd = '\x15\x00\x04\x70' + id_chr + '\x01\x33' self.findfirst_cmd = '\x15\x00\x02\x80' + id_chr self.findfamily_cmd = '\x15\x00\x02\x81' + id_chr self.findnext_cmd = '\x15\x00\x03\x82' + id_chr + '\x00' # The beginning of commands of known length. self.matchrom_base = '\x15\x00\x0c\x70' + id_chr + '\x09\x55' self.skiprom_cmd = '\x15\x00\x04\x70' + id_chr + '\x01\xcc' self.readbits_base = '\x15\x00\x03\x40' + id_chr self.readbytes_base = '\x15\x00\x03\x50' + id_chr # Cached command codes + bus id. self.writebits_id = '\x60' + id_chr self.writebytes_id = '\x70' + id_chr return def configuration(self): self.devices, self.device_addresses = self.findall() self._been_discovered = 0 config = AVRNode.configuration(self) get_attribute(self, 'devices', config) return config def _add_child(self, node): AVRNode._add_child(self, node) if not self.running and self._start_called: self.start() ## # start temperature conversions # def start(self): AVRNode.start(self) self._start_called = 1 self.devices, self.device_addresses = self.findall() if self.running: raise EAlreadyRunning() # Inform in msglog the number of devices on the dallas bus and their addresses (CSCtl81599) if(self.devices == None ): no_of_devices=0 else: no_of_devices=len(self.device_addresses) msglog.log('broadway',msglog.types.INFO,'There are %d devices found on "%s" bus' %(no_of_devices, self.name)) if no_of_devices: addr_str='' for addr in self.device_addresses: dallas_bus_addr=address_to_asciihex(addr) addr_str=addr_str+' '+dallas_bus_addr msglog.log('broadway',msglog.types.INFO,'The device addresses on "%s" bus : %s\n' %(self.name,addr_str)) # Start the thread to read the dallas bus irrespective for whether the devices are # present or not (CSCtl81599) self.running = 1 thread = Thread(name=self.name,target=self._queue_thread,args=()) self.request(self._convert_temperature_sensor_list) thread.start() def stop(self): self.running = 0 ## # discover and create object instances # def _discover_children(self, force=0): if force: self._been_discovered = 0 if self.running == 1 and not self._been_discovered: # do a find_all irrespective of whether there are ny devices found previously (CSCtl81599) self.devices, self.device_addresses = self.findall() # get a list of addresses in existing children existing = self.children_nodes(auto_discover=0) existing = filter(lambda dev : hasattr(dev,'address'), existing) existing = [dev.address for dev in existing] for addr in self.device_addresses: if addr not in existing and not self._nascent_children.get(address_to_asciihex(addr), None): if ord(addr[0]) in (0x28,0x10): # need to add new types # add a new instance to the _nascent children t = Temperature() t.address = addr t.model = _models[family_name[ord(addr[0])]] self._nascent_children[address_to_asciihex(addr)] = t # self._been_discovered = 1 #disabled to allow new objects to be discovered return self._nascent_children ## # Create a dallas message for a cmd and flags. # # @param cmd The command turn into a message. # @param flags Flags to send with message. # @return Dallas_message representing <code>cmd</code> # with <code>flags</code>. # def dallas_message(self, cmd, flags): if flags: hdr = '\x15' + chr(flags) else: hdr = '\x15\x00' return hdr + chr(len(cmd)) + cmd ## # Send a command on dallas_bus. # # @param cmd The dallas_command to send. # @param flags Flags to send with command. # @default 0 # @return Dallas bus response. # def invoke_command(self, cmd, flags=0): return self.avr.invoke_message(self.dallas_message(cmd, flags)) ## # Read specified number of bits from dallas_bus. # # @param n The number of bits to read. # @return <code>n</code> bits from dallas bus. # def readbits(self, n): msg = self.readbits_base + chr(n) return self.avr.invoke_message(msg) ## # Read specified number of bytes from dallas bus. # # @param n Number of bytes to read. # @return <code>n</code> bytes from dallas bus. # def readbytes(self, n): msg = self.readbytes_base + chr(n) return self.avr.invoke_message(msg) ## # Write specified bits to dallas bus. # # @param n Number of bits to write. # @param bits Bits to write to dallas bus. # @return Dallas bus response. # def writebits(self, n, bits): msg = self.writebits_id + chr(n) + bits msg = self.dallas_message(msg, 0) return self.avr.invoke_message(msg) ## # Write specified bytes to dallas bus. # # @param n Number of bytes to write. # @param bytes Bytes to write. # @param flags Flags to include with the message. # @return Dallas bus response. # def writebytes(self, n, bytes, flags): msg = self.writebytes_id + chr(n) + bytes msg = self.dallas_message(msg, flags) return self.avr.invoke_message(msg) ## # Send Get key command to dallas bus. # # @return Dallas bus response. # def getkey(self): return self.avr.invoke_message(self.getkey_cmd) ## # Wait for dallas key to be connected then # call <code>getkey</code> # @see #getkey # def waitforkey(self): self.avr.wait_for_oob() return self.getkey() ## # Tell avr to scan dallas bus for devices that get # connected. # # @return Dallas bus response. # def scan(self): return self.avr.invoke_message(self.scan_cmd)[0] ## # Tell avr to stop scanning dallas bus. # # @return Dallas bus response. # def unscan(self): return self.avr.invoke_message(self.unscan_cmd)[0] ## # Reset dallas bus. # # @return Dallas bus response. # def reset(self): return self.avr.invoke_message(self.reset_cmd)[0] ## # Issue matchrom command to dallas bus. # # @param address The address of a specific # device. # @return Dallas bus response. # @throws EInvalidValue If the <code>address</code> # sent in was invalid. # def matchrom(self, address): if len(address) != 8: raise EInvalidValue, ('address', address) return self.avr.invoke_message(self.matchrom_base + address)[0] ## # Issue skiprom command to dallas bus. # # @param None. # @return Dallas bus response. # def skiprom(self): return self.avr.invoke_message(self.skiprom_cmd)[0] ## # Tell device to do conversion. # # @return Dallas bus response. # @note Convertion is done by device whose # address was previously matched. # def convert(self): return self.avr.invoke_message(self.convert_cmd)[0] ## # Tell device to read scratch onto dallas bus. # # @return dallas bus response. # def readscratch(self): return self.avr.invoke_message(self.readscratch_cmd) ## # Tell device to read ROM onto dallas bus. # # @return ROM of device. # def readrom(self): self.lock() try: self.reset() self.avr.invoke_message(self.readrom_cmd) result = self.readbytes(8) finally: self.unlock() return result ## # Do a search on the bus to find what devices are attached # # @return array of addresses and an array of formatted entries for the nodebrowser # def findall(self): devices = '<ol>' device_addresses = [] self.lock() if not ord(self.reset()): self.unlock() return None, 'No devices attached' self.avr.invoke_message(self.findfirst_cmd) while 1: d = self.avr.invoke_message(self.findnext_cmd) if not ord(d[8]): break device_addresses.append(d[:8]) device = '%s ' % (family_name[ord(d[0])]) for i in range(8): device += '%2.2x' % ord(d[i]) device += ' ' devices += '<li>' + device + '</li>' self.reset() devices += '</ol>' self.unlock() return devices, device_addresses ## # Use a queue to control access to the channel # # Send a request in the form of a callback object with params # def request(self, target, *args): if self.debug: print "DALLASBUS: Add Request to queue", target self._queue.put((target,args,)) def _queue_thread(self): while self.running: try: while 1: request = self._queue.get() if self.debug: print "DALLASBUS: just received request: ", request if type(request) is tuple: if len(request) == 2: apply(request[0], request[1]) except EInvalidResponse, e: msglog.log('Dallas Bus', 'information', str(e)) if self.debug: msglog.exception() except:
class AlarmManager(ServiceNode, EventConsumerMixin): def __init__(self): ServiceNode.__init__(self) EventConsumerMixin.__init__(self, self.handle_alarm, \ self.handle_exception) self.__running = 0 self.__queue = Queue() self._id_manager = None self._dynamic_alarms = {} return def configure(self, config): ServiceNode.configure(self, config) if self._id_manager is None: self._id_manager = UniqueID(self) return def unique_id(self): return self._id_manager.allocate_id() def active(self): children = [] for child in self.chidlren_nodes(): if child.active(): children.append(child) return children def acknowledged(self): children = [] for child in self.children_nodes(): if child.active() and child.acknowledged(): children.append(child) return children def unacknowledged(self): children = [] for child in self.children_nodes(): if child.active() and not child.acknowledged(): children.append(child) return children def add_dynamic_alarm(self, dynamic_alarm): dynamic_alarm_id = id(dynamic_alarm) if not self._dynamic_alarms.has_key(dynamic_alarm_id): self._dynamic_alarms[dynamic_alarm_id] = dynamic_alarm dynamic_alarm.event_subscribe(self, AlarmTriggerEvent) dynamic_alarm.event_subscribe(self, AlarmClearEvent) return def start(self): if not self.__running: known_alarms = self.get_child('alarms') for child in known_alarms.children_nodes(): child.event_subscribe(self, AlarmTriggerEvent) child.event_subscribe(self, AlarmClearEvent) self.__running = 1 self.__thread = ImmortalThread(name=self.name, target=self.__run) self.__thread.start() ServiceNode.start(self) return def stop(self): self.__running = 0 self.__thread = None self.__queue.put(None) try: # @fixme try/except/else is a hack to survive testcases use # of prune. Really should revisit... alarms = self.get_child('alarms') except: pass else: for child in alarms.children_nodes(): child.cancel(self, AlarmTriggerEvent) child.cancel(self, AlarmClearEvent) return ServiceNode.stop(self) def queue_alarm_check(self, alarm): self.__queue.put(alarm) def __run(self): while self.__running: alarm = self.__queue.get() if alarm: alarm.check_condition() else: self.__thread.should_die() return ## # Both AlarmClear and AlarmTrigger events come here, # separate the two and send to the appropriate handler. def handle_alarm(self, alarm): alarm.source.caught(alarm) if self.debug: msglog.log('broadway', msglog.types.ALARM, str(alarm)) if alarm.__class__ == AlarmTriggerEvent: return self._handle_trigger(alarm) elif alarm.__class__ == AlarmClearEvent: return self._handle_clear(alarm) msglog.log( 'broadway', msglog.types.WARN, 'Alarm manager %s got ' + 'an event that it does not recoginize, ignoring.' % self.name) def _handle_trigger(self, alarm): export_thread = Thread(name=alarm.source.name, target=self._run_exports, args=(alarm, )) export_thread.start() def _run_exports(self, alarm): if self.has_child('exporters'): exporters = self.get_child('exporters').children_nodes() exceptions = [] for exporter in exporters: try: exporter.export(alarm) except Exception, e: msglog.exception() exceptions.append(e) if exceptions: alarm.source.fail(alarm) else: alarm.source.success(alarm) else:
class LightingGroup(CompositeNode, EventConsumerMixin): def __init__(self): self.__server = None self.__sched_lock = Lock() self.__sched_thread = None self.__schedules = Queue() self.__pv = None super(LightingGroup, self).__init__() EventConsumerMixin.__init__(self, self._sched_update, self._sched_exception) return def configure(self, cd): super(LightingGroup, self).configure(cd) set_attribute(self, 'group_id', 10, cd, int) set_attribute(self, 'ttl', 120, cd, int) #@fixme - the ability to store schedules locally in the LightPoint is #currently not being utilized. set_attribute(self, 'schedule_link', '', cd) # config attr to deal with current Adura scheduling limitations # a future version of their API will address XML-RPC scheduling # deficiencies. set_attribute(self, 'one_schedule_only', 0, cd, as_boolean) def configuration(self): cd = super(LightingGroup, self).configuration() get_attribute(self, 'group_id', cd) get_attribute(self, 'ttl', cd) get_attribute(self, 'schedule_link', cd) return cd def start(self): if self.schedule_link: try: l = as_node(self.schedule_link) l.event_subscribe(self, ScheduleChangedEvent) self.__sched_thread = Thread(target=self._update_schedules) self.__sched_thread.start() except: msg = '%s: error subscribing to schedule changes' % \ self.as_node_url() msglog.log('Adura', ERR, msg) super(LightingGroup, self).start() for x in self.children_nodes(): #force initial update try: x.get() except: pass def stop(self): if self.schedule_link: try: l = as_node(self.schedule_link) l.event_unsubscribe(self, ScheduleChangedEvent) except: msg = '%s: error unsubscribing from schedule changes' % \ self.as_node_url() msglog.log('Adura', ERR, msg) super(CompositeNode, self).stop() def get(self, skipCache=0): if self.__pv is None: count = 0 in_err = 0 for x in self.children_nodes(): try: count += int(x.get()) except: in_err += 1 if count >= ((len(self.children_names()) - in_err)/2): #set the initial state of the lighting group #based on the average present state of the lightpoints self.__pv = 1 else: self.__pv = 0 return self.__pv #sets all LightPoints that are part of this group def set(self, value): value = int(value) if value < 0 or value > 1: raise EInvalidValue() self.__pv = value for lp in self.children_nodes(): try: lp.set(value) except: msglog.exception() def _sched_exception(self, exc): msglog.exception() def _sched_update(self, evt): if isinstance(evt, ScheduleChangedEvent): daily = evt.new_schedule[0] schedule = AduraSched(daily) self.__schedules.put(schedule) def _update_schedules(self): days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'] day_int_map = {'sunday':0, 'monday':1, 'tuesday':2, 'wednesday':3, 'thursday':4, 'friday':5, 'saturday':6, 'all':10} while 1: sched = self.__schedules.get() self._send_sched_msg('adura_clear_schedule') if self.one_schedule_only: days = ['all'] for day in days: self._send_sched_msg( 'adura_setschedule_day', ADURA_ALL_LIGHT_POINTS_ADDR, day_int_map[day] ) for entry in sched.get_entries(day): self._send_sched_msg( 'adura_setschedule_hour', ADURA_ALL_LIGHT_POINTS_ADDR, entry.h ) self._send_sched_msg( 'adura_setschedule_minute', ADURA_ALL_LIGHT_POINTS_ADDR, entry.m ) self._send_sched_msg( 'adura_setschedule_group', ADURA_ALL_LIGHT_POINTS_ADDR ) self._send_sched_msg( 'adura_setschedule_action', ADURA_ALL_LIGHT_POINTS_ADDR, entry.value ) return def _send_sched_msg(self, method_name, *args): getattr(self.server, method_name)(args) _sched_pause() # Adura currently requires a 2 second pause between individual # schedule update messages def _sched_pause(): time.sleep(ADURA_SCHED_MSG_DELAY) def _get_next_sched_entry(self): self.__sched_lock.acquire() try: self.__schedules.reverse() s = self.__schedules.pop() self.__schedules.reverse() return s finally: self.__sched_lock.release() def __get_server(self): if self.__server is None: self.__server = self.parent return self.__server server = property(__get_server)
class TestCase(DefaultTestFixture): def setUp(self): DefaultTestFixture.setUp(self) self.lock = Lock() self.pool = ThreadPool(3) self.queue = Queue() self.simple_action_counter = 0 return def tearDown(self): self.pool._unload() DefaultTestFixture.tearDown(self) return def simple_action(self, object): # @note It appears that even the '+= 1' operation is not # guaranteed to be atomic. self.lock.acquire() self.simple_action_counter += 1 self.lock.release() return 'simple_action_result' def slow_action(self, object): time.sleep(1.0) return 'slow_action_result' def simple_queue_action(self, object): self.queue.put(object) return def test_simple_queue(self): self.pool.queue(self.simple_queue_action, self) result = self.queue.get(1.0) if result is not self: raise "Queue returned %r instead of self, %r." % (result, self) return def test_result(self): t1 = time.time() pending_result = self.pool.queue(self.simple_action, self) result = pending_result.result(10.0) t2 = time.time() if result != 'simple_action_result': raise ( "pending_result.result() returned the wrong value (%s)." % result ) if (t2-t1) >= 10.0: raise "pending_result.result() blocked for no reason." return def test_pending_reasult(self): t1 = time.time() pending_result = PendingResult(None, None, self.simple_action, self) pending_result_two = self.pool.queue_pending_result(pending_result) if pending_result_two is not pending_result: raise "pending_result_two is NOT pending_result" result = pending_result.result(10.0) t2 = time.time() if result != 'simple_action_result': raise ( "pending_result.result() returned the wrong value (%s)." % result ) if (t2-t1) >= 10.0: raise "pending_result.result() blocked for no reason." return def test_pending_action(self): pending_action = PendingAction(self.simple_queue_action, self) self.pool.queue_pending_action(pending_action) result = self.queue.get(1.0) if result is not self: raise "Queue returned %r instead of self, %r." % (result, self) return return def test_result_timeout(self): t1 = time.time() pending_result = self.pool.queue(self.slow_action, self) result = pending_result.result(0.25) t2 = time.time() if (t2-t2) >= 1.0: raise "Blocked 1 second when a 1/4 second timeout." if result != NORESULT: raise "Got a result (%s) when none was expected." return def test_1000_actions(self): for i in xrange(0,1000): self.pool.queue(self.simple_action, self) time.sleep(0.1) t1 = time.time() while self.simple_action_counter < 1000: tn = time.time() if (tn-t1) > 3.0: raise ( "Taking ridiculously long to process 1000 queued actions." ) time.sleep(0.1) return def test_HIGH_pool_1(self): t1 = time.time() pending_result = HIGH.queue(self.simple_action, self) result = pending_result.result(10.0) t2 = time.time() if result != 'simple_action_result': raise ( "pending_result.result() returned the wrong value (%s)." % result ) if (t2-t1) >= 10.0: raise "pending_result.result() blocked for no reason." return def test_HIGH_pool_2(self): self.test_HIGH_pool_1() return def test_HIGH_pool_resize_1(self): HIGH.resize(1) if HIGH.size() != 1: raise "Resize to 1 thread failed." for i in xrange(0,100): HIGH.queue(self.simple_action, self) t1 = time.time() while self.simple_action_counter < 100: tn = time.time() if (tn-t1) > 3.0: raise ( "Taking ridiculously long to process 100 queued actions." ) time.sleep(0.1) return def test_HIGH_pool_resize_20(self): HIGH.resize(20) if HIGH.size() != 20: raise "Resize to 20 threads failed." for i in xrange(0,100): HIGH.queue(self.simple_action, self) t1 = time.time() while self.simple_action_counter < 100: tn = time.time() if (tn-t1) > 3.0: raise ( "Taking ridiculously long to process 100 queued actions." ) time.sleep(0.1) return
class DallasBus(AVRNode, AutoDiscoveredNode): # Cached commands that are independant of the bus. getkey_cmd = '\x15\x00\x01\xb0' def __init__(self): AVRNode.__init__(self) AutoDiscoveredNode.__init__(self) self._lock = Lock() self.conversion_list = {} self._queue = Queue() self.debug = 0 self.running = 0 self._start_called = 0 self.devices = '' self.device_addresses = [] self._been_discovered = 0 def lock(self): self._lock.acquire() def unlock(self): self._lock.release() ## # @see node.AVRNode#configure # def configure(self, config): AVRNode.configure(self, config) id_chr = chr(self.id) # Commands cached in their entirety self.reset_cmd = '\x15\x00\x02\x10' + id_chr self.scan_cmd = '\x15\x00\x02\xc0' + id_chr self.unscan_cmd = '\x15\x00\x02\xd0' + id_chr self.convert_cmd = '\x15\x01\x04\x70' + id_chr + '\x01\x44' self.readscratch_cmd = '\x15\x00\x04\x70' + id_chr + '\x01\xbe' self.readrom_cmd = '\x15\x00\x04\x70' + id_chr + '\x01\x33' self.findfirst_cmd = '\x15\x00\x02\x80' + id_chr self.findfamily_cmd = '\x15\x00\x02\x81' + id_chr self.findnext_cmd = '\x15\x00\x03\x82' + id_chr + '\x00' # The beginning of commands of known length. self.matchrom_base = '\x15\x00\x0c\x70' + id_chr + '\x09\x55' self.skiprom_cmd = '\x15\x00\x04\x70' + id_chr + '\x01\xcc' self.readbits_base = '\x15\x00\x03\x40' + id_chr self.readbytes_base = '\x15\x00\x03\x50' + id_chr # Cached command codes + bus id. self.writebits_id = '\x60' + id_chr self.writebytes_id = '\x70' + id_chr return def configuration(self): self.devices, self.device_addresses = self.findall() self._been_discovered = 0 config = AVRNode.configuration(self) get_attribute(self, 'devices', config) return config def _add_child(self, node): AVRNode._add_child(self, node) if not self.running and self._start_called: self.start() ## # start temperature conversions # def start(self): AVRNode.start(self) self._start_called = 1 self.devices, self.device_addresses = self.findall() if self.running: raise EAlreadyRunning() # Inform in msglog the number of devices on the dallas bus and their addresses (CSCtl81599) if (self.devices == None): no_of_devices = 0 else: no_of_devices = len(self.device_addresses) msglog.log( 'broadway', msglog.types.INFO, 'There are %d devices found on "%s" bus' % (no_of_devices, self.name)) if no_of_devices: addr_str = '' for addr in self.device_addresses: dallas_bus_addr = address_to_asciihex(addr) addr_str = addr_str + ' ' + dallas_bus_addr msglog.log( 'broadway', msglog.types.INFO, 'The device addresses on "%s" bus : %s\n' % (self.name, addr_str)) # Start the thread to read the dallas bus irrespective for whether the devices are # present or not (CSCtl81599) self.running = 1 thread = Thread(name=self.name, target=self._queue_thread, args=()) self.request(self._convert_temperature_sensor_list) thread.start() def stop(self): self.running = 0 ## # discover and create object instances # def _discover_children(self, force=0): if force: self._been_discovered = 0 if self.running == 1 and not self._been_discovered: # do a find_all irrespective of whether there are ny devices found previously (CSCtl81599) self.devices, self.device_addresses = self.findall() # get a list of addresses in existing children existing = self.children_nodes(auto_discover=0) existing = filter(lambda dev: hasattr(dev, 'address'), existing) existing = [dev.address for dev in existing] for addr in self.device_addresses: if addr not in existing and not self._nascent_children.get( address_to_asciihex(addr), None): if ord(addr[0]) in (0x28, 0x10): # need to add new types # add a new instance to the _nascent children t = Temperature() t.address = addr t.model = _models[family_name[ord(addr[0])]] self._nascent_children[address_to_asciihex(addr)] = t # self._been_discovered = 1 #disabled to allow new objects to be discovered return self._nascent_children ## # Create a dallas message for a cmd and flags. # # @param cmd The command turn into a message. # @param flags Flags to send with message. # @return Dallas_message representing <code>cmd</code> # with <code>flags</code>. # def dallas_message(self, cmd, flags): if flags: hdr = '\x15' + chr(flags) else: hdr = '\x15\x00' return hdr + chr(len(cmd)) + cmd ## # Send a command on dallas_bus. # # @param cmd The dallas_command to send. # @param flags Flags to send with command. # @default 0 # @return Dallas bus response. # def invoke_command(self, cmd, flags=0): return self.avr.invoke_message(self.dallas_message(cmd, flags)) ## # Read specified number of bits from dallas_bus. # # @param n The number of bits to read. # @return <code>n</code> bits from dallas bus. # def readbits(self, n): msg = self.readbits_base + chr(n) return self.avr.invoke_message(msg) ## # Read specified number of bytes from dallas bus. # # @param n Number of bytes to read. # @return <code>n</code> bytes from dallas bus. # def readbytes(self, n): msg = self.readbytes_base + chr(n) return self.avr.invoke_message(msg) ## # Write specified bits to dallas bus. # # @param n Number of bits to write. # @param bits Bits to write to dallas bus. # @return Dallas bus response. # def writebits(self, n, bits): msg = self.writebits_id + chr(n) + bits msg = self.dallas_message(msg, 0) return self.avr.invoke_message(msg) ## # Write specified bytes to dallas bus. # # @param n Number of bytes to write. # @param bytes Bytes to write. # @param flags Flags to include with the message. # @return Dallas bus response. # def writebytes(self, n, bytes, flags): msg = self.writebytes_id + chr(n) + bytes msg = self.dallas_message(msg, flags) return self.avr.invoke_message(msg) ## # Send Get key command to dallas bus. # # @return Dallas bus response. # def getkey(self): return self.avr.invoke_message(self.getkey_cmd) ## # Wait for dallas key to be connected then # call <code>getkey</code> # @see #getkey # def waitforkey(self): self.avr.wait_for_oob() return self.getkey() ## # Tell avr to scan dallas bus for devices that get # connected. # # @return Dallas bus response. # def scan(self): return self.avr.invoke_message(self.scan_cmd)[0] ## # Tell avr to stop scanning dallas bus. # # @return Dallas bus response. # def unscan(self): return self.avr.invoke_message(self.unscan_cmd)[0] ## # Reset dallas bus. # # @return Dallas bus response. # def reset(self): return self.avr.invoke_message(self.reset_cmd)[0] ## # Issue matchrom command to dallas bus. # # @param address The address of a specific # device. # @return Dallas bus response. # @throws EInvalidValue If the <code>address</code> # sent in was invalid. # def matchrom(self, address): if len(address) != 8: raise EInvalidValue, ('address', address) return self.avr.invoke_message(self.matchrom_base + address)[0] ## # Issue skiprom command to dallas bus. # # @param None. # @return Dallas bus response. # def skiprom(self): return self.avr.invoke_message(self.skiprom_cmd)[0] ## # Tell device to do conversion. # # @return Dallas bus response. # @note Convertion is done by device whose # address was previously matched. # def convert(self): return self.avr.invoke_message(self.convert_cmd)[0] ## # Tell device to read scratch onto dallas bus. # # @return dallas bus response. # def readscratch(self): return self.avr.invoke_message(self.readscratch_cmd) ## # Tell device to read ROM onto dallas bus. # # @return ROM of device. # def readrom(self): self.lock() try: self.reset() self.avr.invoke_message(self.readrom_cmd) result = self.readbytes(8) finally: self.unlock() return result ## # Do a search on the bus to find what devices are attached # # @return array of addresses and an array of formatted entries for the nodebrowser # def findall(self): devices = '<ol>' device_addresses = [] self.lock() if not ord(self.reset()): self.unlock() return None, 'No devices attached' self.avr.invoke_message(self.findfirst_cmd) while 1: d = self.avr.invoke_message(self.findnext_cmd) if not ord(d[8]): break device_addresses.append(d[:8]) device = '%s ' % (family_name[ord(d[0])]) for i in range(8): device += '%2.2x' % ord(d[i]) device += ' ' devices += '<li>' + device + '</li>' self.reset() devices += '</ol>' self.unlock() return devices, device_addresses ## # Use a queue to control access to the channel # # Send a request in the form of a callback object with params # def request(self, target, *args): if self.debug: print "DALLASBUS: Add Request to queue", target self._queue.put(( target, args, )) def _queue_thread(self): while self.running: try: while 1: request = self._queue.get() if self.debug: print "DALLASBUS: just received request: ", request if type(request) is tuple: if len(request) == 2: apply(request[0], request[1]) except EInvalidResponse, e: msglog.log('Dallas Bus', 'information', str(e)) if self.debug: msglog.exception() except: