예제 #1
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
예제 #2
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 = []
예제 #3
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.lock_handler=None
         
     self.lock_handler = backup_run
예제 #4
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
예제 #5
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.RTU_VALUE(point='all', **rtu_path), {},
                                ('application/json', jsonapi.dumps(data)))
예제 #6
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
예제 #7
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
예제 #8
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
예제 #9
0
 def start_dr_event(self):
     self.state = 'DR_EVENT'
     self.publish(topics.ACTUATOR_SET(point=volttron_flag, **rtu_path), self.headers, str(3))
     self.publish(topics.ACTUATOR_SET(point=cooling_stpt, **rtu_path), self.headers, str(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.lock_handler = None
         
     self.lock_handler = backup_run
예제 #10
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()
예제 #11
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
예제 #12
0
 def start_restore_event(self, csp, hsp):
     self.state = 'RESTORE'
     _log.debug('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.lock_handler=None
         
     self.lock_handler = backup_run
예제 #13
0
        def on_lock_result(self, topic, headers, message, match):
            '''Respond to lock result events.'''

            print "Topic: {topic}, {headers}, Message: {message}".format(
                topic=topic, headers=headers, message=message)
            if headers['requesterID'] != agent_id:
                #If we didn't request this lock, we don't care about the result
                print "Not me, don't care."
                return

            mess = jsonapi.loads(message[0])
            #If we got a success then set it at a random value
            if mess == 'SUCCESS':
                setting = random.randint(10, 90)
                headers[headers_mod.CONTENT_TYPE] = (
                    headers_mod.CONTENT_TYPE.PLAIN_TEXT)
                headers['requesterID'] = agent_id
                self.publish(topics.ACTUATOR_SET(point=fan_point, **rtu_path),
                             headers, agent_id)
            elif mess == 'RELEASE':
                #Our lock release result was a success
                print "Let go of lock"
예제 #14
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():
                value[0] = not value[0]
                payload = {'state': str(int(value[0]))}
                try:
                    r = requests.put(request_url, params=payload)
                    self.process_smap_request_result(r, publish_topic, None)
                except requests.exceptions.ConnectionError:
                    print "Warning: smap driver not running."

            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):
            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):
            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)
                    self.process_smap_request_result(r, point, requester)
                except (requests.exceptions.ConnectionError) 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):
            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'}
                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
                    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)
                    self.process_smap_request_result(r, point, requester)
                except (requests.exceptions.ConnectionError) 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)

        def check_lock(self, device, 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()

            if request_type == SCHEDULE_ACTION_NEW:
                self.handle_new(headers, message, now)

            elif request_type == SCHEDULE_ACTION_CANCEL:
                self.handle_cancel(headers, now)

            else:
                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')

            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.
                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):
            headers = self.get_headers(requester)
            try:
                request.raise_for_status()
                results = request.json()
                readings = results['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)
                }
                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)}
                self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point,
                                            headers, error)

        def push_result_topic_pair(self, prefix, point, headers, *args):
            topic = normtopic('/'.join([prefix, point]))
            self.publish_json(topic, headers, *args)
예제 #15
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.currently_running_dr_event_handlers = []
         def backup_run():
             self.cancel_event()
             self.lock_handler=None
             
         self.lock_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:
                     _log.debug( "Did not get back expected value for:  " + str(point))
                     
             if not expected_values:
                 self.actuator_handler = None
                 self.lock_handler=None
                 self.error_handler = None
                 self.state = 'IDLE' if not cancel_type == 'OVERRIDE' else 'OVERRIDE'
                 
                 headers = {
                     headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                     'requesterID': agent_id}
                 self.publish(topics.ACTUATOR_LOCK_RELEASE(**rtu_path), headers)
         
         if cancel_type != 'UPDATING':
             self.actuator_handler = result_handler
         else:
             self.actuator_handler = None
             self.lock_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)