예제 #1
0
    class Agent(PublishMixin, BaseAgent):
        """Class agent"""
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.lock_timer = None
            self.lock_acquired = False
            self.tasklet = None
            self.data_queue = green.WaitQueue(self.timer)
            self.value_queue = green.WaitQueue(self.timer)

        def setup(self):
            """acquire lock fom actuator agent"""
            super(Agent, self).setup()
            headers = {
                    'Content-Type': 'text/plain',
                    'requesterID': agent_id,
            }
            self.lock_timer = self.periodic_timer(1, self.publish, topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)
            
        @matching.match_exact(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path))
        def __on_lock_sent(self, topic, headers, message, match):
            """lock recieved"""
            self.lock_timer.cancel()

        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        def __on_lock_result(self, topic, headers, message, match):
            """lock result"""
            msg = jsonapi.loads(message[0])
            holding_lock = self.lock_acquired
            if headers['requesterID'] == agent_id:
                self.lock_acquired = msg == 'SUCCESS'
            elif msg == 'SUCCESS':
                self.lock_acquired = False
            if self.lock_acquired and not holding_lock:
                self.start()

        @matching.match_exact(topics.RTU_VALUE(point='all', **rtu_path))
        def __on_new_data(self, topic, headers, message, match):
            """watching for new data"""
            data = jsonapi.loads(message[0])
            self.data_queue.notify_all(data)
            
        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **rtu_path))
        def __on_set_result(self, topic, headers, message, match):
            """set value in conroller"""
            self.value_queue.notify_all((match.group(1), True))
    
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **rtu_path))
        def __on_set_error(self, topic, headers, message, match):
            """watch for actuator error"""
            self.value_queue.notify_all((match.group(1), False))

        def __sleep(self, timeout=None):
            """built in sleep in green"""
            #_log.debug('sleep({})'.format(timeout))
            green.sleep(timeout, self.timer)

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

        def __command_equip(self, point_name, value, timeout=None):
            """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))
            try:
                return self.value_queue.wait(timeout)
            except green.Timeout:
                return None
            
        def __time_out(self):
            """if timeout occurs"""
            global fan1_norm
            global fan2_norm
            global csp_norm 
            global min_damper
            if fan1_norm == 0:
                self.__sleep(600)#If controller loses volttron heartbeat will reset
                self.start()     #wait at least this long 
            else:
                try:
                    self.__command_equip('CoolSupplyFanSpeed1', fan1_norm)
                except green.Timeout:
                    self.__sleep(600)
                    self.start()
                try:
                    self.__command_equip('CoolSupplyFanSpeed2', fan2_norm)
                except green.Timeout:
                    self.__sleep(600)
                    self.start()
                try:
                    self.__command_equip('ESMDamperMinPosition', min_damper)
                except green.Timeout:
                    self.__sleep(600)
                    self.start()
                try:
                    self.__command_equip('ReturnAirCO2Stpt', csp_norm)
                except green.Timeout:
                    self.__sleep(600)
                    self.start() 
            
         
        @matching.match_exact(topics.RTU_VALUE(point='Occupied', **rtu_path))
        def __overide(self, topic, headers, message, match):
            """watch for override from controller"""
            data = jsonapi.loads(message[0])
            if not bool(data):
                self.tasklet = greenlet.greenlet(self.__on_override)
                self.tasklet.switch()
                

        def __on_override(self): 
            global fan1_norm
            global fan2_norm
            global csp_norm
            global min_damper
            global override_flag
            
            if fan1_norm != 0 and not override_flag:
                override_flag = True
                _log.debug('Override initiated')
                try:
                    self.__command_equip('CoolSupplyFanSpeed1', fan1_norm)
                except green.Timeout:
                    self.__sleep(43200) #if override wait for 12 hours then resume
                    self.start()        #catalyst will default to original operations with no volttron heatbeat
                try:
                    self.__command_equip('CoolSupplyFanSpeed2', fan2_norm)
                except green.Timeout:
                    self.__sleep(43200)
                    self.start()
                try:
                    self.__command_equip('ESMDamperMinPosition', min_damper )
                except green.Timeout:
                    self.__sleep(43200)
                    self.start()
                try:
                    self.__command_equip('ReturnAirCO2Stpt', csp_norm)
                except green.Timeout:
                    self.__sleep(43200)
                    self.start()
            elif fan1_norm == 0 and not override_flag:
                override_flag = True
                self.__sleep(43200)
                self.start()
                
                    
        def start(self):
            """Starting point for DR application"""
            global override
            override_flag = False
            self.tasklet = greenlet.greenlet(self.__go)
            self.tasklet.switch()
        
        def __go(self):
            """start main DR procedure"""
            #self.__command_equip('CoolSupplyFanSpeed1', 75)
            #self.__command_equip('CoolSupplyFanSpeed2', 90)
            #self.__command_equip('ESMDamperMinPosition', 5)
            global fan1_norm
            global fan2_norm
            global csp_norm
            global min_damper
            try:
                self.__command_equip('ReturnAirCO2Stpt', 74)
            except green.Timeout:
                self.__time_out()
            try:
                voltron_data = self.__get_new_data()
            except green.Timeout:
                self.__time_out()
                # Gracefully handle exception
            min_damper = float(voltron_data["ESMDamperMinPosition"]) 
            fan1_norm = float(voltron_data["CoolSupplyFanSpeed1"])
            fan2_norm = float(voltron_data["CoolSupplyFanSpeed2"])
            csp_norm = float(voltron_data["ReturnAirCO2Stpt"])
            _log.debug("Zone normal cooling temperature setpoint:  " + repr(csp_norm))
            _log.debug("Supply fan cooling speed 1:  " + repr(fan1_norm))
            _log.debug("Supply fan cooling speed 2:  " + repr(fan2_norm))
            _log.debug("Normal minimum damper position:  " + repr(min_damper))
            self.tasklet = greenlet.greenlet(self.get_signal)
            self.tasklet.switch()
                
        def __pre_cpp_timer(self):
            """Schedule to run in get_signal"""
            _log.debug("Pre-cooling for CPP Event")  #pre-cool change cooling set point
            self.tasklet = greenlet.greenlet(self.__pre_csp)
            self.tasklet.switch()
            self.pre_timer = self.periodic_timer(settings.pre_cooling_time, self.__pre_cpp_cooling)
            
        def __pre_cpp_cooling(self):
            """start pre cooling procedure"""
            self.tasklet = greenlet.greenlet(self.__pre_csp)
            self.tasklet.switch()
            
        def __pre_csp(self):
            """set cooling temp. set point"""
            self.__sleep(1)
            try:
                voltron_data = self.__get_new_data()
            except green.Timeout:
                self.__time_out()
            csp_now = float(voltron_data["ReturnAirCO2Stpt"]) 
            if csp_now > settings.csp_pre:
                try:
                    csp = csp_now - cooling_slope
                    self.__command_equip("ReturnAirCO2Stpt", csp)
                except green.Timeout:
                    self.__time_out()
            elif csp_now <= settings.csp_pre:
                try:
                    self.__command_equip("ReturnAirCO2Stpt", settings.csp_pre)
                except green.Timeout:
                    self.__time_out()
                self.pre_timer.cancel()
                
        def __accelerated_pre_cpp_timer(self):
            """if DR signal is received after normal pre"""
            _log.debug("Pre-cooling for CPP Event")  #pre-cool change cooling set point
            self.tasklet = greenlet.greenlet(self.__accelerated_pre_csp)
            self.tasklet.switch()
            self.pre_timer = self.periodic_timer(settings.pre_time, self.__accelerated_cpp_cooling) 
              
        def __accelerated_cpp_cooling(self):
            """start accelerated pre-cooling"""
            self.tasklet = greenlet.greenlet(self.__accelerated_pre_csp)
            self.tasklet.switch()
                
        def __accelerated_pre_csp(self):
            """set cooling temp set point"""
            _log.debug("Accelerated pre-cooling for CPP Event")
            global accel_slope
            self.__sleep(2)
            try:
                voltron_data = self.__get_new_data()
            except green.Timeout:
                self.__time_out()  
            csp_now = float(voltron_data["ReturnAirCO2Stpt"]) 
            csp  = csp_now - accel_slope
            if csp_now > settings.csp_pre:
                try:
                    self.__command_equip("ReturnAirCO2Stpt", csp)
                except green.Timeout:
                    self.__time_out()
            elif csp_now <= settings.csp_pre:
                try:
                    self.__command_equip("ReturnAirCO2Stpt", settings.csp_pre)
                except green.Timeout:
                    self.__time_out()
                self.pre_timer.cancel() 

        def __during_cpp_timer(self):
            """during CPP scheduled in get_signal"""
            self.tasklet = greenlet.greenlet(self.__during_cpp)
            self.tasklet.switch() 
                  
        def __during_cpp(self):
            """start CPP procedure"""
            _log.debug("During CPP Event")# remove when done testing
            self.__sleep(2)
            global fan1_norm
            global fan2_norm
            cpp_damper = settings.cpp_damper
            fan_reduction = settings.fan_reduction
            cpp_csp = settings.cpp_csp
            cpp_fan1 = fan1_norm- fan1_norm * fan_reduction
            cpp_fan2 = fan2_norm- fan2_norm * fan_reduction
            self.__sleep(1)
            try:
                self.__command_equip("CoolSupplyFanSpeed1", cpp_fan1)
            except green.Timeout:
                self.__time_out()
            try:
                self.__command_equip("CoolSupplyFanSpeed2", cpp_fan2)
            except green.Timeout:
                self.__time_out()
            try:
                self.__command_equip("ReturnAirCO2Stpt", cpp_csp)
            except green.Timeout:
                self.__time_out()
            try:
                self.__command_equip('ESMDamperMinPosition', cpp_damper)
            except green.Timeout:
                self.__time_out()
            
        def __after_cpp_timer(self):
            """after CPP scheduled in get_signal"""
            self.tasklet = greenlet.greenlet(self.__restore_fan_damper)
            self.tasklet.switch()
            _log.debug("After CPP Event, returning to normal operations")
            self.tasklet = greenlet.greenlet(self.__restore_cooling_setpoint)
            self.tasklet.switch()
            timer = settings.after_time
            self.after_timer = self.periodic_timer(timer, self.__after_cpp_cooling)
             
        def __after_cpp_cooling(self):
            """Start after CPP procedure"""
            _log.debug("After_CPP_COOLING")
            self.tasklet = greenlet.greenlet(self.__restore_cooling_setpoint)
            self.tasklet.switch()
            
        def __restore_fan_damper(self):
            """restore original fan speeds"""
            global fan1_norm
            global fan2_norm
            global min_damper
            self.__sleep(2) # so screen _log.debugs in correct order remove after testing.
            try:
                self.__command_equip("ESMDamperMinPosition", min_damper)
            except green.Timeout:
                self.__time_out()
            try:
                self.__command_equip("CoolSupplyFanSpeed1", fan1_norm)
            except green.Timeout:
                self.__time_out()
            try:
                self.__command_equip("CoolSupplyFanSpeed2", fan2_norm)
            except green.Timeout:
                self.__time_out()
            
        def __restore_cooling_setpoint(self):
            """restore normal cooling temp setpoint"""
            global csp_norm
            self.__sleep(2) #remove after testing
            try:
                voltron_data = self.__get_new_data()
            except green.Timeout:
                self.__time_out()
            csp_now = float(voltron_data["ReturnAirCO2Stpt"]) 
            if csp_now > csp_norm:
                csp = csp_now - cooling_slope
                try:
                    self.__command_equip("ReturnAirCO2Stpt", csp)
                except green.Timeout:
                    self.__time_out()
            elif csp_now <= csp_norm:
                self.after_timer.cancel()
                try:
                    self.__command_equip("ReturnAirCO2Stpt", csp_norm)
                except green.Timeout:
                    self.__time_out()
                
        def get_signal(self):
            """get and format DR signal and schedule DR proc."""
            #Pull signal from source
            self.__sleep(2) #remove after testing
            global csp_norm
            global cooling_slope
            global accel_slope
            time_now = time.mktime(datetime.datetime.now().timetuple())
            time_pre = time.mktime(datetime.datetime.now().replace(hour = settings.pre_cpp_hour, minute = 23, second=0, microsecond = 0).timetuple())
            time_event = time.mktime(datetime.datetime.now().replace(hour = settings.during_cpp_hour, minute = 25, second = 0, microsecond = 0).timetuple())
            time_after = time.mktime(datetime.datetime.now().replace(hour = settings.after_cpp_hour, minute = 27, second = 0, microsecond = 0).timetuple())
            if (settings.signal and time_now<time_pre):
                _log.debug ("Scheduling1") 
                time_step = settings.pre_cooling_time/3600
                #cooling_slope = (csp_norm-settings.csp_pre)/((((time_event-time_pre)/3600)-0.5)*time_step) 
                cooling_slope = 1  # for testing use a constant
                temp = ((time_event-time_pre)/3600)
                _log.debug ("cooling slope: "+ repr(cooling_slope))
                pre_cpp_time = datetime.datetime.now().replace(hour = settings.pre_cpp_hour, minute = 23, second = 0, microsecond = 0)
                self.schedule(pre_cpp_time, sched.Event(self.__pre_cpp_timer))
                during_cpp_time = datetime.datetime.now().replace(hour = settings.during_cpp_hour, minute=25, second = 0, microsecond = 0)
                self.schedule(during_cpp_time, sched.Event(self.__during_cpp_timer))
                after_cpp_time = datetime.datetime.now().replace(hour = settings.after_cpp_hour, minute = 27, second = 0, microsecond = 0)
                self.schedule(after_cpp_time, sched.Event(self.__after_cpp_timer))
                #self.start_timer.cancel()
            elif(settings.signal and time_now>time_pre and time_now<time_event):
                _log.debug("Scheduling2")
                #self.start_timer.cancel()
                #accel_slope = (csp_norm-settings.csp_pre)/((time_event-time_now)/(3600))
                accel_slope = 2 #for testing use a constant
                during_cpp_time = datetime.datetime.now().replace(hour = settings.during_cpp_hour, minute = 36, second =20, microsecond = 0)
                self.schedule(during_cpp_time, sched.Event(self.__during_cpp_timer))
                after_cpp_time = datetime.datetime.now().replace(hour = settings.after_cpp_hour, minute = 39, second = 10, microsecond = 0)
                self.schedule(after_cpp_time, sched.Event(self.__after_cpp_timer))
                self.__accelerated_pre_cpp_timer() 
            elif(settings.signal and time_now>time_event and time_now<time_after):
                _log.debug("Too late to pre-cool!")
                #self.start_timer.cancel()
                after_cpp_time = datetime.datetime.now().replace(hour=settings.after_cpp_hour, minute = 17, second = 0, microsecond = 0)
                self.schedule(after_cpp_time, sched.Event(self.__after_cpp_timer))
                self.tasklet = greenlet.greenlet(self.__during_cpp)
                self.tasklet.switch()
            else:
                _log.debug("CPP Event Is Over")
                #self.start_timer.cancel()
                self.__sleep(60)
                self.get_signal()
예제 #2
0
    class Agent(PublishMixin, BaseAgent):
        """Class agent"""
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.schedule_state = False
            self.fan1_norm = 0
            self.fan2_norm = 0
            self.csp_norm = 0
            self.accel_slope = 0
            self.cooling_slope = 0
            self.min_damper = 0
            self.override_flag = False
            self.lock_timer = None
            self.lock_acquired = False
            self.timers = []
            self.tasks = []
            self.tasklet = None
            self.data_queue = green.WaitQueue(self.timer)
            self.value_queue = green.WaitQueue(self.timer)
            self.running = False

        def setup(self):
            """acquire lock fom actuator agent"""
            super(Agent, self).setup()

        @matching.match_exact(topics.RTU_VALUE(point='CoolCall1', **rtu_path))
        def dr_signal(self, topic, headers, message, match):
            data = jsonapi.loads(message[0])
            if not self.running and bool(data):
                print("start")
                self.running = True
                time_now = time.mktime(datetime.datetime.now().timetuple())
                self.update_schedule_state(time_now)
                #self.schedule(next_time, self.update_schedule_state)
                if (self.schedule_state):
                    self.start()
                else:
                    _log.debug(
                        "DR signal is False or day is not an occupied day")

        def update_schedule_state(self, unix_time):
            headers = {
                'Content-Type': 'text/plain',
                'requesterID': agent_id,
            }
            now = datetime.datetime.fromtimestamp(unix_time)
            day = now.weekday()
            if Schedule[day]:
                self.schedule_state = True
                #TODO: set this up to handle platform not running.
                #This will hang after a while otherwise.
                self.lock_timer = super(Agent, self).periodic_timer(
                    1, self.publish, topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path),
                    headers)
            else:
                self.schedule_state = False
                self.publish(topics.ACTUATOR_LOCK_RELEASE(**rtu_path), headers)

        @matching.match_exact(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path))
        def _on_lock_sent(self, topic, headers, message, match):
            """lock request received"""
            self.lock_timer.cancel()

        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        def _on_lock_result(self, topic, headers, message, match):
            """lock result"""
            msg = jsonapi.loads(message[0])
            holding_lock = self.lock_acquired
            if headers['requesterID'] == agent_id:
                self.lock_acquired = msg == 'SUCCESS'
            elif msg == 'SUCCESS':
                self.lock_acquired = False
            if self.lock_acquired and not holding_lock:
                self.start()

        @matching.match_exact(topics.RTU_VALUE(point='all', **rtu_path))
        def _on_new_data(self, topic, headers, message, match):
            """watching for new data"""
            data = jsonapi.loads(message[0])
            self.data_queue.notify_all(data)

        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **rtu_path))
        def _on_set_result(self, topic, headers, message, match):
            """set value in conroller"""
            self.value_queue.notify_all((match.group(1), True))

        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **rtu_path))
        def _on_set_error(self, topic, headers, message, match):
            """watch for actuator error"""
            self.value_queue.notify_all((match.group(1), False))

        def _sleep(self, timeout=None):
            """built in sleep in green"""
            _log.debug('sleep({})'.format(timeout))
            green.sleep(timeout, self.timer)

        def _get_new_data(self, timeout=data_timeout):  #timeout=data_timeout
            """wait for new data"""
            _log.debug('get_new_data({})'.format(timeout))
            return self.data_queue.wait(timeout)

        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()

        def _time_out(self):
            """if timeout occurs"""
            if (self.fan1_norm and self.fan2_norm and self.min_damper
                    and self.csp_norm):
                try:
                    self._command_equip('CoolSupplyFanSpeed1', self.fan1_norm)
                    self._command_equip('CoolSupplyFanSpeed2', self.fan2_norm)
                    self._command_equip('ESMDamperMinPosition',
                                        self.min_damper)
                    self._command_equip('StandardDamperMinPosition',
                                        self.csp_norm)
                except green.Timeout:
                    pass
            for timer in self.timers:
                timer.cancel()
            del self.timers[:]
            current = greenlet.getcurrent()
            for task in self.tasks:
                if task is not current:
                    task.parent = current
                    task.throw()
            print 'adding current task to task list'
            self.tasks[:] = [current]
            self._sleep(
                600)  #If controller loses volttron heartbeat will reset
            self.running = False

        @matching.match_exact(
            topics.RTU_VALUE(point='Occupied', **rtu_path)
        )  # for now look for Occuppied, DR Override will be added
        def _override(self, topic, headers, message, match):
            """watch for override from controller"""
            data = jsonapi.loads(message[0])
            if not bool(data):
                self.greenlet(self._on_override)

        def _on_override(self):
            if not self.override_flag:
                self.override_flag = True
                _log.debug("Override initiated")
                for timer in self.timers:
                    timer.cancel()
                del self.timers[:]
                current = greenlet.getcurrent()
                for task in self.tasks:
                    if task is not current:
                        task.parent = current
                        task.throw()
            if self.fan1_norm:
                try:
                    self._command_equip('CoolSupplyFanSpeed1', self.fan1_norm)
                    self._command_equip('CoolSupplyFanSpeed2', self.fan2_norm)
                    self._command_equip('ESMDamperMinPosition',
                                        self.min_damper)
                    self._command_equip('ReturnAirCO2Stpt', self.csp_norm)
                except green.Timeout:
                    self._sleep(
                        43200)  #if override wait for 12 hours then resume
                    self._go(
                    )  #catalyst will default to original operations with no volttron heatbeat
                self._sleep(43200)
                self.override_flag = False
                self.running = False
            elif not self.fan1_norm and not self.override_flag:
                self.override_flag = True
                self._sleep(43200)
                self.override_flag = False
                self.running = False

        def start(self):
            """Starting point for DR application"""
            self.override_flag = False
            self.greenlet(self._go)

        def _go(self):
            """start main DR procedure"""
            self._command_equip('StandardDamperChangeOverSetPoint', 75, 20)
            self._command_equip('CoolSupplyFanSpeed2', 90, 20)
            self._command_equip('CoolSupplyFanSpeed1', 75, 20)
            self._command_equip('StandardDamperMinPosition', 65, 20)
            try:
                self._command_equip('ESMDamperMinPosition', 5, 20)

                voltron_data = self._get_new_data()
            except CommandSetError:
                self._time_out()
            except green.Timeout:
                self._time_out()
            self.min_damper = float(voltron_data["ESMDamperMinPosition"])
            self.fan1_norm = float(voltron_data["CoolSupplyFanSpeed1"])
            self.fan2_norm = float(voltron_data["CoolSupplyFanSpeed2"])
            self.csp_norm = float(voltron_data["ReturnAirCO2Stpt"])
            _log.debug("Zone normal cooling temperature setpoint:  " +
                       repr(self.csp_norm))
            _log.debug("Supply fan cooling speed 1:  " + repr(self.fan1_norm))
            _log.debug("Supply fan cooling speed 2:  " + repr(self.fan2_norm))
            _log.debug("Normal minimum damper position:  " +
                       repr(self.min_damper))
            self.get_signal()

        def _pre_cpp_timer(self):
            """Schedule to run in get_signal"""
            _log.debug("Pre-cooling for CPP Event"
                       )  #pre-cool change cooling set point
            self._pre_csp()
            self.pre_timer = self.periodic_timer(settings.pre_time,
                                                 self._pre_cpp_cooling)

        def _pre_cpp_cooling(self):
            """start pre cooling procedure"""
            self.greenlet(self._pre_csp)

        def _pre_csp(self):
            """set cooling temp set point"""
            try:
                voltron_data = self._get_new_data()
            except green.Timeout:
                self._time_out()
            csp_now = float(voltron_data["ReturnAirCO2Stpt"])
            if csp_now > csp_pre and not csp < csp_pre:
                try:
                    csp = csp_now - self.cooling_slope
                    self._command_equip("ReturnAirCO2Stpt", csp)
                except green.Timeout:
                    self._time_out()
            elif csp_now <= csp_pre and not csp < csp_pre:
                try:
                    self._command_equip("ReturnAirCO2Stpt", settings.csp_pre)
                except green.Timeout:
                    self._time_out()
                self.pre_timer.cancel()

        def _accelerated_pre_cpp_timer(self):
            """if DR signal is received after normal pre"""
            _log.debug("Pre-cooling for CPP Event"
                       )  #pre-cool change cooling set point
            self._accelerated_pre_csp()
            self.pre_timer = self.periodic_timer(settings.pre_time,
                                                 self._accelerated_cpp_cooling)

        def _accelerated_cpp_cooling(self):
            """start accelerated pre-cooling"""
            self.greenlet(self._accelerated_pre_csp)

        def _accelerated_pre_csp(self):
            """set cooling temp set point"""
            _log.debug("Accelerated pre-cooling for CPP Event")
            try:
                voltron_data = self._get_new_data()
            except green.Timeout:
                self._time_out()
            csp_now = float(voltron_data["ReturnAirCO2Stpt"])
            csp = csp_now - self.accel_slope
            if csp_now > csp_pre and not csp < csp_pre:
                try:
                    self._command_equip("ReturnAirCO2Stpt", csp)
                except green.Timeout:
                    self._time_out()
            elif csp_now <= csp_pre or csp < csp_pre:
                try:
                    self._command_equip("ReturnAirCO2Stpt", settings.csp_pre)
                except green.Timeout:
                    self._time_out()
                self.pre_timer.cancel()

        def _during_cpp_timer(self):
            """during CPP scheduled in get_signal"""
            self.greenlet(self._during_cpp)

        def _during_cpp(self):
            """start CPP procedure"""
            # remove when done testing
            _log.debug("During CPP Event")
            damper_cpp = settings.damper_cpp
            fan_reduction = settings.fan_reduction
            csp_cpp = settings.csp_cpp
            cpp_fan1 = self.fan1_norm - self.fan1_norm * fan_reduction
            cpp_fan2 = self.fan2_norm - self.fan2_norm * fan_reduction
            try:
                self._command_equip("CoolSupplyFanSpeed1", cpp_fan1)
                self._command_equip("CoolSupplyFanSpeed2", cpp_fan2)
                self._command_equip("ReturnAirCO2Stpt", csp_cpp)
                self._command_equip('ESMDamperMinPosition', damper_cpp)
            except green.Timeout:
                self._time_out()

        def _after_cpp_timer(self):
            """after CPP scheduled in get_signal"""
            self.greenlet(self._restore_fan_damper)
            _log.debug("After CPP Event, returning to normal operations")
            self.greenlet(self._restore_cooling_setpoint)
            timer = settings.after_time
            self.after_timer = self.periodic_timer(timer,
                                                   self._after_cpp_cooling)

        def _after_cpp_cooling(self):
            """Start after CPP procedure"""
            _log.debug("After_CPP_COOLING")
            self.greenlet(self._restore_cooling_setpoint)

        def _restore_fan_damper(self):
            """restore original fan speeds"""
            try:
                self._command_equip("ESMDamperMinPosition", self.min_damper)
                self._command_equip("CoolSupplyFanSpeed1", self.fan1_norm)
                self._command_equip("CoolSupplyFanSpeed2", self.fan2_norm)
            except green.Timeout:
                self._time_out()

        def _restore_cooling_setpoint(self):
            """restore normal cooling temp setpoint"""
            try:
                voltron_data = self._get_new_data()
            except green.Timeout:
                self._time_out()
            csp_now = float(voltron_data["ReturnAirCO2Stpt"])
            if csp_now > self.csp_norm:
                csp = csp_now - self.cooling_slope
                try:
                    self._command_equip("ReturnAirCO2Stpt", csp)
                except green.Timeout:
                    self._time_out()
            elif csp_now <= self.csp_norm:
                self.after_timer.cancel()
                try:
                    self._command_equip("ReturnAirCO2Stpt", self.csp_norm)
                    self._sleep(60)
                    self.running = False
                except green.Timeout:
                    self._time_out()

        def periodic_timer(self, *args, **kwargs):
            timer = super(Agent, self).periodic_timer(*args, **kwargs)
            self.timers.append(timer)
            return timer

        def schedule(self, time, event):
            super(Agent, self).schedule(time, event)
            self.timers.append(event)

        def greenlet(self, *args, **kwargs):
            task = greenlet.greenlet(*args, **kwargs)
            self.tasks.append(task)
            current = greenlet.getcurrent()
            if current.parent is not None:
                task.parent = current.parent
            task.switch()

        def get_signal(self):
            """get and format DR signal and schedule DR proc."""
            time_now = time.mktime(datetime.datetime.now().timetuple())
            time_pre = time.mktime(
                datetime.datetime.now().replace(hour=settings.pre_cpp_hour,
                                                minute=0,
                                                second=0,
                                                microsecond=0).timetuple())
            time_event = time.mktime(datetime.datetime.now().replace(
                hour=settings.during_cpp_hour,
                minute=12,
                second=0,
                microsecond=0).timetuple())
            time_after = time.mktime(datetime.datetime.now().replace(
                hour=settings.after_cpp_hour,
                minute=14,
                second=0,
                microsecond=0).timetuple())

            if (settings.signal and time_now < time_pre):
                _log.debug("Scheduling1")
                time_step = settings.pre_time / 3600
                #self.cooling_slope = (self.csp_norm - settings.csp_pre) / ((((time_event - time_pre) / 3600) - 0.5) * time_step)
                self.cooling_slope = 1  # for testing use a constant
                temp = ((time_event - time_pre) / 3600)
                _log.debug("cooling slope: " + repr(self.cooling_slope))
                self.schedule(time_pre, sched.Event(self._pre_cpp_timer))
                self.schedule(time_event, sched.Event(self._during_cpp_timer))
                after_cpp_time = datetime.datetime.now().replace(
                    hour=settings.after_cpp_hour,
                    minute=59,
                    second=0,
                    microsecond=0)
                self.schedule(time_after, sched.Event(self._after_cpp_timer))
                #self.start_timer.cancel()
            elif (settings.signal and time_now > time_pre
                  and time_now < time_event):
                _log.debug("Scheduling2")
                #self.start_timer.cancel()
                #self.accel_slope = (self.csp_norm - settings.csp_pre) / ((time_event - time_now) / (3600))
                self.accel_slope = 2  #for testing use a constant
                #self.cooling_slope = (self.csp_norm - settings.csp_pre) / ((((time_event - time_pre) / 3600) - 0.5) * time_step)
                self.cooling_slope = 1  # for testing use a constant
                self.schedule(time_event, sched.Event(self._during_cpp_timer))
                self.schedule(time_after, sched.Event(self._after_cpp_timer))
                self._accelerated_pre_cpp_timer()
            elif (settings.signal and time_now > time_event
                  and time_now < time_after):
                _log.debug("Too late to pre-cool!")
                #self.start_timer.cancel()
                self.schedule(time_after, sched.Event(self._after_cpp_timer))
                self._during_cpp()
            else:
                _log.debug("CPP Event Is Over")
                #self.start_timer.cancel()
                self._sleep(60)
                self.get_signal()
예제 #3
0
    class Agent(PublishMixin, BaseAgent):
        """Class agent"""

        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            
            self.normal_firststage_fanspeed = config.get('normal_firststage_fanspeed', 75.0)
            self.normal_secondstage_fanspeed = config.get('normal_secondstage_fanspeed', 90.0)
            self.normal_damper_stpt = config.get('normal_damper_stpt', 5.0)
            self.normal_coolingstpt = config.get('normal_coolingstpt', 74.0)
            self.normal_heatingstpt = config.get('normal_heatingstpt', 67.0)
            self.smap_path = config.get('smap_path')
            self.default_cooling_stage_differential  = 0.5
            self.current_spacetemp = 0.0
            
            self.state = 'STARTUP'
            self.e_start_msg = None
            self.lock_handler = None
            self.error_handler = None
            self.actuator_handler = None
            self.pre_cool_idle = None
            self.e_start = None
            self.e_end = None
            self.pre_stored_spacetemp =None
            
            self.all_scheduled_events = {}
            self.currently_running_dr_event_handlers = []
            self.headers = {headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON, 'requesterID': agent_id}
            
        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id}) 
        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        def _on_lock_result(self, topic, headers, message, match):
            """lock result"""
            msg = jsonapi.loads(message[0])
            _log.debug('Lock Result:  ' + str(headers.get('requesterID', '')) + '   ' + str(msg))
            if msg == 'SUCCESS' and self.lock_handler is not None:
                self.lock_handler()
            if msg == 'FAILURE' and self.error_handler is not None:
                self.error_handler()
                   
        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id}) 
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **rtu_path))
        def _on_error_result(self, topic, headers, message, match):
            """lock result"""
            point = match.group(1)
            msg = jsonapi.loads(message[0])
            point = match.group(1)
            _log.debug('Lock Error Results: '+str(point) + '  '+ str(msg))  
            if self.error_handler is not None:
                _log.debug('Running lock error handler')
                self.error_handler()
                
        @matching.match_headers({headers_mod.REQUESTER_ID: agent_id})                 
        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **rtu_path))
        def _on_actuator_result(self, topic, headers, message, match):
            """lock result"""
            msg = jsonapi.loads(message[0])
            point = match.group(1)
            if point != 'PlatformHeartBeat':
                _log.debug('Actuator Results:  ' + str(point) +'  ' + str(msg))
            if self.actuator_handler is not None:
                _log.debug('Running Actuator Handler')
                self.actuator_handler(point, jsonapi.loads(message[0]))

        @matching.match_exact(topics.RTU_VALUE(point='all', **rtu_path))
        def _on_new_data(self, topic, headers, message, match):
            """watching for new data"""
            data = jsonapi.loads(message[0])
            self.current_spacetemp = float(data[space_temp])
            dr_override = bool(int(data[override_command]))
            occupied = bool(int(data[occupied_status]))
            
            if dr_override and self.state not in ('IDLE', 'CLEANUP', 'STARTUP'):
                _log.debug('User Override Initiated')
                self.cancel_event(cancel_type='OVERRIDE')
            
            if not occupied and self.state in ('DR_EVENT', 'RESTORE'):
                self.cancel_event()
 
            if self.state == 'STARTUP':
                _log.debug('Finished Startup')
                self.state = 'IDLE'
                
        @matching.match_exact(topics.OPENADR_EVENT())
        def _on_dr_event(self, topic, headers, message, match):
            if self.state == 'STARTUP':
                _log.debug('DR event ignored because of startup.')
                return
            """handle openADR events"""
            msg = jsonapi.loads(message[0])
            _log.debug('EVENT Received:  ' + str(msg))
            e_id = msg['id']
            e_status = msg['status']
            e_start = msg['start_at']
            #e_start = datetime.datetime.strptime(e_start, datefmt)
            today = datetime.datetime.now().date()
            e_end = msg['end_at']
            e_end = parser.parse(e_end, fuzzy=True)
            e_start = parser.parse(e_start, fuzzy=True)
            #e_end = datetime.datetime.strptime(e_end, datefmt)
            current_datetime = datetime.datetime.now()
            
            #For UTC offset
            #offset= datetime.datetime.utcnow() - datetime.datetime.now()
            #e_end = e_end - offset
            #e_start = e_start - offset
            
            if current_datetime > e_end:
                _log.debug('Too Late Event is Over')
                return
            
            if e_status == 'cancelled':
                if e_start in self.all_scheduled_events:
                    _log.debug('Event Cancelled')
                    self.all_scheduled_events[e_start].cancel()
                    del self.all_scheduled_events[e_start]
                    
                if e_start.date() == today and (self.state == 'PRECOOL' or self.state == 'DR_EVENT'):
                    self.cancel_event()
                return

            if today > e_start.date():
                if e_start in self.all_scheduled_events:
                    self.all_scheduled_events[e_start].cancel()
                    del self.all_scheduled_events[e_start]
                return
            
            for item in self.all_scheduled_events.keys():
                if e_start.date() == item.date():
                    if e_start.time() != item.time():
                        _log.debug( 'Updating Event')
                        self.all_scheduled_events[item].cancel()
                        del self.all_scheduled_events[item]
                        if e_start.date() == today and (self.state == 'PRECOOL' or self.state == 'DR_EVENT'):
                            self.cancel_event(cancel_type='UPDATING')
                        break
                    elif e_start.time() == item.time():
                        _log.debug("same event")
                        return
            
            #Don't schedule an event if we are currently in OVERRIDE state.    
            if e_start.date() == today and (self.state == 'OVERRIDE'):
                return
            self.e_start = e_start
            self.e_end = e_end
            event_start = e_start - datetime.timedelta(hours = max_precool_hours)  
    
            event = sched.Event(self.pre_cool_get_lock, args=[self.e_start, self.e_end])
            self.schedule(event_start, event) 
            self.all_scheduled_events[e_start] = event                
                
        def pre_cool_get_lock(self, e_start, e_end):
            if self.state == 'OVERRIDE':
                _log.debug("Override today")
                return
            
            if self.pre_cool_idle == False:
                return
            
            now = datetime.datetime.now()
            day=now.weekday()

            if not schedule[day]:
                _log.debug("Unoccupied today")
                return
            
            if self.state == 'PRECOOL' and self.pre_cool_idle == True:
                for event in self.currently_running_dr_event_handlers:
                    event.cancel()
                    self.all_scheduled_events = {}
                    
            self.state = 'PRECOOL'
            e_start_unix = time.mktime(e_start.timetuple())
            e_end_unix = time.mktime(e_end.timetuple())
    
            if self.pre_cool_idle == True:
                event_start = now + datetime.timedelta(minutes=15)   
                event = sched.Event(self.pre_cool_get_lock, args=[self.e_start, self.e_end])
                self.schedule(event_start, event) 
                self.all_scheduled_events[e_start] = event 
                self.pre_cool_idle = True
                self.schedule_builder(e_start_unix, e_end_unix,
                                      current_spacetemp=self.current_spacetemp,
                                      pre_csp=csp_pre,
                                      building_thermal_constant=building_thermal_constant,
                                      normal_coolingstpt=self.normal_coolingstpt,
                                      timestep_length=timestep_length,
                                      dr_csp=csp_cpp)
            else:
                event_start = now + datetime.timedelta(minutes=15)   
                event = sched.Event(self.pre_cool_get_lock, args=[self.e_start, self.e_end])
                self.schedule(event_start, event) 
                self.all_scheduled_events[e_start] = event 
                self.pre_cool_idle = True
                def run_schedule_builder():
                    self.schedule_builder(e_start_unix, e_end_unix,
                                          current_spacetemp=self.current_spacetemp,
                                          pre_csp=csp_pre,
                                          building_thermal_constant=building_thermal_constant,
                                          normal_coolingstpt=self.normal_coolingstpt,
                                          timestep_length=timestep_length,
                                          dr_csp=csp_cpp)
                    self.lock_handler=None
                
                self.lock_handler = run_schedule_builder
                
                headers = {
                    headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                    'requesterID': agent_id}
                self.publish(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)
                
                def retry_lock():
                    def retry_lock_event():
                        headers = {
                                   headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                                   'requesterID': agent_id}
                        self.publish(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)
                        
                    retry_time = datetime.datetime.now() + datetime.timedelta(seconds=5)
                    if retry_time > e_end:
                        self.state = 'IDLE'
                        self.error_handler = None
                        return
                    
                    event = sched.Event(retry_lock_event)
                    self.schedule(retry_time, event) 
                
                self.error_handler = retry_lock
            
        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
            
        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
            
        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
            
        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) 
          
        def schedule_builder(self,start_time, end_time, 
                             current_spacetemp,
                             pre_csp,
                             building_thermal_constant,
                             normal_coolingstpt,
                             timestep_length,
                             dr_csp):
            """schedule all events for a DR event."""
            current_time = time.time()
            if current_time > end_time:
                return

            _log.debug('Scheduling all DR actions')  
            pre_hsp = pre_csp - 5.0

            ideal_cooling_window = int(((current_spacetemp - pre_csp)/building_thermal_constant) *3600)  
            ideal_precool_start_time = start_time - ideal_cooling_window
            
            max_cooling_window = start_time - current_time
            
            cooling_window = ideal_cooling_window if ideal_cooling_window < max_cooling_window else max_cooling_window
            
            precool_start_time = start_time - cooling_window
            pre_cool_step = 0
            if (max_cooling_window > 0):
                _log.debug('Schedule Pre Cooling')
                num_cooling_timesteps = int(math.ceil(float(cooling_window) / float(timestep_length)))         
                cooling_step_delta = (normal_coolingstpt - pre_csp) / num_cooling_timesteps
                
                if num_cooling_timesteps <= 0:
                    num_cooling_timesteps=1
                    
                for step_index in range (1, num_cooling_timesteps):
                    if step_index == 1:
                        pre_cool_step = 2*timestep_length
                    else:
                        pre_cool_step += timestep_length
                        
                    event_time = start_time - pre_cool_step
                    csp = pre_csp + ((step_index-1) * cooling_step_delta)
                    
                    _log.debug('Precool step:  '+ str(datetime.datetime.fromtimestamp(event_time)) + '   CSP:  ' + str(csp))
                    event = sched.Event(self.modify_temp_set_point, args = [csp, pre_hsp])
                    self.schedule(event_time, event)
                    self.currently_running_dr_event_handlers.append(event)
            
            else:
                _log.debug('Too late to pre-cool!')
            
            restore_window = int(((dr_csp - normal_coolingstpt)/building_thermal_constant) *3600)  
            restore_start_time = end_time
            num_restore_timesteps = int(math.ceil(float(restore_window) / float(timestep_length)))         
            restore_step_delta = (dr_csp - normal_coolingstpt) / num_restore_timesteps
                
            _log.debug('Schedule DR Event: ' + str(datetime.datetime.fromtimestamp(start_time)) +'   CSP:  ' + str(dr_csp))
            event = sched.Event(self.start_dr_event)
            self.schedule(start_time, event)
            self.currently_running_dr_event_handlers.append(event)
            
            _log.debug('Schedule Restore Event:  '+ str(datetime.datetime.fromtimestamp(end_time)) + '   CSP:  ' + str(dr_csp-restore_step_delta))
            event = sched.Event(self.start_restore_event, args = [dr_csp-restore_step_delta, self.normal_heatingstpt])
            self.schedule(end_time, event)
            self.currently_running_dr_event_handlers.append(event)
                
            for step_index in range (1, num_restore_timesteps):
                event_time = end_time + (step_index * timestep_length)
                csp = dr_csp - ((step_index + 1) * restore_step_delta)
                
                _log.debug('Restore step: ' + str(datetime.datetime.fromtimestamp(event_time)) +'   CSP:  ' + str(csp))
                event = sched.Event(self.modify_temp_set_point, args = [csp, self.normal_heatingstpt])
                self.schedule(event_time, event)
                self.currently_running_dr_event_handlers.append(event)
            
            event_time = end_time + (num_restore_timesteps * timestep_length)
            _log.debug('Schedule Cleanup Event:  ' + str(datetime.datetime.fromtimestamp(event_time)))
            event = sched.Event(self.cancel_event)
            self.schedule(event_time,event)
            self.currently_running_dr_event_handlers.append(event)
예제 #4
0
    class Agent(PublishMixin, BaseAgent):
        '''Agent to control cool fan speed with outside air temperature.

        This agent listens for outdoor temperature readings then changes
        the cool fan speed.  It demonstrates pub/sub interaction with
        the RTU Controller.

        Requirements for running this agent (or any agent wishing to
        interact with the RTU:

          * Edit the driver.ini file to reflect the sMAP key, UUID, and
            other settings for your installation.
          * Activate the project Python from the project dir: .
            bin/activate.
          * Launch the sMAP driver by starting (from the project
            directory): twistd -n smap your_driver.ini.
          * Launch the ActuatorAgent just as you would launch any other
            agent.

        With these requirements met:

          * Subscribe to the outside air temperature topic.
          * If the new reading is higher than the old reading then
            * Request the actuator lock for the rtu
          * If it receives a lock request success it randomly sets the
            cool supply fan to a new reading.
          * If it does not get the lock, it will try again the next time
            the temperature rises.
          * If the set result is a success, it releases the lock.
        '''
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.prev_temp = 0

        def change_coolspeed(self):
            '''Setup our request'''
            headers = {
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.PLAIN_TEXT,
                'requesterID': agent_id
            }
            self.publish(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)

        @matching.match_exact(
            topics.RTU_VALUE(point='OutsideAirTemperature', **rtu_path))
        def on_outside_temp(self, topic, headers, message, match):
            '''Respond to the outside air temperature events.'''

            print "Topic: {topic}, {headers}, Message: {message}".format(
                topic=topic, headers=headers, message=message)
            #Content type is json, load it for use
            cur_temp = jsonapi.loads(message[0])
            #If temp has risen, attempt to set cool supply fan
            if cur_temp > self.prev_temp:
                self.change_coolspeed()
            self.prev_temp = cur_temp

        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        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"

        @matching.match_exact(
            topics.ACTUATOR_VALUE(point=fan_point, **rtu_path))
        def on_set_result(self, topic, headers, message, match):
            '''Result received, release the lock'''
            print "Topic: {topic}, {headers}, Message: {message}".format(
                topic=topic, headers=headers, message=message)
            self.publish(topics.ACTUATOR_LOCK_RELEASE(**rtu_path), headers,
                         agent_id)
예제 #5
0
 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'))
예제 #6
0
    class Agent(PublishMixin, BaseAgent):
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.lock_timer = None
            self.lock_acquired = False
            self.tasklet = None
            self.data_queue = green.WaitQueue(self.timer)
            self.value_queue = green.WaitQueue(self.timer)

        def setup(self):
            super(Agent, self).setup()
            headers = {
                    'Content-Type': 'text/plain',
                    'requesterID': agent_id,
            }
            self.lock_timer = self.periodic_timer(1, self.publish,
                    topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)

        def start(self, algo=None):
            if algo is None:
                algo = afdd
            self.tasklet = greenlet.greenlet(algo)
            self.tasklet.switch(self)

        @matching.match_exact(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path))
        def on_lock_sent(self, topic, headers, message, match):
            self.lock_timer.cancel()

        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        def on_lock_result(self, topic, headers, message, match):
            msg = jsonapi.loads(message[0])
            holding_lock = self.lock_acquired
            if headers['requesterID'] == agent_id:
                self.lock_acquired = msg == 'SUCCESS'
            elif msg == 'SUCCESS':
                self.lock_acquired = False
            if self.lock_acquired and not holding_lock:
                self.start()

        @matching.match_exact(topics.RTU_VALUE(point='all', **rtu_path))
        def on_new_data(self, topic, headers, message, match):
            data = jsonapi.loads(message[0])
            self.data_queue.notify_all(data)
    
        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **rtu_path))
        def on_set_result(self, topic, headers, message, match):
            self.value_queue.notify_all((match.group(1), True))
    
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **rtu_path))
        def on_set_error(self, topic, headers, message, match):
            self.value_queue.notify_all((match.group(1), False))

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

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

        def 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
예제 #7
0
    class Agent(PublishMixin, BaseAgent):
        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.lock_acquired = False
            self.thread = None
            self.data_queue = multithreading.WaitQueue()
            self.value_queue = multithreading.WaitQueue()

        def setup(self):
            super(Agent, self).setup()
            headers = {
                'Content-Type': 'text/plain',
                'requesterID': agent_id,
            }
            self.publish(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)

        def start(self, algo=None):
            if algo is None:
                algo = afdd

            def run():
                sock = messaging.Socket(zmq.PUSH)
                sock.connect(publish_address)
                with contextlib.closing(sock):
                    algo(self, sock)

            self.thread = threading.Thread(target=run)
            self.thread.daemon = True
            self.thread.start()

        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        def on_lock_result(self, topic, headers, message, match):
            msg = jsonapi.loads(message[0])
            holding_lock = self.lock_acquired
            if headers['requesterID'] == agent_id:
                self.lock_acquired = msg == 'SUCCESS'
            elif msg == 'SUCCESS':
                self.lock_acquired = False
            if self.lock_acquired and not holding_lock:
                self.start()

        @matching.match_exact(topics.RTU_VALUE(point='all', **rtu_path))
        def on_new_data(self, topic, headers, message, match):
            data = jsonapi.loads(message[0])
            self.data_queue.notify_all(data)

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

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

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

        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
예제 #8
0
    class Agent(PublishMixin, BaseAgent):
        """Class agent"""

        def __init__(self, **kwargs):
            super(Agent, self).__init__(**kwargs)
            self.default_firststage_fanspeed = 0.0
            self.default_secondstage_fanspeed = 0.0
            self.default_damperstpt = 0.0
            self.default_coolingstpt = 0.0
            self.default_heatingstpt = 65.0
            
            self.current_spacetemp = 72.0
            
            self.state = 'STARTUP'
            self.e_start_msg = None
            self.lock_handler = None
            self.error_handler = None
            self.actuator_handler = None
            
            self.all_scheduled_events = {}
            self.currently_running_dr_event_handlers = []
            self.headers = {headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON, 'requesterID': agent_id}

        @matching.match_exact(topics.ACTUATOR_LOCK_RESULT(**rtu_path))
        def _on_lock_result(self, topic, headers, message, match):
            """lock result"""
            msg = jsonapi.loads(message[0])
            if headers['requesterID'] == agent_id:
                if msg == 'SUCCESS' and self.lock_handler is not None:
                    self.lock_handler()
                if msg == 'FAILURE' and self.error_handler is not None:
                    self.error_handler(msg)
                    
        @matching.match_glob(topics.ACTUATOR_ERROR(point='*', **rtu_path))
        def _on_error_result(self, topic, headers, message, match):
            """lock result"""
            if headers.get('requesterID', '') == agent_id:
                if self.error_handler is not None:
                    self.error_handler(match, jsonapi.loads(message[0]))
                    
        @matching.match_glob(topics.ACTUATOR_VALUE(point='*', **rtu_path))
        def _on_actuator_result(self, topic, headers, message, match):
            """lock result"""
            msg = jsonapi.loads(message[0])
            print 'Actuator Results:', match, msg                    
            if headers['requesterID'] == agent_id:
                if self.actuator_handler is not None:
                    self.actuator_handler(match, jsonapi.loads(message[0]))

        @matching.match_exact(topics.RTU_VALUE(point='all', **rtu_path))
        def _on_new_data(self, topic, headers, message, match):
            """watching for new data"""
            data = jsonapi.loads(message[0])
#             self.current_spacetemp = float(data["ZoneTemp"])
            self.current_spacetemp = 76
            droveride = bool(int(data["CoolCall2"]))
            occupied = bool(int(data["Occupied"]))
            
            if droveride and self.state not in ('IDLE', 'CLEANUP', 'STARTUP'):
                print 'User Override Initiated'
                self.cancel_event()
            
            if not occupied and self.state in ('DR_EVENT', 'RESTORE'):
                self.cancel_event()
                
            if self.state == 'IDLE' or self.state=='STARTUP':
                #self.default_coolingstpt = float(data["CoolingStPt"])
                #self.default_heatingstpt = float(data["HeatingStPt"])
                self.default_coolingstpt = 75.0
                self.default_heatingstpt = 65.0
                self.default_firststage_fanspeed = float(data["CoolSupplyFanSpeed1"])
                self.default_secondstage_fanspeed = float(data["CoolSupplyFanSpeed2"])
                self.default_damperstpt = float(data["ESMDamperMinPosition"])
                
            if self.state == 'STARTUP':
                self.state = 'IDLE'
                
        @matching.match_exact(topics.OPENADR_EVENT())
        def _on_dr_event(self, topic, headers, message, match):
            if self.state == 'STARTUP':
                print "DR event ignored because of startup."
                return
            """handle openADR events"""
            msg = jsonapi.loads(message[0])
            print('EVENT Received')
            print(msg)
            e_id = msg['id']
            e_status = msg['status']
            e_start = msg['start']
            e_start = datetime.datetime.strptime(e_start, datefmt)
            today = datetime.datetime.now().date()
            #e_start_day = e_start.date()
            #e_end = e_start.replace(hour=cpp_end_hour, minute =0, second = 0)
            current_datetime = datetime.datetime.now()
            e_end = e_start  + datetime.timedelta(minutes=2)
            
            if current_datetime > e_end:
                print 'Too Late Event is Over'
                return
            
            
            if e_status == 'cancelled':
                if e_start in self.all_scheduled_events:
                    print 'Event Cancelled'
                    self.all_scheduled_events[e_start].cancel()
                    del self.all_scheduled_events[e_start]
                    
                if e_start.date() == today and (self.state == 'PRECOOL' or self.state == 'DR_EVENT'):
                    self.cancel_event()
                return
                    
            #TODO: change this to UTC later
            #utc_now = datetime.datetime.utcnow()

            if today > e_start.date():
                if e_start in self.all_scheduled_events:
                    self.all_scheduled_events[e_start].cancel()
                    del self.all_scheduled_events[e_start]
                    
                return
            
            for item in self.all_scheduled_events.keys():
                if e_start.date() == item.date():
                    if e_start.time() != item.time():
                        print "Updating Event"
                        self.all_scheduled_events[item].cancel()
                        del self.all_scheduled_events[item]
                        if e_start.date() == today and (self.state == 'PRECOOL' or self.state == 'DR_EVENT'):
                            self.update_running_event()
                            self.state = 'IDLE'
                        break
                    elif e_start.time() == item.time():
                        print "same event"
                        return
            #if e_id in self.all_scheduled_dr_events and update is None:
#                 if e_id == self.currently_running_msg:
#                 return
                #return
                
            #Minutes used for testing   
            #event_start = e_start - datetime.timedelta(hours = max_precool_hours)  
            event_start = e_start - datetime.timedelta(minutes = max_precool_hours)
            
            event = sched.Event(self.pre_cool_get_lock, args=[e_start, e_end])
            self.schedule(event_start, event) 
            self.all_scheduled_events[e_start] = event                
                
        def pre_cool_get_lock(self, e_start,e_end):
            
            now = datetime.datetime.now()
            day=now.weekday()
          
            if not Schedule[day]:
                print"Unoccupied today"
                return
                
            self.state = 'PRECOOL'
            
            #e_end = e_start.replace(hour=cpp_end_hour, minute =0, second = 0)
            #e_end = e_start + datetime.timedelta(minutes=2)
            e_start_unix = time.mktime(e_start.timetuple())
            e_end_unix = time.mktime(e_end.timetuple())
           
            def run_schedule_builder():
                #current_time = time.mktime(current_time.timetuple())
                self.schedule_builder(e_start_unix, e_end_unix,
                                      current_spacetemp=77.0,
                                      pre_csp=csp_pre,
                                      building_thermal_constant=building_thermal_constant,
                                      normal_coolingstpt=76.0,
                                      timestep_length=timestep_length,
                                      dr_csp=csp_cpp)
                self.lock_handler=None
            
            self.lock_handler = run_schedule_builder
            
            headers = {
                headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.JSON,
                'requesterID': agent_id}
            self.publish(topics.ACTUATOR_LOCK_ACQUIRE(**rtu_path), headers)
            
        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
            
        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
            
        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
            
        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 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
          
        def schedule_builder(self,start_time, end_time, 
                             current_spacetemp,
                             pre_csp,
                             building_thermal_constant,
                             normal_coolingstpt,
                             timestep_length,
                             dr_csp):
            """schedule all events for a DR event."""
           

            print 'Scheduling all DR actions'   
            pre_hsp = pre_csp - 5.0
            current_time = time.time()
            ideal_cooling_window = int(((current_spacetemp - pre_csp)/building_thermal_constant) *3600)  
            ideal_precool_start_time = start_time - ideal_cooling_window
            
            max_cooling_window = start_time - current_time
            
            cooling_window = ideal_cooling_window if ideal_cooling_window < max_cooling_window else max_cooling_window
            
            precool_start_time = start_time - cooling_window
     
            if (max_cooling_window > 0):
                print "Schedule Pre Cooling" 
                num_cooling_timesteps = int(math.ceil(float(cooling_window) / float(timestep_length)))         
                cooling_step_delta = (normal_coolingstpt - pre_csp) / num_cooling_timesteps
                
                for step_index in range (1, num_cooling_timesteps+1):
                    event_time = start_time - (step_index * timestep_length)
                    csp = pre_csp + ((step_index-1)*cooling_step_delta)
                    
                    print 'Precool step:', datetime.datetime.fromtimestamp(event_time), csp
                    event = sched.Event(self.modify_temp_set_point, args = [csp, pre_hsp])
                    self.schedule(event_time, event)
                    self.currently_running_dr_event_handlers.append(event)
            
            else:
                print "Too late to pre-cool!"
            
            restore_window = int(((dr_csp - normal_coolingstpt)/building_thermal_constant) *3600)  
            restore_start_time = end_time
            num_restore_timesteps = int(math.ceil(float(restore_window) / float(timestep_length)))         
            restore_step_delta = (dr_csp - normal_coolingstpt) / num_restore_timesteps
                
            print 'Schedule DR Event:', datetime.datetime.fromtimestamp(start_time), dr_csp
            event = sched.Event(self.start_dr_event)
            self.schedule(start_time, event)
            self.currently_running_dr_event_handlers.append(event)
            
            print 'Schedule Restore Event:', datetime.datetime.fromtimestamp(end_time), dr_csp-restore_step_delta
            event = sched.Event(self.start_restore_event, args = [dr_csp-restore_step_delta, self.default_heatingstpt])
            self.schedule(end_time, event)
            self.currently_running_dr_event_handlers.append(event)
                
            for step_index in range (1, num_restore_timesteps):
                event_time = end_time + (step_index * timestep_length)
                csp = dr_csp - ((step_index + 1) * restore_step_delta)
                
                print 'Restore step:', datetime.datetime.fromtimestamp(event_time), csp
                event = sched.Event(self.modify_temp_set_point, args = [csp, self.default_heatingstpt])
                self.schedule(event_time, event)
                self.currently_running_dr_event_handlers.append(event)
            
            event_time = end_time + (num_restore_timesteps * timestep_length)
            print 'Schedule Cleanup Event:', datetime.datetime.fromtimestamp(event_time)
            event = sched.Event(self.cancel_event)
            self.schedule(event_time,event)
            self.currently_running_dr_event_handlers.append(event)