Пример #1
0
        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()
Пример #2
0
 def command_error_handler(self, error_type):
     '''
     Handle actuator error for attempted set of RTU
     actuation point
     '''
     if error_type.lower() =='lockerror':
         headers1 =  {
                                 'type':  'CANCEL_SCHEDULE',
                                'requesterID': self._agent.task_id,
                                'taskID': self._agent.task_id
                     }
         headers2 = {
                                 'type':  'NEW_SCHEDULE',
                                'requesterID': self._agent.task_id,
                                'taskID': self._agent.task_id,
                                'priority': 'LOW_PREEMPT'
                                }
         self._log.debug('Handling Actuator set/get error')
         self._agent.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(), headers1,[["{campus}/{building}/{unit}".format(**self._agent.rtu_path)]])
         self._agent.sleep(15)
         self._agent.publish_json(topics.ACTUATOR_SCHEDULE_REQUEST(), headers2,[["{campus}/{building}/{unit}".format(**self._agent.rtu_path),self._agent.start,self._agent.end]])
     return
Пример #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 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 actuator_request(self, results):
            """Make actuaor request for modification of device set points."""
            _now = dt.now()
            str_now = _now.strftime(DATE_FORMAT)
            _end = _now + td(minutes=1)
            str_end = _end.strftime(DATE_FORMAT)
            for _device in command_devices:
                actuation_device = base_actuator_path(unit=_device, point='')
                schedule_request = [[actuation_device, str_now, str_end]]
                #
                #                 try:
                #                     result = self.vip.rpc.call('platform.actuator',
                #                                                'request_new_schedule',
                #                                                agent_id, _device, 'HIGH',
                #                                                schedule_request).get(timeout=4)
                #                 except RemoteError as ex:
                #                     _log.warning("Failed to schedule device {} (RemoteError): {}".format(_device, str(ex)))
                #                     request_error = True
                #
                #                 if result['result'] == 'FAILURE':
                #                     _log.warn('Failed to schedule device (unavailable) ' + _device)
                #                     request_error = True
                #                 else:
                #                     request_error = False
                #                 _log.debug('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@')
                #                 _log.debug(str(result))
                #                 _log.debug(str_now)
                #                 _log.debug(str_end)

                headers = {
                    'type': 'NEW_SCHEDULE',
                    'requesterID': agent_id,
                    'taskID': actuator_id,
                    'priority': 'HIGH'
                }
                device_path = "{campus}/{building}/".format(**campus_building)

                self.vip.pubsub.publish(
                    peer='pubsub',
                    topic=topics.ACTUATOR_SCHEDULE_REQUEST(),
                    headers=headers,
                    message=[[device_path + _device, str_now, str_end]])
            #

            return results, False
Пример #6
0
 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 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)
Пример #8
0
 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
Пример #9
0
        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()
Пример #10
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)
Пример #11
0
        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)
                ]])
Пример #12
0
    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)
Пример #13
0
    class Agent(PublishMixin, BaseAgent):
        '''Simulate real device.  Publish csv data to message bus.

        Configuration consists of csv file and publish topic
        '''
        def __init__(self, **kwargs):
            '''Initialize data publisher class attributes.'''
            super(Agent, self).__init__(**kwargs)

            self._agent_id = conf.get('publisherid')
            self._src_file_handle = open(path)
            header_line = self._src_file_handle.readline().strip()
            self._headers = header_line.split(',')
            self.end_time = None
            self.start_time = None
            self.task_id = None
            utils.setup_logging()
            self._log = logging.getLogger(__name__)
            self.scheduled_event = None
            logging.basicConfig(
                level=logging.debug,
                format='%(asctime)s   %(levelname)-8s %(message)s',
                datefmt='%m-%d-%y %H:%M:%S')
            self._log.info('DATA PUBLISHER ID is PUBLISHER')

        def setup(self):
            '''This function is called immediately after initialization'''
            super(Agent, self).setup()

        @periodic(pub_interval)
        def publish_data_or_heartbeat(self):
            '''Publish data from file to message bus.'''
            _data = {}
            now = datetime.datetime.now().isoformat(' ')
            if not self._src_file_handle.closed:
                line = self._src_file_handle.readline()
                line = line.strip()
                data = line.split(',')
                if line:
                    # Create 'all' message
                    for i in xrange(0, len(self._headers)):
                        _data[self._headers[i]] = data[i]
                    if custom_topic:
                        # data_dict = jsonapi.dumps(_data)
                        self.publish_json(
                            custom_topic,
                            {HEADER_NAME_CONTENT_TYPE: MIME_PLAIN_TEXT,
                             HEADER_NAME_DATE: now}, _data)
                        return
                    sub_dev = {}
                    device_dict = {}
                    for _k, _v in dev_list.items():
                        for k, val in _data.items():
                            if k.startswith(_k):
                                pub_k = k[len(_k):]
                                device_dict.update({pub_k.split('_')[1]: val})
                                cur_top = (''.join([BASETOPIC, '/',
                                                    device_path,
                                                    _k, '/',
                                                    pub_k.split('_')[1]]))
                                self.publish_json(
                                    cur_top,
                                    {HEADER_NAME_CONTENT_TYPE: MIME_PLAIN_TEXT,
                                     HEADER_NAME_DATE: now}, val)
                    # device_dict = jsonapi.dumps(device_dict)
                        if device_dict:
                            self.publish_json(
                                BASETOPIC + '/' + device_path + _k + '/all',
                                {HEADER_NAME_CONTENT_TYPE: MIME_PLAIN_TEXT,
                                 HEADER_NAME_DATE: now}, device_dict)
                        for sub in dev_list[_k][dev_list[_k].keys()[0]]:
                            for k, val in _data.items():
                                if k.startswith(sub):
                                    pub_k = k[len(sub):]
                                    sub_dev.update({pub_k.split('_')[1]: val})
                                    cur_top = (''.join([BASETOPIC, '/',
                                                        device_path,
                                                        _k, '/', sub, '/',
                                                        pub_k.split('_')[1]]))
                                    self.publish_json(
                                        cur_top,
                                        {HEADER_NAME_CONTENT_TYPE:
                                            MIME_PLAIN_TEXT,
                                         HEADER_NAME_DATE: now}, val)
                                    # device_dict = jsonapi.dumps(device_dict)
                            if sub_dev:
                                topic = (''.join([BASETOPIC, '/', device_path,
                                                  _k, '/', sub, '/all']))
                                self.publish_json(
                                    topic,
                                    {HEADER_NAME_CONTENT_TYPE: MIME_PLAIN_TEXT,
                                     HEADER_NAME_DATE: now}, sub_dev)
                                sub_dev = {}
                        device_dict = {}
                else:
                    self._src_file_handle.close()
            else:
                self.publish_json(
                    'heartbeat/DataPublisher',
                    {
                        'AgentID': self._agent_id,
                        HEADER_NAME_CONTENT_TYPE: MIME_PLAIN_TEXT,
                        HEADER_NAME_DATE: now,
                    },
                    now)

        @matching.match_regex(topics.ACTUATOR_SET() + '/(.+)')
        def handle_set(self, topic, headers, message, match):
            '''Respond to ACTUATOR_SET topic.'''
            self._log.info('set actuator')
            point = match.group(1)
            _, _, _, point_name = point.rsplit('/', 4)
            requester = headers.get('requesterID')
            headers = self.get_headers(requester)
            value = jsonapi.loads(message[0])
            value_path = topic.replace('actuator/set', '')
            self.push_result_topic_pair(point_name, headers, value_path, value)

        @matching.match_exact(topics.ACTUATOR_SCHEDULE_REQUEST())
        def handle_schedule_request(self, topic, headers, message, match):
            '''Handle device schedule request.'''
            self._log.info('request received')
            request_type = headers.get('type')
            now = datetime.datetime.now()

            if request_type == SCHEDULE_ACTION_NEW:
                self.handle_new(headers, message)
            elif request_type == SCHEDULE_ACTION_CANCEL:
                self.handle_cancel(headers, now)
            else:
                self._log.debug('handle-schedule_request, invalid request')
                self.publish_json(topics.ACTUATOR_SCHEDULE_RESULT(), headers,
                                  {'result': SCHEDULE_RESPONSE_FAILURE,
                                   'data': {},
                                   'info': 'INVALID_REQUEST_TYPE'})

        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'
                              })

        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': {}})

        def get_headers(self, requester, time=None, task_id=None):
            '''Construct headers for responses to
            schedule requests and device sets.
            '''
            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 push_result_topic_pair(self, point, headers, value_path, *args):
            '''Send set success response.'''
            self.publish_json(topics.ACTUATOR_VALUE(point=point, **value_path),
                              headers, *args)

        def announce(self, device_path, requester):
            '''Emulate Actuator agent schedule announce.'''
            self._log.info('announce')
            now = datetime.datetime.now()
            header = self.get_headers(requester,
                                      time=str(now), task_id=self.task_id)
            header['window'] = str(self.end_time - now)
            topic = topics.ACTUATOR_SCHEDULE_ANNOUNCE_RAW.replace('{device}',
                                                                  device_path)
            self.publish_json(topic, header, {})
            next_time = now + datetime.timedelta(seconds=60)
            event = sched.Event(self.announce)
            self.scheduled_event = event
            self.schedule(next_time, event)
Пример #14
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
Пример #15
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
Пример #16
0
    class Agent(PublishMixin, BaseAgent):
        '''Simulate real device.  Publish csv data to message bus.
        
        Configuration consists of csv file and device path (campus/building/device)
        '''
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            path = os.path.abspath(settings.source_file)
            print path
            self._src_file_handle = open(path)
            header_line = self._src_file_handle.readline().strip()
            self._headers = header_line.split(',')
            self.end_time = None
            self.start_time = None
            self.task_id = None
            utils.setup_logging()
            self._log = logging.getLogger(__name__)
            logging.basicConfig(level=logging.debug,
                                format='%(asctime)s   %(levelname)-8s %(message)s',
                                datefmt='%m-%d-%y %H:%M:%S')

        def setup(self):
            '''This function is called imediately after initialization'''
            super(Agent, self).setup()
            self._agent_id = settings.publisherid

        @periodic(settings.check_4_new_data_time)
        def publish_data_or_heartbeat(self):
            published_data = {}
            now = datetime.datetime.now().isoformat(' ')
            if not self._src_file_handle.closed:
                line = self._src_file_handle.readline()
                line = line.strip()
                data = line.split(',')
                if (line):
                    # Create 'all' message
                    for i in xrange(0, len(self._headers)):
                        published_data[self._headers[i]] = data[i]
                    all_data = json.dumps(published_data)
                    print all_data
                    # Pushing out the data
                    self.publish(topics.DEVICES_VALUE(point='all', **rtu_path),
                                 {HEADER_NAME_CONTENT_TYPE: MIME_PLAIN_TEXT,
                                  HEADER_NAME_DATE: now}, all_data)
                else:
                    self._src_file_handle.close()
            else:  # file is closed -> publish heartbeat
                self.publish('heartbeat/DataPublisher',
                             {
                                 'AgentID': self._agent_id,
                                 HEADER_NAME_CONTENT_TYPE: MIME_PLAIN_TEXT,
                                 HEADER_NAME_DATE: now,
                             }, now)

        @matching.match_regex(topics.ACTUATOR_SET() + '/(.+)')
        def handle_set(self, topic, headers, message, match):
            print 'set actuator'
            point = match.group(1)
            discard1, discard2, discard3, point_name = point.rsplit('/', 4)
            requester = headers.get('requesterID')
            headers = self.get_headers(requester)
            value = jsonapi.loads(message[0])
            self.push_result_topic_pair(point_name, headers, value)

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

        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'
                              })

        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 push_result_topic_pair(self, point, headers, *args):
            self.publish_json(topics.ACTUATOR_VALUE(point=point, **rtu_path), headers, *args)

        def announce(self):
            print 'announce'
            now = datetime.datetime.now()
            header = self.get_headers(settings.agent_id, time=str(now), task_id=self.task_id)
            header['window'] = str(self.end_time - now)
            topic = topics.ACTUATOR_SCHEDULE_ANNOUNCE_RAW.replace('{device}', settings.device)
            self.publish_json(topic, header, {})
            next_time =  now + datetime.timedelta(seconds=60)