class GridAPPSDSimIntegration(BaseSimIntegration): """ The class is responsible for integration with GridAPPSD co-simulation platform. It provides integration support to register configuration, start, stop, publish, receive messages, pause and resume simulation """ def __init__(self, config, pubsub): super(GridAPPSDSimIntegration, self).__init__(config) self._work_callback = None self.config = config self.gridappsd = None self.sim = None self.event_callbacks = {} self.topic_callbacks = {} self.sim_id = None self.username = None self.password = None def register_inputs(self, config=None, callback=None, **kwargs): """ Register configuration parameters with GridAppsD. The config parameters may include but not limited to: - power_system_config - application_config - simulation_config - test_config - service_configs : Register agent callback method :return: """ self.config = config self.username = self.config.pop('username', 'system') self.password = self.config.pop('password', 'manager') self._work_callback = callback def register_event_callbacks(self, callbacks={}): """ Register for event callbacks for event notifications such as - on measurement change - on timestep change - on finish """ _log.debug("Registering for event callbacks") self.event_callbacks = callbacks def register_topic_callbacks(self, callbacks={}): """ Register for any simulation topic callbacks """ _log.debug("Registering for topic callbacks") self.topic_callbacks = callbacks def start_simulation(self, *args, **kwargs): """ Simulation start activities involve: - Creating GridAppsD connection gevent thread - Registering for event callbacks (if specified) - Registering for topic callbacks if specified - Starting simulation based on the input config :return: """ try: self.gridappsd = GridAPPSD(override_threading=self.receiver_thread, username=self.username, password=self.password) _log.debug('Gridappsd connected') _log.debug(f"connection config is: {self.config}") self.sim = Simulation(self.gridappsd, self.config) _log.debug('Gridappsd adding onstart callback') # Register for onstart callback to know if simulation has started self.sim.add_onstart_callback(self.sim_on_start) # Register event callbacks - on measurement, on timestep, on finish for name, cb in self.event_callbacks.items(): if name == 'MEASUREMENT': _log.debug('Gridappsd adding measurement callback') self.sim.add_onmesurement_callback(cb) elif name == 'TIMESTEP': _log.debug('Gridappsd adding timestep callback') self.sim.add_ontimestep_callback(cb) elif name == 'FINISH': _log.debug('Gridappsd adding finish callback') self.sim.add_oncomplete_callback(cb) # Register/Subscribe for simulation topics for topic, cb in self.topic_callbacks: _log.debug('Gridappsd subscribing to topics callback') self.gridappsd.subscribe(topic, cb) # Starting GridAppsD simulation self.sim.start_simulation() _log.debug(f"Gridappsd simulation id: {self.sim.simulation_id}") except stomp.exception.NotConnectedException as ex: _log.error("Unable to connect to GridAPPSD: {}".format(ex)) raise ex def sim_on_start(self, sim): """ Simulation on start callback to get notified when simulation starts """ _log.debug( f"GridAppsD simulation id inside sim_on_start(): {sim.simulation_id}" ) self.sim_id = sim.simulation_id def receiver_thread(self, arg): """ GridAPPSD connection thread """ self._receiver_thread = gevent.threading.Thread(group=None, target=arg) self._receiver_thread.daemon = True # Don't let thread prevent termination self._receiver_thread.start() _log.debug('Gridappsd receiver_thread started!') return self._receiver_thread def publish_to_simulation(self, topic, message, **kwargs): """ Publish message to GridAppsD :param topic: GridAppsD publication topic :param message: message :return: """ self.gridappsd.send(topic, message) def pause_simulation(self, timeout=None, **kwargs): """ Pause the GridAppsD simulation """ if timeout is None: self.sim.pause() else: self.sim.pause(timeout) def resume_simulation(self, *args, **kwargs): """ Resume the GridAppsD simulation """ self.sim.resume() def is_sim_installed(self, **kwargs): """ Flag to indicate if GridAppsD is installed """ return HAS_GAPPSD def stop_simulation(self, *args, **kwargs): """ Stop the simulation if running and disconnect from GridAppsD server :return: """ _log.debug('Stopping the simulation') try: if self.sim_id is not None: self.sim.stop() _log.debug('Disconnect GridAppsd') if self.gridappsd is not None: self.gridappsd.disconnect() except Exception: _log.error("Error stop GridAPPSD simulation")
run_config = merge_with_simulation_config(config_file) # from pprint import pprint # pprint(config['gridappsd']) with run_containers(config, stop_after=True) as containers: # Watches the log on the container for the MYSQL data. containers.wait_for_log_pattern("gridappsd", "MYSQL") gappsd = GridAPPSD() gappsd.connect() assert gappsd.connected time.sleep(10) sim = Simulation(gappsd, run_config=run_config) sim.add_onstart_callback(onstart) sim.add_oncomplete_callback(onfinish) sim.add_ontimestep_callback(ontimestep) sim.add_onmesurement_callback(onmeasurement) sim.start_simulation() sim.pause() gappsd.subscribe(t.simulation_output_topic(sim.simulation_id), onsimulationoutput) gappsd.subscribe(t.application_output_topic(GRIDAPPSD_SERVICE_ID, sim.simulation_id), onsensoroutput) time.sleep(10) sim.resume() sim.run_loop() print("Shutting down") fh_sim_measurement.close() fh_sensor_measurement.close()