class RelayScannerEventSink(EventSink): def __init__(self, scan_interval=10): #self._scan_interval = scan_interval EventSink.__init__(self) def check_available(self, event): return self.__is_available def on_start(self): print('starting relay scanner'); def set_event_check_timer(self, time_to_wait=7): try: self.__timer.reset(time_to_wait) except AttributeError: self.__timer = Timer(time_to_wait, RelayScannerEventSink()) self.__timer.register(self) @handler("RelayScannerEventSink") def check_event_sent(self): #TODO: do this asynchronously so we don't always wait 7 seconds??? if self.receive(): print ('receiving something ... ') else: print ('something is wrong ... ') self.set_event_check_timer() def receive(self): #read message from the sigfox adapter ret = '' #return message read while self._ser.inWaiting() > 0: print('in receiving method') def encode_event(self, event): print ('hi there, in encode event') return str(event)
def timed_call(self, time, function, repeat=False, *args, **kwargs): """ This function will call the given function, with the given args, once the timer expires. You can have the timer reset so the function will runrepeatedly by using the repeat argument. NOTE: the function must be an instancemethod of the calling class! :param time: time (in seconds or as a datetime object) until the timer expires and calls the function :param function: function to call (WARNING: use self.__class__.fun not self.fun) :param repeat: whether or not the timer should reset and periodically repeat the function :returns: the Timer object used to manage this timed_call (cancel with Timer.stop()) """ # First build a handler for the event that will be fired by the timer # and register it with circuits only on our object's unique channel # The channel is also unique to this function so that we can support # multiple outstanding timed_calls simultaneously. # TODO: test said support! timer_class_name = timer_expired_event.__name__ timer_channel = "%s/%s/%s" % (self._get_channel_name(), timer_class_name, id(function)) self.subscribe(timer_channel, data_spaces=[timer_class_name], callback=function) # Then set the timer. Note how the args are passed via the timer_expired_event object. t = Timer(time, timer_expired_event(*args, **kwargs), timer_channel, persist=repeat) self._timers.append(t) t.register(self) return t
def on_start(self): print "bomb is lit!" event = BombLit() self.publish(event) timer = Timer(1, BombLit(), persist=True) timer.register(self)
def timed_call(self, time, function, repeat=False, *args, **kwargs): """ This function will call the given function, with the given args, once the timer expires. You can have the timer reset so the function will runrepeatedly by using the repeat argument. NOTE: the function must be an instancemethod of the calling class! NOTE: there is currently no way to cancel the call so if you need that feature, add it yourself! :param time: time (in seconds or as a datetime object) until the timer expires and calls the function :param function: function to call :param repeat: whether or not the timer should reset and periodically repeat the function """ # First build a handler for the event that will be fired by the timer # and register it with circuits only on our object's unique channel # BEWARE: there are some serious dragons here... This is a bunch of # hacky nonsense I had to do to convince the circuits system that the # function we're passing them is a bona-fide handler object. For # whatever reason, we can't just pass the instancemethod directly to # the handler function to make a handler as we get some AttributeError # related to it trying to set attributes that don't exist, so instead # we have to provide a regular method here that wraps the actual # instancemethod. def f(*fargs, **fkwargs): return function(*fargs, **fkwargs) f = handler(False, channel=self._get_channel_name(), override=True)(f) f.handler = True f.names = ["timer_expired_event"] f.channel = self._get_channel_name() f.priority = 10 f.event = False self.addHandler(f) # Then set the timer. Note how the args are passed via the # __timer_expired_event object. self._timer = Timer(time, timer_expired_event(*args, **kwargs), self._get_channel_name(), persist=repeat) self._timer.register(self)
class RelayScannerEventSink(EventSink): def __init__(self, broker=None, scan_interval=10, **kwargs): super(RelayScannerEventSink, self).__init__(broker=broker, **kwargs) # self._scan_interval = scan_interval def check_available(self, event): return self.__is_available and super(RelayScannerEventSink, self).check_available() def on_start(self): print('starting relay scanner') def set_event_check_timer(self, time_to_wait=7): try: self.__timer.reset(time_to_wait) except AttributeError: self.__timer = Timer(time_to_wait, RelayScannerEventSink()) self.__timer.register(self) @handler("RelayScannerEventSink") def check_event_sent(self): # TODO: do this asynchronously so we don't always wait 7 seconds??? if self.receive(): print('receiving something ... ') else: print('something is wrong ... ') self.set_event_check_timer() def receive(self): # read message from the sigfox adapter ret = '' # return message read while self._ser.inWaiting() > 0: print('in receiving method') def encode_event(self, event): print('hi there, in encode event') return str(event)
def set_event_check_timer(self, time_to_wait=7): """ Sets a timer so that the SigfoxEventSink will check whether the event was successfully sent after the given time interval has expired. :param time: a number of seconds to wait or a datetime object representing when the check should be done """ try: self._check_timer.reset(time_to_wait) self._check_timer.register(self) log.debug("Timer reset") except AttributeError: self._check_timer = Timer(time_to_wait, SigfoxCheckEventSent()) self._check_timer.register(self) log.debug("Timer created")
def handle_mqtt_msg(self, msg): if "mac" in msg: if msg["mac"] == self.mac: if "state" in msg: self.state = msg["state"] if self.auto_mode and "motion" in msg: self.change_state(True) if self.timer is not None: # set to None in __init__ and the handler for "light_timeout" self.timer.reset() # if there was a timer from last time, reset it else: self.timer = Timer(motion_timeout_seconds, light_timeout(self.mac)) self += self.timer # otherwise, re-register it (why does "self.register(self.timer)" not work?) else: pass # message not for us, hopefully there's another controller out there
def started(self, *args, **kwargs): self.hostname = self.root.config.get('core', 'hostname') b = self.root.config.get('core', 'persist_backend') b = b.lower() h = self.root.config.get('backend:%s' % b, 'host') p = self.root.config.get('backend:%s' % b, 'port') if b in ('http', 'https'): self._backends = backends = {} self._backend_state = {} while len(self._backends) < self._pool_count: bid = uuid4().hex backends[bid] = RetryHTTPClient(host=h, port=p, scheme=b, channel=bid).register(self) self._backend_state[bid] = IDLE else: logger.error("Invalid backend given: %s" % b) raise SystemExit Timer(CHECK_INTERVAL, Event(), 'persist', t=self, persist=True).register(self)
def set_event_check_timer(self, time_to_wait=7): try: self.__timer.reset(time_to_wait) except AttributeError: self.__timer = Timer(time_to_wait, RelayScannerEventSink()) self.__timer.register(self)
class Application(BaseComponent): """ Applications may subscribe to events and may respond to them (or some internal logic) by publishing events. These are the core of the system and any classes adding additional functionality should at least indirectly subclass Application. OPEN QUESTIONS: 1) How to support historical queries of events? This will certainly be something that VirtualSensors should support and likely some applications will want to do so as well so this should probably be built into this most basic of core classes.... Perhaps this should be handled by exposing some API at the broker rather than building this logic into the Application, which would require one API here and yet another (albeit more internal) API at the broker for actually handling the true low-level logic. 2) In the future, they will support migrating between processes, both between those on local and remote machines. """ def __init__(self, broker=None): # NOTE: this circuits-specific hack helps deliver events only to the right channels, that is ReadSensorData # events will only fire to the object that initiated the timer that fires them. Also note that it MUTS come # before the super() call! BaseComponent.__init__(self, channel=self._get_channel_name()) super(Application, self).__init__() if broker is None: raise NotImplementedError self._register_broker(broker) # TODO: get_name()? def _register_broker(self, broker): """Connects this Application to the specified pub-sub event broker. This method is automatically called for you and should really only be used for testing purposes. The circuits implementation registers the Component to the Broker""" self._broker = broker self.register(broker) def on_event(self, event, topic): """ This callback is where you should handle the Application's logic for responding to events to which it has subscribed. :param event: the Event just published :param topic: the Topic the Event was published to """ pass def on_start(self): """ This callback is fired as soon as the Application is started. Use it to handle any setup such as network connections, constructing classes, or subscribing to event Topics. """ pass def on_stop(self): """ This callback is fired when an Application is shut down (including when the whole system is doing so). Use it to free any resources, close network connections, save state, etc. """ pass def on_publish(self, event, topic): """ Callback fired after successfully publishing an event :param event: the Event published :param topic: Topic the Event was published to :return: ignored """ pass def on_subscribe(self, topic): """ Callback fired after successfully subscribing to a Topic :param topic: Topic the Event was published to :return: ignored """ pass #TODO: unsubscribe? def publish(self, event, topic=None): """ Publishes the given Event to the given Topic :param event: Event to publish :param topic: Topic to publish to :return: a True-ish object if successful """ if topic is None: topic = event.__class__ ret = self._publish(event, topic) self.on_publish(event, topic) return ret def subscribe(self, topic): """ Subscribes to the given Topic so that any Events being published to that Topic will be routed to this Application via the on_event() callback. :param topic: the Topic to subscribe to :return: a True-ish object if successful """ ret = self._subscribe(topic) self.on_subscribe(topic) return ret def timed_call(self, time, function, repeat=False, *args, **kwargs): """ This function will call the given function, with the given args, once the timer expires. You can have the timer reset so the function will runrepeatedly by using the repeat argument. NOTE: the function must be an instancemethod of the calling class! NOTE: there is currently no way to cancel the call so if you need that feature, add it yourself! :param time: time (in seconds or as a datetime object) until the timer expires and calls the function :param function: function to call :param repeat: whether or not the timer should reset and periodically repeat the function """ # First build a handler for the event that will be fired by the timer # and register it with circuits only on our object's unique channel # BEWARE: there are some serious dragons here... This is a bunch of # hacky nonsense I had to do to convince the circuits system that the # function we're passing them is a bona-fide handler object. For # whatever reason, we can't just pass the instancemethod directly to # the handler function to make a handler as we get some AttributeError # related to it trying to set attributes that don't exist, so instead # we have to provide a regular method here that wraps the actual # instancemethod. def f(*fargs, **fkwargs): return function(*fargs, **fkwargs) f = handler(False, channel=self._get_channel_name(), override=True)(f) f.handler = True f.names = ["timer_expired_event"] f.channel = self._get_channel_name() f.priority = 10 f.event = False self.addHandler(f) # Then set the timer. Note how the args are passed via the # __timer_expired_event object. self._timer = Timer(time, timer_expired_event(*args, **kwargs), self._get_channel_name(), persist=repeat) self._timer.register(self) # TODO: allow cancelling the timed_call by returning a reference to # the handler we registered and write a cancel_timed_call(method) # function that takes this reference in and calls removeHandler #TODO: this abstraction nonsense #class Application(circuits.Component, AbstractApplication): # """This class implements the Application using the circuits library""" # def __init__(self): # circuits.Component.__init__() ####################### # NOTE: we have to implement these with separate functions in order to use the handler decorator ####################### # make sure to do channel * to override the fact that we explicitly set all Components' channels @handler("SensedEvent", channel='*') def _on_event(self, event, *args): """ This callback is where you should handle the Application's logic for responding to events to which it has subscribed. :param event: the Event just published :param topic: the Topic the Event was published to """ self.on_event(event, "SensedEvent") @handler("started") def _on_start(self, component): """ This callback is fired as soon as the Application is started. Use it to handle any setup such as network connections, constructing classes, or subscribing to event Topics. """ self.on_start() @handler("stopped") def _on_stop(self, component): """ This callback is fired when an Application is shut down (including when the whole system is doing so). Use it to free any resources, close network connections, save state, etc. """ self.on_stop() def _publish(self, event, topic): """ Publishes the given Event to the given Topic :param event: Event to publish :param topic: Topic to publish to :return: a True-ish object if successful """ return self.fireEvent(event, topic) def _subscribe(self, topic): """ Subscribes to the given Topic so that any Events being published to that Topic will be routed to this Application via the on_event() callback. :param topic: the Topic to subscribe to :return: a True-ish object if successful """ # TODO: this is going to be near-impossible with circuits alone since they've adopted the convention of # subscribing by class type. This doesn't allow hierarchies like "subscribe to all network-related events", # let alone the advanced content-based subscriptions that we're going to want eventually... maybe channels? raise NotImplementedError() self.addHandler(f) # TODO: determine what to do with this part of the API # def run(self): # """ # This should always be called to start the Application. You should expect, but not rely on, implementations of # Application to make this function asynchronous, that is it will likely return immediately and do the actual # starting up in the background. # :return: a True-ish object if successful # """ # return super(self.__class__, self).run() def _get_channel_name(self): """ Returns a channel name to be used by circuits for routing events properly. Currently just the class name plus the unique memory address. :return: """ return '%s@%d' % (self.__class__.__name__, id(self))
class SigfoxEventSink(EventSink): def __init__(self, broker, serialport='/dev/ttyUSB0', _baudrate=9600, _parity=serial.PARITY_NONE, _stopbits=serial.STOPBITS_ONE, _bytesize=serial.EIGHTBITS, **kwargs): super(SigfoxEventSink, self).__init__(broker, **kwargs) self._event_type_json_file = "sigfox_event_types.json" # Read from event type enum definition file (in JSON format) dirname, filename = os.path.split(os.path.abspath(__file__)) log.debug(dirname) type_file = open(dirname + "/" + self._event_type_json_file, "rt") type_stream = type_file.read() self._type_info = json.loads(type_stream) self._serialport = serialport self._baudrate = _baudrate self._parity = _parity self._stopbits = _stopbits self._bytesize = _bytesize self.__is_available = False self._ser = None self._reconnect_timer = None self._reconnect_timeout = 10 def _try_connect(self): if self._ser is None: try: self._ser = serial.Serial(port=self._serialport, baudrate=self._baudrate, parity=self._parity, stopbits=self._stopbits, bytesize=self._bytesize) except serial.SerialException: pass if self._ser is None: log.error("Sigfox adapter not connected") self._reconnect_timer = time.time() return False if not self._ser.isOpen(): self._ser.open() log.info("Sigfox adapter connected") self.__is_available = True self._reconnect_timer = None return True def check_available(self, event): if not event.event_type in self._type_info or not super( SigfoxEventSink, self).check_available(event): return False if self._ser is None or not self._ser.isOpen(): if self._reconnect_timer is None or self._reconnect_timer + self._reconnect_timeout < time.time( ): self._try_connect() return self.__is_available def _ex_handler(self, obj): template = "An exception of type {0} occured. Arguments:\n{1!r}" message = template.format(type(obj).__name__, obj.args) log.error(message) def on_start(self): super(SigfoxEventSink, self).on_start() self._try_connect() def send_raw(self, encoded_event): #TODO: should define false code to indicate different fault reason if encoded_event is False: return False try: #self._ser.write(topic+"||"+msg+'\r\n') # Use the above paras to send actual sensor data self._ser.write(encoded_event) except serial.SerialException: log.error("Sigfox adaper writing failure") self._ser = None self.__is_available = False return False except Exception as err: self._ex_handler(err) return False log.info("Sigfox message: " + encoded_event.rstrip()) self.__is_available = False # Check that message was sent ok after a timeout (no way to check that it was received!) self.set_event_check_timer() def set_event_check_timer(self, time_to_wait=7): """ Sets a timer so that the SigfoxEventSink will check whether the event was successfully sent after the given time interval has expired. :param time: a number of seconds to wait or a datetime object representing when the check should be done """ try: self._check_timer.reset(time_to_wait) self._check_timer.register(self) log.debug("Timer reset") except AttributeError: self._check_timer = Timer(time_to_wait, SigfoxCheckEventSent()) self._check_timer.register(self) log.debug("Timer created") @handler("SigfoxCheckEventSent") def check_event_sent(self): #TODO: do this asynchronously so we don't always wait 7 seconds? ret = self.receive() if ret == 0: self.__is_available = True elif ret == -1: log.error( "Unable to receive() reply from Sigfox adapter. Resetting timer..." ) self.set_event_check_timer() elif ret == -4: # IndexError self.__is_available = True elif ret == -7: # IOError self._ser = None self.__is_available = False def receive(self): # Read message from the sigfox adapter ret = '' # Return message read try: while self._ser.inWaiting() > 0: ret += self._ser.read(1) except IOError: log.error("Sigfox adaper reading failure") return -7 ret_strings = ret.split('\r\n') log.debug("Read from Sigfox adapter: " + ret) try: if ret_strings[1] == "OK": log.info("Sigfox message sent") return 0 else: log.warning("Sigfox message not sent: " + ret_strings[1]) return -1 except IndexError: log.error("Index error when dealing with Sigfox return") return -4 def encode_event(self, event): import ctypes log.debug("Encoding event: %s" % json.dumps(event.data)) # The structure of Sigfox message: # at$ss="payload_part" # The payload part of Sigfox message have following rules: # 1. Each payload character should be Hex character (0-F), which represent half byte. # 2. No more than 12 bytes (24 Hex characters) # 3. The message should be byte aligned, which means there should be even Hex characters hex_payload = " " # The structure of Sigfox message payload: # Event Type (1 Byte/ 2 Hex Characters) # + Value Descriptor (2 Bytes / 4 Hex Characters) #TODO: Need More Work to Define # + Value (8 Bytes / 16 Hex Characters) # + Priority (4 bits / 1 Hex Characters) # + Control (Reserve bits)(4bits / 1 Hex Characters) # 1. Encode Event Type event_type_original = event.event_type try: event_type_encoded = self._type_info[event_type_original] except KeyError: log.warning("Unknown event: " + event_type_original) return False hex_payload_type = event_type_encoded # 2. Encode Value Descriptor # Temporary method hex_payload_vd = "" for i in range(4): hex_payload_vd += "0" # 3. Encode Value # Temporary method, just encode one float to first 4 bytes. Use for temperature data value_original = event.data if type(value_original) == type(9.0): # Handle float value hex_payload_value = hex( ctypes.c_int.from_buffer( ctypes.c_float(value_original)).value)[2:].zfill(8) #for i in range(8): # hex_payload_value += "0" hex_payload_value += "0" * 8 elif type(value_original) == type(9): # Handle int value hex_payload_value = hex( ctypes.c_int(value_original).value)[2:].zfill(4) hex_payload_value += "0" * 12 else: #for i in range(16): # hex_payload_value += "0" hex_payload_value = "0" * 16 # 4. Encode Priority priority_original = event.priority if priority_original < 0 or priority_original > 15: log.warning("Priority value out of range") hex_payload_priority = hex(priority_original)[2] #[0:1] = '0x' # 5. Encode Control bits hex_payload_cb = "0" # 6. Generate Whole Hex Payload hex_payload = hex_payload_type + hex_payload_vd + hex_payload_value + hex_payload_priority + hex_payload_cb # Publish message "||" can be redefined. but '\r\n' is mandatory encoded_event = "at$ss=" + hex_payload + "\r\n" log.debug("Sigfox encoded: " + str(encoded_event).rstrip()) return str(encoded_event)
class SigfoxEventSink(EventSink): def __init__(self, broker, serialport='/dev/ttyUSB0', _baudrate=9600, _parity=serial.PARITY_NONE, _stopbits=serial.STOPBITS_ONE, _bytesize=serial.EIGHTBITS, **kwargs): super(SigfoxEventSink, self).__init__(broker, **kwargs) self._event_type_json_file = "sigfox_event_types.json" # Read from event type enum definition file (in JSON format) dirname, filename = os.path.split(os.path.abspath(__file__)) log.debug(dirname) type_file = open(dirname + "/" + self._event_type_json_file, "rt") type_stream = type_file.read() self._type_info = json.loads(type_stream) self._serialport = serialport; self._baudrate = _baudrate; self._parity = _parity; self._stopbits = _stopbits; self._bytesize = _bytesize; self.__is_available = False self._ser = None self._reconnect_timer = None self._reconnect_timeout = 10 def _try_connect(self): if self._ser is None: try: self._ser = serial.Serial( port=self._serialport, baudrate=self._baudrate, parity=self._parity, stopbits=self._stopbits, bytesize=self._bytesize ) except serial.SerialException: pass if self._ser is None: log.error("Sigfox adapter not connected") self._reconnect_timer = time.time() return False if not self._ser.isOpen(): self._ser.open() log.info("Sigfox adapter connected") self.__is_available = True self._reconnect_timer = None return True def check_available(self, event): if not event.event_type in self._type_info or not super(SigfoxEventSink, self).check_available(event): return False if self._ser is None or not self._ser.isOpen(): if self._reconnect_timer is None or self._reconnect_timer + self._reconnect_timeout < time.time(): self._try_connect() return self.__is_available def _ex_handler(self, obj): template = "An exception of type {0} occured. Arguments:\n{1!r}" message = template.format(type(obj).__name__, obj.args) log.error(message) def on_start(self): super(SigfoxEventSink, self).on_start() self._try_connect() def send_raw(self, encoded_event): #TODO: should define false code to indicate different fault reason if encoded_event is False: return False try: #self._ser.write(topic+"||"+msg+'\r\n') # Use the above paras to send actual sensor data self._ser.write(encoded_event) except serial.SerialException: log.error("Sigfox adaper writing failure") self._ser = None self.__is_available = False return False except Exception as err: self._ex_handler(err) return False log.info("Sigfox message: " + encoded_event.rstrip()) self.__is_available = False # Check that message was sent ok after a timeout (no way to check that it was received!) self.set_event_check_timer() def set_event_check_timer(self, time_to_wait=7): """ Sets a timer so that the SigfoxEventSink will check whether the event was successfully sent after the given time interval has expired. :param time: a number of seconds to wait or a datetime object representing when the check should be done """ try: self._check_timer.reset(time_to_wait) self._check_timer.register(self) log.debug("Timer reset") except AttributeError: self._check_timer = Timer(time_to_wait, SigfoxCheckEventSent()) self._check_timer.register(self) log.debug("Timer created") @handler("SigfoxCheckEventSent") def check_event_sent(self): #TODO: do this asynchronously so we don't always wait 7 seconds? ret = self.receive() if ret == 0: self.__is_available = True elif ret == -1: log.error("Unable to receive() reply from Sigfox adapter. Resetting timer...") self.set_event_check_timer() elif ret == -4: # IndexError self.__is_available = True elif ret == -7: # IOError self._ser = None self.__is_available = False def receive(self): # Read message from the sigfox adapter ret = '' # Return message read try: while self._ser.inWaiting() > 0: ret += self._ser.read(1) except IOError: log.error("Sigfox adaper reading failure") return -7 ret_strings = ret.split('\r\n') log.debug("Read from Sigfox adapter: " + ret) try: if ret_strings[1] == "OK": log.info("Sigfox message sent") return 0 else: log.warning("Sigfox message not sent: " + ret_strings[1]) return -1 except IndexError: log.error("Index error when dealing with Sigfox return") return -4 def encode_event(self, event): import ctypes log.debug("Encoding event: %s" % json.dumps(event.data)) # The structure of Sigfox message: # at$ss="payload_part" # The payload part of Sigfox message have following rules: # 1. Each payload character should be Hex character (0-F), which represent half byte. # 2. No more than 12 bytes (24 Hex characters) # 3. The message should be byte aligned, which means there should be even Hex characters hex_payload = " " # The structure of Sigfox message payload: # Event Type (1 Byte/ 2 Hex Characters) # + Value Descriptor (2 Bytes / 4 Hex Characters) #TODO: Need More Work to Define # + Value (8 Bytes / 16 Hex Characters) # + Priority (4 bits / 1 Hex Characters) # + Control (Reserve bits)(4bits / 1 Hex Characters) # 1. Encode Event Type event_type_original = event.event_type try: event_type_encoded = self._type_info[event_type_original] except KeyError: log.warning("Unknown event: " + event_type_original) return False hex_payload_type = event_type_encoded # 2. Encode Value Descriptor # Temporary method hex_payload_vd = "" for i in range(4): hex_payload_vd += "0" # 3. Encode Value # Temporary method, just encode one float to first 4 bytes. Use for temperature data value_original = event.data if type(value_original) == type(9.0): # Handle float value hex_payload_value = hex(ctypes.c_int.from_buffer(ctypes.c_float(value_original)).value)[2:].zfill(8) #for i in range(8): # hex_payload_value += "0" hex_payload_value += "0" * 8 elif type(value_original) == type(9): # Handle int value hex_payload_value = hex(ctypes.c_int(value_original).value)[2:].zfill(4) hex_payload_value += "0" * 12 else: #for i in range(16): # hex_payload_value += "0" hex_payload_value = "0" * 16 # 4. Encode Priority priority_original = event.priority if priority_original < 0 or priority_original > 15: log.warning("Priority value out of range") hex_payload_priority = hex(priority_original)[2] #[0:1] = '0x' # 5. Encode Control bits hex_payload_cb = "0" # 6. Generate Whole Hex Payload hex_payload = hex_payload_type + hex_payload_vd + hex_payload_value + hex_payload_priority + hex_payload_cb # Publish message "||" can be redefined. but '\r\n' is mandatory encoded_event = "at$ss=" + hex_payload + "\r\n" log.debug("Sigfox encoded: " + str(encoded_event).rstrip()) return str(encoded_event)
class CircuitsApplication(AbstractApplication, BaseComponent): """This class implements the Application using the circuits library""" def __init__(self, broker, **kwargs): """ :param broker: the broker used for the internal pub-sub feature core to the scale_client :param kwargs: used for passing args to other constructors when doing multiple inheritance """ # NOTE: the circuits channel must be set to THIS instance so that the events fired # only affect it and not other Application instances. super(CircuitsApplication, self).__init__(broker=broker, channel=self._get_channel_name(), **kwargs) def _register_broker(self, broker): AbstractApplication._register_broker(self, broker) self.register(broker) ####################### # NOTE: we have to implement these with separate functions in order to use the handler decorator ####################### # make sure to do channel * to override the fact that we explicitly set all Components' channels @handler("SensedEvent", channel='*') def _on_event(self, event, *args): """ This callback is where you should handle the Application's logic for responding to events to which it has subscribed. :param event: the Event just published :param topic: the Topic the Event was published to """ self.on_event(event, "SensedEvent") @handler("started") def _on_start(self, component): """ This callback is fired as soon as the Application is started. Use it to handle any setup such as network connections, constructing classes, or subscribing to event Topics. """ self.on_start() @handler("stopped") def _on_stop(self, component): """ This callback is fired when an Application is shut down (including when the whole system is doing so). Use it to free any resources, close network connections, save state, etc. """ self.on_stop() def timed_call(self, time, function, repeat=False, *args, **kwargs): """ This function will call the given function, with the given args, once the timer expires. You can have the timer reset so the function will runrepeatedly by using the repeat argument. NOTE: the function must be an instancemethod of the calling class! NOTE: there is currently no way to cancel the call so if you need that feature, add it yourself! :param time: time (in seconds or as a datetime object) until the timer expires and calls the function :param function: function to call :param repeat: whether or not the timer should reset and periodically repeat the function """ # First build a handler for the event that will be fired by the timer # and register it with circuits only on our object's unique channel # BEWARE: there are some serious dragons here... This is a bunch of # hacky nonsense I had to do to convince the circuits system that the # function we're passing them is a bona-fide handler object. For # whatever reason, we can't just pass the instancemethod directly to # the handler function to make a handler as we get some AttributeError # related to it trying to set attributes that don't exist, so instead # we have to provide a regular method here that wraps the actual # instancemethod. def f(*fargs, **fkwargs): return function(*fargs, **fkwargs) f = handler(False, channel=self._get_channel_name(), override=True)(f) f.handler = True f.names = ["timer_expired_event"] f.channel = self._get_channel_name() f.priority = 10 f.event = False self.addHandler(f) # Then set the timer. Note how the args are passed via the # __timer_expired_event object. self._timer = Timer(time, timer_expired_event(*args, **kwargs), self._get_channel_name(), persist=repeat) self._timer.register(self) # TODO: allow cancelling the timed_call by returning a reference to # the handler we registered and write a cancel_timed_call(method) # function that takes this reference in and calls removeHandler def _get_channel_name(self): """ Returns a channel name to be used by circuits for routing events properly. Currently just the class name plus the unique memory address. :return: """ return '%s@%d' % (self.__class__.__name__, id(self))