def on_message(self, headers, msg): global stop_simulation message = {} try: message_str = 'received message '+str(msg) if fncs.is_initialized(): _send_simulation_status('RUNNING', message_str, 'DEBUG') else: _send_simulation_status('STARTED', message_str, 'DEBUG') json_msg = yaml.safe_load(str(msg)) print("\n{}\n".format(json_msg['command'])) if json_msg['command'] == 'isInitialized': message_str = 'isInitialized check: '+str(is_initialized) if fncs.is_initialized(): _send_simulation_status('RUNNING', message_str, 'DEBUG') else: _send_simulation_status('STARTED', message_str, 'DEBUG') message['command'] = 'isInitialized' message['response'] = str(is_initialized) if (simulation_id != None): message['output'] = _get_fncs_bus_messages(simulation_id) message_str = 'Added isInitialized output, sending message '+str(message)+' connection '+str(goss_connection) if fncs.is_initialized(): _send_simulation_status('RUNNING', message_str, 'DEBUG') else: _send_simulation_status('STARTED', message_str, 'DEBUG') message['timestamp'] = datetime.utcnow().microsecond goss_connection.send(output_to_simulation_manager , json.dumps(message)) elif json_msg['command'] == 'update': message['command'] = 'update' _publish_to_fncs_bus(simulation_id, json.dumps(json_msg['input'])) #does not return elif json_msg['command'] == 'nextTimeStep': message['command'] = 'nextTimeStep' current_time = json_msg['currentTime'] message_str = 'incrementing to '+str(current_time + 1) _send_simulation_status('RUNNING', message_str, 'DEBUG') _done_with_time_step(current_time) #current_time is incrementing integer 0 ,1, 2.... representing seconds message_str = 'done with timestep '+str(current_time) _send_simulation_status('RUNNING', message_str, 'DEBUG') message['output'] = _get_fncs_bus_messages(simulation_id) message['timestamp'] = datetime.utcnow().microsecond response_msg = json.dumps(message) goss_connection.send(output_to_goss_topic + "{}".format(simulation_id) , response_msg) elif json_msg['command'] == 'stop': message_str = 'Stopping the simulation' _send_simulation_status('CLOSED', message_str, 'INFO') stop_simulation = True fncs.finalize() except Exception as e: message_str = 'Error in command '+str(e) _send_simulation_status('ERROR', message_str, 'ERROR') stop_simulation = True if fncs.is_initialized(): fncs.die()
def on_error(self, headers, message): global stop_simulation message_str = 'Error in goss listener '+str(message) _send_simulation_status('ERROR', message_str, 'ERROR') stop_simulation = True if fncs.is_initialized(): fncs.die()
def __register_federate(self): self.__raise_if_not_installed() cfg = """name = {0[name]} time_delta = {0[time_delta]} broker = {0[broker]} """.format( dict(name=self._federate_name, broker=self._broker, time_delta=self._time_delta)) if self._registered_fncs_topics: cfg += "values" for k, v in self._registered_fncs_topics.items(): cfg += "\n\t{}\n\t\ttopic = {}\n".format(k, v['fncs_topic']) if v.get("default"): cfg += "\t\tdefault = {}\n".format(v.get('default')) if v.get("data_type"): cfg += "\t\ttype = {}\n".format(v.get('data_type')) if v.get("list"): cfg += "\t\tlist = true\n" _log.debug(cfg) cfg = cfg.replace("\t", " ") fncs.initialize(cfg) _log.debug("After initialized!") if not fncs.is_initialized(): raise RuntimeError("Intialization error for fncs.")
def _fncs_loop(self): _log.info("Starting fncs loop") self._simulation_started = True while self._current_step < self._simulation_length: # Block until the work is done here. subKeys = fncs.get_events() self._raise_if_error("After get_events") self._current_values.clear() for x in subKeys: fncs_topic = self._registered_fncs_topics[x].get('fncs_topic') self._current_values[fncs_topic] = fncs.get_value(x) if not fncs.is_initialized(): fncs.die() raise RuntimeError("FNCS unexpected error after get_values") self._work_callback() subKeys = fncs.get_events() if len(subKeys) > 0: for x in subKeys: subkeyvalue = fncs.get_value(x) volttron_topic = self._registered_fncs_topics[x].get('volttron_topic') if volttron_topic: self.pubsub().publish('pubsub', topic=volttron_topic, message=subkeyvalue) # This allows other event loops to run gevent.sleep(0.000000001) self._simulation_complete = True fncs.finalize() if self._stop_agent_when_sim_complete: self.core().stop()
def start_simulation(self): """ Begin the main fncs loop :return: """ self.__raise_if_not_installed() if not fncs.is_initialized(): raise ValueError("intialized must be called before starting simulation") gevent.spawn(self._fncs_loop) # Allow the spawned greenlet to run. gevent.sleep(0.1)
def register_with_fncs(self): fncs_configuration = { "name": "PythonListener{}".format(self.sim_id), "time_delta": "1s", "broker": self.broker_location, "values": { "{}".format(self.sim_id): { "topic": self.subscription_topic, "default": "{}", "type": "JSON", "list": "false" } } } configuration_zpl = ( 'name = {0}\n'.format(fncs_configuration['name']) + 'time_delta = {0}\n'.format(fncs_configuration['time_delta']) + 'broker = {0}\nvalues'.format(fncs_configuration['broker'])) for x in fncs_configuration['values'].keys(): configuration_zpl += '\n {0}'.format(x) configuration_zpl += '\n topic = {0}'.format( fncs_configuration['values'][x]['topic']) configuration_zpl += '\n default = {0}'.format( fncs_configuration['values'][x]['default']) configuration_zpl += '\n type = {0}'.format( fncs_configuration['values'][x]['type']) configuration_zpl += '\n list = {0}'.format( fncs_configuration['values'][x]['list']) try: fncs.initialize(configuration_zpl) if not fncs.is_initialized(): raise RuntimeError( "fncs.initialize(configuration_zpl) failed!\nconfiguration_zpl = {}" .format(configuration_zpl)) except Exception as e: if fncs.is_initialized(): fncs.die() raise
def run_simulation(self): try: current_time = 0 while current_time <= self.sim_length: sim_message_topics = fncs.get_events() if self.sim_id in sim_message_topics: message = fncs.get_value(self.sim_id) time_request = current_time + 1 if time_request > self.sim_length: fncs.finalize() break time_approved = fncs.time_request(time_request) if time_approved != time_request: raise RuntimeError( "The time approved from the fncs broker is not the time requested.\ntime_request = {}.\ntime_approved = {}" .format(time_request, time_approved)) current_time += 1 except Exception as e: if fncs.is_initialized(): fncs.die() raise
def __register_federate(self): self.__raise_if_not_installed() cfg = """name = {0[name]} time_delta = {0[time_delta]} broker = {0[broker]} """.format(dict(name=self._federate_name, broker=self._broker, time_delta=self._time_delta)) if self._registered_fncs_topics: cfg += "values" for k, v in self._registered_fncs_topics.items(): cfg += "\n\t{}\n\t\ttopic = {}\n".format(k, v['fncs_topic']) if v.get("default"): cfg += "\t\tdefault = {}\n".format(v.get('default')) if v.get("data_type"): cfg += "\t\ttype = {}\n".format(v.get('data_type')) if v.get("list"): cfg += "\t\tlist = true\n" _log.debug(cfg) cfg = cfg.replace("\t", " ") fncs.initialize(cfg) if not fncs.is_initialized(): raise RuntimeError("Intialization error for fncs.")
def _publish_to_fncs_bus(simulation_id, goss_message): """publish a message received from the GOSS bus to the FNCS bus. Function arguments: simulation_id -- Type: string. Description: The simulation id. It must not be an empty string. Default: None. goss_message -- Type: string. Description: The message from the GOSS bus as a json string. It must not be an empty string. Default: None. Function returns: None. Function exceptions: RuntimeError() ValueError() """ message_str = 'translating following message for fncs simulation '+simulation_id+' '+str(goss_message) _send_simulation_status('RUNNING', message_str, 'DEBUG') print(message_str) if simulation_id == None or simulation_id == '' or type(simulation_id) != str: raise ValueError( 'simulation_id must be a nonempty string.\n' + 'simulation_id = {0}'.format(simulation_id)) if goss_message == None or goss_message == '' or type(goss_message) != str: raise ValueError( 'goss_message must be a nonempty string.\n' + 'goss_message = {0}'.format(goss_message)) if not fncs.is_initialized(): raise RuntimeError( 'Cannot publish message as there is no connection' + ' to the FNCS message bus.') try: test_goss_message_format = yaml.safe_load(goss_message) if type(test_goss_message_format) != dict: raise ValueError( 'goss_message is not a json formatted string.' + '\ngoss_message = {0}'.format(goss_message)) fncs_input_topic = '{0}/fncs_input'.format(simulation_id) fncs_input_message = {"{}".format(simulation_id) : {}} forward_differences_list = test_goss_message_format["message"]["forward_differences"] for x in forward_differences_list: object_name = (object_mrid_to_name.get(x.get("object"))).get("name") object_phases = (object_mrid_to_name.get(x.get("object"))).get("phases") object_total_phases = (object_mrid_to_name.get(x.get("object"))).get("total_phases") object_type = (object_mrid_to_name.get(x.get("object"))).get("type") object_name_prefix = ((difference_attribute_map.get(x.get("attribute"))).get(object_type)).get("prefix") cim_attribute = x.get("attribute") object_property_list = ((difference_attribute_map.get(x.get("attribute"))).get(object_type)).get("property") phase_in_property = ((difference_attribute_map.get(x.get("attribute"))).get(object_type)).get("phase_sensitive",False) if (object_name_prefix + object_name) not in fncs_input_message["{}".format(simulation_id)].keys(): fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name] = {} if cim_attribute == "RegulatingControl.mode": val = x.get("value") if val == 0: fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][object_property_list[0]] = "VOLT" elif val == 2: fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][object_property_list[0]] = "VAR" elif val == 3: fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][object_property_list[0]] = "CURRENT" else: fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][object_property_list[0]] = "MANUAL" _send_simulation_status("RUNNING", "Unsupported capacitor control mode requested. The only supported control modes for capacitors are voltage, VAr, volt/VAr, and current. Setting control mode to MANUAL.","WARN") elif cim_attribute == "RegulatingControl.targetDeadband": for y in difference_attribute_map[cim_attribute][object_type]["property"]: fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][y] = "{}".format(x.get("value")) elif cim_attribute == "RegulatingControl.targetValue": for y in difference_attribute_map[cim_attribute][object_type]["property"]: fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][y] = "{}".format(x.get("value")) elif cim_attribute == "ShuntCompensator.aVRDelay": for y in difference_attribute_map[cim_attribute][object_type]["property"]: fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][y] = "{}".format(x.get("value")) elif cim_attribute == "ShuntCompensator.sections": if x.get("value") == 1: val = "CLOSED" else: val = "OPEN" for y in object_phases: fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][object_property_list[0].format(y)] = "{}".format(val) elif cim_attribute == "Switch.open": if x.get("value") == 1: val = "OPEN" else: val = "CLOSED" for y in object_total_phases: fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][object_property_list[0].format(y)] = "{}".format(val) elif cim_attribute == "TapChanger.initialDelay": for y in object_property_list: fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][y] = "{}".format(x.get("value")) elif cim_attribute == "TapChanger.step": for y in object_phases: fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][object_property_list[0].format(y)] = "{}".format(x.get("value")) elif cim_attribute == "TapChanger.lineDropCompensation": if x.get("value") == 1: val = "LINE_DROP_COMP" else: val = "MANUAL" fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][object_property_list[0]] = "{}".format(val) elif cim_attribute == "TapChanger.lineDropR": for y in object_phases: fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][object_property_list[0].format(y)] = "{}".format(x.get("value")) elif cim_attribute == "TapChanger.lineDropX": for y in object_phases: fncs_input_message["{}".format(simulation_id)][object_name_prefix + object_name][object_property_list[0].format(y)] = "{}".format(x.get("value")) else: _send_simulation_status("RUNNING", "Attribute, {}, is not a supported attribute in the simulator at this current time. ignoring difference.", "WARN") goss_message_converted = json.dumps(fncs_input_message) _send_simulation_status("RUNNING", "Sending the following message to the simulator. {}".format(goss_message_converted),"INFO") if fncs.is_initialized(): fncs.publish_anon(fncs_input_topic, goss_message_converted) except ValueError as ve: raise ValueError(ve) except Exception as ex: _send_simulation_status("ERROR","An error occured while trying to translate the update message received","ERROR")
def _register_with_fncs_broker(broker_location='tcp://localhost:5570'): """Register with the fncs_broker and return. Function arguments: broker_location -- Type: string. Description: The ip location and port for the fncs_broker. It must not be an empty string. Default: 'tcp://localhost:5570'. Function returns: None. Function exceptions: RuntimeError() ValueError() """ global is_initialized global stop_simulation configuration_zpl = '' try: message_str = 'Registering with FNCS broker '+str(simulation_id)+' and broker '+broker_location ('STARTED', message_str, 'INFO') message_str = 'still connected to goss 1 '+str(goss_connection.is_connected()) _send_simulation_status('STARTED', message_str, 'INFO') if simulation_id == None or simulation_id == '' or type(simulation_id) != str: raise ValueError( 'simulation_id must be a nonempty string.\n' + 'simulation_id = {0}'.format(simulation_id)) if (broker_location == None or broker_location == '' or type(broker_location) != str): raise ValueError( 'broker_location must be a nonempty string.\n' + 'broker_location = {0}'.format(broker_location)) fncs_configuration = { 'name' : 'FNCS_GOSS_Bridge_' + simulation_id, 'time_delta' : '1s', 'broker' : broker_location, 'values' : { simulation_id : { 'topic' : simulation_id + '/fncs_output', 'default' : '{}', 'type' : 'JSON', 'list' : 'false' } } } configuration_zpl = ('name = {0}\n'.format(fncs_configuration['name']) + 'time_delta = {0}\n'.format(fncs_configuration['time_delta']) + 'broker = {0}\nvalues'.format(fncs_configuration['broker'])) for x in fncs_configuration['values'].keys(): configuration_zpl += '\n {0}'.format(x) configuration_zpl += '\n topic = {0}'.format( fncs_configuration['values'][x]['topic']) configuration_zpl += '\n default = {0}'.format( fncs_configuration['values'][x]['default']) configuration_zpl += '\n type = {0}'.format( fncs_configuration['values'][x]['type']) configuration_zpl += '\n list = {0}'.format( fncs_configuration['values'][x]['list']) fncs.initialize(configuration_zpl) is_initialized = fncs.is_initialized() if is_initialized: message_str = 'Registered with fncs '+str(is_initialized) _send_simulation_status('RUNNING', message_str, 'INFO') except Exception as e: message_str = 'Error while registering with fncs broker '+str(e) _send_simulation_status('ERROR', message_str, 'ERROR') stop_simulation = True if fncs.is_initialized(): fncs.die() if not fncs.is_initialized(): message_str = 'fncs.initialize(configuration_zpl) failed!\n' + 'configuration_zpl = {0}'.format(configuration_zpl) _send_simulation_status('ERROR', message_str, 'ERROR') stop_simulation = True if fncs.is_initialized(): fncs.die() raise RuntimeError( 'fncs.initialize(configuration_zpl) failed!\n' + 'configuration_zpl = {0}'.format(configuration_zpl))
def on_disconnected(self): global stop_simulation stop_simulation = True if fncs.is_initialized(): fncs.die()
def initialize(self, sim_start_time, sim_length, topic_mapping, work_callback, federate_name=None, broker_location="tcp://localhost:5570", time_delta="1s", stop_agent_when_sim_complete=False): """ Configure the agent to act as a federated connection to FNCS sim_start_time - Wall clock time for the simulation start time (This is not used at present time other than to be available) sim_length - Time for the simulation to run. Should be formatted as <number><unit> i.e. 60s. topic_mapping - Maps fncs topics onto volttron topics. federate_name - MUST be unique to the broker. If None, then will be the identity of the current agent process. broker - tcp location of the fncs broker (defaults to tcp://localhost:5570) time_delta - Minimum timestep supported for the federate. stop_agent_when_sim_complete - Should we stop the agent when the simulation is completed. :param sim_start_time: :param sim_length: :param topic_mapping: :param work_callback: :param federate_name: :param broker_location: :param time_delta: :param poll_timeout: :return: """ self.__raise_if_not_installed() if fncs.is_initialized(): raise RuntimeError( "Invalid state, fncs has alreayd been initialized") if not topic_mapping: raise ValueError( "Must supply a topic mapping with topics to map onto.") if not sim_start_time: raise ValueError("sim_start_time must be specified.") if not sim_length: raise ValueError("sim_length must be specified.") if not time_delta: raise ValueError("time_delta must be specified.") if not federate_name: raise ValueError("federate_name must be specified.") if not broker_location: raise ValueError("broker_location must be specified.") if not work_callback: raise ValueError("work_callback must be specified.") if not isinstance(sim_start_time, datetime): raise ValueError("sim_start_time must be a datetime object.") self._broker = broker_location self._time_delta = time_delta self._current_simulation_time = self._simulation_start_time = sim_start_time self._simulation_delta = self.parse_time(time_delta) self._simulation_length = self.parse_time(sim_length) if federate_name: self._federate_name = federate_name self._work_callback = work_callback for k, v in topic_mapping.items(): if not v.get('fncs_topic'): raise ValueError( "Invalid fncs_topic specified in key {}.".format(k)) entry = dict(fncs_topic=v.get('fncs_topic')) if 'volttron_topic' in v: entry['volttron_topic'] = v['volttron_topic'] self._registered_fncs_topics[k] = entry self.__register_federate() self._simulation_started = False self._simulation_complete = False self._stop_agent_when_sim_complete = stop_agent_when_sim_complete
def _raise_if_error(self, location): if not fncs.is_initialized(): fncs.die() raise RuntimeError("FNCS unexpected error: {}".format(location))
def initialize(self, sim_start_time, sim_length, topic_maping, work_callback, federate_name=None, broker_location="tcp://localhost:5570", time_delta="1s", stop_agent_when_sim_complete=False): """ Configure the agent to act as a federated connection to FNCS sim_start_time - Wall clock time for the simulation start time (This is not used at present time other than to be available) sim_length - Time for the simulation to run. Should be formatted as <number><unit> i.e. 60s. topic_mapping - Maps fncs topics onto volttron topics. federate_name - MUST be unique to the broker. If None, then will be the identity of the current agent process. broker - tcp location of the fncs broker (defaults to tcp://localhost:5570) time_delta - Minimum timestep supported for the federate. stop_agent_when_sim_complete - Should we stop the agent when the simulation is completed. :param sim_start_time: :param sim_length: :param topic_maping: :param work_callback: :param federate_name: :param broker_location: :param time_delta: :param poll_timeout: :return: """ self.__raise_if_not_installed() if fncs.is_initialized(): raise RuntimeError("Invalid state, fncs has alreayd been initialized") if not topic_maping: raise ValueError("Must supply a topic mapping with topics to map onto.") if not sim_start_time: raise ValueError("sim_start_time must be specified.") if not sim_length: raise ValueError("sim_length must be specified.") if not time_delta: raise ValueError("time_delta must be specified.") if not federate_name: raise ValueError("federate_name must be specified.") if not broker_location: raise ValueError("broker_location must be specified.") if not work_callback: raise ValueError("work_callback must be specified.") if not isinstance(sim_start_time, datetime): raise ValueError("sim_start_time must be a datetime object.") self._broker = broker_location self._time_delta = time_delta self._current_simulation_time = self._simulation_start_time = sim_start_time self._simulation_delta = self.parse_time(time_delta) self._simulation_length = self.parse_time(sim_length) if federate_name: self._federate_name = federate_name self._work_callback = work_callback for k, v in topic_maping.items(): if not v.get('fncs_topic'): raise ValueError("Invalid fncs_topic specified in key {}.".format(k)) entry = dict(fncs_topic=v.get('fncs_topic')) if 'volttron_topic' in v.keys(): entry['volttron_topic'] = v['volttron_topic'] self._registered_fncs_topics[k] = entry self.__register_federate() self._simulation_started = False self._simulation_complete = False self._stop_agent_when_sim_complete = stop_agent_when_sim_complete