Пример #1
0
        def request_new_schedule(self, requester_id, task_id, priority,
                                 requests):
            now = datetime.datetime.now()

            if isinstance(requests[0], basestring):
                requests = [requests]

            requests = [[r[0].strip('/'),
                         parse(r[1]),
                         parse(r[2])] for r in requests]

            _log.debug("Got new schedule request: {}, {}, {}, {}".format(
                requester_id, task_id, priority, requests))

            result = self._schedule_manager.request_slots(
                requester_id, task_id, requests, priority, now)
            success = SCHEDULE_RESPONSE_SUCCESS if result.success else SCHEDULE_RESPONSE_FAILURE

            #Dealing with success and other first world problems.
            if result.success:
                self.update_device_state_and_schedule(now)
                for preempted_task in result.data:
                    topic = topics.ACTUATOR_SCHEDULE_RESULT()
                    headers = self.get_headers(preempted_task[0],
                                               task_id=preempted_task[1])
                    headers['type'] = SCHEDULE_ACTION_CANCEL
                    self.vip.pubsub.publish('pubsub',
                                            topic,
                                            headers=headers,
                                            message={
                                                'result':
                                                SCHEDULE_CANCEL_PREEMPTED,
                                                'info': '',
                                                'data': {
                                                    'agentID': requester_id,
                                                    'taskID': task_id
                                                }
                                            })

            #If we are successful we do something else with the real result data
            data = result.data if not result.success else {}
            topic = topics.ACTUATOR_SCHEDULE_RESULT()
            headers = self.get_headers(requester_id, task_id=task_id)
            headers['type'] = SCHEDULE_ACTION_NEW
            results = {
                'result': success,
                'data': data,
                'info': result.info_string
            }
            self.vip.pubsub.publish('pubsub',
                                    topic,
                                    headers=headers,
                                    message=results)

            return results
Пример #2
0
        def handle_new(self, headers, message, now):
            requester = headers.get('requesterID')
            taskID = headers.get('taskID')
            priority = headers.get('priority')

            _log.debug("Got new schedule request: {headers}, {message}".format(
                headers=str(headers), message=str(message)))

            try:
                requests = jsonapi.loads(message[0])
            except (ValueError, IndexError) as ex:
                # Could be ValueError of JSONDecodeError depending
                # on if simplesjson was used.  JSONDecodeError
                # inherits from ValueError

                #We let the schedule manager tell us this is a bad request.
                _log.error('bad request: {request}, {error}'.format(
                    request=requests, error=str(ex)))
                requests = []

            result = self._schedule_manager.request_slots(
                requester, taskID, requests, priority, now)
            success = SCHEDULE_RESPONSE_SUCCESS if result.success else SCHEDULE_RESPONSE_FAILURE

            #If we are successful we do something else with the real result data
            data = result.data if not result.success else {}

            topic = topics.ACTUATOR_SCHEDULE_RESULT()
            headers = self.get_headers(requester, task_id=taskID)
            headers['type'] = SCHEDULE_ACTION_NEW
            self.publish_json(topic, headers, {
                'result': success,
                'data': data,
                'info': result.info_string
            })

            #Dealing with success and other first world problems.
            if result.success:
                self.update_device_state_and_schedule(now)
                for preempted_task in result.data:
                    topic = topics.ACTUATOR_SCHEDULE_RESULT()
                    headers = self.get_headers(preempted_task[0],
                                               task_id=preempted_task[1])
                    headers['type'] = SCHEDULE_ACTION_CANCEL
                    self.publish_json(
                        topic, headers, {
                            'result': SCHEDULE_CANCEL_PREEMPTED,
                            'info': '',
                            'data': {
                                'agentID': requester,
                                'taskID': taskID
                            }
                        })
Пример #3
0
        def handle_schedule_request(self, peer, sender, bus, topic, headers,
                                    message):
            request_type = headers.get('type')
            _log.debug(
                'handle_schedule_request: {topic}, {headers}, {message}'.
                format(topic=topic, headers=str(headers),
                       message=str(message)))

            requester_id = headers.get('requesterID')
            task_id = headers.get('taskID')
            priority = headers.get('priority')

            if request_type == SCHEDULE_ACTION_NEW:
                try:
                    if len(message) == 1:
                        requests = message[0]
                    else:
                        requests = message

                    self.request_new_schedule(requester_id, task_id, priority,
                                              requests)
                except StandardError as ex:
                    _log.error('bad request: {request}, {error}'.format(
                        request=requests, error=str(ex)))
                    self.vip.pubsub.publish(
                        'pubsub', topics.ACTUATOR_SCHEDULE_RESULT(), headers, {
                            'result': SCHEDULE_RESPONSE_FAILURE,
                            'data': {},
                            'info': 'INVALID_REQUEST_TYPE'
                        })

            elif request_type == SCHEDULE_ACTION_CANCEL:
                try:
                    self.request_cancel_schedule(requester_id, task_id)
                except StandardError as ex:
                    _log.error('bad request: {request}, {error}'.format(
                        request=requests, error=str(ex)))
                    self.vip.pubsub.publish(
                        'pubsub', topics.ACTUATOR_SCHEDULE_RESULT(), headers, {
                            'result': SCHEDULE_RESPONSE_FAILURE,
                            'data': {},
                            'info': 'INVALID_REQUEST_TYPE'
                        })

            else:
                _log.debug('handle-schedule_request, invalid request type')
                self.vip.pubsub.publish(
                    'pubsub', topics.ACTUATOR_SCHEDULE_RESULT(), headers, {
                        'result': SCHEDULE_RESPONSE_FAILURE,
                        'data': {},
                        'info': 'INVALID_REQUEST_TYPE'
                    })
Пример #4
0
 def handle_new(self, headers, message):
     '''Send schedule request response.'''
     self._log.info('handle new schedule request')
     requester = headers.get('requesterID')
     self.task_id = headers.get('taskID')
     # priority = headers.get('priority')
     requests = []
     try:
         requests = jsonapi.loads(message[0])
         requests = requests[0]
     except (ValueError, IndexError) as ex:
         self._log.info('error, message not in expected format (json)')
         self._log.error('bad request: {request}, {error}'.format(
             request=requests, error=str(ex)))
         requests = []
     _, start, end = requests
     self.start_time = parser.parse(start, fuzzy=True)
     self.end_time = parser.parse(end, fuzzy=True)
     event = sched.Event(self.announce, args=[requests, requester])
     self.scheduled_event = event
     self.schedule(self.start_time, event)
     topic = topics.ACTUATOR_SCHEDULE_RESULT()
     headers = self.get_headers(requester, task_id=self.task_id)
     headers['type'] = SCHEDULE_ACTION_NEW
     self.publish_json(topic, headers, {
         'result': 'SUCCESS',
         'data': 'NONE',
         'info': 'NONE'
     })
Пример #5
0
 def handle_new(self, headers, message, now):
     print 'handle new'
     requester = headers.get('requesterID')
     self.task_id = headers.get('taskID')
     # priority = headers.get('priority')
     try:
         requests = jsonapi.loads(message[0])
         requests = requests[0]
     except (ValueError, IndexError) as ex:
         # Could be ValueError of JSONDecodeError depending
         # on if simples json was used.  JSONDecodeError
         # inherits from ValueError
         # We let the schedule manager tell us this is a bad request.
         self._log.error('bad request: {request}, {error}'
                         .format(request=requests, error=str(ex)))
         requests = []
     device, start, end = requests
     self.start_time = parser.parse(start, fuzzy=True)
     self.end_time = parser.parse(end, fuzzy=True)
     event = sched.Event(self.announce)
     self.schedule(self.start_time, event)
     topic = topics.ACTUATOR_SCHEDULE_RESULT()
     headers = self.get_headers(requester, task_id=self.task_id)
     headers['type'] = SCHEDULE_ACTION_NEW
     self.publish_json(topic, headers,
                       {
                           'result': 'SUCCESS',
                           'data': 'NONE',
                           'info': 'NONE'
                       })
Пример #6
0
        def handle_cancel(self, headers, now):
            '''Handle schedule request cancel.'''
            task_id = headers.get('taskID')

            success = SCHEDULE_RESPONSE_SUCCESS
            self.scheduled_event.cancel()
            topic = topics.ACTUATOR_SCHEDULE_RESULT()
            self.publish_json(topic, headers, {'result': success,
                                               'info': task_id,
                                               'data': {}})
Пример #7
0
 def _handle_unknown_schedule_error(self, ex, headers, message):
     _log.error('bad request: {header}, {request}, {error}'.format(
         header=headers, request=message, error=str(ex)))
     results = {
         'result': "FAILURE",
         'data': {},
         'info':
         'MALFORMED_REQUEST: ' + ex.__class__.__name__ + ': ' + str(ex)
     }
     self.vip.pubsub.publish('pubsub',
                             topics.ACTUATOR_SCHEDULE_RESULT(),
                             headers=headers,
                             message=results)
     return results
Пример #8
0
 def handle_cancel(self, headers, now):
     requester = headers.get('requesterID')
     taskID = headers.get('taskID')
     
     result = self._schedule_manager.cancel_task(requester, taskID, now)
     success = SCHEDULE_RESPONSE_SUCCESS if result.success else SCHEDULE_RESPONSE_FAILURE
     
     topic = topics.ACTUATOR_SCHEDULE_RESULT()
     self.publish_json(topic, headers, {'result':success,  
                                        'info': result.info_string,
                                        'data':{}})
     
     if result.success:
         self.update_device_state_and_schedule(now)            
Пример #9
0
        def handle_schedule_request(self, topic, headers, message, match):
            print 'request received'
            request_type = headers.get('type')
            now = datetime.datetime.now()

            if request_type == SCHEDULE_ACTION_NEW:
                self.handle_new(headers, message, now)
            else:
                self._log.debug('handle-schedule_request, invalid request type')
                self.publish_json(topics.ACTUATOR_SCHEDULE_RESULT(), headers,
                                  {
                    'result': SCHEDULE_RESPONSE_FAILURE,
                    'data': {},
                    'info': 'INVALID_REQUEST_TYPE'
                    })
Пример #10
0
 def handle_schedule_request(self, topic, headers, message, match):
     request_type = headers.get('type')
     now = datetime.datetime.now()
     _log.debug('handle_schedule_request: {topic}, {headers}, {message}'.
                format(topic=topic, headers=str(headers), message=str(message)))
     
     if request_type == SCHEDULE_ACTION_NEW:
         self.handle_new(headers, message, now)
         
     elif request_type == SCHEDULE_ACTION_CANCEL:
         self.handle_cancel(headers, now)
         
     else:
         _log.debug('handle-schedule_request, invalid request type')
         self.publish_json(topics.ACTUATOR_SCHEDULE_RESULT(), headers,
                           {'result':SCHEDULE_RESPONSE_FAILURE, 
                            'data': {},
                            'info': 'INVALID_REQUEST_TYPE'})
Пример #11
0
    def _request_cancel_schedule(self, sender, task_id):

        now = self.volttime
        headers = self._get_headers(sender, task_id=task_id)
        headers['type'] = SCHEDULE_ACTION_CANCEL

        result = self._schedule_manager.cancel_task(sender, task_id, now)
        success = SCHEDULE_RESPONSE_SUCCESS if result.success else \
            SCHEDULE_RESPONSE_FAILURE

        topic = topics.ACTUATOR_SCHEDULE_RESULT()
        message = {'result': success, 'info': result.info_string, 'data': {}}
        self.vip.pubsub.publish('pubsub',
                                topic,
                                headers=headers,
                                message=message)

        if result.success:
            self._update_device_state_and_schedule(now)

        return message
Пример #12
0
        def request_cancel_schedule(self, requester_id, task_id):
            now = datetime.datetime.now()
            headers = self.get_headers(requester_id, task_id=task_id)

            result = self._schedule_manager.cancel_task(
                requester_id, task_id, now)
            success = SCHEDULE_RESPONSE_SUCCESS if result.success else SCHEDULE_RESPONSE_FAILURE

            topic = topics.ACTUATOR_SCHEDULE_RESULT()
            message = {
                'result': success,
                'info': result.info_string,
                'data': {}
            }
            self.vip.pubsub.publish('pubsub',
                                    topic,
                                    headers=headers,
                                    message=message)

            if result.success:
                self.update_device_state_and_schedule(now)

            return message
Пример #13
0
    def request_cancel_schedule(self, requester_id, task_id):
        """RPC method
        
        Requests the cancelation of the specified task id.
        
        :param requester_id: Requester name. 
        :param task_id: Task name.
        
        :type requester_id: str
        :type task_id: str
        :returns: Request result
        :rtype: dict
        
        :Return Values: 

        The return values are described in `Cancel Task Response`_.
        
        """
        now = utils.get_aware_utc_now()
        headers = self._get_headers(requester_id, task_id=task_id)
        headers['type'] = SCHEDULE_ACTION_CANCEL

        result = self._schedule_manager.cancel_task(requester_id, task_id, now)
        success = SCHEDULE_RESPONSE_SUCCESS if result.success else \
            SCHEDULE_RESPONSE_FAILURE

        topic = topics.ACTUATOR_SCHEDULE_RESULT()
        message = {'result': success, 'info': result.info_string, 'data': {}}
        self.vip.pubsub.publish('pubsub',
                                topic,
                                headers=headers,
                                message=message)

        if result.success:
            self._update_device_state_and_schedule(now)

        return message
Пример #14
0
    class Agent(PublishMixin, BaseAgent):
        '''Agent listens to message bus device and runs when data is published.
        '''
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self._update_event = None
            self._update_event_time = None
            self.keys = None
            self._device_states = {}
            self._kwargs = kwargs
            self.commands = {}
            self.current_point = None
            self.current_key = None
            self.received_input_datetime = None
            if output_file != None:
                with open(output_file, 'w') as writer:
                    writer.close()
            self._header_written = False

        @matching.match_exact(topics.DEVICES_VALUE(point='all', **device))
        def on_received_message(self, topic, headers, message, matched):
            '''Subscribe to device data and convert data to correct type for
            the driven application.
            '''
            _log.debug("Message received")
            _log.debug("MESSAGE: " + jsonapi.dumps(message[0]))
            _log.debug("TOPIC: " + topic)
            data = jsonapi.loads(message[0])

            #TODO: grab the time from the header if it's there or use now if not
            self.received_input_datetime = datetime.utcnow()
            results = app_instance.run(self.received_input_datetime, data)
            self._process_results(results)

        def _process_results(self, results):
            '''Run driven application with converted data and write the app
            results to a file or database.
            '''
            _log.debug('Processing Results!')
            for key, value in results.commands.iteritems():
                _log.debug("COMMAND: {}->{}".format(key, value))
            for value in results.log_messages:
                _log.debug("LOG: {}".format(value))
            for key, value in results.table_output.iteritems():
                _log.debug("TABLE: {}->{}".format(key, value))
            # publish to output file if available.
            if output_file != None:
                if len(results.table_output.keys()) > 0:
                    for _, v in results.table_output.items():
                        fname = output_file  # +"-"+k+".csv"
                        for r in v:
                            with open(fname, 'a+') as f:
                                keys = r.keys()
                                fout = csv.DictWriter(f, keys)
                                if not self._header_written:
                                    fout.writeheader()
                                    self._header_written = True
                                # if not header_written:
                                # fout.writerow(keys)
                                fout.writerow(r)
                                f.close()
            # publish to message bus.
            if len(results.table_output.keys()) > 0:
                now = utils.format_timestamp(self.received_input_datetime)
                headers = {
                    headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                    headers_mod.DATE: now,
                    headers_mod.TIMESTAMP: now
                }

                for _, v in results.table_output.items():
                    for r in v:
                        for key, value in r.iteritems():
                            if isinstance(value, bool):
                                value = int(value)
                            topic = topics.ANALYSIS_VALUE(
                                point=key, **
                                config['device'])  #.replace('{analysis}', key)
                            #print "publishing {}->{}".format(topic, value)
                            self.publish_json(topic, headers, value)

            if results.commands and mode:
                self.commands = results.commands
                if self.keys is None:
                    self.keys = self.commands.keys()
                self.schedule_task()

        def schedule_task(self):
            '''Schedule access to modify device controls.'''
            _log.debug('Schedule Device Access')
            headers = {
                'type': 'NEW_SCHEDULE',
                'requesterID': agent_id,
                'taskID': actuator_id,
                'priority': 'LOW'
            }
            start = datetime.now()
            end = start + td(seconds=300)
            start = str(start)
            end = str(end)
            _log.debug("{campus}/{building}/{unit}".format(**device))
            self.publish_json(
                topics.ACTUATOR_SCHEDULE_REQUEST(), headers,
                [["{campus}/{building}/{unit}".format(**device), start, end]])

        def command_equip(self):
            '''Execute commands on configured device.'''
            self.current_key = self.keys[0]
            value = self.commands[self.current_key]
            headers = {
                'Content-Type': 'text/plain',
                'requesterID': agent_id,
            }
            self.publish(topics.ACTUATOR_SET(point=self.current_key, **device),
                         headers, str(value))

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_exact(topics.ACTUATOR_SCHEDULE_RESULT())
        def schedule_result(self, topic, headers, message, match):
            '''Actuator response (FAILURE, SUCESS).'''
            print 'Actuator Response'
            msg = jsonapi.loads(message[0])
            msg = msg['result']
            _log.debug('Schedule Device ACCESS')
            if self.keys:
                if msg == "SUCCESS":
                    self.command_equip()
                elif msg == "FAILURE":
                    print 'auto correction failed'
                    _log.debug('Auto-correction of device failed.')

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **device))
        def on_set_result(self, topic, headers, message, match):
            '''Setting of point on device was successful.'''
            print('Set Success:  {point} - {value}'.format(
                point=self.current_key,
                value=str(self.commands[self.current_key])))
            _log.debug('set_point({}, {})'.format(
                self.current_key, self.commands[self.current_key]))
            self.keys.remove(self.current_key)
            if self.keys:
                self.command_equip()
            else:
                print 'Done with Commands - Release device lock.'
                headers = {
                    'type': 'CANCEL_SCHEDULE',
                    'requesterID': agent_id,
                    'taskID': actuator_id
                }
                self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(), headers,
                                  {})
                self.keys = None

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **device))
        def on_set_error(self, topic, headers, message, match):
            '''Setting of point on device failed, log failure message.'''
            print 'Set ERROR'
            msg = jsonapi.loads(message[0])
            msg = msg['type']
            _log.debug('Actuator Error: ({}, {}, {})'.format(
                msg, self.current_key, self.commands[self.current_key]))
            self.keys.remove(self.current_key)
            if self.keys:
                self.command_equip()
            else:
                headers = {
                    'type': 'CANCEL_SCHEDULE',
                    'requesterID': agent_id,
                    'taskID': actuator_id
                }
                self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(), headers,
                                  {})
                self.keys = None
Пример #15
0
    def _request_new_schedule(self, sender, task_id, priority, requests):

        now = self.volttime
        topic = topics.ACTUATOR_SCHEDULE_RESULT()
        headers = self._get_headers(sender, task_id=task_id)
        headers['type'] = SCHEDULE_ACTION_NEW
        local_tz = get_localzone()
        try:
            if requests and isinstance(requests[0], basestring):
                requests = [requests]

            tmp_requests = requests
            requests = []
            for r in tmp_requests:
                device, start, end = r

                device = device.strip('/')
                start = utils.parse_timestamp_string(start)
                end = utils.parse_timestamp_string(end)

                if start.tzinfo is None:
                    start = local_tz.localize(start)
                if end.tzinfo is None:
                    end = local_tz.localize(end)

                requests.append([device, start, end])

        except StandardError as ex:
            return self._handle_unknown_schedule_error(ex, headers, requests)

        _log.debug("Got new schedule request: {}, {}, {}, {}".format(
            sender, task_id, priority, requests))

        if self._schedule_manager is None:

            #config = self.default_config.copy()
            # config.update(contents)
            #state_string = self.vip.config.get(self.schedule_state_file)
            #preempt_grace_time = float(config["preempt_grace_time"])
            #self._setup_schedule(preempt_grace_time, state_string)

            try:
                config = self.default_config.copy()
                # config.update(contents)
                state_string = self.vip.config.get(self.schedule_state_file)
                preempt_grace_time = float(config["preempt_grace_time"])
                self._setup_schedule(preempt_grace_time, state_string)
            except KeyError as e:
                state_string = None
                print "ERROR :::: ", e
                print "This is STILL being NONE"

            if not self.subscriptions_setup and self._schedule_manager is not None:
                #Do this after the scheduler is setup.
                self.vip.pubsub.subscribe(peer='pubsub',
                                          prefix=topics.ACTUATOR_GET(),
                                          callback=self.handle_get)

                self.vip.pubsub.subscribe(peer='pubsub',
                                          prefix=topics.ACTUATOR_SET(),
                                          callback=self.handle_set)

                self.vip.pubsub.subscribe(
                    peer='pubsub',
                    prefix=topics.ACTUATOR_SCHEDULE_REQUEST(),
                    callback=self.handle_schedule_request)

                self.vip.pubsub.subscribe(
                    peer='pubsub',
                    prefix=topics.ACTUATOR_REVERT_POINT(),
                    callback=self.handle_revert_point)

                self.vip.pubsub.subscribe(
                    peer='pubsub',
                    prefix=topics.ACTUATOR_REVERT_DEVICE(),
                    callback=self.handle_revert_device)

                self.subscriptions_setup = True

        result = self._schedule_manager.request_slots(sender, task_id,
                                                      requests, priority, now)
        success = SCHEDULE_RESPONSE_SUCCESS if result.success else \
            SCHEDULE_RESPONSE_FAILURE

        # Dealing with success and other first world problems.
        if result.success:
            self._update_device_state_and_schedule(now)
            for preempted_task in result.data:
                preempt_headers = self._get_headers(preempted_task[0],
                                                    task_id=preempted_task[1])
                preempt_headers['type'] = SCHEDULE_ACTION_CANCEL
                self.vip.pubsub.publish('pubsub',
                                        topic,
                                        headers=preempt_headers,
                                        message={
                                            'result':
                                            SCHEDULE_CANCEL_PREEMPTED,
                                            'info': '',
                                            'data': {
                                                'agentID': sender,
                                                'taskID': task_id
                                            }
                                        })

        # If we are successful we do something else with the real result data
        data = result.data if not result.success else {}

        results = {'result': success, 'data': data, 'info': result.info_string}
        self.vip.pubsub.publish('pubsub',
                                topic,
                                headers=headers,
                                message=results)

        return results
Пример #16
0
    class DrivenMatlabAgent(Agent):
        '''Agent listens to message bus device and runs when data is published.
        '''
        def __init__(self, **kwargs):
            super(DrivenMatlabAgent, self).__init__(**kwargs)
            self._update_event = None
            self._update_event_time = None
            self.keys = None
            # master is where we copy from to get a poppable list of
            # subdevices that should be present before we run the analysis.
            self._master_subdevices = subdevices
            self._needed_subdevices = []
            self._master_devices = units
            self._subdevice_values = {}
            self._needed_devices = []
            self._device_values = {}
            self._initialize_devices()
            self.received_input_datetime = None
            self._kwargs = kwargs
            self.commands = {}
            self.current_point = None
            self.current_key = None
            if output_file is not None:
                with open(output_file, 'w') as writer:
                    writer.close()
            self._header_written = False

        def _initialize_devices(self):
            self._needed_subdevices = deepcopy(self._master_subdevices)
            self._needed_devices = deepcopy(self._master_devices)
            self._subdevice_values = {}
            self._device_values = {}

        def _should_run_now(self):
            # Assumes the unit/all values will have values.
            if not len(self._device_values.keys()) > 0:
                return False
            return not (len(self._needed_subdevices) > 0
                        or len(self._needed_devices) > 0)

        #@matching.match_regex(devices_topic)
        @PubSub.subscribe('pubsub', '')
        def on_rec_analysis_message(self, peer, sender, bus, topic, headers,
                                    message):
            '''Subscribe to device data and assemble data set to pass
            to applications.
            '''
            topic_rex = re.compile(devices_topic)
            if not topic_rex.match(topic):
                _log.debug("Unmatched topic: {}".format(topic))
                return
            #obj = jsonapi.loads(message[0])
            #if isinstance(obj, list):
            #    obj = obj[0]
            obj = message[0]
            dev_list = topic.split('/')
            device_or_subdevice = dev_list[-2]
            device_id = [
                dev for dev in self._master_devices
                if dev == device_or_subdevice
            ]
            subdevice_id = [
                dev for dev in self._master_subdevices
                if dev == device_or_subdevice
            ]
            if not device_id and not subdevice_id:
                return
            if isinstance(device_or_subdevice, str):
                device_or_subdevice = (
                    device_or_subdevice.decode('utf-8').encode('ascii'))

            def agg_subdevice(obj):
                sub_obj = {}
                for key, value in obj.items():
                    sub_key = ''.join([key, '_', device_or_subdevice])
                    sub_obj[sub_key] = value
                if len(dev_list) > 5:
                    self._subdevice_values.update(sub_obj)
                    self._needed_subdevices.remove(device_or_subdevice)
                else:
                    self._device_values.update(sub_obj)
                    self._needed_devices.remove(device_or_subdevice)
                return

            # The below if statement is used to distinguish between unit/all
            # and unit/sub-device/all
            if (device_or_subdevice not in self._needed_devices
                    and device_or_subdevice not in self._needed_subdevices):
                _log.error("Warning device values already present, "
                           "reinitializing")
                self._initialize_devices()
            agg_subdevice(obj)

            if self._should_run_now():
                field_names = {}
                self._device_values.update(self._subdevice_values)
                for k, v in self._device_values.items():
                    field_names[k.lower() if isinstance(k, str) else k] = v
                if not converter.initialized and \
                        conv_map is not None:
                    converter.setup_conversion_map(map_names, field_names)
                if from_file:
                    _timestamp = parse(headers.get('Date'), fuzzy=True)
                    self.received_input_datetime = _timestamp
                else:
                    _timestamp = datetime.now()
                    self.received_input_datetime = datetime.utcnow()

                obj = converter.process_row(field_names)
                results = app_instance.run(_timestamp, obj)
                # results = app_instance.run(
                # dateutil.parser.parse(self._subdevice_values['Timestamp'],
                #                       fuzzy=True), self._subdevice_values)
                self._process_results(results)
                self._initialize_devices()
            else:
                needed = deepcopy(self._needed_devices)
                needed.extend(self._needed_subdevices)
                _log.info("Still need {} before running.".format(needed))

        def _process_results(self, results):
            '''Run driven application with converted data and write the app
            results to a file or database.
            '''
            _log.debug('Processing Results!')
            for key, value in results.commands.items():
                _log.debug("COMMAND: {}->{}".format(key, value))
            for value in results.log_messages:
                _log.debug("LOG: {}".format(value))
            for key, value in results.table_output.items():
                _log.debug("TABLE: {}->{}".format(key, value))
            if output_file is not None:
                _log.debug('Writing Output File!')
                if len(results.table_output.keys()) > 0:
                    for v in results.table_output.values():
                        fname = output_file  # +"-"+k+".csv"
                        for r in v:
                            with open(fname, 'a+') as f:
                                keys = r.keys()
                                fout = csv.DictWriter(f, keys)
                                if not self._header_written:
                                    fout.writeheader()
                                    self._header_written = True
                                # if not header_written:
                                # fout.writerow(keys)
                                fout.writerow(r)
                                f.close()
            _log.debug('File Written!')

            def get_unit(point):
                ''' Get a unit type based upon the regular expression in the config file.

                    if NOT found returns percent as a default unit.
                '''
                _log.debug('In get unit!')
                for k, v in unittype_map.items():
                    if re.match(k, point):
                        return v
                return 'percent'

            # publish to message bus.
            _log.debug('Publishing table output to message bus')
            if len(results.table_output.keys()) > 0:
                headers = {
                    headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                    headers_mod.DATE: str(self.received_input_datetime),
                }
                for v in list(results.table_output.values()):
                    for r in v:
                        for key, value in list(r.items()):
                            if isinstance(value, bool):
                                value = int(value)
                            for item in units:
                                _analysis['unit'] = item
                                analysis_topic = topics.ANALYSIS_VALUE(
                                    point=key, **_analysis)

                                datatype = 'float'
                                if isinstance(value, int):
                                    datatype = 'int'
                                kbase = key[key.rfind('/') + 1:]
                                message = [{
                                    kbase: value
                                }, {
                                    kbase: {
                                        'tz': 'US/Pacific',
                                        'type': datatype,
                                        'units': 'float',
                                    }
                                }]
                                self.vip.pubsub.publish(peer="pubsub",
                                                        topic=analysis_topic,
                                                        headers=headers,
                                                        message=message)

            _log.debug('Publishing commands to message bus')
            _log.debug(mode)
            if results.commands and mode:
                self.commands = results.commands
                if self.keys is None:
                    self.keys = list(self.commands.keys())
                _log.debug("we have commands")
                #self.schedule_task()
                self.command_equip()

        def schedule_task(self):
            '''Schedule access to modify device controls.'''
            _log.debug('Schedule Device Access')
            headers = {
                'type': 'NEW_SCHEDULE',
                'requesterID': agent_id,
                'taskID': actuator_id,
                'priority': 'LOW'
            }
            start = datetime.now()
            end = start + td(seconds=30)
            start = str(start)
            end = str(end)
            self.vip.pubsub.publish(
                peer='pubsub',
                topic=topics.ACTUATOR_SCHEDULE_REQUEST(),
                headers=headers,
                message=[[
                    "{campus}/{building}/{unit}".format(**device), start, end
                ]])

        def command_equip(self):
            '''Execute commands on configured device.'''
            for key in self.keys:
                self.current_key = key
                value = self.commands[self.current_key]
                headers = {
                    'Content-Type': 'text/plain',
                    'requesterID': agent_id,
                }
                device.update({'unit': ''})
                self.vip.pubsub.publish(peer="pubsub",
                                        topic=topics.ACTUATOR_SET(
                                            point=self.current_key, **device),
                                        headers=headers,
                                        message=str(value))

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_exact(topics.ACTUATOR_SCHEDULE_RESULT())
        def schedule_result(self, peer, sender, bus, topic, headers, message):
            '''Actuator response (FAILURE, SUCESS).'''
            _log.debug('Actuator Response')
            msg = jsonapi.loads(message[0])
            msg = msg['result']
            _log.debug('Schedule Device ACCESS')
            if self.keys:
                if msg == "SUCCESS":
                    self.command_equip()
                elif msg == "FAILURE":
                    _log.debug('Auto-correction of device failed.')

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **device))
        def on_set_result(self, peer, sender, bus, topic, headers, message):
            '''Setting of point on device was successful.'''
            _log.debug('Set Success:  {point} - {value}'.format(
                point=self.current_key,
                value=str(self.commands[self.current_key])))
            _log.debug('set_point({}, {})'.format(
                self.current_key, self.commands[self.current_key]))
            self.keys.remove(self.current_key)
            if self.keys:
                self.command_equip()
            else:
                _log.debug('Done with Commands - Release device lock.')
                headers = {
                    'type': 'CANCEL_SCHEDULE',
                    'requesterID': agent_id,
                    'taskID': actuator_id
                }
                self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(), headers,
                                  {})
                self.keys = None

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **device))
        def on_set_error(self, topic, headers, message, match):
            '''Setting of point on device failed, log failure message.'''
            _log.debug('Set ERROR')
            msg = jsonapi.loads(message[0])
            msg = msg['type']
            _log.debug('Actuator Error: ({}, {}, {})'.format(
                msg, self.current_key, self.commands[self.current_key]))
            self.keys.remove(self.current_key)
            if self.keys:
                self.command_equip()
            else:
                headers = {
                    'type': 'CANCEL_SCHEDULE',
                    'requesterID': agent_id,
                    'taskID': actuator_id
                }
                self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(), headers,
                                  {})
                self.keys = None
Пример #17
0
    def handle_schedule_request(self, peer, sender, bus, topic, headers,
                                message):
        """        
        Schedule request pub/sub handler
        
        An agent can request a task schedule by publishing to the
        ``devices/actuators/schedule/request`` topic with the following header:
        
        .. code-block:: python
        
            {
                'type': 'NEW_SCHEDULE',
                'requesterID': <Agent ID>, #The name of the requesting agent.
                'taskID': <unique task ID>, #The desired task ID for this
                task. It must be unique among all other scheduled tasks.
                'priority': <task priority>, #The desired task priority,
                must be 'HIGH', 'LOW', or 'LOW_PREEMPT'
            }
            
        The message must describe the blocks of time using the format
        described in `Device Schedule`_.
            
        A task may be canceled by publishing to the
        ``devices/actuators/schedule/request`` topic with the following header:
        
        .. code-block:: python
        
            {
                'type': 'CANCEL_SCHEDULE',
                'requesterID': <Agent ID>, #The name of the requesting agent.
                'taskID': <unique task ID>, #The task ID for the canceled Task.
            }
            
        requesterID
            The name of the requesting agent.
        taskID
            The desired task ID for this task. It must be unique among all
            other scheduled tasks.
        priority
            The desired task priority, must be 'HIGH', 'LOW', or 'LOW_PREEMPT'
            
        No message is requires to cancel a schedule.
            
        """
        if sender == 'pubsub.compat':
            message = compat.unpack_legacy_message(headers, message)

        request_type = headers.get('type')
        _log.debug(
            'handle_schedule_request: {topic}, {headers}, {message}'.format(
                topic=topic, headers=str(headers), message=str(message)))

        requester_id = headers.get('requesterID')
        task_id = headers.get('taskID')
        priority = headers.get('priority')

        if request_type == SCHEDULE_ACTION_NEW:
            try:
                if len(message) == 1:
                    requests = message[0]
                else:
                    requests = message

                self.request_new_schedule(requester_id, task_id, priority,
                                          requests)
            except StandardError as ex:
                return self._handle_unknown_schedule_error(
                    ex, headers, message)

        elif request_type == SCHEDULE_ACTION_CANCEL:
            try:
                self.request_cancel_schedule(requester_id, task_id)
            except StandardError as ex:
                return self._handle_unknown_schedule_error(
                    ex, headers, message)
        else:
            _log.debug('handle-schedule_request, invalid request type')
            self.vip.pubsub.publish(
                'pubsub', topics.ACTUATOR_SCHEDULE_RESULT(), headers, {
                    'result': SCHEDULE_RESPONSE_FAILURE,
                    'data': {},
                    'info': 'INVALID_REQUEST_TYPE'
                })
Пример #18
0
    class Agent(PublishMixin, BaseAgent):
        """Class agent"""
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)

            self.normal_firststage_fanspeed = config.get(
                'normal_firststage_fanspeed', 75.0)
            self.normal_secondstage_fanspeed = config.get(
                'normal_secondstage_fanspeed', 90.0)
            self.normal_damper_stpt = config.get('normal_damper_stpt', 5.0)
            self.normal_coolingstpt = config.get('normal_coolingstpt', 74.0)
            self.normal_heatingstpt = config.get('normal_heatingstpt', 67.0)
            self.smap_path = config.get('smap_path')
            self.default_cooling_stage_differential = 0.5
            self.current_spacetemp = 0.0
            self.building_thermal_constant = config.get(
                'building_thermal_constant', 4.0)
            self.timestep_length = config.get('timestep_length', 900)
            self.csp_cpp = config.get('csp_cpp', 80.0)
            self.csp_pre = config.get('csp_pre', 67.0)
            self.restore_window = int(
                ((self.csp_cpp - self.normal_coolingstpt) /
                 self.building_thermal_constant) * 3600)

            self.state = 'STARTUP'
            self.e_start_msg = None
            self.error_handler = None
            self.actuator_handler = None
            self.pre_cool_idle = None
            self.e_start = None
            self.e_end = None
            self.pre_stored_spacetemp = None

            self.device_schedule = {}
            self.all_scheduled_events = {}
            self.currently_running_dr_event_handlers = []
            self.headers = {
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                'requesterID': agent_id
            }
            utils.setup_logging()
            self._log = logging.getLogger(__name__)

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_exact(topics.ACTUATOR_SCHEDULE_RESULT())
        def schedule_result(self, topic, headers, message, match):
            msg = jsonapi.loads(message[0])
            self._log.info('Schedule Request Acknowledged')
            self.task_timer.cancel()
            task_id = headers.get('taskID', 0)
            response_type = headers.get('type', 0)
            schedule_start = self.device_schedule[task_id]["schedule_start"]
            event_start = schedule_start + datetime.timedelta(minutes=1)
            schedule_end = self.device_schedule[task_id]["schedule_end"]
            e_start = self.device_schedule[task_id]["event_start"]
            e_end = self.device_schedule[task_id]["event_end"]

            if response_type == 'NEW_SCHEDULE' and self.error_handler == None:
                if msg.get('result', 0) == 'SUCCESS':
                    event = sched.Event(self.pre_cool_setup,
                                        args=[e_start, e_end])
                    self.schedule(event_start, event)
                    self.all_scheduled_events[e_start] = event
                elif msg.get('result',
                             0) == 'FAILURE' and schedule_start < schedule_end:
                    schedule_start = schedule_start + datetime.timedelta(
                        minutes=10)
                    headers = {
                        'type': 'NEW_SCHEDULE',
                        'requesterID': agent_id,
                        'taskID': task_id,
                        'priority': 'High'
                    }
                    self.task_timer = self.periodic_timer(
                        20, self.publish_json,
                        topics.ACTUATOR_SCHEDULE_REQUEST(), headers, [[
                            "{campus}/{building}/{unit}".format(**rtu_path),
                            str(schedule_start), schedule_end
                        ]])
                elif schedule_start >= schedule_end:
                    return
            if self.error_handler is not None:
                self.error_handler()

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **rtu_path))
        def _on_error_result(self, topic, headers, message, match):
            """ERROR result"""
            point = match.group(1)
            msg = jsonapi.loads(message[0])
            point = match.group(1)
            today = datetime.datetime.now().date()
            for key, schedule in self.device_schedule.items():
                if schedule["date"] == today:
                    schedule_start = schedule["schedule_start"]
                    schedule_end = schedule["schedule_end"]
                    task_id = key
                    break

            self._log.info('Error Results: ' + str(point) + '  ' + str(msg))
            if msg.get('type', 0) == 'LockError':
                headers = {
                    'type': 'NEW_SCHEDULE',
                    'requesterID': agent_id,
                    'taskID': task_id,
                    'priority': 'HIGH'
                }
                self.task_timer = self.periodic_timer(
                    20, self.publish_json, topics.ACTUATOR_SCHEDULE_REQUEST(),
                    headers, [[
                        "{campus}/{building}/{unit}".format(**rtu_path),
                        str(schedule_start),
                        str(schedule_end)
                    ]])

            elif self.error_handler is not None:
                self._log.info('Running error handler')
                self.error_handler()

        @matching.match_exact(topics.DEVICES_VALUE(point='all', **rtu_path))
        def _on_new_data(self, topic, headers, message, match):
            """watching for new data"""
            data = jsonapi.loads(message[0])
            self.current_spacetemp = float(data[space_temp])
            dr_override = bool(int(data[override_command]))
            occupied = bool(int(data[occupied_status]))

            if dr_override and self.state not in ('IDLE', 'CLEANUP',
                                                  'STARTUP'):
                self._log.info('User Override Initiated')
                self.cancel_event(cancel_type='OVERRIDE')

            if not occupied and self.state in ('DR_EVENT', 'RESTORE'):
                self.cancel_event()

            if self.state == 'STARTUP':
                self._log.info('Finished Startup')
                self.state = 'IDLE'

        @matching.match_exact(topics.OPENADR_EVENT())
        def _on_dr_event(self, topic, headers, message, match):
            if self.state == 'STARTUP':
                self._log.info('DR event ignored because of startup.')
                return
            """handle openADR events"""
            msg = jsonapi.loads(message[0])
            self._log.info('EVENT Received:  ' + str(msg))
            e_id = msg['id']
            e_status = msg['status']
            e_start = msg['start_at']
            task_id = msg['id']
            #e_start = datetime.datetime.strptime(e_start,datefmt)
            today = datetime.datetime.now().date()
            e_end = msg['end_at']
            e_end = parser.parse(e_end, fuzzy=True)
            e_start = parser.parse(e_start, fuzzy=True)
            dr_date = e_start.date()
            current_datetime = datetime.datetime.now()

            if current_datetime > e_end:
                self._log.info('Too Late Event is Over')
                return

            if e_status == 'cancelled':
                if e_start in self.all_scheduled_events:
                    self._log.info('Event Cancelled')
                    self.all_scheduled_events[e_start].cancel()
                    del self.all_scheduled_events[e_start]

                if e_start.date() == today and (self.state == 'PRECOOL'
                                                or self.state == 'DR_EVENT'):
                    self.cancel_event()
                return

            if today > e_start.date():
                if e_start in self.all_scheduled_events:
                    self.all_scheduled_events[e_start].cancel()
                    del self.all_scheduled_events[e_start]
                return

            for item in self.all_scheduled_events:
                if e_start.date() == item.date():
                    if e_start.time() != item.time():
                        self._log.info('Updating Event')
                        self.all_scheduled_events[item].cancel()
                        del self.all_scheduled_events[item]
                        if e_start.date() == today and (self.state == 'PRECOOL'
                                                        or self.state
                                                        == 'DR_EVENT'):
                            self.cancel_event(cancel_type='UPDATING')
                        break
                    elif e_start.time() == item.time():
                        self._log.info("same event")
                        return

            #Don't schedule an event if we are currently in OVERRIDE state.
            if e_start.date() == today and (self.state == 'OVERRIDE'):
                return

            schedule_start = e_start - datetime.timedelta(
                hours=max_precool_hours)
            schedule_end = e_end + datetime.timedelta(
                seconds=self.restore_window)
            schedule_end = schedule_end + datetime.timedelta(minutes=10)
            self.device_schedule[task_id] = {
                "date": dr_date,
                "schedule_start": schedule_start,
                "schedule_end": schedule_end,
                "event_start": e_start,
                "event_end": e_end
            }
            headers = {
                'type': 'NEW_SCHEDULE',
                'requesterID': agent_id,
                'taskID': task_id,
                'priority': 'HIGH'
            }
            self.task_timer = self.periodic_timer(
                20, self.publish_json, topics.ACTUATOR_SCHEDULE_REQUEST(),
                headers, [[
                    "{campus}/{building}/{unit}".format(**rtu_path),
                    str(schedule_start),
                    str(schedule_end)
                ]])

        def pre_cool_setup(self, e_start, e_end):
            if self.state == 'OVERRIDE':
                self._log.info("Override today")
                return

            if self.pre_cool_idle == False:
                return

            now = datetime.datetime.now()
            day = now.weekday()

            if not schedule[day]:
                self._log.info("Unoccupied today")
                return

            if self.state == 'PRECOOL' and self.pre_cool_idle == True:
                for event in self.currently_running_dr_event_handlers:
                    event.cancel()
                self.currently_running_dr_event_handlers = []

            self.state = 'PRECOOL'
            e_start_unix = time.mktime(e_start.timetuple())
            e_end_unix = time.mktime(e_end.timetuple())

            event_start = now + datetime.timedelta(minutes=15)
            event = sched.Event(self.pre_cool_setup, args=[e_start, e_end])
            self.schedule(event_start, event)
            self.all_scheduled_events[e_start] = event
            self.schedule_builder(e_start_unix, e_end_unix)

        def modify_temp_set_point(self, csp, hsp):
            self.publish(topics.ACTUATOR_SET(point=volttron_flag, **rtu_path),
                         self.headers, str(3.0))
            self.publish(
                topics.ACTUATOR_SET(point=min_damper_stpt, **rtu_path),
                self.headers, str(self.normal_damper_stpt))
            self.publish(
                topics.ACTUATOR_SET(point=cooling_stage_diff, **rtu_path),
                self.headers, str(self.default_cooling_stage_differential))
            self.publish(topics.ACTUATOR_SET(point=cooling_stpt, **rtu_path),
                         self.headers, str(csp))
            self.publish(topics.ACTUATOR_SET(point=heating_stpt, **rtu_path),
                         self.headers, str(hsp))

            if self.pre_cool_idle == True:
                self.pre_cool_idle = False

            def backup_run():
                self.modify_temp_set_point(csp, hsp)
                self.error_handler = None

            self.error_handler = backup_run

        def start_dr_event(self):
            self.state = 'DR_EVENT'
            self.publish(topics.ACTUATOR_SET(point=volttron_flag, **rtu_path),
                         self.headers, str(3.0))
            self.publish(topics.ACTUATOR_SET(point=cooling_stpt, **rtu_path),
                         self.headers, str(self.csp_cpp))

            new_fan_speed = self.normal_firststage_fanspeed - (
                self.normal_firststage_fanspeed * fan_reduction)
            new_fan_speed = max(new_fan_speed, 0)
            self.publish(
                topics.ACTUATOR_SET(point=cooling_fan_sp1, **rtu_path),
                self.headers, str(new_fan_speed))

            new_fan_speed = self.normal_secondstage_fanspeed - (
                self.normal_firststage_fanspeed * fan_reduction)
            new_fan_speed = max(new_fan_speed, 0)
            self.publish(
                topics.ACTUATOR_SET(point=cooling_fan_sp2, **rtu_path),
                self.headers, str(new_fan_speed))

            self.publish(
                topics.ACTUATOR_SET(point=min_damper_stpt, **rtu_path),
                self.headers, str(damper_cpp))
            self.publish(
                topics.ACTUATOR_SET(point=cooling_stage_diff, **rtu_path),
                self.headers, str(cooling_stage_differential))
            mytime = int(time.time())
            content = {
                "Demand Response Event": {
                    "Readings": [[mytime, 1.0]],
                    "Units": "TU",
                    "data_type": "double"
                }
            }
            self.publish(self.smap_path, self.headers, jsonapi.dumps(content))

            def backup_run():
                self.start_dr_event()
                self.error_handler = None

            self.error_handler = backup_run

        def start_restore_event(self, csp, hsp):
            self.state = 'RESTORE'
            self._log.info('Restore:  Begin restoring normal operations')
            self.publish(topics.ACTUATOR_SET(point=cooling_stpt, **rtu_path),
                         self.headers, str(csp))
            self.publish(topics.ACTUATOR_SET(point=heating_stpt, **rtu_path),
                         self.headers, str(hsp))  #heating
            self.publish(
                topics.ACTUATOR_SET(point=cooling_fan_sp1, **rtu_path),
                self.headers, str(self.normal_firststage_fanspeed))
            self.publish(
                topics.ACTUATOR_SET(point=cooling_fan_sp2, **rtu_path),
                self.headers, str(self.normal_secondstage_fanspeed))

            self.publish(
                topics.ACTUATOR_SET(point=min_damper_stpt, **rtu_path),
                self.headers, str(self.normal_damper_stpt))
            self.publish(
                topics.ACTUATOR_SET(point=cooling_stage_diff, **rtu_path),
                self.headers, str(self.default_cooling_stage_differential))

            def backup_run():
                self.start_restore_event(csp, hsp)
                self.error_handler = None

            self.error_handler = backup_run

        def cancel_event(self, cancel_type='NORMAL'):
            if cancel_type == 'OVERRIDE':
                self.state = 'OVERRIDE'
                smap_input = 3.0
            elif cancel_type != 'UPDATING':
                self.state = 'CLEANUP'
                smap_input = 2.0

            self.publish(topics.ACTUATOR_SET(point=cooling_stpt, **rtu_path),
                         self.headers, str(self.normal_coolingstpt))
            self.publish(topics.ACTUATOR_SET(point=heating_stpt, **rtu_path),
                         self.headers, str(self.normal_heatingstpt))
            self.publish(
                topics.ACTUATOR_SET(point=cooling_fan_sp1, **rtu_path),
                self.headers, str(self.normal_firststage_fanspeed))
            self.publish(
                topics.ACTUATOR_SET(point=cooling_fan_sp2, **rtu_path),
                self.headers, str(self.normal_secondstage_fanspeed))

            self.publish(
                topics.ACTUATOR_SET(point=min_damper_stpt, **rtu_path),
                self.headers, str(self.normal_damper_stpt))
            self.publish(
                topics.ACTUATOR_SET(point=cooling_stage_diff, **rtu_path),
                self.headers, str(self.default_cooling_stage_differential))
            self.publish(topics.ACTUATOR_SET(point=volttron_flag, **rtu_path),
                         self.headers, str(0))

            for event in self.currently_running_dr_event_handlers:
                event.cancel()

            if cancel_type != 'UPDATING':
                mytime = int(time.time())
                content = {
                    "Demand Response Event": {
                        "Readings": [[mytime, smap_input]],
                        "Units": "TU",
                        "data_type": "double"
                    }
                }
                self.publish(self.smap_path, self.headers,
                             jsonapi.dumps(content))
            self.device_schedule = {}
            self.all_scheduled_events = {}
            self.currently_running_dr_event_handlers = []

            def backup_run():
                self.cancel_event()
                self.error_handler = None

            self.error_handler = backup_run

            expected_values = {
                cooling_stpt: self.normal_coolingstpt,
                heating_stpt: self.normal_heatingstpt,
                cooling_fan_sp1: self.normal_firststage_fanspeed,
                cooling_fan_sp2: self.normal_secondstage_fanspeed,
                min_damper_stpt: self.normal_damper_stpt,
                cooling_stage_diff: self.default_cooling_stage_differential
            }

            EPSILON = 0.5  #allowed difference from expected value

            def result_handler(point, value):
                #print "actuator point being handled:", point, value
                expected_value = expected_values.pop(point, None)
                if expected_value is not None:
                    diff = abs(expected_value - value)
                    if diff > EPSILON:
                        self._log.info(
                            "Did not get back expected value for:  " +
                            str(point))

                if not expected_values:
                    self.actuator_handler = None

                    self.error_handler = None
                    self.state = 'IDLE' if not cancel_type == 'OVERRIDE' else 'OVERRIDE'

            if cancel_type != 'UPDATING':
                self.actuator_handler = result_handler
            else:
                self.actuator_handler = None

            if cancel_type == 'OVERRIDE':

                def on_reset():
                    self.error_handler = None
                    self.state = 'IDLE'

                today = datetime.datetime.now()
                reset_time = today + datetime.timedelta(days=1)
                reset_time = reset_time.replace(hour=0, minute=0, second=0)

                event = sched.Event(on_reset)
                self.schedule(reset_time, event)

        def schedule_builder(self, start_time, end_time):
            """schedule all events for a DR event."""
            current_time = time.time()
            if current_time > end_time:
                return

            self._log.info('Scheduling all DR actions')
            pre_hsp = self.csp_pre - 5.0

            ideal_cooling_window = int(
                ((self.current_spacetemp - self.csp_pre) /
                 self.building_thermal_constant) * 3600)
            ideal_precool_start_time = start_time - ideal_cooling_window

            max_cooling_window = start_time - current_time

            cooling_window = ideal_cooling_window if ideal_cooling_window < max_cooling_window else max_cooling_window

            precool_start_time = start_time - cooling_window
            pre_cool_step = 0
            if (max_cooling_window > 0):
                self._log.info('Schedule Pre Cooling')
                num_cooling_timesteps = int(
                    math.ceil(
                        float(cooling_window) / float(self.timestep_length)))
                cooling_step_delta = (self.normal_coolingstpt -
                                      self.csp_pre) / num_cooling_timesteps

                if num_cooling_timesteps <= 0:
                    num_cooling_timesteps = 1

                for step_index in range(1, num_cooling_timesteps):
                    if step_index == 1:
                        pre_cool_step = 2 * self.timestep_length
                    else:
                        pre_cool_step += self.timestep_length

                    event_time = start_time - pre_cool_step
                    csp = self.csp_pre + (
                        (step_index - 1) * cooling_step_delta)

                    self._log.info(
                        'Precool step:  ' +
                        str(datetime.datetime.fromtimestamp(event_time)) +
                        '   CSP:  ' + str(csp))
                    event = sched.Event(self.modify_temp_set_point,
                                        args=[csp, pre_hsp])
                    self.schedule(event_time, event)
                    self.currently_running_dr_event_handlers.append(event)

            else:
                self._log.info('Too late to pre-cool!')

            restore_start_time = end_time
            num_restore_timesteps = int(
                math.ceil(
                    float(self.restore_window) / float(self.timestep_length)))
            restore_step_delta = (
                self.csp_pre - self.normal_coolingstpt) / num_restore_timesteps

            self._log.info('Schedule DR Event: ' +
                           str(datetime.datetime.fromtimestamp(start_time)) +
                           '   CSP:  ' + str(self.csp_cpp))
            event = sched.Event(self.start_dr_event)
            self.schedule(start_time, event)
            self.currently_running_dr_event_handlers.append(event)

            self._log.info('Schedule Restore Event:  ' +
                           str(datetime.datetime.fromtimestamp(end_time)) +
                           '   CSP:  ' +
                           str(self.csp_pre - restore_step_delta))
            event = sched.Event(self.start_restore_event,
                                args=[
                                    self.csp_pre - restore_step_delta,
                                    self.normal_heatingstpt
                                ])
            self.schedule(end_time, event)
            self.currently_running_dr_event_handlers.append(event)

            for step_index in range(1, num_restore_timesteps):
                event_time = end_time + (step_index * self.timestep_length)
                csp = self.csp_pre - ((step_index + 1) * restore_step_delta)

                self._log.info(
                    'Restore step: ' +
                    str(datetime.datetime.fromtimestamp(event_time)) +
                    '   CSP:  ' + str(csp))
                event = sched.Event(self.modify_temp_set_point,
                                    args=[csp, self.normal_heatingstpt])
                self.schedule(event_time, event)
                self.currently_running_dr_event_handlers.append(event)

            event_time = end_time + (num_restore_timesteps *
                                     self.timestep_length)
            self._log.info('Schedule Cleanup Event:  ' +
                           str(datetime.datetime.fromtimestamp(event_time)))
            event = sched.Event(self.cancel_event)
            self.schedule(event_time, event)
            self.currently_running_dr_event_handlers.append(event)
Пример #19
0
    def request_new_schedule(self, requester_id, task_id, priority, requests):
        """
        RPC method
        
        Requests one or more blocks on time on one or more device.
        
        :param requester_id: Requester name. 
        :param task_id: Task name.
        :param priority: Priority of the task. Must be either "HIGH", "LOW",
        or "LOW_PREEMPT"
        :param requests: A list of time slot requests in the format
        described in `Device Schedule`_.
        
        :type requester_id: str
        :type task_id: str
        :type priority: str
        :returns: Request result
        :rtype: dict       
        
        :Return Values:
        
            The return values are described in `New Task Response`_.
        """

        now = utils.get_aware_utc_now()

        topic = topics.ACTUATOR_SCHEDULE_RESULT()
        headers = self._get_headers(requester_id, task_id=task_id)
        headers['type'] = SCHEDULE_ACTION_NEW
        local_tz = get_localzone()
        try:
            if requests and isinstance(requests[0], basestring):
                requests = [requests]

            tmp_requests = requests
            requests = []
            for r in tmp_requests:
                device, start, end = r

                device = device.strip('/')
                start = utils.parse_timestamp_string(start)
                end = utils.parse_timestamp_string(end)

                if start.tzinfo is None:
                    start = local_tz.localize(start)
                if end.tzinfo is None:
                    end = local_tz.localize(end)

                requests.append([device, start, end])

        except StandardError as ex:
            return self._handle_unknown_schedule_error(ex, headers, requests)

        _log.debug("Got new schedule request: {}, {}, {}, {}".format(
            requester_id, task_id, priority, requests))

        result = self._schedule_manager.request_slots(requester_id, task_id,
                                                      requests, priority, now)
        success = SCHEDULE_RESPONSE_SUCCESS if result.success else \
            SCHEDULE_RESPONSE_FAILURE

        # Dealing with success and other first world problems.
        if result.success:
            self._update_device_state_and_schedule(now)
            for preempted_task in result.data:
                preempt_headers = self._get_headers(preempted_task[0],
                                                    task_id=preempted_task[1])
                preempt_headers['type'] = SCHEDULE_ACTION_CANCEL
                self.vip.pubsub.publish('pubsub',
                                        topic,
                                        headers=preempt_headers,
                                        message={
                                            'result':
                                            SCHEDULE_CANCEL_PREEMPTED,
                                            'info': '',
                                            'data': {
                                                'agentID': requester_id,
                                                'taskID': task_id
                                            }
                                        })

        # If we are successful we do something else with the real result data
        data = result.data if not result.success else {}

        results = {'result': success, 'data': data, 'info': result.info_string}
        self.vip.pubsub.publish('pubsub',
                                topic,
                                headers=headers,
                                message=results)

        return results
    class Agent(PublishMixin, BaseAgent):
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.lock_timer = None
            self.lock_acquired = False
            self.tasklet = None
            self.data_queue = green.WaitQueue(self.timer)
            self.value_queue = green.WaitQueue(self.timer)
            self.weather_data_queue = green.WaitQueue(self.timer)

            self.last_run_time = None
            self.is_running = False
            self.remaining_time = None
            self.task_id = agent_id
            self.retry_schedule = None
            self.start = None
            self.end = None

        def setup(self):
            super(Agent, self).setup()
            self.scheduled_task()

        def startrun(self, algo=None):
            _log.debug('start diagnostic')
            if algo is None:
                algo = afdd.AFDD(self, config_path).run_all
            self.tasklet = greenlet.greenlet(algo)
            self.is_running = True
            self.last_run_time = datetime.datetime.now()
            self.tasklet.switch()

        def scheduled_task(self):
            '''
            Schedule re-occuring diagnostics
            '''
            _log.debug('Schedule Dx')
            headers = {
                'type': 'NEW_SCHEDULE',
                'requesterID': agent_id,
                'taskID': agent_id,
                'priority': 'LOW_PREEMPT'
            }

            min_run_hour = math.floor(min_run_window / 3600)
            min_run_minute = int((min_run_window / 3600 - min_run_hour) * 60)

            self.start = datetime.datetime.now().replace(hour=start_hour,
                                                         minute=start_minute)
            self.end = self.start + datetime.timedelta(hours=2, minutes=30)
            run_start = self.end - datetime.datetime.now()
            required_diagnostic_time = datetime.timedelta(
                hours=min_run_hour, minutes=min_run_minute)

            if run_start < required_diagnostic_time:
                self.start = self.start + datetime.timedelta(days=1)
                self.end = self.start + datetime.timedelta(hours=2, minutes=30)
                sched_time = datetime.datetime.now() + datetime.timedelta(
                    days=day_run_interval + 1)
                sched_time = sched_time.replace(hour=0, minute=1)
            else:
                sched_time = datetime.datetime.now() + datetime.timedelta(
                    days=day_run_interval)

            self.start = str(self.start)
            self.end = str(self.end)
            self.task_timer = self.periodic_timer(
                60, self.publish_json, topics.ACTUATOR_SCHEDULE_REQUEST(),
                headers, [[
                    "{campus}/{building}/{unit}".format(**rtu_path),
                    self.start, self.end
                ]])
            event = sched.Event(self.scheduled_task)
            self.next = self.schedule(sched_time, event)

        @matching.match_headers({
            headers_mod.REQUESTER_ID: agent_id,
            'type': 'CANCEL_SCHEDULE'
        })
        @matching.match_exact(topics.ACTUATOR_SCHEDULE_RESULT())
        def preempt(self):
            if self.is_running:
                self.cancel_greenlet()

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_exact(topics.ACTUATOR_SCHEDULE_ANNOUNCE(**rtu_path))
        def on_schedule(self, topic, headers, message, match):
            msg = jsonapi.loads(message[0])
            now = datetime.datetime.now()
            self.remaining_time = headers.get('window', 0)
            if self.task_id == headers.get('taskID', ''):
                if self.remaining_time < termination_window:
                    if self.is_running:
                        self.cancel_greenlet()
                elif (self.remaining_time > min_run_window and
                      (self.last_run_time is None or (now - self.last_run_time)
                       > datetime.timedelta(hours=23, minutes=50))):
                    self.startrun()

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_exact(topics.ACTUATOR_SCHEDULE_RESULT())
        def schedule_result(self, topic, headers, message, match):
            msg = jsonapi.loads(message[0])
            _log.debug('Actuator response received')
            self.task_timer.cancel()

        @matching.match_exact(topics.DEVICES_VALUE(point='all', **rtu_path))
        def on_new_data(self, topic, headers, message, match):
            data = jsonapi.loads(message[0])
            #Check override status
            if int(data["VoltronPBStatus"]) == 1:
                if self.is_running:
                    _log.debug("User override is initiated...")
                    headers = {
                        'Content-Type': 'text/plain',
                        'requesterID': agent_id,
                    }
                    self.publish(
                        topics.ACTUATOR_SET(point="VoltronFlag", **rtu_path),
                        headers, str(0.0))
                    self.cancel_greenlet()
            else:
                self.data_queue.notify_all(data)

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **rtu_path))
        def on_set_result(self, topic, headers, message, match):
            self.value_queue.notify_all((match.group(1), True))

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **rtu_path))
        def on_set_error(self, topic, headers, message, match):
            self.value_queue.notify_all((match.group(1), False))

        def cancel_greenlet(self):
            #kill all tasks currently in the queue
            self.data_queue.kill_all()
            self.value_queue.kill_all()
            #kill current tasklet
            self.tasklet.throw()
            self.is_running = False

        def sleep(self, timeout):
            _log.debug('wait for steady state({})'.format(timeout))
            green.sleep(timeout, self.timer)

        def get_new_data(self, timeout=None):
            _log.debug('get_new_data({})'.format(timeout))
            return self.data_queue.wait(timeout)

        def command_equip(self, point_name, value, timeout=None):
            _log.debug('set_point({}, {}, {})'.format(point_name, value,
                                                      timeout))
            headers = {
                'Content-Type': 'text/plain',
                'requesterID': agent_id,
            }
            self.publish(topics.ACTUATOR_SET(point=point_name, **rtu_path),
                         headers, str(value))
            try:
                return self.value_queue.wait(timeout)
            except green.Timeout:
                return True

        def weather_request(self, timeout=None):
            _log.debug('weather request for {}'.format(zip_code))
            headers = {'Content-Type': 'text/plain', 'requesterID': agent_id}
            msg = {'zipcode': str(zip_code)}
            self.publish_json('weather/request', headers, msg)
            try:
                return self.weather_data_queue.wait(timeout)
            except green.Timeout:
                return 'INCONCLUSIVE'

        matching.match_headers({headers_mod.REQUESTER_ID: agent_id})

        @matching.match_exact('weather/response/temperature/temp_f')
        def weather_response(self, topic, headers, message, match):
            data = float(jsonapi.loads(message[0]))
            print data
            self.weather_data_queue.notify_all(data)
Пример #21
0
    class Agent(PublishMixin, BaseAgent):
        '''Agent listens to message bus device and runs when data is published.
        '''
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self._update_event = None
            self._update_event_time = None
            self.keys = None
            # master is where we copy from to get a poppable list of
            # subdevices that should be present before we run the analysis.
            self._master_subdevices = subdevices
            self._needed_subdevices = []
            self._master_devices = units
            self._subdevice_values = {}
            self._needed_devices = []
            self._device_values = {}
            self._initialize_devices()
            self.received_input_datetime = None
            self._kwargs = kwargs
            self.commands = {}
            self.current_point = None
            self.current_key = None
            if output_file is not None:
                with open(output_file, 'w') as writer:
                    writer.close()
            self._header_written = False

        def _initialize_devices(self):
            self._needed_subdevices = deepcopy(self._master_subdevices)
            self._needed_devices = deepcopy(self._master_devices)
            self._subdevice_values = {}
            self._device_values = {}

        def _should_run_now(self):
            # Assumes the unit/all values will have values.
            if not len(self._device_values.keys()) > 0:
                return False
            return not (len(self._needed_subdevices) > 0 or
                        len(self._needed_devices) > 0)

        @matching.match_regex(devices_topic)
        def on_rec_analysis_message(self, topic, headers, message, matched):
            # Do the analysis based upon the data passed (the old code).
            # print self._subdevice_values, self._device_values
            obj = jsonapi.loads(message[0])
            dev_list = topic.split('/')
            device_or_subdevice = dev_list[-2]
            device_id = [dev for dev in self._master_devices
                         if dev == device_or_subdevice]
            subdevice_id = [dev for dev in self._master_subdevices
                            if dev == device_or_subdevice]
            if not device_id and not subdevice_id:
                return
            if isinstance(device_or_subdevice, unicode):
                device_or_subdevice = (
                    device_or_subdevice.decode('utf-8').encode('ascii')
                )

            def agg_subdevice(obj):
                sub_obj = {}
                for key, value in obj.items():
                    sub_key = ''.join([key, '_', device_or_subdevice])
                    sub_obj[sub_key] = value
                if len(dev_list) > 5:
                    self._subdevice_values.update(sub_obj)
                    self._needed_subdevices.remove(device_or_subdevice)
                else:
                    self._device_values.update(sub_obj)
                    self._needed_devices.remove(device_or_subdevice)
                return
            # The below if statement is used to distinguish between unit/all
            # and unit/sub-device/all
            if (device_or_subdevice not in self._needed_devices and
                    device_or_subdevice not in self._needed_subdevices):
                _log.error("Warning device values already present, "
                           "reinitializing")
                self._initialize_devices()
            agg_subdevice(obj)
            if self._should_run_now():
                field_names = {}
                self._device_values.update(self._subdevice_values)
                for k, v in self._device_values.items():
                    field_names[k.lower() if isinstance(k, str) else k] = v
                if not converter.initialized and \
                        conv_map is not None:
                    converter.setup_conversion_map(
                        map_names,
                        field_names
                    )
                obj = converter.process_row(field_names)
                results = app_instance.run(datetime.now(),
                                           obj)
                self.received_input_datetime = datetime.utcnow()
                # results = app_instance.run(
                # dateutil.parser.parse(self._subdevice_values['Timestamp'],
                #                       fuzzy=True), self._subdevice_values)
                self._process_results(results)
                self._initialize_devices()
            else:
                needed = deepcopy(self._needed_devices)
                needed.extend(self._needed_subdevices)
                _log.info("Still need {} before running."
                          .format(needed))

        def _process_results(self, results):
            '''Run driven application with converted data and write the app
            results to a file or database.
            '''
            _log.debug('Processing Results!')
            for key, value in results.commands.iteritems():
                _log.debug("COMMAND: {}->{}".format(key, value))
            for value in results.log_messages:
                _log.debug("LOG: {}".format(value))
            for key, value in results.table_output.iteritems():
                _log.debug("TABLE: {}->{}".format(key, value))
            if output_file is not None:
                if len(results.table_output.keys()) > 0:
                    for _, v in results.table_output.items():
                        fname = output_file  # +"-"+k+".csv"
                        for r in v:
                            with open(fname, 'a+') as f:
                                keys = r.keys()
                                fout = csv.DictWriter(f, keys)
                                if not self._header_written:
                                    fout.writeheader()
                                    self._header_written = True
                                # if not header_written:
                                    # fout.writerow(keys)
                                fout.writerow(r)
                                f.close()
            # publish to message bus.
            if len(results.table_output.keys()) > 0:
                headers = {
                    headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                    headers_mod.DATE: str(self.received_input_datetime),
                }

                for _, v in results.table_output.items():
                    for r in v:
                        for key, value in r.iteritems():
                            if isinstance(value, bool):
                                value = int(value)
                            for item in units:
                                _analysis['unit'] = item
                                analysis_topic = topics.ANALYSIS_VALUE(
                                    point=key, **_analysis)
                                self.publish_json(analysis_topic, headers, value)
#                                 mytime = int(time.time())
#                                 content = {
#                                     analysis_topic: {
#                                         "Readings": [[mytime, value]],
#                                         "Units": "TU",
#                                         "data_type": "double"
#                                     }
#                                 }
#                                 self.publish_json(topics.LOGGER_LOG, headers,
#                                                   content)
            if results.commands and mode:
                self.commands = results.commands
                if self.keys is None:
                    self.keys = self.commands.keys()
                self.schedule_task()

        def schedule_task(self):
            '''Schedule access to modify device controls.'''
            _log.debug('Schedule Device Access')
            headers = {
                'type':  'NEW_SCHEDULE',
                'requesterID': agent_id,
                'taskID': actuator_id,
                'priority': 'LOW'
                }
            start = datetime.now()
            end = start + td(seconds=30)
            start = str(start)
            end = str(end)
            self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(), headers,
                              [["{campus}/{building}/{unit}".format(**device),
                                start, end]])

        def command_equip(self):
            '''Execute commands on configured device.'''
            self.current_key = self.keys[0]
            value = self.commands[self.current_key]
            headers = {
                'Content-Type': 'text/plain',
                'requesterID': agent_id,
                }
            self.publish(topics.ACTUATOR_SET(point=self.current_key, **device),
                         headers, str(value))

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_exact(topics.ACTUATOR_SCHEDULE_RESULT())
        def schedule_result(self, topic, headers, message, match):
            '''Actuator response (FAILURE, SUCESS).'''
            _log.debug('Actuator Response')
            msg = jsonapi.loads(message[0])
            msg = msg['result']
            _log.debug('Schedule Device ACCESS')
            if self.keys:
                if msg == "SUCCESS":
                    self.command_equip()
                elif msg == "FAILURE":
                    _log.debug('Auto-correction of device failed.')

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **device))
        def on_set_result(self, topic, headers, message, match):
            '''Setting of point on device was successful.'''
            _log.debug('Set Success:  {point} - {value}'
                       .format(point=self.current_key,
                               value=str(self.commands[self.current_key])))
            _log.debug('set_point({}, {})'.
                       format(self.current_key,
                              self.commands[self.current_key]))
            self.keys.remove(self.current_key)
            if self.keys:
                self.command_equip()
            else:
                _log.debug('Done with Commands - Release device lock.')
                headers = {
                    'type': 'CANCEL_SCHEDULE',
                    'requesterID': agent_id,
                    'taskID': actuator_id
                    }
                self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(),
                                  headers, {})
                self.keys = None

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **device))
        def on_set_error(self, topic, headers, message, match):
            '''Setting of point on device failed, log failure message.'''
            _log.debug('Set ERROR')
            msg = jsonapi.loads(message[0])
            msg = msg['type']
            _log.debug('Actuator Error: ({}, {}, {})'.
                       format(msg,
                              self.current_key,
                              self.commands[self.current_key]))
            self.keys.remove(self.current_key)
            if self.keys:
                self.command_equip()
            else:
                headers = {
                    'type':  'CANCEL_SCHEDULE',
                    'requesterID': agent_id,
                    'taskID': actuator_id
                    }
                self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(),
                                  headers, {})
                self.keys = None
Пример #22
0
    class Agent(PublishMixin, BaseAgent):
        '''Agent listens to message bus device and runs when data is published.
        '''
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self._update_event = None
            self._update_event_time = None
            self.keys = None
            self._device_states = {}
            self._required_subdevice_values = subdevices
            self._subdevice_values = {}
            self._kwargs = kwargs
            self.commands = {}
            self.current_point = None
            self.current_key = None
            if output_file != None:
                with open(output_file, 'w') as writer:
                    writer.close()
            self._header_written = False

        def initialize_subdevices(self):
            self._subdevice_values = {}
            for r in self._required_subdevice_values:
                for s in r:
                    self._subdevice_values[r][s] = None

        def should_run_now(self):
            if len(self._required_subdevice_values) < 1:
                return True

            def has_subdevice_value(unit, subdevice):
                return self.subdevice_value[unit][subdevice] != None

            for r in self._required_subdevice_values:
                for s in r:
                    if not has_subdevice_value(r, s):
                        return False

            return True

        @matching.match_exact(topics.DEVICES_VALUE(point='all', **device))
        def on_received_message(self, topic, headers, message, matched):
            '''Subscribe to device data and convert data to correct type for
            the driven application.
            '''
            _log.debug("Message received")
            _log.debug("MESSAGE: " + jsonapi.dumps(message[0]))
            _log.debug("TOPIC: " + topic)
            data = jsonapi.loads(message[0])
            if not converter.initialized and \
                config.get('conversion_map') is not None:
                converter.setup_conversion_map(config.get('conversion_map'),
                                               data.keys())
            data = converter.process_row(data)

            if len(self._required_subdevice_values) < 1:
                results = app_instance.run(datetime.now(), data)
                self._process_results(results)
            else:
                # apply data to subdevice values.
                if self.should_run_now():
                    results = app_instance.run(datetime.now(), self._subdevice_values)
                    self._process_results(results)

        @matching.match_exact(topics.ANALYSIS_VALUE(point='all', **device))
        def on_rec_analysis_message(self, topic, headers, message, matched):
            print('here!')


        def _process_results(self, results):
            '''Run driven application with converted data and write the app
            results to a file or database.
            '''
            _log.debug('Processing Results!')
            for key, value in results.commands.iteritems():
                _log.debug("COMMAND: {}->{}".format(key, value))
            for value in results.log_messages:
                _log.debug("LOG: {}".format(value))
            for key, value in results.table_output.iteritems():
                _log.debug("TABLE: {}->{}".format(key, value))
            if output_file != None:
                if len(results.table_output.keys()) > 0:
                    for _, v in results.table_output.items():
                        fname = output_file  # +"-"+k+".csv"
                        for r in v:
                            with open(fname, 'a+') as f:
                                keys = r.keys()
                                fout = csv.DictWriter(f, keys)
                                if not self._header_written:
                                    fout.writeheader()
                                    self._header_written = True
                                # if not header_written:
                                    # fout.writerow(keys)
                                fout.writerow(r)
                                f.close()
            if results.commands and mode:
                self.commands = results.commands
                if self.keys is None:
                    self.keys = self.commands.keys()
                self.schedule_task()

        def schedule_task(self):
            '''Schedule access to modify device controls.'''
            _log.debug('Schedule Device Access')
            headers = {
                'type':  'NEW_SCHEDULE',
                'requesterID': agent_id,
                'taskID': actuator_id,
                'priority': 'LOW'
                }
            start = datetime.now()
            end = start + td(seconds=30)
            start = str(start)
            end = str(end)
            self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(), headers,
                              [["{campus}/{building}/{unit}".format(**device),
                                start, end]])

        def command_equip(self):
            '''Execute commands on configured device.'''
            self.current_key = self.keys[0]
            value = self.commands[self.current_key]
            headers = {
                'Content-Type': 'text/plain',
                'requesterID': agent_id,
                }
            self.publish(topics.ACTUATOR_SET(point=self.current_key, **device),
                         headers, str(value))

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_exact(topics.ACTUATOR_SCHEDULE_RESULT())
        def schedule_result(self, topic, headers, message, match):
            '''Actuator response (FAILURE, SUCESS).'''
            print 'Actuator Response'
            msg = jsonapi.loads(message[0])
            msg = msg['result']
            _log.debug('Schedule Device ACCESS')
            if self.keys:
                if msg == "SUCCESS":
                    self.command_equip()
                elif msg == "FAILURE":
                    print 'auto correction failed'
                    _log.debug('Auto-correction of device failed.')

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **device))
        def on_set_result(self, topic, headers, message, match):
            '''Setting of point on device was successful.'''
            print ('Set Success:  {point} - {value}'
                   .format(point=self.current_key,
                           value=str(self.commands[self.current_key])))
            _log.debug('set_point({}, {})'.
                       format(self.current_key,
                              self.commands[self.current_key]))
            self.keys.remove(self.current_key)
            if self.keys:
                self.command_equip()
            else:
                print 'Done with Commands - Release device lock.'
                headers = {
                    'type': 'CANCEL_SCHEDULE',
                    'requesterID': agent_id,
                    'taskID': actuator_id
                    }
                self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(),
                                  headers, {})
                self.keys = None

        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **device))
        def on_set_error(self, topic, headers, message, match):
            '''Setting of point on device failed, log failure message.'''
            print 'Set ERROR'
            msg = jsonapi.loads(message[0])
            msg = msg['type']
            _log.debug('Actuator Error: ({}, {}, {})'.
                       format(msg,
                              self.current_key,
                              self.commands[self.current_key]))
            self.keys.remove(self.current_key)
            if self.keys:
                self.command_equip()
            else:
                headers = {
                    'type':  'CANCEL_SCHEDULE',
                    'requesterID': agent_id,
                    'taskID': actuator_id
                    }
                self.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(),
                                  headers, {})
                self.keys = None

        def publish_to_smap(self, smap_identifier, value, smap_identifier2,
                        value2, time_value):
            '''
            Push diagnostic results and energy
            impact to sMAP historian.
            '''
            self._log.debug(''.join(['Push to sMAP - ', smap_identifier, str(dx_msg),
                                     ' Energy Impact: ', str(energy_impact)]))
            if time_value is None:
                mytime = int(time.time())
            else:
                mytime = time.mktime(time_value.timetuple())
            if value2 is not None:
                content = {
                    smap_identifier: {
                         "Readings": [[mytime, value]],
                         "Units": "TU",
                         "data_type": "double"
                     },
                      smap_identifier2: {
                         "Readings": [[mytime, value2]],
                         "Units": "kWh/h",
                         "data_type": "double"}
                 }
            else:
                content = {
                    smap_identifier: {
                         "Readings": [[mytime, value]],
                         "Units": "TU",
                         "data_type": "double"
                     }
                }
            self._agent.publish(self.smap_path, self.headers, jsonapi.dumps(content))