diff --git a/emscore/ems_main.py b/emscore/ems_main.py index bee3f45..b76b2f7 100644 --- a/emscore/ems_main.py +++ b/emscore/ems_main.py @@ -1,295 +1,298 @@ __author__ = 'Olivier Van Cutsem' from threading import Thread from ems_config import * from building_data_management.bdms_main import BuildingDataManagementSystem from building_data_management.signal_and_constraint_management.signal_data import PiecewiseConstantSignal from building_data_management.category_management.category_config import * from rtevents_interface.rtevents_interf import * from ems_gui.gui import * import json from abc import abstractmethod import time from multiprocessing import Queue from building_data_management.ambient_data import EnvironmentalDataManagement from building_data_management.room_data import BuildingRoomData ## Various strategies class EnergyManagementSystem(Thread): def __init__(self, in_q, out_q, gui_q): """ Initialize ClusteringManagementSystem object. Read messages from the BMS interface bubble messages through a Queue """ Thread.__init__(self) self.in_stream = in_q self.out_stream = out_q self.gui_data = gui_q ## Clustering and Data Structure system self.building_data = BuildingDataManagementSystem() # The empty BD-MS ## Environmental data storage self.environment_data = EnvironmentalDataManagement() ## EMS running mode and time configuratin # In RT control mode, keep track of the current time ; In simulation mode, keep track of the simulation time if EMS_MODE != EMS_MODE_VE_SIMULATION: self.current_time = time.time() else: self.current_time = SIMULATION_DAY * 24 * 60 * 60 if EMS_MODE == EMS_MODE_VE_SIMULATION: self.simu_param = None self.simu_status_run = True # run the simulation self.last_ems_step = self.current_time def run(self): """ EMS main loop: @init: BMS request for CMS data structure filling @main_loop: check the incoming message to update CMS and then run the EMS control step """ # INITIALIZATION of the models ret_code = self.init_cms() if ret_code != 0: print("An error occurred during CMS initialization, shutting down EMS thread ...") return else: print("CMS initialized with success") while True: + # This boolean decides whether to run the next EMS control step or not within this loop iteration run_ems_step = False if EMS_MODE == EMS_MODE_RT_SAMPLED: # The EMS is in Sampled mode if time.time() - self.last_ems_step >= EMS_TIME_STEP: run_ems_step = True # CHECK FOR NEW MESSAGE try: msg = self.in_stream.get(EMS_MODE == EMS_MODE_VE_SIMULATION) # block in simulation mode except Queue.empty(): msg = None if msg is not None: - print(msg) if RT_EVENT_MSG_TYPE_KEY in msg: if msg[RT_EVENT_MSG_TYPE_KEY] == RT_EVENT_MSG_TYPE_MEASURE_STATUS: # new measure/status for the CMS if EMS_MODE == EMS_MODE_RT_EVENT_DRIVEN: run_ems_step = True + print("[{1}s] EMS received: {0}".format(msg, self.current_time)) # Process this event in order to update the appropriate structures self.decode_rtevent(msg) elif msg[RT_EVENT_MSG_TYPE_KEY] == RT_EVENT_MSG_TYPE_SIMULATION_SYNQ: # Synchronisation msg for simu # Analyse the incoming simulation-related message self.decode_simulation_event(msg) - self.current_time += SIMULATION_TIME_STEP if self.simu_status_run is True: run_ems_step = True # Update the interface data self.gui_data.put({EMS_gui.MSG_TYPE_KEY: EMS_gui.MSG_TYPE_ADD, EMS_gui.MSG_CONTENT_DATA: self.print_state(), EMS_gui.MSG_CONTENT_TIME: self.current_time}) # RUN THE EMS CONTROL STEP if run_ems_step: ems_commands = self.update() if ems_commands is not None: for command in ems_commands: self.out_stream.put(command) # in case of simulation, send the READY signal ready_signal = {EMS_DOWNSTREAM_MSG_TYPE: EMS_DOWNSTREAM_SIMU_READY} self.out_stream.put(ready_signal) + if EMS_MODE_VE_SIMULATION == EMS_MODE_VE_SIMULATION: + self.current_time += SIMULATION_TIME_STEP + ########################## ######### INITIALIZATION ######### ########################## def init_cms(self): """ Fill the empty CMS structure with data coming from the BMS :return: 0 if everything went right ; non-zero value otherwise """ # Initialize the building data structure through the BMS API self.building_data.initFromBMS() return 0 ############################## ######### MESSAGE PROCESSING ######### ############################## def decode_rtevent(self, msg): # Type of entity: sensor or actuator ? type_sender = msg[RT_EVENT_SENDER_TYPE] #print("processing {0}".format(msg)) if type_sender == RT_EVENT_SENDER_TYPE_SENSOR: sensor_id = msg[RT_EVENT_SENDER_ID] sensing_type = msg[RT_EVENT_QUANTITY_TYPE] comfort_value = msg[RT_EVENT_VALUE] if sensing_type in EMS_LIST_COMFORT_QUANTITIES: # Update the corresponding room self.building_data.updateComfortRoom(sensor_id=sensor_id, type_comfort=sensing_type, value=comfort_value) elif sensing_type in EMS_LIST_ENERGY_RELATED_QUANTITIES: # Update the corresponding energy-related entity self.building_data.updateEnergyRelatedEntity(sensor_id=sensor_id, type_energy=sensing_type, value=comfort_value) elif type_sender == RT_EVENT_SENDER_TYPE_ACTUATOR: actuator_id = msg[RT_EVENT_SENDER_ID] comfort_type = msg[RT_EVENT_QUANTITY_TYPE] comfort_value = msg[RT_EVENT_VALUE] pass #todo def decode_simulation_event(self, msg_simu): """ Decode the parameters sent for the simulation :param msg_simu: :return: """ self.simu_param = msg_simu[RT_EVENT_SIMULATION_PARAM_KEY] if self.simu_param is not None: if RT_EVENT_SIMULATION_PARAM_STOP in self.simu_param: self.simu_status_run = False print("STOP SIMULATION !") self.gui_data.put({EMS_gui.MSG_TYPE_KEY: EMS_gui.MSG_TYPE_PLOT}) else: self.simu_status_run = True def battery_set_point(self, s_id, new_value): storage_command = {EMS_DOWNSTREAM_MSG_TYPE: EMS_DOWNSTREAM_ENERGY_ENTITY_COMMAND, EMS_DOWNSTREAM_ENERGY_ENTITY_COMMAND_TYPE: EMS_ENERGY_power, EMS_DOWNSTREAM_MSG_ENTITY_TYPE: EMS_CATEGORY_ENTITY_STORAGE, EMS_DOWNSTREAM_MSG_ENTITY_ID: s_id, EMS_DOWNSTREAM_MSG_PAYLOAD: {EMS_ENERGY_power: new_value}} return storage_command def hvac_set_point(self, l_id, new_value): hvac_command = {EMS_DOWNSTREAM_MSG_TYPE: EMS_DOWNSTREAM_ENERGY_ENTITY_COMMAND, EMS_DOWNSTREAM_ENERGY_ENTITY_COMMAND_TYPE: EMS_ENERGY_power, EMS_DOWNSTREAM_MSG_ENTITY_TYPE: EMS_CATEGORY_ENTITY_LOAD, EMS_DOWNSTREAM_MSG_ENTITY_ID: l_id, EMS_DOWNSTREAM_MSG_PAYLOAD: {EMS_ENERGY_power: new_value}} return hvac_command ########################## ######### CORE FUNCTIONS ######### ########################## @abstractmethod def update(self): """ This abstract method runs the main energy management algorithm. :return: a set of commands for the flexible (controllable) building entities """ pass def get_environment_data(self, type_data, time_data): """ TODO """ (t_cur, t_hor, t_step) = time_data # TODO: Make it dynamic ! for the moment the raw data are encoded in a vector if type_data == EMS_ENV_DATA_IRR: data_raw = self.environment_data.environmentIrr elif type_data == EMS_ENV_DATA_TEMP: data_raw = self.environment_data.environmentTemp elif type_data == EMS_ENV_DATA_ELEC_PRICE: data_raw = self.environment_data.elecPrice else: return None sig_object = PiecewiseConstantSignal(data_raw['t'], data_raw['v']) return sig_object.get_interval_val(t_cur, t_cur+t_hor, t_step) def get_elec_price(self, time_data): """ TODO """ return self.get_environment_data('electricity_price', time_data) def print_state(self): """ Return a structured dictionary describing the actual state of the building :return: """ state = EMS_gui.get_data_format() # Loads, generation and storage grid_power = 0.0 _loads = self.building_data.get_entity_list(EMS_CATEGORY_ENTITY_LOAD) _stor = self.building_data.get_entity_list(EMS_CATEGORY_ENTITY_STORAGE) _gen = self.building_data.get_entity_list(EMS_CATEGORY_ENTITY_GENERATION) for (l_id, l_obj) in _loads: if l_obj.current_power is not None: #print("{0}: {1}".format(l_obj.name, l_obj.current_power)) grid_power += l_obj.current_power state[EMS_gui.MSG_EMS_STATE_STRUCT_ENERGY][EMS_CATEGORY_ENTITY_LOAD][l_obj.name] = l_obj.current_power for (s_id, s_obj) in _stor: if s_obj.current_power is not None: grid_power += s_obj.current_power state[EMS_gui.MSG_EMS_STATE_STRUCT_ENERGY][EMS_CATEGORY_ENTITY_STORAGE][EMS_ENERGY_power][s_obj.name] = s_obj.current_power if s_obj.current_soc is not None: state[EMS_gui.MSG_EMS_STATE_STRUCT_ENERGY][EMS_CATEGORY_ENTITY_STORAGE][EMS_ENERGY_soc][s_obj.name] = s_obj.current_soc for (g_id, g_obj) in _gen: if g_obj.current_power is not None: #print("{0}: {1}".format(g_obj.name, g_obj.current_power)) grid_power += g_obj.current_power state[EMS_gui.MSG_EMS_STATE_STRUCT_ENERGY][EMS_CATEGORY_ENTITY_GENERATION][g_obj.name] = g_obj.current_power # Room comfort for _r in self.building_data.room_list: room_name = _r.name state[EMS_gui.MSG_EMS_STATE_STRUCT_COMFORT][room_name] = {} # temp, etc list_of_comfort = BuildingRoomData.TYPE_COMFORT_LIST for type_comfort in list_of_comfort: v = _r.get_comfort_value(type_comfort) if v is not None: state[EMS_gui.MSG_EMS_STATE_STRUCT_COMFORT][room_name][type_comfort] = v # Electricity price time_data = (self.current_time, self.current_time+EMS_TIME_STEP, EMS_TIME_STEP) price_vector = self.get_elec_price(time_data) state[EMS_gui.MSG_EMS_STATE_STRUCT_ENV][EMS_ENV_DATA_ELEC_PRICE] = price_vector[0] state[EMS_gui.MSG_EMS_STATE_STRUCT_ENV][EMS_ENV_DATA_GRID] = grid_power print(state) return state