Introduction
Setting Up
This page describes how to set up Dies for development.
Requirements
To compile the project all you need a recent version of stable Rust. We recommend using rustup to manage your Rust installation.
For development, you should make sure that rustfmt
and clippy
are installed. You can install them with rustup component add rustfmt clippy
.
To use the ER-Force simulator, you will also need to install a recent version of Docker and ensure that the Docker daemon is running.
Building and Running
Dies uses cargo workspaces to manage its crates. The dies-cli
crate contains the Dies CLI, which can be used to run matches.
To run tests for all crates, run cargo test
in the root directory. You can run specific tests by specifying the crate name, eg. cargo test -p dies-core
.
To run the CLI from the workspace root, use cargo run
.
TODO: describe web UI.
Developer Guide
This document provides some guidelines for developing Dies.
Using Git
The main branch should remain stable at all times. All new features and fixes should be developed in feature branches, and merged into main only after they are complete and have been tested. Feature branches should be named <name of author>/<feature name>
, eg. balint/fix-ersim-env-win
.
The CI will run tests and formatting checks on all branches, but you should run these locally before pushing your changes.
Commit names should adhere to a lite version of the Conventional Commits spec, ie. they should follow the format: <commit type>: <short description>
. Scopes are not needed. The commit type should be one of the following:
feat
: new featurefix
: bug fixrefactor
: refactoringdocs
: documentationtest
: adding testschore
: other changes (eg. CI, formatting, etc.)wip
: work in progressmisc
: other changes
Code Style
Dies uses the rustfmt formatter. It is recommended to use the rust-analyzer extension for VS Code, and configure it to automatically format the code on save.
We also use clippy for linting. The VS Code extension should also automatically run clippy on save.
In general, the Rust code should be idiomatic, and should attempt to follow the Rust API Guidelines.
Generics should be used with consideration. They are powerful, but they make the code more complex and significantly increase compile times. As a rule of thumb, remember YAGNI (you ain't gonna need it) -- don't overengineer, you can always add generics later if you need them.
Crate Structure
Dies is split into multiple crates. We use cargo workspaces to manage these. Having a lot of crates is not necessarily a bad thing -- crates are the unit of compilation in Rust, so more granular crates help caching and parallel compilation.
In general, the dependency graph between our crates should resemble a diamond:
+- feature crates -+
/ \
dies-core ------------> dies-cli
\ /
+- feature crates -+
There should be a few root crates (dies-core
) which will rarely change, a number of specialized feature crates (dies-basestation-client
, dies-executor
, etc.), and a few small leaf crates which just glue things together (dies-cli
).
For an up-to-date list of crates, see the README.
Error Handling
Errors should be handled according to Rust idioms. In general, we should avoid panicking, and instead propagate Result
to the very top, where it can be handled by the executor.
Throughout the codebase, the anyhow
crate should be used for error handling. Anwhow provides a convenient dynamic wrapper type for errors, so we do not have to define our own error types. anyhow::Result
should be the return type of all functions which can fail.
Logging
Logging is really important for Dies, as it is the only way to debug issues that occured during a match. Logs should contain all the information needed to replay matches, including all the data we receive from the vision system, the world state, and the commands we send to the robots.
Dies uses the tracing
crate for logging. The following log levels should be used:
error
: should be used only for fatal errors, which cause the executor to stop, ie. errors should only be logged in the executorwarn
: should be used for unexpected events or recoverable errors, which do not cause the executor to stop and do not get propagated upinfo
: should be used rarely, only for information that the user needs to know while running Dies - for example when long operations start and finish, or when error-prone operations succeeddebug
: should be used most of the time: everything from the data we receive from the vision system, commands we send to the robots, to all minor events should be logged at this level
We do not use trace
.
println!
and all other forms of direct output should be avoided, as they are not captured by the logger.
Testing
Reliability is very important for Dies, so we aim for high test coverage. All new features should come with corresponding unit tests. Unit tests should test the code located in the module they are in, and should avoid relying on other modules.
Property-based testing should be explored for testing the core logic of Dies. See the proptest crate for more details on this technique.
Documentation
This section is WIP.
Dies Architecture
Dies is Delft Mercurians' open source framework for building AIs, specifically the central team controller, for RoboCup Small Size League.
Dies runs on a central server, and communicates with the tournament software (ssl-vision/simulator, game controller), and with the robots on the field. It is responsible for running the AI, and for sending commands to the robots.
graph TD GameController[/Game Controller/] <--> Dies SSLVision[/SSL Vision/] --> Dies Robots[/Robots/] <--> Dies
Overview
Dies is desigend to be modular and flexible, allowing for easy integration of new features and algorithms. At its core is the Executor
, which is responsible for setting up the environment, running the AI, processing incoming data, communicating with the game controller and with the robots on the field or a simulator.
The executor interfaces with the world through the SSLClient
, which is responsible for communicating with the game controller and the SSL Vision, and with the BasestationClient
(aka. base station client), which is responsible for communicating with the robots.
Data coming from the vision is processed by World
, which is responsible for keeping track of the state of the game world, and for filtering and processing incoming data.
The behavior of the players is determined by the TeamController
. This in turn creates a PlayerController
and a Role
for each player, with the former handling lower level control (position, heading, failsafes) and the latter dealing with strategy and tactics. The TeamController also uses a Strategy
instance to determine the assignment of Role
s depending on the current game state.
Role
s function as actors in an actor model. They can be run concurrently and make independent decisions. They can also communicate with each other through the TeamController
.
Role
s can in turn use skills to perform common reusable and composable tasks, such as moving to a position, or kicking the ball.
flowchart LR SSL[SSL Client] -- Vision + Referee Data --> Executor BS[Basestation Client] <-- Robot Commands/Feedback --> Executor Executor -- Data from environment --> World World -- Filtered Data --> Executor Executor -- World Data --> Team[Team Controller] Team -- Player Commands --> Executor Team <-- World Data/Player Commands --> Strategy[Strategy] Strategy --> Role1[Role] Strategy --> Role2[Role] Strategy --> Role3[Role] Team <--> Player1[Player Controller] Team <--> Player2[Player Controller] Team <--> Player3[Player Controller]
Crates
Dies is split into several crates, each with a specific purpose:
dies-core
: Contains the most widely used types and traits. Should be kept slim.dies-executor
: Contains theExecutor
,TeamController
,PlayerController
,Role
, andStrategy
types.dies-basestation-client
: Contains theBasestationClient
type.dies-ssl-client
: Contains theSSLClient
type.dies-protos
: Contains the protobuf definitions used for communication with the game controller and vision.dies-simulator
: Contains theSimulator
type, which can be used to replace theSSLClient
andBasestationClient
with a simulator for testing.dies-world
: Contains theWorld
type, which is used to represent the state of the game world, as well as filters and processors for incoming data.dies-webui
: Contains the backend for the web interface, which can be used to monitor and control the AI. The frotend is in thewebui
directory.dies-cli
: Contains the command line interface for running the AI. This is the main entry point for the framework.