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'))
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.DEVICES_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
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.DEVICES_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)
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.DEVICES_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.DEVICES_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()
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.DEVICES_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
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.DEVICES_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.DEVICES_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.DEVICES_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()