-
Notifications
You must be signed in to change notification settings - Fork 1
The System
| The Fundamentals → The System |
|---|
Written by Connor Jakubik.
Space Teams Systems are pieces of code that hold all of the behavior of the simulation. Systems are applied to Entities, and during simulation execution they affect the Parameters of the entities to evolve the simulation over time. Systems typically have zero or very few dependencies on other Systems, meaning they can be specialized to a certain environmental effect (e.g. gravity), physical process (e.g. heat transfer), analysis computation (e.g. logging telemetry), or autonomous logic (e.g. path-planning) which can be applied on any Entity.
- On each update,
- Read parameters to see current state of things such as a battery.
- React to those parameters by changing internally tracked state and/or modifying Entity parameters.
- For example, a System simulating the power system on a vehicle may update the battery state of charge based on the total power usage of the vehicle.
- On each update,
- Read in the states of each entity in the previous update's time context.
- Use numerical integration with a certain set of ODEs to propagate dynamics forward from the previous update to the current update's time.
- Set the states of each entity with the propagated values.
- On each update, or with an on-receive async task,
- Parse input from a socket or other inter-process messaging system.
- Set Entity parameters or trigger other functionality based on the latest telemetry.
- On each update, or with a loop handled by third party code,
- Read button and axis states.
- Apply those directly to Entity parameter values, or
- Perform operations to present the button presses and axis changes to Entity parameters in a different way, such as applying a response curve to axes, or treating button presses as events.
In a Space Teams sim, all Systems are initialized as System Instances in the SimConfig. A System Instance has a Nametag, Type, Source, and other properties that depend on the type. All Systems additionally have Instance Parameters. System Instance Parameters differ from Entity parameters in that they are entirely internal to that System; they are only accessible on that System Instance and don't have value changes transmitted to other programs in the sim.
Multiple instances of the same System code (same Source) can be declared in a Sim Config, each with a different Nametag, Inst_Parameters, assignment to entities, etc.
System Instances are applied to Entities by adding their Nametag to the Entity's Systems list. Alternatively, Systems can be given access to Entities through EntityRef parameters, either on the System Instance Params or on the params of another Entity the System has direct access to.
To write simulation behavior in C++, a user duplicates our template System subclass through a tool to establish all the boilerplate code, then writes code inside the load(), init(), update() and other System member functions that will be called by the Compute_Server in a thread specific to that System during the simulation. The Space Teams Platform is distributed publicly with certain C++ core code header files available, which allows users to integrate their System code directly with the server for maximum performance. Systems are subclasses of the core System class. Before running the simulation, Systems are compiled into dynamically linked libraries which are loaded to create System Instances according to what is specified in a Sim Config file.
Each C++ System Instance gets its own thread, where the Compute_Server attempts to call its update functions at the specified frequency, or as close to that as possible. This frequency is specified in Frequency on the definition of that System Instance.
Taskflow Systems are declared in the Taskflows section of the Sim Config. TaskflowTask System Instances, instead of operating in their own thread, operate as part of the Taskflow executor, which runs the tasks in a concurrent environment while satisfying dependencies on the run-order of certain tasks. This is how you can enforce strict ordering between Systems if required. Typically, it will be possible to avoid bad behavior such as race conditions even without resorting to a Taskflow by designing your System code well and using Events where appropriate.
Work in progress
For writing simulation behavior in Python instead of C++, Space Teams includes a Python API library. This library encapsulates most of the functionality of the Compute_Server. The Python scripts that use it are 100% "normal" Python, as opposed to "embedded" python scripting where you can't install pip packages. We include a Python installer for a particular version (accessible in windows as .\sc_python.bat) alongside our application because we don't make prebuilt Python API versions for more than that one version. Python System scripts are spun off from the Compute_Server with special configuration options in order to connect to the running sim. The st.connect_to_sim(sys.argv) function is where the Python API starts a local copy of the simulation and connects.
Python Systems have their .py file specified as Source instead of their DLL, and they do not use the Frequency parameter. Python systems are free to set up their own loop, use the loop of a UI library, just run through some scripts without a loop, or whatever the programmer desires. Exit the sim without stopping it by st.leave_sim(), or stop the sim with st.stop_sim().
The Python API is as close as possible to the C++ API syntax. Instead of templates on param functions to identify the variable type of the param value, the first argument is st.VarType.something.
Take a look at how we make packages/TestingTools/ParamWrangler.py.
Work in progress
During a simulation, this is a timeline of events relevant to Systems in the Compute_Server:
- SimState
Loading- Load the sim config as a starting point, independent of any other programs
- Python System processes are started but will hang on
st.connect_to_sim()untilSimState::Runtimehappens. - System
load()functions are run
-
Lobby- Wait for other clients to finish connecting/loading, communicate and organize with other users (if applicable).
-
Sync- With a final user list known, enter a process where we ensure all clients are joined and warmed up before
Init/RunningSimState is made available. - Some propagators such as SPICE start running in order to initialize the solar system environment
- (v0.41.2+) Python Systems
BeforeInitdelegate functions are run
- With a final user list known, enter a process where we ensure all clients are joined and warmed up before
-
Init- System
init()theninitPerEntity()functions are run in sequence, on a single thread - (v0.41.2+) Python Systems
BeforeRuntimedelegate functions are run
- System
-
Runtime- Timed updates begin. Physics starts moving.
- Normal UI interaction resumes
- Async Scheduler:
- System
update()thenupdatePerEntity()are called in a loop on a unique thread per System or Taskflow, with time calculations to try to maintain the given frequencies with the OS scheduler.
- System
- Deterministic Scheduler:
- System
update()thenupdatePerEntity()are called in a single update thread,
- System
- Python Systems continue from
connect_to_sim()call
-
Stopping- (If Async Scheduler) Wait for an update/updatePerEntity loop to complete for each System.
-
end()for each System. - After all Systems
end(), Compute_Server program is allowed to terminate.
If the program is terminated by other means such as a Ctrl-C in the terminal or through Task Manager, the program terminates immediately without finishing init/update stuff or calling the end() function.
Space Teams has an Event feature to fill the gap of modeling individual events such as a button being pushed instead of constantly-updating effects such as gravity. An Event Listener can be added with an ID it's listening for from anywhere in the program, and Events that were dispatched from anywhere in the sim with that ID execute a lambda function "delegate" that was supplied when adding the Listener. Events are dispatched with a ParamMap "payload", which is supplied to any delegates it executes.
The user must be careful when using Events to implement complex behavior, as they violate the design constraint of each C++ and Python System Instance having its own single thread. Event Listener delegate functions are ran immediately upon reception of the dispatched Event, and that code may cause multithreading issues if the data that is accessed by the delegate is also being accessed by the code that registered the delegate. In event listener delegates, using parameter data structures and features built on top of that like entity body-fixed frames are not going to cause exceptions or crashes, but they still might introduce issues like race conditions.
Work in progress