示例#1
0
        def start_dr_event(self):
            self.state = 'DR_EVENT'
            self.publish(
                topics.ACTUATOR_SET(point='StandardDamperChangeOverSetPoint',
                                    **rtu_path), self.headers, str(csp_cpp))

            new_fan_speed = self.default_firststage_fanspeed - (
                self.default_firststage_fanspeed * fan_reduction)
            new_fan_speed = max(new_fan_speed, 0)
            self.publish(
                topics.ACTUATOR_SET(point='CoolSupplyFanSpeed1', **rtu_path),
                self.headers, str(new_fan_speed))

            new_fan_speed = self.default_secondstage_fanspeed - (
                self.default_firststage_fanspeed * fan_reduction)
            new_fan_speed = max(new_fan_speed, 0)
            self.publish(
                topics.ACTUATOR_SET(point='CoolSupplyFanSpeed2', **rtu_path),
                self.headers, str(new_fan_speed))

            self.publish(
                topics.ACTUATOR_SET(point='ESMDamperMinPosition', **rtu_path),
                self.headers, str(damper_cpp))

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

            self.lock_handler = backup_run
示例#2
0
        def start_restore_event(self, csp, hsp):
            self.state = 'RESTORE'
            print 'Restore:  Begin restoring normal operations'
            self.publish(
                topics.ACTUATOR_SET(point='StandardDamperChangeOverSetPoint',
                                    **rtu_path), self.headers, str(csp))
            self.publish(
                topics.ACTUATOR_SET(point='StandardDamperMinPosition',
                                    **rtu_path), self.headers,
                str(hsp))  #heating
            self.publish(
                topics.ACTUATOR_SET(point='CoolSupplyFanSpeed1', **rtu_path),
                self.headers, str(self.default_firststage_fanspeed))
            self.publish(
                topics.ACTUATOR_SET(point='CoolSupplyFanSpeed2', **rtu_path),
                self.headers, str(self.default_secondstage_fanspeed))

            self.publish(
                topics.ACTUATOR_SET(point='ESMDamperMinPosition', **rtu_path),
                self.headers, str(self.default_damperstpt))

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

            self.lock_handler = backup_run
示例#3
0
 def modify_temp_set_point(self, csp, hsp):
     self.publish(topics.ACTUATOR_SET(point='StandardDamperChangeOverSetPoint', **rtu_path), self.headers, str(csp))
     self.publish(topics.ACTUATOR_SET(point='StandardDamperMinPosition', **rtu_path), self.headers, str(hsp))
     
     def backup_run():
         self.modify_temp_set_point(csp, hsp)
         self.lock_handler=None
         
     self.lock_handler = backup_run
示例#4
0
 def update_running_event(self):
     self.publish(topics.ACTUATOR_SET(point='StandardDamperChangeOverSetPoint', **rtu_path), self.headers, str(self.default_coolingstpt))
     self.publish(topics.ACTUATOR_SET(point='StandardDamperMinPosition', **rtu_path), self.headers, str(self.default_heatingstpt))
     self.publish(topics.ACTUATOR_SET(point='CoolSupplyFanSpeed1', **rtu_path), self.headers, str(self.default_firststage_fanspeed))
     self.publish(topics.ACTUATOR_SET(point='CoolSupplyFanSpeed2', **rtu_path), self.headers, str(self.default_secondstage_fanspeed))            
     
     self.publish(topics.ACTUATOR_SET(point='ESMDamperMinPosition', **rtu_path), self.headers, str(self.default_damperstpt))
     
     for event in self.currently_running_dr_event_handlers:
         event.cancel()
     self.currently_running_dr_event_handlers = []
        def actuator_set(self, results):
            """Set point on device."""
            for device, point_value_dict in results.devices.items():
                for point, value in point_value_dict.items():
                    point_path = base_actuator_path(unit=device, point=point)
                    try:
                        #                         result = self.vip.rpc.call('platform.actuator', 'set_point',
                        #                                                    agent_id, point_path,
                        #                                                    new_value).get(timeout=4)
                        headers = {
                            'Content-Type': 'text/plain',
                            'requesterID': agent_id,
                        }
                        self.vip.pubsub.publish(peer="pubsub",
                                                topic=topics.ACTUATOR_SET(
                                                    point=point,
                                                    unit=device,
                                                    **campus_building),
                                                headers=headers,
                                                message=str(value))
                        _log.debug("Set point {} to {}".format(
                            point_path, value))
                    except RemoteError as ex:
                        _log.warning("Failed to set {} to {}: {}".format(
                            point_path, value, str(ex)))
                        continue

            for _device in command_devices:
                for point, new_value in results.commands.items():
                    point_path = base_actuator_path(unit=_device, point=point)
                    try:
                        #                         result = self.vip.rpc.call('platform.actuator', 'set_point',
                        #                                                    agent_id, point_path,
                        #                                                    new_value).get(timeout=4)
                        headers = {
                            'Content-Type': 'text/plain',
                            'requesterID': agent_id,
                        }
                        self.vip.pubsub.publish(peer="pubsub",
                                                topic=topics.ACTUATOR_SET(
                                                    point=point,
                                                    unit=_device,
                                                    **campus_building),
                                                headers=headers,
                                                message=str(new_value))
                        _log.debug("Set point {} to {}".format(
                            point_path, new_value))
                    except RemoteError as ex:
                        _log.warning("Failed to set {} to {}: {}".format(
                            point_path, new_value, str(ex)))
                        continue
示例#6
0
        def handle_set(self, peer, sender, bus, topic, headers, message):
            point = topic.replace(topics.ACTUATOR_SET() + '/', '', 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 = 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

            try:
                self.set_point(requester, point, message)
            except StandardError as ex:

                error = {'type': ex.__class__.__name__, 'value': str(ex)}
                self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point,
                                            headers, error)
                _log.debug('Actuator Agent Error: ' + str(error))
示例#7
0
        class Agent(PublishMixin, BaseAgent):
            def setup(self):
                super(Agent, self).setup()
                self.damper = 0

            @matching.match_regex(topics.ACTUATOR_LOCK_ACQUIRE() + '(/.*)')
            def on_lock_result(self, topic, headers, message, match):
                _log.debug(
                    "Topic: {topic}, {headers}, Message: {message}".format(
                        topic=topic, headers=headers, message=message))
                self.publish(topics.ACTUATOR_LOCK_RESULT() + match.group(0),
                             headers, jsonapi.dumps('SUCCESS'))

            @matching.match_regex(topics.ACTUATOR_SET() + '(/.*/([^/]+))')
            def on_new_data(self, topic, headers, message, match):
                _log.debug(
                    "Topic: {topic}, {headers}, Message: {message}".format(
                        topic=topic, headers=headers, message=message))
                if match.group(2) == 'Damper':
                    self.damper = int(message[0])
                self.publish(topics.ACTUATOR_VALUE() + match.group(0), headers,
                             message[0])

            @periodic(5)
            def send_data(self):
                data = {
                    'ReturnAirTemperature': 55,
                    'OutsideAirTemperature': 50,
                    'MixedAirTemperature': 45,
                    'Damper': self.damper
                }
                self.publish_ex(topics.DEVICES_VALUE(point='all', **rtu_path),
                                {}, ('application/json', jsonapi.dumps(data)))
示例#8
0
 def cancel_event(self):
     self.state = 'CLEANUP'
     self.publish(topics.ACTUATOR_SET(point='StandardDamperChangeOverSetPoint', **rtu_path), self.headers, str(self.default_coolingstpt))
     self.publish(topics.ACTUATOR_SET(point='StandardDamperMinPosition', **rtu_path), self.headers, str(self.default_heatingstpt))
     self.publish(topics.ACTUATOR_SET(point='CoolSupplyFanSpeed1', **rtu_path), self.headers, str(self.default_firststage_fanspeed))
     self.publish(topics.ACTUATOR_SET(point='CoolSupplyFanSpeed2', **rtu_path), self.headers, str(self.default_secondstage_fanspeed))            
     
     self.publish(topics.ACTUATOR_SET(point='ESMDamperMinPosition', **rtu_path), self.headers, str(self.default_damperstpt))
     
     for event in self.currently_running_dr_event_handlers:
         event.cancel()
         
     self.currently_running_dr_event_handlers = []
     def backup_run():
         self.cancel_event()
         self.lock_handler=None
         
     self.lock_handler = backup_run
     
     expected_values = {'StandardDamperChangeOverSetPoint': self.default_coolingstpt,
                        'StandardDamperMinPosition': self.default_heatingstpt,
                        'CoolSupplyFanSpeed1': self.default_firststage_fanspeed,
                        'CoolSupplyFanSpeed2': self.default_secondstage_fanspeed,
                        'ESMDamperMinPosition': self.default_damperstpt}
     
     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:
                 _log.debug( "Did not get back expected value for", point)
                 
         if not expected_values:
             self.actuator_handler = None
             self.lock_handler=None
             self.state = 'IDLE'
             
             headers = {
                 headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                 'requesterID': agent_id}
             self.publish(topics.ACTUATOR_LOCK_RELEASE(**rtu_path), headers)
     
     self.actuator_handler = result_handler
示例#9
0
 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))
示例#10
0
 def actuate(self, topic, headers, message, match):
     '''Match the announce for our fake device with our ID
     Then take an action. Note, this command will fail since there is no 
     actual device'''
     headers = {
                 'requesterID': agent_id,
                }
     self.publish_json(topics.ACTUATOR_SET(campus='campus',
                                      building='building',unit='unit',
                                      point='point'),
                              headers, str(0.0))
示例#11
0
 def set_point(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 None
示例#12
0
        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
示例#13
0
        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
示例#14
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)
示例#15
0
 def set_point(self, sock, point_name, value, timeout=None):
     _log.debug('set_point({}, {}, {})'.format(point_name, value, timeout))
     headers = {
             'Content-Type': 'text/plain',
             'requesterID': agent_id,
     }
     with self.value_queue.condition:
         sock.send_message(
                 topics.ACTUATOR_SET(point=point_name, **rtu_path),
                 headers, str(value), flags=zmq.NOBLOCK)
         try:
             return self.value_queue._wait(timeout)
         except multithreading.Timeout:
             return None
示例#16
0
 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))
示例#17
0
    def handle_set(self, peer, sender, bus, topic, headers, message):
        """
        Set the value of a point.
        
        To set a value publish a message to the following topic:

        ``devices/actuators/set/<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.
        
        Errors will be published on 
        
        ``devices/actuators/error/<full device path>/<actuation point>``
        
        with the same header as the request.
        
        """
        if sender == 'pubsub.compat':
            message = compat.unpack_legacy_message(headers, message)

        point = topic.replace(topics.ACTUATOR_SET() + '/', '', 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

        try:
            self.set_point(requester, point, message)
        except RemoteError as ex:
            self._handle_remote_error(ex, point, headers)
        except StandardError as ex:
            self._handle_standard_error(ex, point, headers)
示例#18
0
 def actuate(self, peer, sender, bus,  topic, headers, message):
     print ("response:",topic,headers,message)
     if headers[headers_mod.REQUESTER_ID] != agent_id:
         return
     '''Match the announce for our fake device with our ID
     Then take an action. Note, this command will fail since there is no 
     actual device'''
     headers = {
                 'requesterID': agent_id,
                }
     self.vip.pubsub.publish(
     'pubsub', topics.ACTUATOR_SET(campus='campus',
                                      building='building',unit='unit',
                                      point='point'),
                              headers, str(0.0))
 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)
示例#20
0
 def _command_equip(self, point_name, value, timeout):
     """set value in controller"""
     _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))
     while True:
         point, success = self.value_queue.wait(timeout)
         if point == point_name:
             if success:
                 return
             raise CommandSetError()
示例#21
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)
示例#22
0
        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
示例#23
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)
示例#24
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
示例#25
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
示例#26
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)
示例#27
0
        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)
示例#28
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)