NRP-Core experiments can be run from the provided application, NRPCoreSim. In this way, the experiment is loaded from a specified configuration file and run until the specified timeout. Additionally, we provide a Python module, nrp_client.NrpCore, which can be used to start experiments from Python and which enable more control over the experiment execution.
The main class used to launch and control NRP-Core experiment is nrp_client.NrpCore. When instantiated, it internally launches an NRPCoreSim process in server mode and forwards requests made through its methods to the NRPCoreSim GRPC server. The NRPCoreSim process can be launched either as a forked process or inside of a docker container. More details on these options are given below.
nrp_client.NrpCore
provides methods which match the available services in NRPCoreSim server: initialize()
, run_loop(int number_of_iterations)
, run_until_timeout()
, stop()
, reset()
and shutdown()
. Each of these methods calls the corresponding GRPC service as described here.
Below there is an example on how to use NrpCore for launching the tf_exchange experiment and running it:
NrpCore takes as arguments:
experiment_folder
. By default, "simulation_config.json"Each of the methods provided to control the experiment returns a boolean value which is True if the request was processed correctly and False otherwise. They can return False either because the request was not possible from the current state of the experiment (see here for more information on the experiment lifecycle FSM) or because there was an error while executing the request. In the latter case the experiment is transitioned to Failed
state and only shutdown()
is possible.
Finally, all the request methods are blocking, i.e. the return only after the request has been processed and the corresponding GRPC service returns. With the exception of run_loop() and run_until_timeout(), which accepts a parameter run_async (bool) which allows to execute the request in a separate thread, and therefore non-blockingly. When called with run_async=True they return a Thread object which can be used to monitor the state of the request. In combination with the stop() request and the datatransfer engine, this option could be used to monitor the simulation and stop it after some condition fulfills or visualize or process experiment data in realtime.
It is possible to send data from the Python client directly to the Engines that take part in the simulation. The data can be in one of the following formats:
The data should be passed to the NRP Core client object using the following methods:
The data will be passed to NRP Core on the first subsequent call to the run_loop()
service. NRP Core will convert the data into DataPacks
using the datapack name and engine name specified as arguments to the set_x_datapack
methods. These DataPacks will be then forwarded to the proper engines.
The same mechanism can be used to pass DataPacks to the reset()
engine function:
The data (observations) may also be passed from the engines to the client, using the so-called Status Function. The Status Function is capable of producing observations continuously when the simulation is executed step-by-step with the run_loop()
service. It can also return trajectories (observations accumulated over multiple simulation steps) in case of episode-based simulations that are executed using run_until_timeout()
service.
The run_loop()
method returns a tuple that consists of the done_flag
and the trajectory (a list of observations). Both are generated by the Status Function. If the done_flag
is False
, the trajectory will be empty.
The run_until_timeout()
returns a tuple with the following:
done_flag
- when set to True
, the simulation episode has ended. Set by the Status Function.timeout_flag
- when set to True
, the simulation has reached the timeout define in the simulation config. Set internally by NRP Core.trajectory
- a list of observations accumulated during the episode. The trajectory will be returned no matter which of the two status flags (done_flag
, timeout_flag
) is set. The order of observations returned from the Status Function is preserved.More information about how to write the Status Function can be found on this page.
Additionally, DataPacks can be returned from initialize()
and reset()
service calls:
An easy way to vectorize NRP Core environments is to create a simple wrapper with gym interface around the NRP Core Python client, and to use this wrapped environment with e.g. SubprocVecEnv
provided with stable baselines. An example of such interface, together with the vectorization process, is present in examples/nrp_vectorization
.
By default, when instantiating a NrpCore
object, NRPCoreSim is started in a forked process. In case of willing to run NRPCoreSim in a docker container instead, set the parameters image_name
, and optionally docker_daemon_address
, as described above.
When running NRPCoreSim in a container from the NrpCore Python client, the behavior of the latter remains unchanged, i.e. as explained above in this page. The only noticeable difference / constraint, is that the path specified in the config_file
parameter of NrpCore client must be relative to experiment_folder
, and the configuration file should be itself placed inside of the experiment folder. This is the normal case anyways, therefore this constraint will go unnoticed most of the times.
As explained above, if log_output
is set to True when instantiating the NrpCore client, the experiment console output is logged to a file named .console_output.log
. When running the experiment in a docker container this file is fetched from the container and saved to experiment_folder
when the experiment is shutdown.
In the same way, it is possible to specify in the NrpCore constructor an additional list of archives (files or folders) that should be fetched from the container and saved to experiment_folder
when the experiment is shutdown. These archives are passed to the constructor using the get_archives
parameter.
The latter is useful for example when including a Data Transfer Engine in the experiment. In this case datapacks are either streamed to an MQTT topic (which can be subscribed to from outside the container) or logged into a file. These files are stored into a folder (data
by default) which can be added to the get_archives
list parameter so the logs are retrieved from the container before stopping it.
NOTE:
In order to launch experiments in docker containers, a docker daemon must be accessible at the IP specified in the parameter docker_daemon_address
. See this guide for more information on how to do this.
Also keep in mind that the address of the NRPCoreSim server ('address' parameter in the NrpCore constructor) will be an address in the host where docker daemon is running, and must be accessible from the host where NrpCore is instantiated.