Beispiel #1
0
class Gateway(CompositeNode):
    def __init__(self):
        self.lightpoints = {}
        self.running = False
        self.endpoint = None
        self.lh = None
        self._seq_num = 1
        self._sched_lock = Lock()
        super(Gateway, self).__init__()
        
    def configure(self, cd):
        super(Gateway, self).configure(cd)
        set_attribute(self, 'address', REQUIRED, cd)
        set_attribute(self, 'debug', 1, cd, int)
        set_attribute(self, 'net_grp_id', 125, cd, int) # network net_grp_id
        set_attribute(self, 'rpc_port', 9003, cd, int)
        set_attribute(self, 'serial_forwarder_port', 9001, cd, int)
        if self.running is True:
            # force endpoint reconfiguration
            if self.endpoint and self.endpoint.connection_ok():
                self.endpoint.close_connection()
            self.running = False 
            self.start() 
        
    def configuration(self):
        cd = super(Gateway, self).configuration()
        get_attribute(self, 'address', cd)
        get_attribute(self, 'debug', cd)
        get_attribute(self, 'net_grp_id', cd)
        get_attribute(self, 'rpc_port', cd)
        get_attribute(self, 'serial_forwarder_port', cd)
        return cd
        
    def start(self):
        if self.running is False:
            self.endpoint = XCommandIface(
                self.rpc_port, 
                self.address, 
                self.debug
            )
            self.lh = SerialFramerIface(
                self.serial_forwarder_port, 
                self.address, 
                self.debug
            )
            self._thread = ImmortalThread(target=self._run, args=())
            self.running = True
            self._thread.start()
        super(Gateway, self).start()
        
    def stop(self):
        self.running = False
        
    def _run(self):
        while self.running:
            # initialize the communications interface
            try:
                msglog.log('Adura', INFO, 'Initializing XServer SerialFramer connection.')
                self.lh.open_connection()
                msglog.log('Adura', INFO, 'XServer connection succeeded.')
            except EResourceError:
                self.stop()
                msglog.log('Adura', ERR, 'Failed to connect to XServer.')
                raise
            except (EConnectionError, ETimeout):
                msglog.log('Adura', WARN, 'Failed to connect to XServer - attempting to reconnect.') 
                pass
            except:
                # unaccounted for exception - send it to the msglog
                msglog.log('Adura', ERR, 'Failed to connect to XServer.')
                msglog.exception()
            while self.lh.connection_ok():
                msg = None
                try:
                    msg = self.lh.get_next_msg()
                except:
                    # connection will now be closed, re-init comm.
                    msglog.exception(prefix='Ignored')
                if msg:
                    frame = AduraFrame(msg)
                    if self.debug:
                        msglog.log('Adura', INFO, frame.get_value_string())
                    if frame.frame_type == 'lightpoint_data_config':
                        lp = self.lightpoints.get(frame.get('firmware_id'))
                        if lp is not None:
                            lp.update(frame)
            time.sleep(30) #pause before attempting to reconnect
        else: 
            if self.lh.connection_ok():
                self.lh.close_connection()
            self._thread.should_die()
        return
        
    def register(self, lightpoint):
        self.lightpoints[lightpoint.lightpoint_id] = lightpoint
        
    def _check_cov_timeout(self):
        for lp in self.lightpoints:
            if (uptime.secs() - lp._last_update) > lp.group.ttl:
                # generate an ETimeout for the benefit of event consumers
                lp.update({'relayState1':ETimeout()})
        
    def _get_next_seq(self):
        seq = self._seq_num
        self._seq_num += 1
        if self._seq_num >= 0xffff:
            self._seq_num = 1
        return seq
        
    # LightLogic XML-RPC methods follow.  Note, HTTP is *not* used
    # for transport.  It's just a raw socket that begins with a 4-byte 
    # length field followed by xml-rpc based payload.
    
    ##
    # The control command for an individual LightPoint
    # 
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network (125)
    # @param actDevice  The id of the relay (5)
    # @actState  The action (0 = OFF, 1 = ON)
    #
    def actuate(self, dest, act_device, act_state):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':act_device,
                  'actState':act_state}
        if act_device == 12:
            # broadcast to all lightpoints - requires additional param 
            # that is a 16 bit incrementing seq number.
            params['seqNumber'] = self._get_next_seq()
        return self.endpoint.write('xmesh.actuate', params)
            
    ##
    # Clears previous schedule events
    #
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actState  1536
    # @param seqNumber  Unique sequence number for the specific command
    #
    def adura_clear_schedule(self, dest=ADURA_ALL_LIGHT_POINTS_ADDR):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actState':ADURA_CLR_SCHED,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('xmesh.adura_clear_schedule', params)
        
    # The following 5 messages are repeated n times, depending on how many
    # schedule events are required.
    
    ##
    # Sets the day of the schedule event (currently 10 - all days)
    #
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actDevice  Day ID (10) 
    # @param actState  1792
    # @param seqNumber  Unique sequence number for the specific command
    #
    def adura_setschedule_day(self, dest, act_device):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':act_device,
                  'actState':ADURA_SET_SCHED_DAY,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('adura_setschedule_day', params)
        
    ##
    # Sets the number of hours relative to sunrise
    #
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actDevice  The hours 0 - 23 or
    # Sunrise == hour + 128 = number of hours after sunrise
    #            hour + 160 = number of hours before sunrise
    # Sunset == hour + 64 = number of hourse after sunset
    #           hour + 96 = number of hours before sunset
    # @param  actState  2304
    # @param  seqNumber  Unique sequence number for the specific command 
    def adura_setschedule_hour(self, dest, act_device):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':act_device,
                  'actState':ADURA_SET_SCHED_HOUR,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('adura_setschedule_hour', params)
                                                    
    ##
    # Sets the number of minutes relative to adura_setschedule_hour
    #
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actDevice  Minutes (0-60).  Added to hour above
    # @param actState  2560
    # @param seqNumber  Unique sequence number for the specific command 
    #
    def adura_setschedule_minute(self, dest, act_device):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':act_device,
                  'actState':ADURA_SET_SCHED_MIN,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('adura_setschedule_minute', params)
                                                      
    ##
    # Sets the lighting group that the schedule controls.  Currently that is limited to the 
    # "All LightPoints group (12).
    #
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actDevice  The lighting group to actuate
    # @param actState  2816
    # @param seqNumber  Unique sequence number for the specific command 
    #
    def adura_setschedule_group(self, dest):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':ADURA_ALL_LIGHT_POINTS_GRP,
                  'actState':ADURA_SET_SCHED_GROUP,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('adura_setschedule_group', params)
                                                     
    ##
    # Sets the action for the schedule event and initiates the schedule event
    #
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actDevice  0 = Off, 1 = On
    # @param actState  2048
    # @param seqNumber  Unique sequence number for the specific command 
    #
    def adura_setschedule_action(self, dest, act_device):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':act_device,
                  'actState':ADURA_SET_SCHED_ACTION,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('adura_setschedule_group', params)
                                                              
    ##
    # Sets the current date and time.  Date and time are stored in a 32-bit format.
    # Two messages must be sent, the first to set the high word, the latter the low word.
    # 
    # @param destAddress  The LightPoint ID to which the command is sent or 0xffff (all)
    # @param groupId  The network group id of the lighting network
    # @param actDevice  DateTime value (16bit value)
    # @param actState  Which word to set, 0x0502 == high, 0x0501 == low
    # @param seqNumber  Unique sequence number for the specific command 
    #
    def adura_setdatetime(self, dest, act_device, act_state):
        params = {'destAddress':dest,
                  'groupId':self.net_grp_id,
                  'actDevice':act_device,
                  'actState':act_state,
                  'seqNumber':self._get_next_seq()}
        return self.endpoint.write('adura_setdatetime', params)
Beispiel #2
0
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:
Beispiel #3
0
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: