示例#1
0
    def handle_get(self, peer, sender, bus, topic, headers, message):
        """
        Requests up to date value of a point.
        
        To request a value publish a message to the following topic:

        ``devices/actuators/get/<device path>/<actuation point>``
        
        with the fallowing header:
        
        .. code-block:: python
        
            {
                'requesterID': <Agent ID>
            }
        
        The ActuatorAgent will reply on the **value** topic 
        for the actuator:

        ``devices/actuators/value/<full device path>/<actuation point>``
        
        with the message set to the value the point.
        
        """
        point = topic.replace(topics.ACTUATOR_GET() + '/', '', 1)
        requester = headers.get('requesterID')
        headers = self._get_headers(requester)
        try:
            value = self.get_point(point)
            self._push_result_topic_pair(VALUE_RESPONSE_PREFIX, point, headers,
                                         value)
        except RemoteError as ex:
            self._handle_remote_error(ex, point, headers)
        except StandardError as ex:
            self._handle_standard_error(ex, point, headers)
示例#2
0
 def handle_get(self, peer, sender, bus, topic, headers, message):
     point = topic.replace(topics.ACTUATOR_GET() + '/', '', 1)
     requester = headers.get('requesterID')
     headers = self.get_headers(requester)
     try:
         value = self.get_point(point)
         self.push_result_topic_pair(VALUE_RESPONSE_PREFIX, point,
                                     headers, value)
     except StandardError as ex:
         error = {'type': ex.__class__.__name__, 'value': str(ex)}
         self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point,
                                     headers, error)
示例#3
0
        def on_start(self, sender, **kwargs):
            self.setup_schedule()
            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)
示例#4
0
    def _on_start(self, sender, **kwargs):
        self._setup_schedule()
        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.core.periodic(self.heartbeat_interval, self._heart_beat)
示例#5
0
文件: agent.py 项目: zbeech/volttron
    class Agent(PublishMixin, BaseAgent):
        '''Agent to listen for requests to talk to the sMAP driver.'''

        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            
            self._update_event = None
            self._update_event_time = None
            self._device_states = {}
            
            self.setup_schedule()
            
            self.setup_heartbeats()
            
        def setup_heartbeats(self):
            for point in points:
                heartbeat_point = points[point].get("heartbeat_point")
                if heartbeat_point is None:
                    continue
                
                heartbeat_handler = self.heartbeat_factory(point, heartbeat_point)
                self.periodic_timer(heartbeat_interval, heartbeat_handler)
        
        def heartbeat_factory(self, point, actuator):
            #Stupid lack on nonlocal in 2.x
            value = [False]
            request_url = '/'.join([url, point,
                                    ACTUATOR_COLLECTION, actuator])
            
            publish_topic = '/'.join([point, actuator])
            
            def update_heartbeat_value():
                _log.debug('update_heartbeat')
                value[0] = not value[0]
                payload = {'state': str(int(value[0]))}
                try:
                    _log.debug('About to publish actuation')
                    r = requests.put(request_url, params=payload, timeout=connection_timeout)
                    self.process_smap_request_result(r, publish_topic, None)
                except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as ex:
                    print "Warning: smap driver not running."
                    _log.error("Connection error: "+str(ex))
            
            return update_heartbeat_value
        
        def setup_schedule(self):
            now = datetime.datetime.now()
            self._schedule_manager = ScheduleManager(preempt_grace_time, now=now,
                                                     state_file_name=schedule_state_file)
            
            self.update_device_state_and_schedule(now)
            
        def update_device_state_and_schedule(self, now):
            _log.debug("update_device_state_and schedule")
            self._device_states = self._schedule_manager.get_schedule_state(now)
            schedule_next_event_time = self._schedule_manager.get_next_event_time(now)
            new_update_event_time = self._get_ajusted_next_event_time(now, schedule_next_event_time)
            
            for device, state in self._device_states.iteritems():
                header = self.get_headers(state.agent_id, time=str(now), task_id=state.task_id)
                header['window'] = state.time_remaining
                topic = topics.ACTUATOR_SCHEDULE_ANNOUNCE_RAW.replace('{device}', device)
                self.publish_json(topic, header, {})
                
            if self._update_event is not None:
                #This won't hurt anything if we are canceling ourselves.
                self._update_event.cancel()
            self._update_event_time = new_update_event_time
            self._update_event = EventWithTime(self._update_schedule_state)
            self.schedule(self._update_event_time, self._update_event)  
            
            
        def _get_ajusted_next_event_time(self, now, next_event_time):
            _log.debug("_get_adjusted_next_event_time")
            latest_next = now + datetime.timedelta(seconds=schedule_publish_interval)
            #Round to the next second to fix timer goofyness in agent timers.
            if latest_next.microsecond:
                latest_next = latest_next.replace(microsecond=0) + datetime.timedelta(seconds=1)
            if next_event_time is None or latest_next < next_event_time:
                return latest_next
            return next_event_time
        
            
        def _update_schedule_state(self, unix_time):
            #Find the current slot and update the state
            now = datetime.datetime.fromtimestamp(unix_time)
            
            self.update_device_state_and_schedule(now)
                            

        @matching.match_regex(topics.ACTUATOR_GET() + '/(.+)')
        def handle_get(self, topic, headers, message, match):
            point = match.group(1)
            collection_tokens, point_name = point.rsplit('/', 1)
            requester = headers.get('requesterID')
            if self.check_lock(collection_tokens, requester):
                request_url = '/'.join([url, collection_tokens,
                                        ACTUATOR_COLLECTION, point_name])
                try:
                    r = requests.get(request_url, timeout=connection_timeout)
                    self.process_smap_request_result(r, point, requester)
                except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as ex:
                    error = {'type': ex.__class__.__name__, 'value': str(ex)}
                    self.push_result_topic_pair(ERROR_RESPONSE_PREFIX,
                                                point, headers, error)
            else:
                error = {'type': 'LockError',
                         'value': 'does not have this lock'}
                self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point,
                                            headers, error)

        @matching.match_regex(topics.ACTUATOR_SET() + '/(.+)')
        def handle_set(self, topic, headers, message, match):
            _log.debug('handle_set: {topic},{headers}, {message}'.
                       format(topic=topic, headers=headers, message=message))
            point = match.group(1)
            collection_tokens, point_name = point.rsplit('/', 1)
            requester = headers.get('requesterID')
            headers = self.get_headers(requester)
            if not message:
                error = {'type': 'ValueError', 'value': 'missing argument'}
                _log.debug('ValueError: '+str(error))
                self.push_result_topic_pair(ERROR_RESPONSE_PREFIX,
                                            point, headers, error)
                return
            else:
                try:
                    message = jsonapi.loads(message[0])
                    if isinstance(message, bool):
                        message = int(message)
                except ValueError as ex:
                    # Could be ValueError of JSONDecodeError depending
                    # on if simplesjson was used.  JSONDecodeError
                    # inherits from ValueError
                    _log.debug('ValueError: '+message)
                    error = {'type': 'ValueError', 'value': str(ex)}
                    self.push_result_topic_pair(ERROR_RESPONSE_PREFIX,
                                                point, headers, error)
                    return

            if self.check_lock(collection_tokens, requester):
                request_url = '/'.join([url, collection_tokens,
                                        ACTUATOR_COLLECTION, point_name])
                payload = {'state': str(message)}
                try:
                    r = requests.put(request_url, params=payload, timeout=connection_timeout)
                    self.process_smap_request_result(r, point, requester)
                except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as ex:
                    
                    error = {'type': ex.__class__.__name__, 'value': str(ex)}
                    self.push_result_topic_pair(ERROR_RESPONSE_PREFIX,
                                                point, headers, error)
                    _log.debug('ConnectionError: '+str(error))
            else:
                error = {'type': 'LockError',
                         'value': 'does not have this lock'}
                _log.debug('LockError: '+str(error))
                self.push_result_topic_pair(ERROR_RESPONSE_PREFIX,
                                            point, headers, error)

        def check_lock(self, device, requester):
            _log.debug('check_lock: {device}, {requester}'.format(device=device, 
                                                                  requester=requester))
            device = device.strip('/')
            if device in self._device_states:
                device_state = self._device_states[device]
                return device_state.agent_id == requester
            return False

        @matching.match_exact(topics.ACTUATOR_SCHEDULE_REQUEST())
        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'})
            
            
        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}})
                    
                    
        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)            
            

        def get_headers(self, requester, time=None, task_id=None):
            headers = {}
            if time is not None:
                headers['time'] = time
            else:
                headers = {'time': str(datetime.datetime.utcnow())}
            if requester is not None:
                headers['requesterID'] = requester
            if task_id is not None:
                headers['taskID'] = task_id
            return headers

        def process_smap_request_result(self, request, point, requester):
            _log.debug('Start of process_smap: \n{request}, \n{point}, \n{requester}'.
                       format(request=request,point=point,requester=requester))
            headers = self.get_headers(requester)
            try:
                
                request.raise_for_status()
                results = request.json()
                readings = results['Readings']
                _log.debug('Readings: {readings}'.format(readings=readings))
                reading = readings[0][1]
                self.push_result_topic_pair(VALUE_RESPONSE_PREFIX,
                                            point, headers, reading)
            except requests.exceptions.HTTPError as ex:
                error = {'type': ex.__class__.__name__, 'value': str(request.text)}
                _log.error('process_smap HTTPError: '+str(error))
                self.push_result_topic_pair(ERROR_RESPONSE_PREFIX,
                                            point, headers, error)
            except (ValueError, IndexError, KeyError,
                        requests.exceptions.ConnectionError) as ex:
                error = {'type': ex.__class__.__name__, 'value': str(ex)}
                _log.error('process_smap RequestError: '+str(error))
                self.push_result_topic_pair(ERROR_RESPONSE_PREFIX,
                                            point, headers, error)
                
            _log.debug('End of process_smap: \n{request}, \n{point}, \n{requester}'.
                       format(request=request,point=point,requester=requester))

        def push_result_topic_pair(self, prefix, point, headers, *args):
            topic = normtopic('/'.join([prefix, point]))
            self.publish_json(topic, headers, *args)
示例#6
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
示例#7
0
    def configure(self, config_name, action, contents):
        config = self.default_config.copy()
        config.update(contents)

        _log.debug("Configuring Actuator Agent")

        try:
            driver_vip_identity = str(config["driver_vip_identity"])
            schedule_publish_interval = float(
                config["schedule_publish_interval"])

            heartbeat_interval = float(config["heartbeat_interval"])
            preempt_grace_time = float(config["preempt_grace_time"])
        except ValueError as e:
            _log.error("ERROR PROCESSING CONFIGURATION: {}".format(e))
            #TODO: set a health status for the agent
            return

        self.driver_vip_identity = driver_vip_identity
        self.schedule_publish_interval = schedule_publish_interval

        _log.debug("MasterDriver VIP IDENTITY: {}".format(
            self.driver_vip_identity))
        _log.debug("Schedule publish interval: {}".format(
            self.schedule_publish_interval))

        #Only restart the heartbeat if it changes.
        if (self.heartbeat_interval != heartbeat_interval or action == "NEW"
                or self.heartbeat_greenlet is None):
            if self.heartbeat_greenlet is not None:
                self.heartbeat_greenlet.kill()

            self.heartbeat_interval = heartbeat_interval

            self.heartbeat_greenlet = self.core.periodic(
                self.heartbeat_interval, self._heart_beat)

        _log.debug("Heartbeat interval: {}".format(self.heartbeat_interval))
        _log.debug("Preemption grace period: {}".format(preempt_grace_time))

        if self._schedule_manager is None:
            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:
                state_string = None

        else:
            self._schedule_manager.set_grace_period(preempt_grace_time)

        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