Example #1
0
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
Example #3
0
    def on_start(self):
        print "bomb is lit!"
        event = BombLit()
        self.publish(event)

        timer = Timer(1, BombLit(), persist=True)
        timer.register(self)
Example #4
0
    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)
Example #5
0
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")
Example #7
0
 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
Example #8
0
 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)
Example #9
0
    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)
 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)
Example #11
0
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))
Example #12
0
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)
Example #14
0
 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)
Example #15
0
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))