mirror of
https://github.com/BotChain-Robots/firmware.git
synced 2026-07-08 17:47:21 +02:00
Add support for ACK response for Generic Frame Fragments
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -4,4 +4,6 @@
|
||||
sdkconfig.old
|
||||
.vscode/*
|
||||
/.cache
|
||||
test/build/*
|
||||
test/build/*
|
||||
build_A
|
||||
build_B
|
||||
48
README.md
48
README.md
@@ -90,11 +90,17 @@ Build the project
|
||||
idf.py build
|
||||
```
|
||||
|
||||
Flash and open serial monitor
|
||||
Flash and open serial monitor (Use Ctrl+T then Ctrl+X to exit, or Ctrl+C to force terminate the python script)
|
||||
```
|
||||
idf.py flash monitor
|
||||
```
|
||||
|
||||
Use the `-p <port>` or `--port <port>` flag to specify which board to build, flash, and/or monitor:
|
||||
```
|
||||
idf.py build flash monitor -p COM3
|
||||
```
|
||||
Ensure the port exists. Check Device Manager to see available ports on Windows.
|
||||
|
||||
### Using an IDE <a name="UsinganIDE"></a>
|
||||
Any IDE that supports CMake or has an ESP-IDF extension should be compatible with this project.
|
||||
|
||||
@@ -108,3 +114,43 @@ CLion is compatible with this project. Follow [the tutorial on Espressif's site]
|
||||
- When asked to select a project, simply import the root directory of this repository (ie. the `firmware` folder).
|
||||
- If the `Open Project Wizard` does not open, access it via `Settings > Build, Execution, Deployment > CMake`.
|
||||
- When setting the `DIDF_TARGET`, ensure you specify `esp32s3`.
|
||||
|
||||
# me when it works
|
||||
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⡤⣖⣤⣶⣿⣿⣿⣿⣿⣭⡶⠶⠒⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠈⠉⠁⠒⠤⠀⡔⠄⠀⠂
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⡴⢋⣥⣾⣿⣿⣿⣿⣿⣿⡿⠛⠁⢀⣠⠔⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠛⠓⢤⡀⠀⠀⢀⣀⠈⠂⠉⠀
|
||||
⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠘⣿⣀⢻⡟⠉⣷⠀⠀⠀⠀⠀⠀⢀⡤⠎⠁⣀⣿⣿⣿⣿⣿⣿⣿⠟⢉⣠⣶⡾⠋⠀⠀⠀⠀⣀⣠⡤⢶⡾⠶⠀⠀⠀⠀⠀⠉⠶⣽⣾⣿⣷⣄⡀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣶⣬⣺⡟⠀⠀⠀⠀⢀⡴⠋⢀⣤⣾⣿⣿⣿⣿⣿⣿⡿⣣⣴⡿⠟⠁⠀⠀⣠⣤⣶⡿⠟⢉⣴⠋⠁⠀⠀⢀⡞⠀⠀⣦⠀⠙⢿⣿⣿⣿⣿⣦
|
||||
⠀⠀⠀⠀⠀⠰⡄⠀⠀⠀⠀⠀⠀⠀⠈⠁⠀⠀⠀⢀⣴⣯⣶⣿⣿⣿⡿⣽⣿⣿⣿⣿⣿⣿⢿⠋⡀⣀⣤⣾⠿⣿⡿⢋⣴⣾⠟⠀⠀⠀⠀⣴⠋⠀⠀⠀⢹⣦⠠⢪⣻⣿⣿⣿⣿
|
||||
⠀⠀⠀⠀⠀⠀⠻⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣻⢋⡞⣿⣿⣿⣿⣿⣿⣿⣷⣾⡿⠟⠉⣱⡾⣯⡶⣿⡿⠃⠀⠀⠀⢀⡼⠃⢠⣆⠀⠀⣇⣿⡆⡜⡏⢿⣿⣿⣿
|
||||
⠀⠀⠀⠀⠀⠀⠀⢷⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⣿⡟⠁⠀⠁⣰⣿⣿⣿⣿⣿⣿⡿⠛⠁⠀⢠⣾⣿⠟⢡⣾⣿⠅⠀⠀⠀⣠⡿⠁⣰⣿⢸⢸⡆⣿⣿⣿⣼⣼⡈⣿⣿⣿
|
||||
⠀⠀⠀⠀⠀⠀⠀⠈⡇⠀⠀⠀⠀⠀⠀⠀⣴⣿⠟⣹⣿⡏⠀⠀⠀⢰⣿⣿⣿⣿⡿⠟⠉⠀⠀⠀⣰⣿⡿⠃⢠⣾⣿⠞⠀⠀⡴⣷⣿⠅⠀⣿⣿⢸⣾⡇⢻⣿⣿⣿⣿⣇⠸⣿⣿
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⢰⡿⠁⣸⣿⡟⢠⢀⢠⢀⣿⣿⣿⡿⠋⠀⠀⠀⠀⠀⢰⣿⠟⠀⢠⣿⣿⡟⠀⢀⣼⣿⣿⡿⠀⢰⣿⣿⡟⣿⣇⢸⣿⣿⣿⣿⣿⠀⣿⣿
|
||||
⠀⠀⠀⠀⢰⡾⢶⣀⣥⣤⡀⠀⠀⠀⢰⡟⠁⣴⣿⣿⣧⡟⣾⢋⣼⣿⡿⠋⠀⠀⠀⠀⠀⠀⢠⣿⠏⠀⠀⣼⣿⣿⠀⢀⣾⣿⣳⡿⠁⠀⡇⣿⣿⣿⣿⣿⡸⣿⣿⣿⣿⣿⡀⢻⡏
|
||||
⠀⠀⠀⠀⢸⣇⠀⠻⣇⣸⡇⠀⠀⢀⡞⠀⣼⢿⣿⣿⣿⣹⡟⣾⡿⠋⠑⠦⠤⠤⣀⣀⣀⠤⣾⡿⠂⠀⢸⣿⣿⡇⢀⣾⡿⢱⡿⠁⠀⠐⠁⣿⣿⣿⣿⣿⣇⣿⣿⣿⣿⣿⡇⢸⡇
|
||||
⠀⠀⠀⠀⠀⠙⢷⣴⠟⠋⠀⠀⠀⡾⠀⢰⠃⢈⣿⣿⣿⣿⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⣄⡏⠀⠀⠀⢸⣿⡿⢀⣾⡿⣇⡾⠁⠀⠀⠀⠀⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⢸⡇
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠃⢰⠇⠀⠸⢸⣿⣿⣿⢋⡀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠸⠃⠀⠀⠀⢸⣿⡇⣾⡿⠁⡸⢣⡀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⢸⡇
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢺⣠⠏⢀⡀⠀⣸⣿⣿⡏⢀⣹⣶⣶⣶⣿⣯⡒⢆⡀⠀⠀⠀⠀⠀⠀⢸⣿⢳⡿⠀⢰⠇⠀⠙⢦⡀⢀⠀⠀⠸⣿⣿⣿⡿⣿⣿⣿⣿⣿⡃⣼⠁
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⠋⣠⠞⠀⣼⢿⣿⣿⣱⡿⠋⠁⢠⣴⣿⣿⣿⣧⡀⠀⠀⠀⠀⠀⠀⠸⣿⣿⠃⠀⠀⠀⠀⠀⠀⠙⢮⡄⠀⠀⢻⣿⣿⣿⢿⣿⣿⣿⣿⠀⣿⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡾⣁⠞⠁⢀⣾⣿⢸⣿⡿⡿⠀⠀⣰⣿⣿⣿⣿⡟⢻⡇⠀⠀⠀⠀⠀⠀⠀⢻⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠲⣀⠈⢿⣿⣿⠈⣿⣿⣿⣿⢠⣿⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⠟⠁⢀⣴⣿⣿⣿⣾⣿⣿⠃⠀⢠⡟⠉⠙⠛⢻⣷⣿⡇⠀⠀⠀⠀⠀⠀⠀⠸⠃⠀⠀⠀⠀⢠⣤⣀⠀⡀⠀⠀⠀⠙⠚⢿⣿⣧⠸⣿⣿⣿⣾⣷⢰
|
||||
⠀⠀⠀⠀⠀⠀⢀⣴⡿⢛⡥⢀⣴⣿⣿⣿⠟⠀⢹⣿⣧⠀⠀⠘⢇⠀⣀⡀⣸⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⣿⣿⣷⣿⣤⡀⠀⠀⠀⠈⢿⣿⣆⠸⣿⣿⣿⣿⣾
|
||||
⠀⠀⠀⢀⣠⠾⠋⢉⣴⣫⣶⣿⣿⡿⠋⠁⠀⠀⠀⣿⣿⡄⠀⠈⢿⡀⠈⠉⢁⡼⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣷⣧⡀⠀⠈⢻⣿⣆⠹⣿⣿⣿⣿
|
||||
⠀⠀⠀⠈⢱⣄⣾⣯⣿⣿⠟⠋⠀⠀⠀⠀⠀⣠⣼⠃⠙⢷⠀⠐⠚⠿⠖⠚⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⠛⠿⢿⣿⡏⢙⡏⠻⣿⣆⠀⠀⠻⣿⣷⣿⣿⣿⣿
|
||||
⡀⠀⠀⣠⣿⣿⣿⠟⣹⣵⣤⣤⣤⣤⣤⣤⡼⢿⡏⠀⠀⠀⠙⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⡃⠀⠀⠀⠀⢘⣿⣿⠇⠀⠹⣿⡆⠀⠀⢸⣏⣿⣿⣿⣿
|
||||
⣡⣴⣾⣻⣿⠟⣩⣾⣿⣿⣿⣿⣿⣿⣿⡏⠀⣸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢉⣳⠀⢤⣀⣠⠞⢉⡿⠀⠀⠀⢹⣷⠀⢀⣾⣾⣿⣿⣿⣿
|
||||
⣿⣿⢣⣿⣗⣼⣿⣿⡿⠿⢿⡟⠛⠛⠛⣷⠞⠋⠛⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣧⠀⠀⠀⠀⣠⠟⠀⠀⠀⠀⣸⠇⢀⣾⣾⣿⢿⣿⣿⣿
|
||||
⣿⣿⠠⣿⠿⣿⡁⠈⠀⠀⠀⠑⣄⣠⠞⠁⠀⠀⠀⣀⡹⠶⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣒⠖⠋⠁⠀⠀⠀⠀⠞⠁⣰⣿⣿⠟⢡⣿⡿⠋⠀
|
||||
⡋⠙⣸⠃⠀⠈⠻⣦⠀⢀⡴⣶⠟⠁⠀⠀⣀⡴⠚⠁⠀⠀⠀⠙⣆⠀⠀⠀⢦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣾⡿⢟⡥⣺⣿⠟⠀⠊⠀
|
||||
⠀⠀⠃⠀⠀⠀⠀⢈⡿⠋⣰⠇⠀⠀⠐⠚⠁⠀⠀⠀⠀⣠⠴⠛⠋⠓⣆⠀⠈⢇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣛⣻⠵⠚⢁⡴⠟⠁⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⣰⠏⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⣠⠖⠋⠀⠀⠀⠀⠀⢸⣦⠀⠈⠳⠤⠴⠖⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⠊⠉⠉⠀⢀⣤⠶⠭⠄⠀⠀⠀⠀⠀⢀
|
||||
⠀⠀⠀⠀⠀⡼⠁⠀⠀⠀⠘⢦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⠞⠉⠈⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣶⠋⠁⠀⠀⠀⠀⢀⣀⣀⠐⠃
|
||||
⠀⠀⠀⠀⡼⠁⠀⠀⠀⠀⠀⡎⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠖⠋⠀⢀⠀⠀⢸⠀⠀⠀⡤⠤⠤⣤⡴⠒⠒⠒⢦⡖⠒⠲⠤⣄⣀⡀⠀⣀⣤⣾⡋⠉⠓⠶⠖⠚⠛⠉⠁⠀⠀⠀⢀
|
||||
⠀⠀⠀⡸⠁⠀⠀⠀⠀⠀⠀⡇⠀⠈⢣⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠿⣦⡞⠀⢠⡞⠀⠀⠀⠈⡇⠀⠀⠀⢸⠇⠀⠀⠀⡇⠈⠉⠙⢿⣿⣿⣿⣆⠀⠀⠀⠀⢠⠀⠀⣠⣀⣠⣿
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⡇⠀⡇⠀⢸⡓⢤⡀⠀⠀⠀⠀⠀⠀⢀⡴⢻⠉⢹⣿⠇⠀⠀⠀⠀⡇⠀⠀⠀⡸⠀⠀⠀⢰⡇⠀⠀⠀⢸⣿⣿⡿⠟⠳⠤⠖⠶⠤⣤⣴⣿⣌⢿⣿
|
||||
⠀⠀⠀⠀⠀⠀⢀⠴⠋⠉⠁⡇⠀⠀⠀⠀⠇⠀⠉⠑⠶⢤⡤⠴⠞⠉⠄⣼⣠⠾⠿⠀⠀⠀⠀⠀⠃⠀⠀⠀⠃⠀⠀⠀⠸⠁⠀⠀⢰⠟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠦⡹
|
||||
⠠⠂⠀⠀⠀⠀⢸⠀⠀⠀⠀⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣲⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡏⠘⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⠃⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠁⠀⠀⠀⠀⠀⠙⠓⠤⢤⣤⣀⣀⣀⣀⣀⣠⡤⢖⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠀⠀⠀⠀⠀⠀⣀⠀⠀⠀⠀⢀⠀⠀⢀⡔⠉⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
@@ -1,6 +1,12 @@
|
||||
#include "DataLinkManager.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
/**
|
||||
* @brief Creates a Control Frame from `FrameHeader`
|
||||
*
|
||||
* @param header
|
||||
* @return ControlFrame
|
||||
*/
|
||||
ControlFrame make_control_frame_from_header(const FrameHeader& header) {
|
||||
ControlFrame frame{};
|
||||
frame.preamble = header.preamble;
|
||||
@@ -13,6 +19,12 @@ ControlFrame make_control_frame_from_header(const FrameHeader& header) {
|
||||
return frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a Generic Frame from `FrameHeader`
|
||||
*
|
||||
* @param header
|
||||
* @return GenericFrame
|
||||
*/
|
||||
GenericFrame make_generic_frame_from_header(const FrameHeader& header) {
|
||||
GenericFrame frame{};
|
||||
frame.preamble = header.preamble;
|
||||
@@ -27,6 +39,13 @@ GenericFrame make_generic_frame_from_header(const FrameHeader& header) {
|
||||
return frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Store a fragment that has been received
|
||||
*
|
||||
* @param fragment
|
||||
* @param channel
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::store_fragment(GenericFrame* fragment, uint8_t channel){
|
||||
if (fragment == nullptr){
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
@@ -40,8 +59,18 @@ esp_err_t DataLinkManager::store_fragment(GenericFrame* fragment, uint8_t channe
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (fragment_map[fragment->receiver_id].find(fragment->seq_num) == fragment_map[fragment->receiver_id].end()){
|
||||
FragmentMetadata& metadata = fragment_map[fragment->receiver_id][fragment->seq_num];
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "got frame %d, fragment %d of %d", fragment->seq_num, fragment->frag_num, fragment->total_frag);
|
||||
|
||||
if (rx_fragment_mutex[channel] == NULL){
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(rx_fragment_mutex[channel], pdMS_TO_TICKS(ASYNC_QUEUE_WAIT_TICKS)) != pdTRUE){
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (fragment_map[channel][fragment->receiver_id].find(fragment->seq_num) == fragment_map[channel][fragment->receiver_id].end()){
|
||||
FragmentMetadata& metadata = fragment_map[channel][fragment->receiver_id][fragment->seq_num];
|
||||
metadata.num_fragments_rx = 0;
|
||||
|
||||
metadata.fragments.reserve(fragment->total_frag);
|
||||
@@ -50,21 +79,51 @@ esp_err_t DataLinkManager::store_fragment(GenericFrame* fragment, uint8_t channe
|
||||
}
|
||||
}
|
||||
|
||||
FragmentMetadata& metadata = fragment_map[fragment->receiver_id][fragment->seq_num];
|
||||
if (fragment->frag_num >= metadata.fragments.size()){
|
||||
FragmentMetadata& metadata = fragment_map[channel][fragment->receiver_id][fragment->seq_num];
|
||||
if ((fragment->frag_num-1) > metadata.fragments.size()){
|
||||
xSemaphoreGive(rx_fragment_mutex[channel]);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (metadata.fragments[fragment->frag_num].data_len ==0){
|
||||
metadata.fragments[fragment->frag_num] = *fragment;
|
||||
if (metadata.fragments[fragment->frag_num-1].data_len == 0){
|
||||
metadata.fragments[fragment->frag_num-1] = *fragment;
|
||||
metadata.num_fragments_rx++;
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "store frame %d fragment %d success; got %d out of %d", fragment->seq_num, fragment->frag_num, metadata.num_fragments_rx, metadata.fragments.size());
|
||||
}
|
||||
|
||||
if (metadata.num_fragments_rx == metadata.fragments.size()){
|
||||
uint16_t last_consec_rx_frag = 0;
|
||||
if (static_cast<FrameType>(GET_TYPE(fragment->type_flag)) != FrameType::MISC_UDP_GENERIC_TYPE){
|
||||
for (; last_consec_rx_frag < fragment->total_frag; last_consec_rx_frag++){
|
||||
if (metadata.fragments[last_consec_rx_frag].data_len == 0){
|
||||
//found missing fragment
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t metadata_fragment_size = metadata.fragments.size();
|
||||
xSemaphoreGive(rx_fragment_mutex[channel]);
|
||||
|
||||
if (static_cast<FrameType>(GET_TYPE(fragment->type_flag)) != FrameType::MISC_UDP_GENERIC_TYPE){
|
||||
SendAckMetaData data = {
|
||||
.data = {GENERIC_FRAG_ACK_PREAMBLE, static_cast<uint8_t>((last_consec_rx_frag & 0xFF00) >> 8), static_cast<uint8_t>(last_consec_rx_frag & 0xFF),
|
||||
static_cast<uint8_t>((fragment->total_frag & 0xFF00) >> 8), static_cast<uint8_t>(fragment->total_frag & 0xFF),
|
||||
static_cast<uint8_t>((fragment->seq_num & 0xFF00) >> 8), static_cast<uint8_t>(fragment->seq_num & 0xFF)},
|
||||
.sender_id = fragment->sender_id,
|
||||
};
|
||||
if (xSemaphoreTake(send_ack_queue_mutex[channel], pdMS_TO_TICKS(SEND_ACK_MUTEX_WAIT)) != pdTRUE){
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
send_ack_queue[channel].push(data);
|
||||
xSemaphoreGive(send_ack_queue_mutex[channel]);
|
||||
}
|
||||
|
||||
if (metadata.num_fragments_rx == metadata_fragment_size){
|
||||
//all fragments received
|
||||
return complete_fragment(fragment->receiver_id, fragment->seq_num, channel);
|
||||
}
|
||||
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -76,23 +135,44 @@ esp_err_t DataLinkManager::store_fragment(GenericFrame* fragment, uint8_t channe
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::complete_fragment(uint16_t board_id, uint16_t sequence_num, uint8_t channel){
|
||||
if (fragment_map[board_id].find(sequence_num) == fragment_map[board_id].end()){
|
||||
Rx_Metadata rx;
|
||||
|
||||
if (xSemaphoreTake(rx_fragment_mutex[channel], pdMS_TO_TICKS(ASYNC_QUEUE_WAIT_TICKS)) != pdTRUE){
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (fragment_map[channel][board_id].find(sequence_num) == fragment_map[channel][board_id].end()){
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
FragmentMetadata& metadata = fragment_map[board_id][sequence_num];
|
||||
FragmentMetadata& metadata = fragment_map[channel][board_id][sequence_num];
|
||||
if (metadata.num_fragments_rx != metadata.fragments.size()){
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
Rx_Metadata rx;
|
||||
uint16_t total_data_len = metadata.num_fragments_rx*MAX_CONTROL_DATA_LEN; //max data size with n fragments
|
||||
uint16_t total_data_len = metadata.num_fragments_rx*MAX_FRAME_SIZE; //max data size with n fragments
|
||||
xSemaphoreGive(rx_fragment_mutex[channel]);
|
||||
|
||||
uint8_t* combined_data = (uint8_t*)pvPortMalloc(total_data_len);
|
||||
rx.data_len = total_data_len;
|
||||
if (combined_data == nullptr){
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if (rx_fragment_mutex[channel] == NULL){
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "completing %d fragments for frame %d", metadata.num_fragments_rx, sequence_num);
|
||||
|
||||
if (xSemaphoreTake(rx_fragment_mutex[channel], pdMS_TO_TICKS(ASYNC_QUEUE_WAIT_TICKS)) != pdTRUE){
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (fragment_map[channel][board_id].find(sequence_num) == fragment_map[channel][board_id].end()){
|
||||
xSemaphoreGive(rx_fragment_mutex[channel]);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
rx.data = combined_data;
|
||||
uint16_t prev_index = 0;
|
||||
for (size_t i = 0; i < metadata.num_fragments_rx; i++){
|
||||
@@ -100,6 +180,8 @@ esp_err_t DataLinkManager::complete_fragment(uint16_t board_id, uint16_t sequenc
|
||||
prev_index += metadata.fragments[i].data_len;
|
||||
}
|
||||
|
||||
xSemaphoreGive(rx_fragment_mutex[channel]);
|
||||
|
||||
rx.data_len = prev_index;
|
||||
|
||||
if (async_rx_queue_mutex[channel] == nullptr){
|
||||
@@ -119,6 +201,8 @@ esp_err_t DataLinkManager::complete_fragment(uint16_t board_id, uint16_t sequenc
|
||||
.crc_16 = 0
|
||||
};
|
||||
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "pushing frame %d onto async rx queue", sequence_num);
|
||||
|
||||
if (xSemaphoreTake(async_rx_queue_mutex[channel], pdMS_TO_TICKS(ASYNC_QUEUE_WAIT_TICKS)) != pdTRUE){
|
||||
vPortFree(combined_data);
|
||||
return ESP_ERR_TIMEOUT;
|
||||
@@ -128,15 +212,31 @@ esp_err_t DataLinkManager::complete_fragment(uint16_t board_id, uint16_t sequenc
|
||||
|
||||
xSemaphoreGive(async_rx_queue_mutex[channel]);
|
||||
|
||||
fragment_map[board_id].erase(sequence_num);
|
||||
fragment_map[channel][board_id].erase(sequence_num);
|
||||
|
||||
if (fragment_map[board_id].empty()) {
|
||||
fragment_map.erase(board_id);
|
||||
if (fragment_map[channel][board_id].empty()) {
|
||||
fragment_map[channel].erase(board_id);
|
||||
}
|
||||
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "frame %d pushed success", sequence_num);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends an ACK
|
||||
*
|
||||
* @param sender_id This is the board id that is receiving the ACK (the original sender board id)
|
||||
* @param data
|
||||
* @param data_len
|
||||
* @return esp_err_t
|
||||
*
|
||||
* @note This may be moved to a private function - Unsure if users should be able to manually send ACKs
|
||||
*/
|
||||
esp_err_t DataLinkManager::send_ack(uint8_t sender_id, uint8_t* data, uint16_t data_len){
|
||||
return send(sender_id, data, data_len, FrameType::ACK_TYPE, 0x0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks the channel receive queue for any received frames. If there is, return the first frame's data size
|
||||
*
|
||||
@@ -214,11 +314,13 @@ esp_err_t DataLinkManager::async_receive(uint8_t* data, uint16_t data_len, Frame
|
||||
memcpy(data, top.data, top.data_len);
|
||||
vPortFree(top.data);
|
||||
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "pushed frame %d onto async queue", header->seq_num);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t DataLinkManager::receive_rmt(uint8_t channel){
|
||||
uint16_t data_len = MAX_CONTROL_DATA_LEN+GENERIC_FRAME_OVERHEAD; //max possible data len
|
||||
uint16_t data_len = MAX_FRAME_SIZE; //max possible data len
|
||||
uint8_t data[data_len];
|
||||
memset(data, 0, data_len);
|
||||
|
||||
@@ -231,7 +333,7 @@ esp_err_t DataLinkManager::receive_rmt(uint8_t channel){
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (recv_len > MAX_CONTROL_DATA_LEN + GENERIC_FRAME_OVERHEAD){
|
||||
if (recv_len > MAX_FRAME_SIZE){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Received frame is too large to be control or generic");
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
@@ -241,7 +343,7 @@ esp_err_t DataLinkManager::receive_rmt(uint8_t channel){
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
uint8_t message[MAX_CONTROL_DATA_LEN];
|
||||
uint8_t message[MAX_FRAME_SIZE];
|
||||
memset(message, 0, sizeof(message));
|
||||
size_t message_size = 0;
|
||||
FrameHeader header;
|
||||
@@ -254,10 +356,37 @@ esp_err_t DataLinkManager::receive_rmt(uint8_t channel){
|
||||
|
||||
// print_buffer_binary(message, message_size);
|
||||
|
||||
//push control frame onto async_receive_queue
|
||||
if (static_cast<FrameType>(GET_TYPE(header.type_flag)) == FrameType::ACK_TYPE){
|
||||
if (message_size != GENERIC_FRAG_ACK_DATA_SIZE || message_size == 0){
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (message[0] != GENERIC_FRAG_ACK_PREAMBLE){
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
FrameAckRecord record = {
|
||||
.last_ack = static_cast<uint16_t>((message[1] << 8) | (message[2])),
|
||||
.total_frags = static_cast<uint16_t>((message[3] << 8) | (message[4])),
|
||||
.seq_num = static_cast<uint16_t>((message[5] << 8) | (message[6]))
|
||||
};
|
||||
|
||||
res = inc_head_sliding_window(channel, header.sender_id, record.seq_num, &record);
|
||||
|
||||
// if (res == ESP_OK){
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Got ACK for seq number %d from board %d! Highest Conseq ACK: 0x%X%X Total Frag: 0x%X%X", record.seq_num, header.sender_id, message[1], message[2], message[3], message[4]);
|
||||
// } else {
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Got ACK for seq number %d from board %d but got a lower conseq ack 0x%x%X Total Frag: 0x%X%X", record.seq_num, header.sender_id, message[1], message[2], message[3], message[4]);
|
||||
// }
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (!IS_CONTROL_FRAME(header.type_flag)){
|
||||
//Handle generic frame fragment
|
||||
GenericFrame frame = make_generic_frame_from_header(header);
|
||||
if (message_size > MAX_CONTROL_DATA_LEN){
|
||||
if (message_size > MAX_FRAME_SIZE){
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@@ -268,16 +397,16 @@ esp_err_t DataLinkManager::receive_rmt(uint8_t channel){
|
||||
|
||||
//control frame handling: - TODO: clean up :)
|
||||
memcpy(data, message, message_size);
|
||||
ESP_LOGI(DEBUG_LINK_TAG, "Received frame of type 0x%X destined for board %d", GET_TYPE(header.type_flag), header.receiver_id);
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Received frame of type 0x%X destined for board %d", GET_TYPE(header.type_flag), header.receiver_id);
|
||||
|
||||
//check for a rip frame
|
||||
if (static_cast<FrameType>(GET_TYPE(header.type_flag)) == FrameType::RIP_TABLE_CONTROL){
|
||||
// printf("Got a RIP frame\n");
|
||||
ESP_LOGI(DEBUG_LINK_TAG, "Got a RIP frame");
|
||||
|
||||
for (size_t i = 0; i < message_size-1; i+=2){
|
||||
uint8_t board_id = message[i];
|
||||
uint8_t hops = message[i+1];
|
||||
ESP_LOGI(DEBUG_LINK_TAG, "Received: board_id %d and number of hops %d on channel %d", board_id, hops, channel);
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Received: board_id %d and number of hops %d on channel %d", board_id, hops, channel);
|
||||
|
||||
RIPRow* entry = nullptr;
|
||||
|
||||
@@ -318,16 +447,25 @@ esp_err_t DataLinkManager::receive_rmt(uint8_t channel){
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//got frame but not destined for this board
|
||||
if (header.receiver_id != this_board_id && header.receiver_id != BROADCAST_ADDR && header.seq_num > sequence_num_map[header.receiver_id]){
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Sending message to board %d with message %s", header.receiver_id, message);
|
||||
res = send(header.receiver_id, message, message_size, FrameType::DEBUG_CONTROL_TYPE, 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
//push control frame onto async_receive_queue
|
||||
uint16_t seq_num = 0;
|
||||
|
||||
res = get_sequence_num(header.receiver_id, &seq_num);
|
||||
|
||||
if (res != ESP_OK){
|
||||
return res;
|
||||
}
|
||||
|
||||
//got frame but not destined for this board
|
||||
if (header.receiver_id != this_board_id && header.receiver_id != BROADCAST_ADDR && header.seq_num > seq_num){
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Sending message to board %d with message %s", header.receiver_id, message);
|
||||
res = send(header.receiver_id, message, message_size, FrameType::MISC_CONTROL_TYPE, 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint8_t* metadata_message = (uint8_t*)pvPortMalloc(message_size);
|
||||
if (metadata_message == nullptr){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to malloc for receive");
|
||||
@@ -370,7 +508,48 @@ esp_err_t DataLinkManager::receive_rmt(uint8_t channel){
|
||||
res = link_layer_obj->start_receive_frames_rmt(i);
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(SCHEDULER_PERIOD_MS));
|
||||
vTaskDelay(pdMS_TO_TICKS(RECEIVE_TASK_PERIOD_MS));
|
||||
}
|
||||
|
||||
vTaskDelete(nullptr);
|
||||
}
|
||||
|
||||
[[noreturn]] void DataLinkManager::send_ack_thread_main(void* args){
|
||||
DataLinkManager* link_layer_obj = static_cast<DataLinkManager*>(args);
|
||||
if (link_layer_obj == nullptr || link_layer_obj->manual_broadcasts == nullptr){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Send Ack thread failed to start due to invalid pointer");
|
||||
vTaskDelete(nullptr);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < link_layer_obj->num_channels; i++){
|
||||
if (link_layer_obj->send_ack_queue_mutex[i] == NULL){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "%d send ack queue mutex is null!", i);
|
||||
vTaskDelete(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(DEBUG_LINK_TAG, "Starting Send ACK task");
|
||||
|
||||
esp_err_t res;
|
||||
|
||||
while(!link_layer_obj->stop_tasks){
|
||||
for (uint8_t channel = 0; channel < link_layer_obj->num_channels; channel++){
|
||||
vTaskDelay(pdMS_TO_TICKS(SEND_ACK_PERIOD_MS));
|
||||
|
||||
if (xSemaphoreTake(link_layer_obj->send_ack_queue_mutex[channel], pdMS_TO_TICKS(SEND_ACK_MUTEX_WAIT)) != pdTRUE){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (link_layer_obj->send_ack_queue[channel].empty()){
|
||||
xSemaphoreGive(link_layer_obj->send_ack_queue_mutex[channel]);
|
||||
continue;
|
||||
}
|
||||
|
||||
SendAckMetaData data = link_layer_obj->send_ack_queue[channel].front();
|
||||
link_layer_obj->send_ack_queue[channel].pop();
|
||||
link_layer_obj->send_ack(data.sender_id, data.data, GENERIC_FRAG_ACK_DATA_SIZE);
|
||||
xSemaphoreGive(link_layer_obj->send_ack_queue_mutex[channel]);
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelete(nullptr);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "nvs_flash.h"
|
||||
|
||||
/**
|
||||
* @brief Construct a new Data Link Manager:: Data Link Manager object
|
||||
* @brief Constructs a new Data Link Manager object
|
||||
*
|
||||
* @param board_id Board ID of the current board. Will be written to the NVM under key "board" if not already written.
|
||||
*/
|
||||
@@ -16,22 +16,84 @@ DataLinkManager::DataLinkManager(uint8_t board_id, uint8_t num_channels = MAX_CH
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t existing_board_id = 0;
|
||||
get_board_id(existing_board_id);
|
||||
|
||||
this_board_id = board_id;
|
||||
set_board_id(this_board_id);
|
||||
if (existing_board_id != board_id){
|
||||
set_board_id(this_board_id);
|
||||
}
|
||||
|
||||
this->num_channels = num_channels;
|
||||
|
||||
sequence_num_map_mutex = xSemaphoreCreateMutex();
|
||||
|
||||
init_scheduler();
|
||||
init_rip();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns if the link layer is ready to receive frames
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::ready(){
|
||||
return (phys_comms == nullptr || rip_broadcast_task == NULL || rip_ttl_task == NULL || scheduler_task == NULL || receive_task == NULL) ? ESP_FAIL : ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Atomic function to get and post increment sequence number map
|
||||
*
|
||||
* @param board_id
|
||||
* @param seq_num
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::get_inc_sequence_num(uint8_t board_id, uint16_t* seq_num){
|
||||
if (seq_num == NULL){
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(sequence_num_map_mutex, pdMS_TO_TICKS(SEQUENCE_NUM_MAP_MUTEX_MAX_WAIT_MS)) != pdTRUE){
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
*seq_num = sequence_num_map[board_id]++;
|
||||
|
||||
xSemaphoreGive(sequence_num_map_mutex);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Atomic function to get sequence number map
|
||||
*
|
||||
* @param board_id
|
||||
* @param seq_num
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::get_sequence_num(uint8_t board_id, uint16_t* seq_num){
|
||||
if (seq_num == NULL){
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(sequence_num_map_mutex, pdMS_TO_TICKS(SEQUENCE_NUM_MAP_MUTEX_MAX_WAIT_MS)) != pdTRUE){
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
*seq_num = sequence_num_map[board_id];
|
||||
|
||||
xSemaphoreGive(sequence_num_map_mutex);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
DataLinkManager::~DataLinkManager(){
|
||||
stop_tasks = true;
|
||||
|
||||
bool dummy = true;
|
||||
xQueueSend(manual_broadcasts, &dummy, 0);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(50)); //delay to allow tasks to be killed
|
||||
vTaskDelay(pdMS_TO_TICKS(100)); //delay to allow tasks to be killed
|
||||
|
||||
if (rip_broadcast_task == NULL){
|
||||
vTaskDelete(rip_broadcast_task);
|
||||
@@ -45,6 +107,14 @@ DataLinkManager::~DataLinkManager(){
|
||||
vTaskDelete(scheduler_task);
|
||||
scheduler_task = NULL;
|
||||
}
|
||||
if (receive_task == NULL){
|
||||
vTaskDelete(receive_task);
|
||||
receive_task = NULL;
|
||||
}
|
||||
if (send_ack_task == NULL){
|
||||
vTaskDelete(send_ack_task);
|
||||
send_ack_task = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t DataLinkManager::set_board_id(uint8_t board_id){
|
||||
@@ -54,13 +124,13 @@ esp_err_t DataLinkManager::set_board_id(uint8_t board_id){
|
||||
}
|
||||
|
||||
nvs_handle_t handle;
|
||||
esp_err_t res = nvs_open("board", NVS_READWRITE, &handle);
|
||||
esp_err_t res = nvs_open(NVS_BOARD_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to open NVS Handle");
|
||||
return res;
|
||||
}
|
||||
|
||||
res = nvs_set_u8(handle, "id", board_id);
|
||||
|
||||
res = nvs_set_u8(handle, NVS_BOARD_ID_KEY, board_id);
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to write ID %d to NVM", board_id);
|
||||
nvs_close(handle);
|
||||
@@ -75,7 +145,7 @@ esp_err_t DataLinkManager::set_board_id(uint8_t board_id){
|
||||
}
|
||||
|
||||
this_board_id = board_id;
|
||||
printf("Successfully wrote %d to NVM\n", board_id);
|
||||
ESP_LOGI(DEBUG_LINK_TAG, "Successfully wrote %d to NVM", board_id);
|
||||
|
||||
nvs_close(handle);
|
||||
|
||||
@@ -84,20 +154,20 @@ esp_err_t DataLinkManager::set_board_id(uint8_t board_id){
|
||||
|
||||
esp_err_t DataLinkManager::get_board_id(uint8_t& board_id){
|
||||
nvs_handle_t handle;
|
||||
esp_err_t res = nvs_open("board", NVS_READWRITE, &handle);
|
||||
esp_err_t res = nvs_open(NVS_BOARD_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to open NVS Handle");
|
||||
return res;
|
||||
}
|
||||
|
||||
res = nvs_get_u8(handle, "id", &board_id);
|
||||
res = nvs_get_u8(handle, NVS_BOARD_ID_KEY, &board_id);
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to get ID from NVM. Please make sure NVM is already assigned a board id!");
|
||||
nvs_close(handle);
|
||||
return res;
|
||||
}
|
||||
|
||||
printf("Successfully got board id %d from NVM\n", board_id);
|
||||
ESP_LOGI(DEBUG_LINK_TAG, "Successfully got board id %d from NVM", board_id);
|
||||
|
||||
nvs_close(handle);
|
||||
|
||||
@@ -125,8 +195,8 @@ esp_err_t DataLinkManager::create_control_frame(uint8_t* data, uint16_t data_len
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (data_len > MAX_CONTROL_DATA_LEN){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Data for control frame is too large. Maximum size is %d. Current data length is %d", MAX_CONTROL_DATA_LEN, data_len);
|
||||
if (data_len > MAX_FRAME_SIZE){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Data for control frame is too large. Maximum size is %d. Current data length is %d", MAX_FRAME_SIZE, data_len);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
@@ -181,6 +251,17 @@ esp_err_t DataLinkManager::create_control_frame(uint8_t* data, uint16_t data_len
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function to create a generic frame
|
||||
*
|
||||
* @param data
|
||||
* @param data_len
|
||||
* @param generic_frame
|
||||
* @param offset
|
||||
* @param send_data
|
||||
* @param send_data_len
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::create_generic_frame(uint8_t* data, uint16_t data_len, GenericFrame generic_frame, uint16_t offset, uint8_t* send_data, size_t* send_data_len){
|
||||
if (data == nullptr){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Data array does not exist");
|
||||
@@ -192,8 +273,8 @@ esp_err_t DataLinkManager::create_generic_frame(uint8_t* data, uint16_t data_len
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (data_len > MAX_CONTROL_DATA_LEN){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Data for control frame is too large. Maximum size is %d. Current data length is %d", MAX_CONTROL_DATA_LEN, data_len);
|
||||
if (data_len > MAX_FRAME_SIZE){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Data for generic frame is too large. Maximum size is %d. Current data length is %d", MAX_FRAME_SIZE, data_len);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
@@ -268,13 +349,18 @@ esp_err_t DataLinkManager::create_generic_frame(uint8_t* data, uint16_t data_len
|
||||
esp_err_t DataLinkManager::send(uint8_t dest_board, uint8_t* data, uint16_t data_len, FrameType type, uint8_t flag){
|
||||
bool isControlFrame = IS_CONTROL_FRAME((uint8_t)type);
|
||||
|
||||
if (isControlFrame && data_len > MAX_CONTROL_DATA_LEN){
|
||||
//Control frames has max data size of MAX_CONTROL_DATA_LEN
|
||||
if (isControlFrame && data_len > MAX_FRAME_SIZE){
|
||||
//Control frames has max data size of MAX_FRAME_SIZE
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!isControlFrame && data_len > MAX_GENERIC_NUM_FRAG * MAX_CONTROL_DATA_LEN){
|
||||
//Generic frames has max MAX_GENERIC_NUM_FRAG fragments, each max size of MAX_CONTROL_DATA_LEN (data size)
|
||||
if (!isControlFrame && data_len > MAX_GENERIC_NUM_FRAG * MAX_GENERIC_DATA_LEN){
|
||||
//Generic frames has max MAX_GENERIC_NUM_FRAG fragments, each max size of MAX_GENERIC_DATA_LEN (data size)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!isControlFrame && dest_board == BROADCAST_ADDR && type != FrameType::MISC_UDP_GENERIC_TYPE){
|
||||
//If broadcasting generic frames, we don't to spam acks to this board
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
@@ -293,17 +379,26 @@ esp_err_t DataLinkManager::send(uint8_t dest_board, uint8_t* data, uint16_t data
|
||||
if (data_len <= MAX_CONTROL_DATA_LEN){
|
||||
frag_info = (1 << 16); //1 total fragment required
|
||||
} else {
|
||||
frag_info = (data_len / MAX_CONTROL_DATA_LEN + 1) << 16;
|
||||
uint32_t total_frags = (data_len + MAX_GENERIC_DATA_LEN - 1) / MAX_GENERIC_DATA_LEN;
|
||||
frag_info = (total_frags) << 16;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t seq_num = 0;
|
||||
|
||||
esp_err_t res = get_inc_sequence_num(dest_board, &seq_num);
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed atomic get increment sequence number map");
|
||||
return res;
|
||||
}
|
||||
|
||||
SchedulerMetadata metadata = {
|
||||
.header = {
|
||||
.preamble = START_OF_FRAME,
|
||||
.sender_id = this_board_id,
|
||||
.receiver_id = dest_board,
|
||||
.seq_num = sequence_num_map[dest_board]++,
|
||||
.seq_num = seq_num,
|
||||
.type_flag = (uint8_t)((static_cast<uint8_t>(type) & 0xF0) | (flag & 0xF)),
|
||||
.frag_info = frag_info,
|
||||
.data_len = data_len,
|
||||
@@ -313,10 +408,13 @@ esp_err_t DataLinkManager::send(uint8_t dest_board, uint8_t* data, uint16_t data
|
||||
.enqueue_time_ns = 0,
|
||||
.data = saved_data,
|
||||
.len = data_len,
|
||||
.last_ack = 0,
|
||||
.curr_fragment = 0,
|
||||
.timeout = 0,
|
||||
};
|
||||
|
||||
uint8_t channel = 0;
|
||||
esp_err_t res = route_frame(dest_board, &channel);
|
||||
res = route_frame(dest_board, &channel);
|
||||
|
||||
if (res != ESP_OK){
|
||||
// ESP_LOGE(DEBUG_LINK_TAG, "Failed to route message to board %d", dest_board);
|
||||
@@ -424,6 +522,10 @@ esp_err_t DataLinkManager::get_data_from_frame(uint8_t* data, size_t data_len, u
|
||||
header->seq_num = (uint16_t)data[3] | ((uint16_t)data[4] << 8);
|
||||
header->type_flag = data[5];
|
||||
if (IS_CONTROL_FRAME(data[5])){
|
||||
if (data_len < 9){
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
header->data_len = (uint16_t)data[6] | ((uint16_t)data[7] << 8);
|
||||
|
||||
if (header->data_len > data_len){
|
||||
@@ -437,6 +539,12 @@ esp_err_t DataLinkManager::get_data_from_frame(uint8_t* data, size_t data_len, u
|
||||
}
|
||||
|
||||
*message_size = header->data_len;
|
||||
|
||||
if (*message_size > MAX_CONTROL_DATA_LEN || (10 + *message_size > data_len)){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Invalid payload length: %u", *message_size);
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
memcpy(message, &data[8], header->data_len);
|
||||
|
||||
geneate_crc_16(data, 8*sizeof(uint8_t) + header->data_len, &header->crc_16);
|
||||
@@ -452,19 +560,34 @@ esp_err_t DataLinkManager::get_data_from_frame(uint8_t* data, size_t data_len, u
|
||||
|
||||
} else {
|
||||
//generic frame
|
||||
|
||||
if (data_len < 13){
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
uint16_t total_frag = (uint16_t)data[6] | ((uint16_t)data[7] << 8);
|
||||
uint16_t frag_num = (uint16_t)data[8] | ((uint16_t)data[9] << 8);
|
||||
header->frag_info = (total_frag << 16) | (frag_num);
|
||||
header->data_len = (uint16_t)data[10] | ((uint16_t)data[11] << 8);
|
||||
|
||||
*message_size = header->data_len;
|
||||
memcpy(message, &data[12], header->data_len);
|
||||
|
||||
geneate_crc_16(data, 12*sizeof(uint8_t) + header->data_len, &header->crc_16);
|
||||
|
||||
uint16_t crc_calc = ((uint16_t)data[12 + header->data_len] | ((uint16_t)data[13 + header->data_len] << 8));
|
||||
if ((*message_size > MAX_GENERIC_DATA_LEN && total_frag != 1) || (14 + *message_size > data_len)){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Invalid payload length: %u", *message_size);
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
memcpy(message, &data[12], *message_size);
|
||||
|
||||
if (total_frag != 1){
|
||||
geneate_crc_16(data, 12*sizeof(uint8_t) + *message_size, &header->crc_16);
|
||||
} else {
|
||||
header->crc_16 = 0;
|
||||
}
|
||||
|
||||
uint16_t crc_calc = ((uint16_t)data[12 + *message_size] | ((uint16_t)data[13 + *message_size] << 8));
|
||||
|
||||
if (crc_calc != header->crc_16){
|
||||
if (crc_calc != header->crc_16 && total_frag != 1){
|
||||
//CRC mismatch
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "CRC Mismatch - Generic Frame");
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Got 0x%04X but calculated 0x%04X\n", crc_calc, header->crc_16);
|
||||
@@ -479,7 +602,7 @@ esp_err_t DataLinkManager::get_data_from_frame(uint8_t* data, size_t data_len, u
|
||||
// printf("0x%02X %-12d %-13d %-15d 0x%02X %-10d 0x%04X\n",
|
||||
// header->preamble, header->sender_id, header->receiver_id, header->seq_num, header->type_flag, header->data_len, header->crc_16);
|
||||
|
||||
printf("Message received: %.*s\n", header->data_len, message);
|
||||
// printf("Message received: %.*s\n", *message_size, message);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -511,6 +634,19 @@ esp_err_t DataLinkManager::geneate_crc_16(uint8_t* data, size_t data_len, uint16
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prints to console the encoded frame information from a byte array recevied from RMT
|
||||
*
|
||||
* @note Should only be used for debug purposes
|
||||
*
|
||||
* @warning This function may not be reliable/buggy
|
||||
*
|
||||
* @param data
|
||||
* @param data_len
|
||||
* @param message
|
||||
* @param message_len
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::print_frame_info(uint8_t* data, size_t data_len, uint8_t* message, size_t message_len){
|
||||
// printf("Received frame of size %d:\n", data_len);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
/**
|
||||
* @brief Initializes the RIP table
|
||||
*
|
||||
*
|
||||
*/
|
||||
void DataLinkManager::init_rip(){
|
||||
for (size_t i = 0; i < RIP_MAX_ROUTES; i++){
|
||||
@@ -42,7 +42,7 @@ esp_err_t DataLinkManager::rip_add_entry(uint8_t board_id, uint8_t hops, uint8_t
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake((*entry)->row_sem, (TickType_t)RIP_MAX_SEM_WAIT) != pdTRUE){
|
||||
if (xSemaphoreTake((*entry)->row_sem, pdMS_TO_TICKS(RIP_MAX_SEM_WAIT_MS)) != pdTRUE){
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@@ -54,11 +54,11 @@ esp_err_t DataLinkManager::rip_add_entry(uint8_t board_id, uint8_t hops, uint8_t
|
||||
(*entry)->ttl = RIP_TTL_START;
|
||||
(*entry)->valid = 1;
|
||||
|
||||
|
||||
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "board_id %d now has hops %d from channel %d", (*entry)->info.board_id, (*entry)->info.hops, channel);
|
||||
|
||||
|
||||
xSemaphoreGive((*entry)->row_sem);
|
||||
|
||||
|
||||
if (uxQueueMessagesWaiting(manual_broadcasts) == 0){
|
||||
bool dummy = true;
|
||||
xQueueSend(manual_broadcasts, &dummy, 0); //new row - send broadcast
|
||||
@@ -81,7 +81,7 @@ esp_err_t DataLinkManager::rip_reset_entry_ttl(uint8_t board_id){
|
||||
return ESP_FAIL; //board doesn't exist
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(entry->row_sem, (TickType_t)RIP_MAX_SEM_WAIT) != pdTRUE){
|
||||
if (xSemaphoreTake(entry->row_sem, pdMS_TO_TICKS(RIP_MAX_SEM_WAIT_MS)) != pdTRUE){
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ esp_err_t DataLinkManager::rip_update_entry(uint8_t new_hop, uint8_t channel, RI
|
||||
return ESP_FAIL; //board doesn't exist
|
||||
}
|
||||
|
||||
if (xSemaphoreTake((*entry)->row_sem, (TickType_t)RIP_MAX_SEM_WAIT) != pdTRUE){
|
||||
if (xSemaphoreTake((*entry)->row_sem, pdMS_TO_TICKS(RIP_MAX_SEM_WAIT_MS)) != pdTRUE){
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@@ -108,9 +108,9 @@ esp_err_t DataLinkManager::rip_update_entry(uint8_t new_hop, uint8_t channel, RI
|
||||
(*entry)->channel = channel;
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "updated board_id %d now has hops %d from channel %d", (*entry)->info.board_id, (*entry)->info.hops, channel);
|
||||
}
|
||||
|
||||
|
||||
(*entry)->ttl = RIP_TTL_START;
|
||||
(*entry)->valid = 1;
|
||||
(*entry)->valid = 1;
|
||||
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "refreshed board_id %d ttl", (*entry)->info.board_id);
|
||||
|
||||
@@ -128,15 +128,15 @@ esp_err_t DataLinkManager::rip_update_entry(uint8_t new_hop, uint8_t channel, RI
|
||||
/**
|
||||
* @brief Finds the board_id in the table if it exists and stores that row in `entry`
|
||||
* TODO: use an unordered map instead of an array?
|
||||
*
|
||||
* @param board_id
|
||||
* @param entry
|
||||
* @return esp_err_t
|
||||
*
|
||||
* @param board_id
|
||||
* @param entry
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::rip_find_entry(uint8_t board_id, RIPRow** entry, bool reserve_row = false){
|
||||
RIPRow* free_slot = nullptr;
|
||||
for (size_t i = 0; i < RIP_MAX_ROUTES; i++){
|
||||
if (xSemaphoreTake(rip_table[i].row_sem, (TickType_t)RIP_MAX_SEM_WAIT) != pdTRUE){
|
||||
if (xSemaphoreTake(rip_table[i].row_sem, pdMS_TO_TICKS(RIP_MAX_SEM_WAIT_MS)) != pdTRUE){
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (rip_table[i].valid == RIP_VALID_ROW && rip_table[i].info.board_id == board_id){
|
||||
@@ -144,7 +144,7 @@ esp_err_t DataLinkManager::rip_find_entry(uint8_t board_id, RIPRow** entry, bool
|
||||
xSemaphoreGive(rip_table[i].row_sem);
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Found %d in table at row %d", board_id, i);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
if (rip_table[i].valid == RIP_INVALID_ROW && free_slot == nullptr){
|
||||
free_slot = &rip_table[i];
|
||||
}
|
||||
@@ -158,7 +158,7 @@ esp_err_t DataLinkManager::rip_find_entry(uint8_t board_id, RIPRow** entry, bool
|
||||
}
|
||||
|
||||
if (free_slot != nullptr){
|
||||
if (xSemaphoreTake(free_slot->row_sem, RIP_MAX_SEM_WAIT) != pdTRUE) {
|
||||
if (xSemaphoreTake(free_slot->row_sem, pdMS_TO_TICKS(RIP_MAX_SEM_WAIT_MS)) != pdTRUE) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@@ -176,10 +176,10 @@ esp_err_t DataLinkManager::rip_find_entry(uint8_t board_id, RIPRow** entry, bool
|
||||
|
||||
/**
|
||||
* @brief Returns the associated RIP Table row by row number. Information returned is read only.
|
||||
*
|
||||
* @param entry
|
||||
* @param row_num
|
||||
* @return esp_err_t
|
||||
*
|
||||
* @param entry
|
||||
* @param row_num
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::rip_get_row(RIPRow** entry, uint8_t row_num){
|
||||
if (entry == nullptr){
|
||||
@@ -190,7 +190,7 @@ esp_err_t DataLinkManager::rip_get_row(RIPRow** entry, uint8_t row_num){
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(rip_table[row_num].row_sem, (TickType_t)RIP_MAX_SEM_WAIT) != pdTRUE) return ESP_ERR_TIMEOUT;
|
||||
if (xSemaphoreTake(rip_table[row_num].row_sem, pdMS_TO_TICKS(RIP_MAX_SEM_WAIT_MS)) != pdTRUE) return ESP_ERR_TIMEOUT;
|
||||
|
||||
if (rip_table[row_num].valid == RIP_INVALID_ROW){
|
||||
xSemaphoreGive(rip_table[row_num].row_sem);
|
||||
@@ -216,10 +216,10 @@ esp_err_t DataLinkManager::rip_get_row(RIPRow** entry, uint8_t row_num){
|
||||
|
||||
/**
|
||||
* @brief Sends RIP frame
|
||||
*
|
||||
*
|
||||
* @param broadcast True - broadcasts (sends rip table to all available channels); False - sends rip table via routing based on `dest_id`
|
||||
* @param dest_id Destination board (requesting board) to send the rip table to (ignored if `broadcast is true`)
|
||||
* @return esp_err_t
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::send_rip_frame(bool broadcast, uint8_t dest_id){
|
||||
//use the control frame for the demo (as the number of rows increase, we will need to use the generic frame)
|
||||
@@ -231,22 +231,23 @@ esp_err_t DataLinkManager::send_rip_frame(bool broadcast, uint8_t dest_id){
|
||||
esp_err_t res;
|
||||
|
||||
RIPRow* entry = nullptr;
|
||||
uint16_t seq_num = 0;
|
||||
|
||||
if(broadcast){
|
||||
for (size_t channel = 0; channel < num_channels; channel++){
|
||||
for (size_t i = 0; i < RIP_MAX_ROUTES; i++){
|
||||
res = rip_get_row(&entry, i);
|
||||
|
||||
|
||||
if (res != ESP_OK){
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (entry == nullptr){
|
||||
continue;
|
||||
}
|
||||
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Found entry for board %d with hops %d", entry->info.board_id, entry->info.hops);
|
||||
|
||||
|
||||
if (entry->channel == channel){
|
||||
//poisoned reverse
|
||||
rip_message[message_idx++] = entry->info.board_id;
|
||||
@@ -265,12 +266,18 @@ esp_err_t DataLinkManager::send_rip_frame(bool broadcast, uint8_t dest_id){
|
||||
memset(send_data, 0, message_idx);
|
||||
memcpy(send_data, rip_message, message_idx);
|
||||
|
||||
res = get_inc_sequence_num(BROADCAST_ADDR, &seq_num);
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed atomic get increment sequence number map");
|
||||
return res;
|
||||
}
|
||||
|
||||
SchedulerMetadata metadata = {
|
||||
.header = {
|
||||
.preamble = START_OF_FRAME,
|
||||
.sender_id = this_board_id,
|
||||
.receiver_id = BROADCAST_ADDR,
|
||||
.seq_num = sequence_num_map[BROADCAST_ADDR]++,
|
||||
.seq_num = seq_num,
|
||||
.type_flag = static_cast<uint8_t>(FrameType::RIP_TABLE_CONTROL),
|
||||
.data_len = message_idx,
|
||||
.crc_16 = 0,
|
||||
@@ -279,6 +286,9 @@ esp_err_t DataLinkManager::send_rip_frame(bool broadcast, uint8_t dest_id){
|
||||
.enqueue_time_ns = 0,
|
||||
.data = send_data,
|
||||
.len = message_idx,
|
||||
.last_ack = 0,
|
||||
.curr_fragment = 0,
|
||||
.timeout = 0,
|
||||
};
|
||||
|
||||
res = push_frame_to_scheduler(metadata, channel);
|
||||
@@ -314,10 +324,10 @@ esp_err_t DataLinkManager::send_rip_frame(bool broadcast, uint8_t dest_id){
|
||||
|
||||
/**
|
||||
* @brief Determines which channel to route the frame to, depending on the dest (board) id
|
||||
*
|
||||
* @param dest_id
|
||||
* @param channel_to_send
|
||||
* @return esp_err_t
|
||||
*
|
||||
* @param dest_id
|
||||
* @param channel_to_send
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::route_frame(uint8_t dest_id, uint8_t* channel_to_send){
|
||||
RIPRow* entry = nullptr;
|
||||
@@ -338,6 +348,15 @@ esp_err_t DataLinkManager::route_frame(uint8_t dest_id, uint8_t* channel_to_send
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fetches the current routing table at the perspective of the host board
|
||||
*
|
||||
* @details The routing table is based off of RIP
|
||||
*
|
||||
* @param table
|
||||
* @param table_size
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::get_routing_table(RIPRow_public* table, size_t* table_size){
|
||||
if (table == nullptr){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Invalid table pointer");
|
||||
@@ -357,14 +376,14 @@ esp_err_t DataLinkManager::get_routing_table(RIPRow_public* table, size_t* table
|
||||
size_t curr_size = 0;
|
||||
|
||||
for (size_t i = 0; i < RIP_MAX_ROUTES; i++){
|
||||
if (xSemaphoreTake(rip_table[i].row_sem, (TickType_t)RIP_MAX_SEM_WAIT) != pdTRUE){
|
||||
if (xSemaphoreTake(rip_table[i].row_sem, pdMS_TO_TICKS(RIP_MAX_SEM_WAIT_MS)) != pdTRUE){
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (rip_table[i].valid == RIP_VALID_ROW){
|
||||
table[i].info = rip_table[i].info;
|
||||
table[i].channel = rip_table[i].channel;
|
||||
curr_size++;
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(rip_table[i].row_sem);
|
||||
}
|
||||
|
||||
@@ -409,7 +428,7 @@ esp_err_t DataLinkManager::get_routing_table(RIPRow_public* table, size_t* table
|
||||
vTaskDelay(pdMS_TO_TICKS(RIP_MS_TO_SEC)); //run every second
|
||||
for (size_t i = 1; i < RIP_MAX_ROUTES; i++){
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Decrementing ttl on entry %d", i);
|
||||
if (xSemaphoreTake(link_layer_obj->rip_table[i].row_sem, (TickType_t)RIP_MAX_SEM_WAIT) !=pdTRUE){
|
||||
if (xSemaphoreTake(link_layer_obj->rip_table[i].row_sem, pdMS_TO_TICKS(RIP_MAX_SEM_WAIT_MS)) !=pdTRUE){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to get sem from entry %d", i);
|
||||
continue;
|
||||
}
|
||||
@@ -427,7 +446,7 @@ esp_err_t DataLinkManager::get_routing_table(RIPRow_public* table, size_t* table
|
||||
link_layer_obj->rip_table[i].ttl_flush = RIP_FLUSH_COUNT;
|
||||
broadcast = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xSemaphoreGive(link_layer_obj->rip_table[i].row_sem);
|
||||
}
|
||||
@@ -452,5 +471,5 @@ void DataLinkManager::start_rip_tasks(){
|
||||
ESP_LOGI(DEBUG_LINK_TAG, "Starting RIP Broadcast task");
|
||||
xTaskCreate(DataLinkManager::rip_broadcast_timer_function, "RIPBroadcast", 4096, static_cast<void*>(this), 5, &rip_broadcast_task);
|
||||
ESP_LOGI(DEBUG_LINK_TAG, "Starting RIP TTL task");
|
||||
xTaskCreate(DataLinkManager::rip_ttl_decrement_task, "RIPTTL", 4096, static_cast<void*>(this), 5, &rip_ttl_task);
|
||||
}
|
||||
xTaskCreate(DataLinkManager::rip_ttl_decrement_task, "RIPTTL", 2048, static_cast<void*>(this), 5, &rip_ttl_task);
|
||||
}
|
||||
@@ -2,18 +2,32 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_random.h"
|
||||
|
||||
void DataLinkManager::init_scheduler(){
|
||||
for (int i = 0; i < num_channels; i++){
|
||||
sq_handle[i] = xSemaphoreCreateMutex();
|
||||
async_rx_queue_mutex[i] = xSemaphoreCreateMutex();
|
||||
rx_fragment_mutex[i] = xSemaphoreCreateMutex();
|
||||
sliding_window_mutex[i] = xSemaphoreCreateMutex();
|
||||
send_ack_queue_mutex[i] = xSemaphoreCreateMutex();
|
||||
}
|
||||
|
||||
ESP_LOGI(DEBUG_LINK_TAG, "Starting Frame Scheduler task");
|
||||
xTaskCreate(DataLinkManager::frame_scheduler, "Scheduler", 4096, static_cast<void*>(this), 5, &scheduler_task);
|
||||
xTaskCreate(DataLinkManager::receive_thread_main, "Scheduler", 8192, static_cast<void*>(this), 5, &receive_task);
|
||||
xTaskCreate(DataLinkManager::frame_scheduler, "Scheduler", 4096, static_cast<void*>(this), 4, &scheduler_task);
|
||||
xTaskCreate(DataLinkManager::receive_thread_main, "Receiver", 8192, static_cast<void*>(this), 5, &receive_task);
|
||||
xTaskCreate(DataLinkManager::send_ack_thread_main, "Send ACKs", 8192, static_cast<void*>(this), 5, &send_ack_task);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Schedules which frame to send
|
||||
*
|
||||
* Scheduler:
|
||||
* - All frames will be pushed to the back onto a queue
|
||||
* - When a generic frame sends a chunk, it will be pushed back to the queue for the next chunk to be sent
|
||||
*
|
||||
* Scheduling may change (above scheduler will lead to starvation of control frames depending on the number of generic frames/fragments to send)
|
||||
*/
|
||||
[[noreturn]] void DataLinkManager::frame_scheduler(void* args){
|
||||
DataLinkManager* link_layer_obj = static_cast<DataLinkManager*>(args);
|
||||
if (link_layer_obj == nullptr){
|
||||
@@ -30,7 +44,6 @@ void DataLinkManager::init_scheduler(){
|
||||
|
||||
}
|
||||
vTaskDelete(nullptr);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,6 +92,11 @@ esp_err_t DataLinkManager::push_frame_to_scheduler(SchedulerMetadata frame, uint
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Scheduler sending the actual frame at the top of the heap on a channel
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::scheduler_send(uint8_t channel){
|
||||
if (phys_comms == nullptr){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to send frame due to no RMT object");
|
||||
@@ -136,71 +154,157 @@ esp_err_t DataLinkManager::scheduler_send(uint8_t channel){
|
||||
return res;
|
||||
}
|
||||
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Sending %d bytes", frame_size);
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Sending control frame with %d bytes", frame_size);
|
||||
return scheduler_send_rmt(channel, frame, send_data, frame_size, false);
|
||||
} else {
|
||||
//generic frame
|
||||
|
||||
if (frame.len > MAX_CONTROL_DATA_LEN){
|
||||
if (frame.len > (MAX_GENERIC_DATA_LEN)){
|
||||
//fragment here
|
||||
uint16_t curr_offset = frame.generic_frame_data_offset;
|
||||
uint16_t fragment_size = 0;
|
||||
|
||||
//calculate fragment data size
|
||||
if (curr_offset + MAX_CONTROL_DATA_LEN <= frame.len){
|
||||
fragment_size = curr_offset + MAX_CONTROL_DATA_LEN;
|
||||
if (frame.timeout == 0){
|
||||
frame.timeout = GENERIC_FRAME_MIN_TIMEOUT;
|
||||
} else {
|
||||
fragment_size = frame.len - curr_offset;
|
||||
frame.timeout--;
|
||||
res = push_frame_to_scheduler(frame, channel);
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to schedule next generic frame fragment");
|
||||
vPortFree(frame.data);
|
||||
return res;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (static_cast<FrameType>(GET_TYPE(frame.header.type_flag)) != FrameType::MISC_UDP_GENERIC_TYPE){
|
||||
FrameAckRecord record = {
|
||||
.last_ack = 0,
|
||||
.total_frags = 0
|
||||
};
|
||||
|
||||
res = get_record_sliding_window(channel, frame.header.receiver_id, frame.header.seq_num, &record);
|
||||
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to get sliding window ack record for board id %d seq num %d", frame.header.receiver_id, frame.header.seq_num);
|
||||
vPortFree(frame.data);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (record.last_ack == 0 && record.total_frags == 0){
|
||||
//no acks has arrived yet - can only send fragment 1 ... fragment GENERIC_FRAME_SLIDING_WINDOW_SIZE (inclusive)
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "no ack received yet for board id %d seq num %d", frame.header.receiver_id, frame.header.seq_num);
|
||||
if (frame.curr_fragment < GENERIC_FRAME_SLIDING_WINDOW_SIZE){
|
||||
frame.curr_fragment++;
|
||||
} else {
|
||||
frame.curr_fragment = 1;
|
||||
}
|
||||
} else {
|
||||
//some acks has arrived
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "some ack received for board id %d seq num %d last ack %d total frags %d", frame.header.receiver_id, frame.header.seq_num, record.last_ack, record.total_frags);
|
||||
|
||||
//check if all acks are received
|
||||
frame.last_ack = record.last_ack;
|
||||
if (record.last_ack == record.total_frags){
|
||||
//all acks received, can simply exit
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "All acks recevied for board id %d seq num %d", frame.header.receiver_id, frame.header.seq_num);
|
||||
complete_record_sliding_window(channel, frame.header.receiver_id, frame.header.seq_num);
|
||||
vPortFree(frame.data);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (frame.curr_fragment - frame.last_ack < GENERIC_FRAME_SLIDING_WINDOW_SIZE && frame.curr_fragment - frame.last_ack < record.total_frags){
|
||||
frame.curr_fragment++;
|
||||
} else {
|
||||
frame.curr_fragment = frame.last_ack;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
frame.curr_fragment++;
|
||||
}
|
||||
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "current fragment to be sent for seq num %d is %d", frame.header.seq_num, frame.curr_fragment);
|
||||
|
||||
//calculate data offset from curr_fragment
|
||||
uint16_t fragment_size = 0;
|
||||
|
||||
if (frame.curr_fragment != (frame.header.frag_info >> 16)) {
|
||||
fragment_size = MAX_GENERIC_DATA_LEN;
|
||||
} else {
|
||||
fragment_size = frame.len - (MAX_GENERIC_DATA_LEN * (frame.curr_fragment-1));
|
||||
}
|
||||
|
||||
uint16_t curr_offset = MAX_GENERIC_DATA_LEN * (frame.curr_fragment - 1);
|
||||
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "frame %d curr offset %d\n", frame.header.seq_num, curr_offset);
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "frame %d fragment size %d\n", frame.header.seq_num, fragment_size);
|
||||
|
||||
frame.header.frag_info = (frame.header.frag_info & 0xFFFF0000) | frame.curr_fragment; //increment frag_num
|
||||
//create fragment
|
||||
res = create_generic_frame(frame.data, fragment_size,
|
||||
make_generic_frame_from_header(frame.header), curr_offset, send_data, &frame_size);
|
||||
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to create generic frame fragment");
|
||||
vPortFree(frame.data); //free entire data for now - will need to implement retries/sliding window soon
|
||||
vPortFree(frame.data);
|
||||
return res;
|
||||
}
|
||||
|
||||
//need to schedule the next fragment
|
||||
if (curr_offset != MAX_CONTROL_DATA_LEN){
|
||||
frame.generic_frame_data_offset += fragment_size;
|
||||
frame.header.frag_info += 1; //increment frag_num
|
||||
res = scheduler_send_rmt(channel, frame, send_data, frame_size, true);
|
||||
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to send generic frame fragment");
|
||||
res = push_frame_to_scheduler(frame, channel);
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to schedule next generic frame fragment");
|
||||
vPortFree(frame.data); //free entire data for now - will need to implement retries/sliding window soon
|
||||
vPortFree(frame.data);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
//need to schedule the next fragment (if total_frags != frag_num)
|
||||
if ((frame.header.frag_info >> 16) > (frame.header.frag_info & 0xFF) || (frame.last_ack != (frame.header.frag_info >> 16) &&
|
||||
static_cast<FrameType>(GET_TYPE(frame.header.type_flag)) != FrameType::MISC_UDP_GENERIC_TYPE)){
|
||||
// frame.generic_frame_data_offset += fragment_size;
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "scheduling frame %d with frag_info 0x%X", frame.header.seq_num, frame.header.frag_info);
|
||||
res = push_frame_to_scheduler(frame, channel);
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to schedule next generic frame fragment");
|
||||
vPortFree(frame.data);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
//Done fragmenting, can free data array
|
||||
ESP_LOGI(DEBUG_LINK_TAG, "finished fragmenting seq num %d frag_info 0x%X", frame.header.seq_num, frame.header.frag_info);
|
||||
vPortFree(frame.data);
|
||||
}
|
||||
|
||||
} else {
|
||||
//no fragmenting
|
||||
res = create_generic_frame(frame.data, frame.len,
|
||||
make_generic_frame_from_header(frame.header), 0, send_data, &frame_size);
|
||||
vPortFree(frame.data);
|
||||
}
|
||||
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to create generic frame");
|
||||
return res;
|
||||
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to create generic frame");
|
||||
return res;
|
||||
}
|
||||
return scheduler_send_rmt(channel, frame, send_data, frame_size, false);
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t DataLinkManager::scheduler_send_rmt(uint8_t channel, SchedulerMetadata frame, uint8_t* send_data, size_t frame_size, bool wait_for_tx_done){
|
||||
esp_err_t res;
|
||||
uint8_t channel_to_route = MAX_CHANNELS;
|
||||
rmt_transmit_config_t config = {
|
||||
.loop_count = 0,
|
||||
.flags = {
|
||||
.eot_level = 0 // typically 0 or 1, depending on your output idle level
|
||||
.eot_level = 0, // typically 0 or 1, depending on your output idle level
|
||||
}
|
||||
};
|
||||
if (frame.header.receiver_id == BROADCAST_ADDR){
|
||||
// printf("Sending on channel %d\n", i);
|
||||
phys_comms->send(send_data, frame_size, &config, channel);
|
||||
|
||||
res = phys_comms->send(send_data, frame_size, &config, channel);
|
||||
} else {
|
||||
res = route_frame(frame.header.receiver_id, &channel_to_route);
|
||||
|
||||
@@ -208,16 +312,133 @@ esp_err_t DataLinkManager::scheduler_send(uint8_t channel){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to find entry for %d", frame.header.receiver_id);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Sending message to %d", frame.header.receiver_id);
|
||||
phys_comms->send(send_data, frame_size, &config, channel_to_route);
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Sending frame %d frag_info 0x%X", frame.header.seq_num, frame.header.frag_info);
|
||||
res = phys_comms->send(send_data, frame_size, &config, channel_to_route);
|
||||
// if (wait_for_tx_done){
|
||||
// phys_comms->wait_until_send_complete(channel_to_route);
|
||||
// }
|
||||
}
|
||||
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE(DEBUG_LINK_TAG, "Failed to send message");
|
||||
return ESP_FAIL;
|
||||
} else{
|
||||
// printf("Sent message to board %d\n", frame.header.receiver_id);
|
||||
// ESP_LOGI(DEBUG_LINK_TAG, "Sent frame %d frag_info 0x%X", frame.header.seq_num, frame.header.frag_info);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increases the head of the sliding window associated with the board id and sequence number
|
||||
*
|
||||
* @param channel
|
||||
* @param board_id Receiving Board ID (the board who ACK'd)
|
||||
* @param seq_num
|
||||
* @param ack_record
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::inc_head_sliding_window(uint8_t channel, uint8_t board_id, uint16_t seq_num, FrameAckRecord* ack_record){
|
||||
if (ack_record == NULL){
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (ack_record->total_frags == 0 || ack_record->total_frags > MAX_GENERIC_NUM_FRAG
|
||||
|| ack_record->last_ack == 0 || ack_record->total_frags < ack_record->last_ack){
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (sliding_window_mutex[channel] == NULL){
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(sliding_window_mutex[channel], pdMS_TO_TICKS(SLIDING_WINDOW_MUTEX_TIMEOUT_MS)) != pdTRUE){
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
FrameAckRecord& record = sliding_window[channel][board_id][seq_num];
|
||||
|
||||
if (record.last_ack > ack_record->last_ack){
|
||||
xSemaphoreGive(sliding_window_mutex[channel]);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
record.last_ack = ack_record->last_ack;
|
||||
if (record.total_frags == 0){
|
||||
record.total_frags = ack_record->total_frags;
|
||||
}
|
||||
|
||||
xSemaphoreGive(sliding_window_mutex[channel]);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the current record associated with the board id and sequence number from the sliding window
|
||||
*
|
||||
* @param channel
|
||||
* @param board_id Receiving Board ID (the board who ACK'd)
|
||||
* @param seq_num
|
||||
* @param ack_record
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::get_record_sliding_window(uint8_t channel, uint8_t board_id, uint16_t seq_num, FrameAckRecord* ack_record){
|
||||
if (ack_record == NULL){
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (sliding_window_mutex[channel] == NULL){
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(sliding_window_mutex[channel], pdMS_TO_TICKS(SLIDING_WINDOW_MUTEX_TIMEOUT_MS)) != pdTRUE){
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (sliding_window[channel][board_id].find(seq_num) == sliding_window[channel][board_id].end()){
|
||||
//record for this board id + seq number doesn't exist -- we don't want to create one
|
||||
xSemaphoreGive(sliding_window_mutex[channel]);
|
||||
ack_record->last_ack = 0;
|
||||
ack_record->total_frags = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
FrameAckRecord& record = sliding_window[channel][board_id][seq_num];
|
||||
|
||||
ack_record->last_ack = record.last_ack;
|
||||
ack_record->total_frags = record.total_frags;
|
||||
|
||||
xSemaphoreGive(sliding_window_mutex[channel]);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the board id + sequence number record fromt the sliding window (map)
|
||||
*
|
||||
* @param channel
|
||||
* @param board_id Receiving Board ID (the board who ACK'd)
|
||||
* @param seq_num
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t DataLinkManager::complete_record_sliding_window(uint8_t channel, uint8_t board_id, uint16_t seq_num){
|
||||
if (sliding_window_mutex[channel] == NULL){
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(sliding_window_mutex[channel], pdMS_TO_TICKS(SLIDING_WINDOW_MUTEX_TIMEOUT_MS)) != pdTRUE){
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (sliding_window[channel][board_id].find(seq_num) == sliding_window[channel][board_id].end()){
|
||||
//record for this board id + seq number doesn't exist -- we don't want to create one
|
||||
xSemaphoreGive(sliding_window_mutex[channel]);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
sliding_window[channel][board_id].erase(seq_num);
|
||||
|
||||
xSemaphoreGive(sliding_window_mutex[channel]);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -1,36 +1,134 @@
|
||||
WIP
|
||||
|
||||
# Data Link Layer
|
||||
|
||||
This component represents the data link layer. It will handle board to board communication (abstracting away the RMT specific details of transmitting physical raw bits over wires).
|
||||
|
||||
## Frame Definitions
|
||||
This component is also the culmination of many 3-8 am work nights in front of vscode and/or at the labs.
|
||||
|
||||
See `./include/Frames.h` for frame definitions.
|
||||
This component also uses many C style function parameters
|
||||
- C++ is just C with classes >:)
|
||||
|
||||
There will be two types of frames: Control and Generic.
|
||||
# Usage
|
||||
|
||||
Control frames will contain all control information (eg. Spinning a DC motor, moving a servo to a particular angle, sensor information). These frames will have a limit of 256B of data size with a total packet size of 41B. These frames will not be fragmented.
|
||||
See [`main/main_rmt_test.cpp`](https://git.uwaterloo.ca/capstone-group2/firmware/-/blob/main/main/main_rmt_test.cpp?ref_type=heads) for details.
|
||||
|
||||
Generic frames will contain all other information. They will have a max data size of 16MiB if fragmented (max 2**16 fragments). Each fragment will still maintain a max 256B data size.
|
||||
Simply create the object of the data link layer class with the desired board id and number of channels you wish the board to use (1-4 inclusive). See the [`RMTManager`](https://git.uwaterloo.ca/capstone-group2/firmware/-/tree/main/components/rmt?ref_type=heads) class for more details about channel pinouts/configurations.
|
||||
The constructor will automatically create an object of the RMT class (physical layer) for you.
|
||||
|
||||
Board IDs are stored in the ESP32-S3's NVS hashmap (under namespace `board` and key `id`).
|
||||
|
||||
To send some data, simply use `send()` with the destination board id (or using `BROADCAST_ADDR` for all boards on the network), a `uint8_t` array of user defined data (up to `MAX_FRAME_SIZE` B), frame type (defined in enum `FrameType`), and any flags (defined in enum `FrameFlags`).
|
||||
|
||||
To receive some data (if it exists), simply use `async_receive_info()` to see if there is any received data available and `async_receive()` to actually get the received data. Using either functions will also return the associated frame header.
|
||||
|
||||
# Structure
|
||||
|
||||
## Frames
|
||||
|
||||
The data link layer has support for two types of frames: Control and Generic. Their definitions can be found in [Frames.h](https://git.uwaterloo.ca/capstone-group2/firmware/-/blob/main/components/dataLink/include/Frames.h?ref_type=heads).
|
||||
|
||||
Regardless of the type of frame, the maximum (raw) number of bytes that can be sent over RMT in a single transmission (per channel) is 121 B. The low MTU is being investigated as a [potential bug](https://uwaterloo-botchain.atlassian.net/browse/BTS-123).
|
||||
|
||||
Each frame has a unique sequence number (16 bits wide) associated with the receiver id. An exception to this is the generic frame fragmentation, where there sequence number is shared amongst the fragments (associated with the same sequence number to the combined data). For example, the first frame sent from board 1 to board 2 will have a sequence number of 1. Any subsequent frame sent from 1 to 2 will have an increasing sequence number.
|
||||
|
||||
Sequence numbers are also incremented when RIP frames are sent, however broadcasting RIP tables between boards uses a unique receiver id `BROADCAST_ADDR`, which tracks its own sequence number.
|
||||
|
||||
Sequence numbers are interally tracked in an unordered map, where the receiever id is the key and the current sequence number is the value stored in the hash map.
|
||||
|
||||
### Control Frames
|
||||
Control frames will contain all control information (eg. Spinning a DC motor, moving a servo to a particular angle, sensor information). These frames will have a maximum user data size of 112 B with a 9 B overhead (headers). These frames will not be fragmented and will have the highest priority for transmission.
|
||||
|
||||
### Generic Frames
|
||||
Generic frames will contain all other information (eg. longer text, images/video frames, or a really large homework data). They will have a max data size of 6.7 MiB if fragmented (max 2^16 fragments). Each fragment will still maintain a max 107 B user data size with a 14 B overhead (headers).
|
||||
|
||||
Generic frames are able to be received in out of order due to the sliding window + ACK frames. ACK frames will be sent by the receiver board to the transmitter board upon successful receive by the receiver board. ACK will contain the highest fragment number that is consecutive from the first fragment (eg. if fragments 1-5 have been received successfully, ACK will contain `5`. however, if fragments 1-3, and 7 were received successfully, ACK will only contain `3`). These ACK frames are generic frames.
|
||||
|
||||
Fragments are stored in the unordered map `fragment_map`. The mapping of this hash map is as follows:
|
||||
```
|
||||
channel -> receiver id -> sequence number -> [Vector of Generic Frame Fragments, size of total_frags]
|
||||
```
|
||||
|
||||
Upon the successful store of a fragment, a `ACK_TYPE` frame will be created and queued (onto `send_ack_queue` to be sent back to the original sender).
|
||||
|
||||
Note that if the fragment has type `MISC_UDP_GENERIC_TYPE`, no ACK frame will be sent in reply.
|
||||
|
||||
Fragments will only be sent at most `GENERIC_FRAME_SLIDING_WINDOW_SIZE` from the last ACK'd fragment. For example, if fragment 2 was last ACK'd, the sender will only be able to send fragments 3 - `GENERIC_FRAME_SLIDING_WINDOW_SIZE`. It will continuously resend `GENERIC_FRAME_SLIDING_WINDOW_SIZE` fragments until a new ACK has been recevied, updating the last ACK'd count.
|
||||
|
||||
ACKs, upon receiving a generic frame fragment, will be scheduled on a worker thread (apart from the receive thread). This is to (hopefully) limit blockages on the receive thread and minimizes unintentional dropped frames.
|
||||
|
||||
To differentiate between the two frames, the `type` field in both frames will be compared against. If the MSB is set to 1, it will be determined to be a control packet.
|
||||
|
||||
## Routing Information Protocol (RIP) Table
|
||||
Currently, if all fragments have been received, it will pass the combined data (across all user data in the fragments), to the queue `async_receive_queue`.
|
||||
|
||||
See `./include/Tables.h` for table definitions. See `DataLinkRIP.cpp` for function definitions.
|
||||
### ACK Frames
|
||||
|
||||
ACK frames have the following format in the data section:
|
||||
|
||||
```
|
||||
[0] GENERIC_FRAG_ACK_PREAMBLE
|
||||
[1] last_ack (MSB)
|
||||
[2] last_ack (LSB)
|
||||
[3] total_frags (MSB)
|
||||
[4] total_frags (LSB)
|
||||
[5] seq_num (MSB)
|
||||
[6] seq_num (LSB)
|
||||
```
|
||||
|
||||
Note that the multi-byte data is stored with big endian ordering.
|
||||
|
||||
These ACK frames are sent automatically upon successful receive of a generic frame fragment.
|
||||
|
||||
ACK frames themselves are sent via the `send_ack_thread_main` thread/task (after the receive thread pushes the ACK frame data onto queue `send_ack_queue`).
|
||||
|
||||
Any ACK frames received will not be passed to the user.
|
||||
|
||||
## User Receive
|
||||
|
||||
Users will be able to access the first frame received from the queue `async_receive_queue` (separated by channel). Users will be able to peek/view the top of the queue (without popping) with the function `async_receive_info()`. Actual data popping/receiving is done with `async_receive()`.
|
||||
|
||||
If the queue is empty, a `ESP_ERR_NOT_FOUND` error will be returned.
|
||||
|
||||
This structure is set up with an "async" fashion is to avoid constant polling of RMT RX by the user and instead moving the RMT RX polling to a separate thread/task managed by the data link layer. The "async" fashion also allows the user to 'instantly' (as opposed to waiting for the RMT RX queue timeout) know if there were any received frames for the user to consume, to peak at the next received frame (to consume) by passing the frame header (important for dynamically allocating memory for the variable message data size), and to simplify dynamic memory management used by the link layer itself.
|
||||
|
||||
# Routing Information Protocol (RIP) Table
|
||||
|
||||
See [here](https://git.uwaterloo.ca/capstone-group2/firmware/-/blob/main/components/dataLink/include/Tables.h?ref_type=heads) for table definitions. See [`DataLinkRIP.cpp`](https://git.uwaterloo.ca/capstone-group2/firmware/-/blob/main/components/dataLink/DataLinkRIP.cpp?ref_type=heads) for function definitions.
|
||||
|
||||
It handles all known (advertised) boards on the same network of ESP32-S3 boards. It will broadcast its stored routing table to its neighbours roughly every 30 seconds or upon receiving a routing table from its neighbours. All routes on a routing table will expire after 180 seconds, unless refreshed/updated/re-advertised by that respective board. The routing table itself will be used to route frames using the path with the least number of hops (known locally).
|
||||
|
||||
## Frame Management
|
||||
All RIP related messages will be sent as Control Frames, giving the highest priority to discovering newly joined boards onto the network, determining the shortest path to other boards on the network, and to discover dead/disconnected boards from the network.
|
||||
|
||||
See `DataLinkFrames.cpp` for more information.
|
||||
RIP broadcast messages uses the receiver id `BROADCAST_ADDR`.
|
||||
|
||||
Users are able to get the current routing table via `get_routing_table()`.
|
||||
|
||||
# Frame Management
|
||||
|
||||
See [`DataLinkFrames.cpp`](https://git.uwaterloo.ca/capstone-group2/firmware/-/blob/main/components/dataLink/DataLinkFrames.cpp?ref_type=heads) for more information.
|
||||
|
||||
It handles generic frame fragmentation, user handling of receiving received frames (stored in a queue, per channel), and a thread to handle polling the available channels for receiving.
|
||||
|
||||
### Send Frames Scheduler
|
||||
It is known that if a board is sending a lot of fragmented generic frames, it can lead to dropped received frames. This is especially important with RIP table control frames being dropped, which will eventually lead to being blocked on not detecting other boards on the connected network.
|
||||
|
||||
See `DataLinkScheduler.cpp` for more information.
|
||||
Note that any RIP frames received will not be passed to the user receive queue. To get the RIP table, the user would need to call the public function provided by the class.
|
||||
|
||||
It handles all TX frames passed from the user and schedules them to be sent. The frames are stored in a priority queue (per channel), where Control frames has a higher priority than Generic frames (generally). The actual scheduler algorithm can be found in `Scheduler.h`.
|
||||
## Send Frames Scheduler
|
||||
|
||||
See [`DataLinkScheduler.cpp`](https://git.uwaterloo.ca/capstone-group2/firmware/-/blob/main/components/dataLink/DataLinkScheduler.cpp?ref_type=heads) for more information.
|
||||
|
||||
It handles all TX frames passed from the user and schedules them to be sent. The frames are stored in a priority queue (per channel), where Control frames has a higher priority than Generic frames (generally). The actual scheduler algorithm can be found in [`Scheduler.h`](https://git.uwaterloo.ca/capstone-group2/firmware/-/blob/main/components/dataLink/include/Scheduler.h?ref_type=heads).
|
||||
|
||||
The scheduler uses an aging based priority scheduler that linearly increases the priority of the waiting frames with time with the formula: $P_f = B_f - A_f\alpha$ where:
|
||||
- $P_f$ is the effective priority value (lower comes first)
|
||||
- $B_f$ is the base priority
|
||||
- $A_f$ is the age (amount of time the frame has waited in the queue)
|
||||
- $\alpha$ is the aging factor (rate at which a frame increases priority)
|
||||
|
||||
## Receive Structure
|
||||
|
||||
See [`DataLinkFrames.cpp`](https://git.uwaterloo.ca/capstone-group2/firmware/-/blob/main/components/dataLink/DataLinkFrames.cpp?ref_type=heads) for more information.
|
||||
|
||||
The data link layer has a thread/task `receive_thread_main`, which simply starts the RMT RX job on a given channel. This essentially makes RMT start listening to the channel for any incoming frames (since RMT does not allow for continuous sensing/listening). This arrangement unfortunately will cause some frames to be missed or dropped due to data corruption (could be caused by sensing in the middle of a transmission). The period of checking all channels is defined by `RECEIVE_TASK_PERIOD_MS`.
|
||||
|
||||
# Diagram
|
||||
|
||||

|
||||
|
||||
708
components/dataLink/images/wired_communication_diagram.drawio
Normal file
708
components/dataLink/images/wired_communication_diagram.drawio
Normal file
@@ -0,0 +1,708 @@
|
||||
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0" version="29.2.9">
|
||||
<diagram name="Page-1" id="tz_IVeIy1WVS5DlZtvlV">
|
||||
<mxGraphModel dx="1146" dy="727" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-1" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="" vertex="1">
|
||||
<mxGeometry height="270" width="1000" x="60" y="225" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-2" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Link Layer" vertex="1">
|
||||
<mxGeometry height="30" width="70" x="515.5" y="225" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-3" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="" vertex="1">
|
||||
<mxGeometry height="140" width="1000" x="60" y="65" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-4" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Application Layer" vertex="1">
|
||||
<mxGeometry height="30" width="105" x="498" y="75" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-5" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="<i>Some data to send</i>" vertex="1">
|
||||
<mxGeometry height="40" width="80" x="100" y="115" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-8" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="<i>Some data received</i>" vertex="1">
|
||||
<mxGeometry height="40" width="80" x="950" y="115" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-44" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="140" width="225" x="75" y="275" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-10" parent="ggdiRDA4oRMZT7l_JB3I-44" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" value="" vertex="1">
|
||||
<mxGeometry height="140" width="225" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-12" parent="ggdiRDA4oRMZT7l_JB3I-44" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="Broadcast Timer Task" vertex="1">
|
||||
<mxGeometry height="30" width="95" x="10" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-34" edge="1" parent="ggdiRDA4oRMZT7l_JB3I-44" source="ggdiRDA4oRMZT7l_JB3I-13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" target="ggdiRDA4oRMZT7l_JB3I-24">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="192" y="82" />
|
||||
<mxPoint x="192" y="82" />
|
||||
<mxPoint x="192" y="109" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-13" parent="ggdiRDA4oRMZT7l_JB3I-44" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="TTL Decrement Task" vertex="1">
|
||||
<mxGeometry height="30" width="90" x="125" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-19" parent="ggdiRDA4oRMZT7l_JB3I-44" style="shape=table;startSize=18;container=1;collapsible=0;childLayout=tableLayout;strokeColor=default;fontSize=8;" value="Routing Table" vertex="1">
|
||||
<mxGeometry height="57.5" width="125" x="52.5" y="72.5" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-20" parent="ggdiRDA4oRMZT7l_JB3I-19" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;strokeColor=inherit;top=0;left=0;bottom=0;right=0;collapsible=0;dropTarget=0;fillColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="13" width="125" y="18" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-21" parent="ggdiRDA4oRMZT7l_JB3I-20" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=7;verticalAlign=bottom;" value="<div>Channel</div>" vertex="1">
|
||||
<mxGeometry height="13" width="42" as="geometry">
|
||||
<mxRectangle height="13" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-22" parent="ggdiRDA4oRMZT7l_JB3I-20" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=7;verticalAlign=bottom;" value="Board ID" vertex="1">
|
||||
<mxGeometry height="13" width="41" x="42" as="geometry">
|
||||
<mxRectangle height="13" width="41" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-23" parent="ggdiRDA4oRMZT7l_JB3I-20" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=7;verticalAlign=middle;" value="Hops" vertex="1">
|
||||
<mxGeometry height="13" width="42" x="83" as="geometry">
|
||||
<mxRectangle height="13" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-24" parent="ggdiRDA4oRMZT7l_JB3I-19" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;strokeColor=inherit;top=0;left=0;bottom=0;right=0;collapsible=0;dropTarget=0;fillColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="12" width="125" y="31" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-25" parent="ggdiRDA4oRMZT7l_JB3I-24" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="12" width="42" as="geometry">
|
||||
<mxRectangle height="12" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-26" parent="ggdiRDA4oRMZT7l_JB3I-24" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="12" width="41" x="42" as="geometry">
|
||||
<mxRectangle height="12" width="41" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-27" parent="ggdiRDA4oRMZT7l_JB3I-24" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="12" width="42" x="83" as="geometry">
|
||||
<mxRectangle height="12" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-28" parent="ggdiRDA4oRMZT7l_JB3I-19" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;strokeColor=inherit;top=0;left=0;bottom=0;right=0;collapsible=0;dropTarget=0;fillColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="15" width="125" y="43" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-29" parent="ggdiRDA4oRMZT7l_JB3I-28" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="15" width="42" as="geometry">
|
||||
<mxRectangle height="15" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-30" parent="ggdiRDA4oRMZT7l_JB3I-28" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="15" width="41" x="42" as="geometry">
|
||||
<mxRectangle height="15" width="41" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-31" parent="ggdiRDA4oRMZT7l_JB3I-28" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="15" width="42" x="83" as="geometry">
|
||||
<mxRectangle height="15" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-33" edge="1" parent="ggdiRDA4oRMZT7l_JB3I-44" source="ggdiRDA4oRMZT7l_JB3I-24" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;" target="ggdiRDA4oRMZT7l_JB3I-12">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="34" y="110" />
|
||||
<mxPoint x="34" y="82" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-11" parent="ggdiRDA4oRMZT7l_JB3I-44" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Route Information Protocol" vertex="1">
|
||||
<mxGeometry height="30" width="147.5" x="41.25" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-45" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" value="" vertex="1">
|
||||
<mxGeometry height="170" width="1000" x="60" y="525" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-46" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Physical Layer (RMT)" vertex="1">
|
||||
<mxGeometry height="30" width="120" x="500" y="535" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-17" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" value="" vertex="1">
|
||||
<mxGeometry height="140" width="150" x="310" y="275" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-18" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Scheduler" vertex="1">
|
||||
<mxGeometry height="30" width="60" x="355" y="275" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-140" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fillColor=#1ba1e2;strokeColor=#006EAF;" target="ggdiRDA4oRMZT7l_JB3I-138">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="320" y="320" />
|
||||
<mxPoint x="320" y="455" />
|
||||
<mxPoint x="110" y="455" />
|
||||
<mxPoint x="110" y="535" />
|
||||
<mxPoint x="193" y="535" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-177" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-140" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="Send Next Scheduled Frame" vertex="1">
|
||||
<mxGeometry relative="1" x="0.0137" as="geometry">
|
||||
<mxPoint x="19" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-35" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="Frame Scheduler Task" vertex="1">
|
||||
<mxGeometry height="30" width="95" x="337.5" y="305" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-116" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="55" width="109.25" x="333" y="355" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-117" parent="ggdiRDA4oRMZT7l_JB3I-116" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=10;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fontSize=7;opacity=40;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="40" width="96.25" as="geometry">
|
||||
<mxRectangle height="20" width="80" x="368.75" y="340" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-118" parent="ggdiRDA4oRMZT7l_JB3I-117" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="10" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-119" parent="ggdiRDA4oRMZT7l_JB3I-117" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-120" parent="ggdiRDA4oRMZT7l_JB3I-117" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-121" parent="ggdiRDA4oRMZT7l_JB3I-116" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=10;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fontSize=7;opacity=40;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="40" width="96.25" x="4" y="5" as="geometry">
|
||||
<mxRectangle height="20" width="80" x="368.75" y="340" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-122" parent="ggdiRDA4oRMZT7l_JB3I-121" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="10" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-123" parent="ggdiRDA4oRMZT7l_JB3I-121" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-124" parent="ggdiRDA4oRMZT7l_JB3I-121" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-125" parent="ggdiRDA4oRMZT7l_JB3I-116" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=10;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fontSize=7;opacity=40;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="40" width="96.25" x="8.75" y="10" as="geometry">
|
||||
<mxRectangle height="20" width="80" x="368.75" y="340" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-126" parent="ggdiRDA4oRMZT7l_JB3I-125" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="10" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-127" parent="ggdiRDA4oRMZT7l_JB3I-125" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-128" parent="ggdiRDA4oRMZT7l_JB3I-125" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-129" parent="ggdiRDA4oRMZT7l_JB3I-116" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=10;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fontSize=7;" value="RX Frame Queue (per Ch.)" vertex="1">
|
||||
<mxGeometry height="40" width="96.25" x="13" y="15" as="geometry">
|
||||
<mxRectangle height="20" width="80" x="368.75" y="340" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-130" parent="ggdiRDA4oRMZT7l_JB3I-129" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="Frame 1" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="10" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-131" parent="ggdiRDA4oRMZT7l_JB3I-129" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="Frame 2" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-132" parent="ggdiRDA4oRMZT7l_JB3I-129" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="Frame 3" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-133" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-121" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="ggdiRDA4oRMZT7l_JB3I-35" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-9" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-84" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;fillColor=#f0a30a;strokeColor=#BD7000;" target="ggdiRDA4oRMZT7l_JB3I-8">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="539" y="375" />
|
||||
<mxPoint x="480" y="375" />
|
||||
<mxPoint x="480" y="215" />
|
||||
<mxPoint x="990" y="215" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-180" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-9" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="Received User Data" vertex="1">
|
||||
<mxGeometry relative="1" x="0.2209" y="-1" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-7" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;fillColor=#f0a30a;strokeColor=#BD7000;">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="140" y="215" />
|
||||
<mxPoint x="450" y="215" />
|
||||
<mxPoint x="450" y="390" />
|
||||
</Array>
|
||||
<mxPoint x="429.3799999999999" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-179" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-7" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="Schedule User Data Message" vertex="1">
|
||||
<mxGeometry relative="1" x="-0.2252" y="2" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-147" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="To Board" vertex="1">
|
||||
<mxGeometry height="30" width="70" x="212.5" y="755" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-148" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="From Board" vertex="1">
|
||||
<mxGeometry height="30" width="70" x="865" y="745" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-160" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fillColor=#1ba1e2;strokeColor=#006EAF;" target="ggdiRDA4oRMZT7l_JB3I-132">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="40" y="320" />
|
||||
<mxPoint x="40" y="425" />
|
||||
<mxPoint x="310" y="425" />
|
||||
<mxPoint x="310" y="405" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-178" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-160" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="Schedule Next Broadcast with Current Table" vertex="1">
|
||||
<mxGeometry relative="1" x="0.2268" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-48" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" value="" vertex="1">
|
||||
<mxGeometry height="200" width="540" x="490" y="265" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-49" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Receiver" vertex="1">
|
||||
<mxGeometry height="30" width="60" x="610" y="265" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-76" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-50" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="ggdiRDA4oRMZT7l_JB3I-57">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="563" y="360" />
|
||||
<mxPoint x="718" y="360" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-186" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-76" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="<font style="font-size: 8px;">Store Unfinished Fragment</font>" vertex="1">
|
||||
<mxGeometry relative="1" x="-0.0024" as="geometry">
|
||||
<mxPoint y="-3" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-114" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-50" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="ggdiRDA4oRMZT7l_JB3I-84">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-185" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-114" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="<font style="font-size: 8px;">Store Received Frame for User</font>" vertex="1">
|
||||
<mxGeometry relative="1" x="0.3597" y="-1" as="geometry">
|
||||
<mxPoint y="2" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-159" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-50" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="ggdiRDA4oRMZT7l_JB3I-56" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-181" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-159" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="Received Fragment, ACK" vertex="1">
|
||||
<mxGeometry relative="1" x="-0.0712" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-174" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-50" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="ggdiRDA4oRMZT7l_JB3I-161">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="563" y="335" />
|
||||
<mxPoint x="903" y="335" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-187" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-174" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="On ACK, Update Sliding Window" vertex="1">
|
||||
<mxGeometry relative="1" x="-0.1628" y="1" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-189" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-50" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fillColor=#1ba1e2;strokeColor=#006EAF;" target="ggdiRDA4oRMZT7l_JB3I-28">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="563" y="255" />
|
||||
<mxPoint x="50" y="255" />
|
||||
<mxPoint x="50" y="398" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-190" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-189" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="Received Table, Update Routing Table" vertex="1">
|
||||
<mxGeometry relative="1" x="-0.0257" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-50" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="Receive Task" vertex="1">
|
||||
<mxGeometry height="30" width="95" x="515" y="295" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-56" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="Send ACK Task" vertex="1">
|
||||
<mxGeometry height="30" width="95" x="872" y="295" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-57" parent="1" style="shape=table;startSize=18;container=1;collapsible=0;childLayout=tableLayout;strokeColor=default;fontSize=8;" value="Fragment Table (per Ch.)" vertex="1">
|
||||
<mxGeometry height="57.5" width="125" x="655" y="391.25" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-58" parent="ggdiRDA4oRMZT7l_JB3I-57" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;strokeColor=inherit;top=0;left=0;bottom=0;right=0;collapsible=0;dropTarget=0;fillColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="13" width="125" y="18" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-59" parent="ggdiRDA4oRMZT7l_JB3I-58" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=7;verticalAlign=bottom;" value="<div>Board ID</div>" vertex="1">
|
||||
<mxGeometry height="13" width="42" as="geometry">
|
||||
<mxRectangle height="13" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-60" parent="ggdiRDA4oRMZT7l_JB3I-58" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=7;verticalAlign=bottom;" value="Seq. Num" vertex="1">
|
||||
<mxGeometry height="13" width="41" x="42" as="geometry">
|
||||
<mxRectangle height="13" width="41" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-61" parent="ggdiRDA4oRMZT7l_JB3I-58" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=7;verticalAlign=bottom;" value="Fragments" vertex="1">
|
||||
<mxGeometry height="13" width="42" x="83" as="geometry">
|
||||
<mxRectangle height="13" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-62" parent="ggdiRDA4oRMZT7l_JB3I-57" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;strokeColor=inherit;top=0;left=0;bottom=0;right=0;collapsible=0;dropTarget=0;fillColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="12" width="125" y="31" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-63" parent="ggdiRDA4oRMZT7l_JB3I-62" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="12" width="42" as="geometry">
|
||||
<mxRectangle height="12" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-64" parent="ggdiRDA4oRMZT7l_JB3I-62" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="12" width="41" x="42" as="geometry">
|
||||
<mxRectangle height="12" width="41" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-65" parent="ggdiRDA4oRMZT7l_JB3I-62" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="12" width="42" x="83" as="geometry">
|
||||
<mxRectangle height="12" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-66" parent="ggdiRDA4oRMZT7l_JB3I-57" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;strokeColor=inherit;top=0;left=0;bottom=0;right=0;collapsible=0;dropTarget=0;fillColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="15" width="125" y="43" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-67" parent="ggdiRDA4oRMZT7l_JB3I-66" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="15" width="42" as="geometry">
|
||||
<mxRectangle height="15" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-68" parent="ggdiRDA4oRMZT7l_JB3I-66" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="15" width="41" x="42" as="geometry">
|
||||
<mxRectangle height="15" width="41" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-69" parent="ggdiRDA4oRMZT7l_JB3I-66" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="15" width="42" x="83" as="geometry">
|
||||
<mxRectangle height="15" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-112" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="55" width="109.25" x="515" y="391.25" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-84" parent="ggdiRDA4oRMZT7l_JB3I-112" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=10;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fontSize=7;opacity=40;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="40" width="96.25" as="geometry">
|
||||
<mxRectangle height="20" width="80" x="368.75" y="340" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-85" parent="ggdiRDA4oRMZT7l_JB3I-84" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="10" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-86" parent="ggdiRDA4oRMZT7l_JB3I-84" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-87" parent="ggdiRDA4oRMZT7l_JB3I-84" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-100" parent="ggdiRDA4oRMZT7l_JB3I-112" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=10;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fontSize=7;opacity=40;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="40" width="96.25" x="4" y="5" as="geometry">
|
||||
<mxRectangle height="20" width="80" x="368.75" y="340" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-101" parent="ggdiRDA4oRMZT7l_JB3I-100" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="10" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-102" parent="ggdiRDA4oRMZT7l_JB3I-100" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-103" parent="ggdiRDA4oRMZT7l_JB3I-100" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-104" parent="ggdiRDA4oRMZT7l_JB3I-112" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=10;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fontSize=7;opacity=40;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="40" width="96.25" x="8.75" y="10" as="geometry">
|
||||
<mxRectangle height="20" width="80" x="368.75" y="340" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-105" parent="ggdiRDA4oRMZT7l_JB3I-104" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="10" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-106" parent="ggdiRDA4oRMZT7l_JB3I-104" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-107" parent="ggdiRDA4oRMZT7l_JB3I-104" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="&nbsp;" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-108" parent="ggdiRDA4oRMZT7l_JB3I-112" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=10;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fontSize=7;" value="RX Frame Queue (per Ch.)" vertex="1">
|
||||
<mxGeometry height="40" width="96.25" x="13" y="15" as="geometry">
|
||||
<mxRectangle height="20" width="80" x="368.75" y="340" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-109" parent="ggdiRDA4oRMZT7l_JB3I-108" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="Frame 1" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="10" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-110" parent="ggdiRDA4oRMZT7l_JB3I-108" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="Frame 2" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-111" parent="ggdiRDA4oRMZT7l_JB3I-108" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontSize=5;" value="Frame 3" vertex="1">
|
||||
<mxGeometry height="10" width="96.25" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-115" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-62" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1.007;entryY=0.286;entryDx=0;entryDy=0;entryPerimeter=0;" target="ggdiRDA4oRMZT7l_JB3I-110">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-158" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-56" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;fillColor=#1ba1e2;strokeColor=#006EAF;" target="ggdiRDA4oRMZT7l_JB3I-129">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="920" y="295" />
|
||||
<mxPoint x="920" y="255" />
|
||||
<mxPoint x="470" y="255" />
|
||||
<mxPoint x="470" y="380" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-183" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-158" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="Schedule ACK Frame" vertex="1">
|
||||
<mxGeometry relative="1" x="-0.2015" y="-1" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-161" parent="1" style="shape=table;startSize=18;container=1;collapsible=0;childLayout=tableLayout;strokeColor=default;fontSize=8;" value="Sliding Window" vertex="1">
|
||||
<mxGeometry height="57.5" width="125" x="840" y="391.25" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-162" parent="ggdiRDA4oRMZT7l_JB3I-161" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;strokeColor=inherit;top=0;left=0;bottom=0;right=0;collapsible=0;dropTarget=0;fillColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="13" width="125" y="18" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-163" parent="ggdiRDA4oRMZT7l_JB3I-162" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=7;verticalAlign=bottom;" value="<div>Board ID</div>" vertex="1">
|
||||
<mxGeometry height="13" width="42" as="geometry">
|
||||
<mxRectangle height="13" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-164" parent="ggdiRDA4oRMZT7l_JB3I-162" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=7;verticalAlign=bottom;" value="Seq. Num" vertex="1">
|
||||
<mxGeometry height="13" width="41" x="42" as="geometry">
|
||||
<mxRectangle height="13" width="41" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-165" parent="ggdiRDA4oRMZT7l_JB3I-162" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=7;verticalAlign=bottom;" value="Last ACK" vertex="1">
|
||||
<mxGeometry height="13" width="42" x="83" as="geometry">
|
||||
<mxRectangle height="13" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-166" parent="ggdiRDA4oRMZT7l_JB3I-161" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;strokeColor=inherit;top=0;left=0;bottom=0;right=0;collapsible=0;dropTarget=0;fillColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="12" width="125" y="31" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-167" parent="ggdiRDA4oRMZT7l_JB3I-166" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="12" width="42" as="geometry">
|
||||
<mxRectangle height="12" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-168" parent="ggdiRDA4oRMZT7l_JB3I-166" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="12" width="41" x="42" as="geometry">
|
||||
<mxRectangle height="12" width="41" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-169" parent="ggdiRDA4oRMZT7l_JB3I-166" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="12" width="42" x="83" as="geometry">
|
||||
<mxRectangle height="12" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-170" parent="ggdiRDA4oRMZT7l_JB3I-161" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;strokeColor=inherit;top=0;left=0;bottom=0;right=0;collapsible=0;dropTarget=0;fillColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="15" width="125" y="43" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-171" parent="ggdiRDA4oRMZT7l_JB3I-170" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="15" width="42" as="geometry">
|
||||
<mxRectangle height="15" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-172" parent="ggdiRDA4oRMZT7l_JB3I-170" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="15" width="41" x="42" as="geometry">
|
||||
<mxRectangle height="15" width="41" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-173" parent="ggdiRDA4oRMZT7l_JB3I-170" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;strokeColor=inherit;overflow=hidden;fillColor=none;top=0;left=0;bottom=0;right=0;pointerEvents=1;fontSize=16;" value="" vertex="1">
|
||||
<mxGeometry height="15" width="42" x="83" as="geometry">
|
||||
<mxRectangle height="15" width="42" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-175" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-166" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.493;entryY=1.051;entryDx=0;entryDy=0;entryPerimeter=0;fillColor=#1ba1e2;strokeColor=#006EAF;" target="ggdiRDA4oRMZT7l_JB3I-111">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="902" y="475" />
|
||||
<mxPoint x="576" y="475" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-176" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-175" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="<font style="font-size: 9px;">Received All Fragments, Store for User</font>" vertex="1">
|
||||
<mxGeometry relative="1" x="-0.0445" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-137" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-135" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;fillColor=#e51400;strokeColor=#B20000;" target="ggdiRDA4oRMZT7l_JB3I-50">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="840" y="515" />
|
||||
<mxPoint x="475" y="515" />
|
||||
<mxPoint x="475" y="345" />
|
||||
<mxPoint x="539" y="345" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-184" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-137" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="Received bits" vertex="1">
|
||||
<mxGeometry relative="1" x="-0.1848" y="-1" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-195" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="90" width="235" x="75" y="565" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-138" parent="ggdiRDA4oRMZT7l_JB3I-195" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" value="" vertex="1">
|
||||
<mxGeometry height="90" width="235" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-139" parent="ggdiRDA4oRMZT7l_JB3I-195" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="RMT Send" vertex="1">
|
||||
<mxGeometry height="30" width="90" x="72.5" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-150" parent="ggdiRDA4oRMZT7l_JB3I-195" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="Encoder" vertex="1">
|
||||
<mxGeometry height="30" width="70" x="147.5" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-194" edge="1" parent="ggdiRDA4oRMZT7l_JB3I-195" source="ggdiRDA4oRMZT7l_JB3I-192" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="ggdiRDA4oRMZT7l_JB3I-150" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-192" parent="ggdiRDA4oRMZT7l_JB3I-195" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="RMT TX Job" vertex="1">
|
||||
<mxGeometry height="30" width="97.5" x="7.5" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-196" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="90" width="235" x="90" y="575" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-197" parent="ggdiRDA4oRMZT7l_JB3I-196" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" value="" vertex="1">
|
||||
<mxGeometry height="90" width="235" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-198" parent="ggdiRDA4oRMZT7l_JB3I-196" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="RMT Send" vertex="1">
|
||||
<mxGeometry height="30" width="90" x="72.5" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-199" parent="ggdiRDA4oRMZT7l_JB3I-196" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="Encoder" vertex="1">
|
||||
<mxGeometry height="30" width="70" x="147.5" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-200" edge="1" parent="ggdiRDA4oRMZT7l_JB3I-196" source="ggdiRDA4oRMZT7l_JB3I-201" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="ggdiRDA4oRMZT7l_JB3I-199" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-201" parent="ggdiRDA4oRMZT7l_JB3I-196" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="RMT TX Job" vertex="1">
|
||||
<mxGeometry height="30" width="97.5" x="7.5" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-203" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="90" width="235" x="110" y="585" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-204" parent="ggdiRDA4oRMZT7l_JB3I-203" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" value="" vertex="1">
|
||||
<mxGeometry height="90" width="235" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-205" parent="ggdiRDA4oRMZT7l_JB3I-203" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="RMT Send" vertex="1">
|
||||
<mxGeometry height="30" width="90" x="72.5" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-206" parent="ggdiRDA4oRMZT7l_JB3I-203" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="Encoder" vertex="1">
|
||||
<mxGeometry height="30" width="70" x="147.5" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-207" edge="1" parent="ggdiRDA4oRMZT7l_JB3I-203" source="ggdiRDA4oRMZT7l_JB3I-208" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="ggdiRDA4oRMZT7l_JB3I-206" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-208" parent="ggdiRDA4oRMZT7l_JB3I-203" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="RMT TX Job" vertex="1">
|
||||
<mxGeometry height="30" width="97.5" x="7.5" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-209" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="90" width="235" x="130" y="595" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-210" parent="ggdiRDA4oRMZT7l_JB3I-209" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" value="" vertex="1">
|
||||
<mxGeometry height="90" width="235" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-211" parent="ggdiRDA4oRMZT7l_JB3I-209" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="RMT Send (per Ch.)" vertex="1">
|
||||
<mxGeometry height="30" width="117.5" x="58.75" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-212" parent="ggdiRDA4oRMZT7l_JB3I-209" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="Encoder" vertex="1">
|
||||
<mxGeometry height="30" width="70" x="147.5" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-213" edge="1" parent="ggdiRDA4oRMZT7l_JB3I-209" source="ggdiRDA4oRMZT7l_JB3I-214" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="ggdiRDA4oRMZT7l_JB3I-212" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-214" parent="ggdiRDA4oRMZT7l_JB3I-209" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="RMT TX Job" vertex="1">
|
||||
<mxGeometry height="30" width="97.5" x="7.5" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-215" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-210" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="ggdiRDA4oRMZT7l_JB3I-147" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-237" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-215" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="GPIO" vertex="1">
|
||||
<mxGeometry relative="1" x="-0.0095" y="-2" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-217" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="90" width="240" x="720" y="565" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-135" parent="ggdiRDA4oRMZT7l_JB3I-217" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" value="" vertex="1">
|
||||
<mxGeometry height="90" width="240" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-136" parent="ggdiRDA4oRMZT7l_JB3I-217" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="RMT Receive" vertex="1">
|
||||
<mxGeometry height="30" width="90" x="75" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-151" parent="ggdiRDA4oRMZT7l_JB3I-217" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="Decoder" vertex="1">
|
||||
<mxGeometry height="30" width="70" x="150" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-193" edge="1" parent="ggdiRDA4oRMZT7l_JB3I-217" source="ggdiRDA4oRMZT7l_JB3I-191" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="ggdiRDA4oRMZT7l_JB3I-151" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-191" parent="ggdiRDA4oRMZT7l_JB3I-217" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="RMT Receive Job" vertex="1">
|
||||
<mxGeometry height="30" width="100" x="20" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-218" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="90" width="240" x="740" y="575" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-219" parent="ggdiRDA4oRMZT7l_JB3I-218" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" value="" vertex="1">
|
||||
<mxGeometry height="90" width="240" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-220" parent="ggdiRDA4oRMZT7l_JB3I-218" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="RMT Receive" vertex="1">
|
||||
<mxGeometry height="30" width="90" x="75" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-221" parent="ggdiRDA4oRMZT7l_JB3I-218" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="Decoder" vertex="1">
|
||||
<mxGeometry height="30" width="70" x="150" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-222" edge="1" parent="ggdiRDA4oRMZT7l_JB3I-218" source="ggdiRDA4oRMZT7l_JB3I-223" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="ggdiRDA4oRMZT7l_JB3I-221" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-223" parent="ggdiRDA4oRMZT7l_JB3I-218" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="RMT Receive Job" vertex="1">
|
||||
<mxGeometry height="30" width="100" x="20" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-224" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="90" width="240" x="760" y="585" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-225" parent="ggdiRDA4oRMZT7l_JB3I-224" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" value="" vertex="1">
|
||||
<mxGeometry height="90" width="240" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-226" parent="ggdiRDA4oRMZT7l_JB3I-224" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="RMT Receive" vertex="1">
|
||||
<mxGeometry height="30" width="90" x="75" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-227" parent="ggdiRDA4oRMZT7l_JB3I-224" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="Decoder" vertex="1">
|
||||
<mxGeometry height="30" width="70" x="150" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-228" edge="1" parent="ggdiRDA4oRMZT7l_JB3I-224" source="ggdiRDA4oRMZT7l_JB3I-229" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="ggdiRDA4oRMZT7l_JB3I-227" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-229" parent="ggdiRDA4oRMZT7l_JB3I-224" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="RMT Receive Job" vertex="1">
|
||||
<mxGeometry height="30" width="100" x="20" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-230" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="90" width="240" x="780" y="595" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-231" parent="ggdiRDA4oRMZT7l_JB3I-230" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" value="" vertex="1">
|
||||
<mxGeometry height="90" width="240" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-232" parent="ggdiRDA4oRMZT7l_JB3I-230" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="RMT Receive (per Ch.)" vertex="1">
|
||||
<mxGeometry height="30" width="132.5" x="53.75" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-233" parent="ggdiRDA4oRMZT7l_JB3I-230" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="Decoder" vertex="1">
|
||||
<mxGeometry height="30" width="70" x="150" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-234" edge="1" parent="ggdiRDA4oRMZT7l_JB3I-230" source="ggdiRDA4oRMZT7l_JB3I-235" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" target="ggdiRDA4oRMZT7l_JB3I-233" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-235" parent="ggdiRDA4oRMZT7l_JB3I-230" style="text;html=1;whiteSpace=wrap;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;rounded=0;dashed=1;dashPattern=8 8;" value="RMT Receive Job" vertex="1">
|
||||
<mxGeometry height="30" width="100" x="20" y="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-236" edge="1" parent="1" source="ggdiRDA4oRMZT7l_JB3I-231" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=classic;startFill=1;endArrow=none;endFill=0;" target="ggdiRDA4oRMZT7l_JB3I-148" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ggdiRDA4oRMZT7l_JB3I-238" connectable="0" parent="ggdiRDA4oRMZT7l_JB3I-236" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" value="GPIO" vertex="1">
|
||||
<mxGeometry relative="1" x="-0.0111" y="1" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
BIN
components/dataLink/images/wired_communication_diagram.png
Normal file
BIN
components/dataLink/images/wired_communication_diagram.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
@@ -18,6 +18,9 @@
|
||||
|
||||
#define CRC_POLYNOMIAL 0x1021
|
||||
|
||||
static const char* NVS_BOARD_ID_KEY = "id";
|
||||
static const char* NVS_BOARD_NAMESPACE = "board";
|
||||
|
||||
//look up table for crc
|
||||
static const uint16_t crc16_table[256] = {
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
|
||||
@@ -39,6 +42,7 @@ static const uint16_t crc16_table[256] = {
|
||||
};
|
||||
|
||||
#define ASYNC_QUEUE_WAIT_TICKS 100
|
||||
#define SEQUENCE_NUM_MAP_MUTEX_MAX_WAIT_MS 50
|
||||
|
||||
/**
|
||||
* @brief Class to represent the Data Link Layer
|
||||
@@ -56,11 +60,17 @@ class DataLinkManager{
|
||||
esp_err_t get_routing_table(RIPRow_public* table, size_t* table_size);
|
||||
esp_err_t async_receive_info(uint16_t* frame_size, FrameHeader* header, uint8_t channel);
|
||||
esp_err_t async_receive(uint8_t* data, uint16_t data_len, FrameHeader* header, uint8_t channel);
|
||||
esp_err_t ready();
|
||||
esp_err_t send_ack(uint8_t sender_id, uint8_t* data, uint16_t data_len);
|
||||
private:
|
||||
uint8_t this_board_id = 0;
|
||||
uint8_t num_channels = MAX_CHANNELS;
|
||||
std::unique_ptr<RMTManager> phys_comms;
|
||||
|
||||
std::unordered_map<uint8_t, uint16_t> sequence_num_map;
|
||||
SemaphoreHandle_t sequence_num_map_mutex;
|
||||
esp_err_t get_inc_sequence_num(uint8_t board_id, uint16_t* seq_num);
|
||||
esp_err_t get_sequence_num(uint8_t board_id, uint16_t* seq_num);
|
||||
|
||||
volatile bool stop_tasks = false; //used by the tasks to know when to stop (set true when DataLinkManager is destroyed)
|
||||
TaskHandle_t rip_broadcast_task = NULL;
|
||||
@@ -76,6 +86,7 @@ class DataLinkManager{
|
||||
esp_err_t create_generic_frame(uint8_t* data, uint16_t data_len, GenericFrame generic_frame, uint16_t offset, uint8_t* send_data, size_t* send_data_len);
|
||||
|
||||
//==== RIP related functions ====
|
||||
|
||||
void init_rip();
|
||||
esp_err_t rip_find_entry(uint8_t board_id, RIPRow** entry, bool reserve_row);
|
||||
esp_err_t rip_update_entry(uint8_t new_hop, uint8_t channel, RIPRow** entry);
|
||||
@@ -108,59 +119,38 @@ class DataLinkManager{
|
||||
void init_scheduler();
|
||||
esp_err_t push_frame_to_scheduler(SchedulerMetadata frame, uint8_t channel);
|
||||
TaskHandle_t scheduler_task = NULL;
|
||||
/**
|
||||
* @brief Schedules which frame to send
|
||||
*
|
||||
* Scheduler:
|
||||
* - All frames will be pushed to the back onto a queue
|
||||
* - When a generic frame sends a chunk, it will be pushed back to the queue for the next chunk to be sent
|
||||
*
|
||||
* Scheduling may change (above scheduler will lead to starvation of control frames depending on the number of generic frames/fragments to send)
|
||||
*/
|
||||
|
||||
[[noreturn]] static void frame_scheduler(void* args);
|
||||
/**
|
||||
* @brief Scheduler sending the actual frame at the top of the heap on a channel
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
|
||||
esp_err_t scheduler_send(uint8_t channel);
|
||||
|
||||
esp_err_t scheduler_send_rmt(uint8_t channel, SchedulerMetadata frame, uint8_t* send_data, size_t frame_size, bool wait_for_tx_done);
|
||||
|
||||
//Generic Frame Receive Fragments
|
||||
|
||||
/**
|
||||
* @brief Store a fragment that has been received
|
||||
*
|
||||
* @param fragment
|
||||
* @param channel
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t store_fragment(GenericFrame* fragment, uint8_t channel);
|
||||
|
||||
/**
|
||||
* @brief Stores generic frame fragments
|
||||
*
|
||||
* Mapping:
|
||||
* Board ID -> Sequence number -> Array of Generic Frame Fragments, with size of the number of expected fragments
|
||||
* Board ID (of the receiver) -> Sequence number -> Array of Generic Frame Fragments, with size of the number of expected fragments
|
||||
*
|
||||
* TODO:
|
||||
* - When receiving a fragment, insert it into the map
|
||||
* - When all fragments have been received in a sequence, remove the entire entry (sequence) from the map, and push final data for async receive
|
||||
* - Sliding window + ACKs
|
||||
*
|
||||
*/
|
||||
std::unordered_map<uint16_t, std::unordered_map<uint16_t, FragmentMetadata>> fragment_map;
|
||||
std::unordered_map<uint16_t, std::unordered_map<uint16_t, FragmentMetadata>> fragment_map[MAX_CHANNELS];
|
||||
|
||||
esp_err_t complete_fragment(uint16_t board_id, uint16_t sequence_num, uint8_t channel);
|
||||
|
||||
SemaphoreHandle_t async_rx_queue_mutex[MAX_CHANNELS];
|
||||
SemaphoreHandle_t rx_fragment_mutex[MAX_CHANNELS];
|
||||
|
||||
//Async receive
|
||||
/**
|
||||
* @brief Queue to store complete received frame data
|
||||
*
|
||||
* TODO:
|
||||
* - Replace the public `receive()` with the `async_receive()`.
|
||||
*
|
||||
*/
|
||||
std::queue<Rx_Metadata> async_receive_queue[MAX_CHANNELS];
|
||||
|
||||
@@ -185,6 +175,33 @@ class DataLinkManager{
|
||||
|
||||
TaskHandle_t receive_task = NULL;
|
||||
|
||||
/**
|
||||
* @brief Generic Frame Sliding Window
|
||||
*
|
||||
* Mapping:
|
||||
* Board Id (of the receiver) -> Sequence Number -> FrameAckRecord
|
||||
*
|
||||
*/
|
||||
std::unordered_map<uint16_t, std::unordered_map<uint16_t, FrameAckRecord>> sliding_window[MAX_CHANNELS];
|
||||
|
||||
SemaphoreHandle_t sliding_window_mutex[MAX_CHANNELS];
|
||||
|
||||
esp_err_t inc_head_sliding_window(uint8_t channel, uint8_t board_id, uint16_t seq_num, FrameAckRecord* ack_record);
|
||||
|
||||
esp_err_t get_record_sliding_window(uint8_t channel, uint8_t board_id, uint16_t seq_num, FrameAckRecord* ack_record);
|
||||
|
||||
esp_err_t complete_record_sliding_window(uint8_t channel, uint8_t board_id, uint16_t seq_num);
|
||||
|
||||
/**
|
||||
* @brief Thread for sending acks - Send ACKs on a separate thread to not hold up the receive thread (missing other frames)
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
[[noreturn]] static void send_ack_thread_main(void* args);
|
||||
TaskHandle_t send_ack_task = NULL;
|
||||
|
||||
SemaphoreHandle_t send_ack_queue_mutex[MAX_CHANNELS];
|
||||
std::queue<SendAckMetaData> send_ack_queue[MAX_CHANNELS];
|
||||
};
|
||||
|
||||
#endif //DATA_LINK
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
#define START_OF_FRAME 0xAB //0b1010_1011 - denotes the start of frame
|
||||
|
||||
#define MAX_CONTROL_DATA_LEN (1 << 8) // Max 256B
|
||||
#define MAX_FRAME_SIZE 121 //Max 121B (due to rmt) - note this includes the overhead of the frame. the actual payload max depends on the frame type (eg. 121 - 9 B is the max control data length)
|
||||
|
||||
#define MAX_GENERIC_NUM_FRAG (1 << 16) // Max 2**16 Fragments can be made with a generic frame (total 2**16 *MAX_CONTROL_DATA_LEN B of data can be sent ~ 16 MiB)
|
||||
#define MAX_GENERIC_NUM_FRAG (1 << 16) // Max 2**16 Fragments can be made with a generic frame (total 2**16 *MAX_GENERIC_DATA_LEN B of data can be sent ~ 6.7 MiB)
|
||||
|
||||
#define MAX_FRAME_QUEUE_SIZE 15 //Size of the queue for the frame scheduler (per channel)
|
||||
|
||||
@@ -30,17 +30,33 @@
|
||||
#define CONTROL_FRAME_OVERHEAD 9
|
||||
#define GENERIC_FRAME_OVERHEAD 14
|
||||
|
||||
#define MAX_GENERIC_DATA_LEN (MAX_FRAME_SIZE - GENERIC_FRAME_OVERHEAD)
|
||||
#define MAX_CONTROL_DATA_LEN (MAX_FRAME_SIZE - CONTROL_FRAME_OVERHEAD)
|
||||
|
||||
//Generic Frame Fragment ACK
|
||||
#define GENERIC_FRAG_ACK_DATA_SIZE 7
|
||||
#define GENERIC_FRAG_ACK_PREAMBLE 0x69
|
||||
|
||||
#define CONTROL_FRAME_TYPE 0x80 //if the frame type MSB is set to 1, use the control frame
|
||||
//Types (total 2^4 = 16 different types)
|
||||
enum class FrameType : uint8_t {
|
||||
//Control Frames
|
||||
MOTOR_TYPE = 0x80, //0b1000_0000
|
||||
SERVO_TYPE = 0xC0, //0b1100_0000
|
||||
DISTANCE_SENSOR_TYPE = 0xE0, //0b1110_0000
|
||||
DEBUG_CONTROL_TYPE = 0xC0, //0b1100_0000
|
||||
DEBUG_GENERIC_TYPE = 0x00, //0b0000_0000
|
||||
SYSTEM_TYPE = 0x30, //0x0011_0000 - used for statuses, discovery, and other maintainence requests
|
||||
RIP_TABLE_CONTROL = 0x90, //0b1001_0000 - using the control frame to broadcast the RIP table
|
||||
RIP_TABLE_GENERIC = 0x10 //0b0001_000 - using the generic frame to broadcast the RIP table
|
||||
DISTANCE_SENSOR_TYPE = 0xA0, //0b1010_0000
|
||||
SERVO_TYPE = 0xC0, //0b1100_0000
|
||||
MISC_CONTROL_TYPE = 0xD0, //0b1101_0000
|
||||
|
||||
//Generic Frames
|
||||
MISC_GENERIC_TYPE = 0x00, //0b0000_0000
|
||||
MISC_UDP_GENERIC_TYPE = 0x10, // 0b0001_0000 - Same as MISC_GENERIC_TYPE except no ACK frames will be expected
|
||||
SYSTEM_TYPE = 0x30, //0b0011_0000 - used for statuses, discovery, and other maintainence requests
|
||||
ACK_TYPE = 0x60, //0b0110_0000 - ACK frames for Generic Fragments
|
||||
RIP_TABLE_GENERIC = 0x70 //0b0111_0000 - using the generic frame to broadcast the RIP table (not used rn)
|
||||
};
|
||||
|
||||
enum class FrameFlags : uint8_t {
|
||||
ANY_FLAG = 0x0,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1) //these structs will be transmitted as is (ensure the structs are structured using 1B alignment - no padding)
|
||||
@@ -51,7 +67,7 @@ typedef struct _control_frame{
|
||||
uint16_t seq_num; //sequence number to differentiate frames being sent from sender to receiver
|
||||
uint8_t type_flag; //(type << 4) | flag - both are 4 bits
|
||||
uint16_t data_len; //Data Length (max 256B)
|
||||
uint8_t data[MAX_CONTROL_DATA_LEN]; //Variable Length of Data
|
||||
uint8_t data[MAX_FRAME_SIZE]; //Variable Length of Data
|
||||
uint16_t crc_16; //CRC-16
|
||||
} ControlFrame; //this will have a max size of 9 + 256B = 265B
|
||||
|
||||
@@ -64,7 +80,7 @@ typedef struct _data_link_frame{
|
||||
uint16_t total_frag; //total number of fragments for this sequence
|
||||
uint16_t frag_num; //current fragment number
|
||||
uint16_t data_len; //Data Length (max 178B)
|
||||
uint8_t data[MAX_CONTROL_DATA_LEN]; //Variable Length of Data
|
||||
uint8_t data[MAX_FRAME_SIZE]; //Variable Length of Data
|
||||
uint16_t crc_16; //CRC-16
|
||||
} GenericFrame; //this will have a max size of 14 + 2^8 B = 270 B
|
||||
#pragma pack(pop)
|
||||
|
||||
@@ -3,23 +3,43 @@
|
||||
#include "esp_timer.h"
|
||||
#include <cstdint>
|
||||
|
||||
//TODO: need to make a private send() to accept a Frame type.
|
||||
//the public send() is fine to keep but needs to be modified to simply create the Frame type based on the flags, data size, and args, and then push to the priority queue based on routing
|
||||
//TODO: receive also needs to be updated, when receiving a frame not destined for that board; need to route that frame towards the actual destination (requires pushing that frame onto the priority queue)
|
||||
//TODO: generic frames receive needs to be created (handle fragmenting, resend on corruption). ethe resend on corruption will be a lot of work as it requires ACK frames to be send and currently there is 0 support for ACK since we don't save any recently sent frames (can't resend)
|
||||
|
||||
#define SCHEDULER_MUTEX_WAIT 10 //max time duration to wait
|
||||
#define SCHEDULER_PERIOD_MS 25
|
||||
#define SCHEDULER_PERIOD_MS 140
|
||||
#define RECEIVE_TASK_PERIOD_MS 2
|
||||
|
||||
#define GENERIC_FRAME_SLIDING_WINDOW_SIZE 5 //defines the maximum size of the sliding window before resending previously un-ack'd fragments
|
||||
#define SLIDING_WINDOW_MUTEX_TIMEOUT_MS 5
|
||||
#define GENERIC_FRAME_MOD_TIMEOUT 10 //be scheduled at most 9 + GENERIC_FRAME_MIN_TIMEOUT times before sending another fragment
|
||||
#define GENERIC_FRAME_MIN_TIMEOUT 10
|
||||
|
||||
#define SEND_ACK_PERIOD_MS 50
|
||||
#define SEND_ACK_MUTEX_WAIT 10
|
||||
|
||||
//Metadata representing the frame to be sent but is currently scheduled
|
||||
typedef struct _frame_scheduler_metadata {
|
||||
FrameHeader header; //header of the frame
|
||||
uint16_t generic_frame_data_offset; //For data greater than MAX_CONTROL_DATA_LEN to keep track of fragment positions
|
||||
uint16_t generic_frame_data_offset; //For data greater than MAX_GENERIC_DATA_LEN to keep track of fragment positions
|
||||
int64_t enqueue_time_ns; //when the frame has been first enqueued into the priority queue
|
||||
uint8_t* data; //dyanmically allocated memory - contains the actual data
|
||||
uint16_t len; // length of the actual data
|
||||
|
||||
//sliding window
|
||||
uint16_t last_ack; //fragment number represnting the last ack'd fragment (from rx) - head
|
||||
uint16_t curr_fragment; //fragment number of the current fragment being sent
|
||||
uint32_t timeout;
|
||||
} SchedulerMetadata;
|
||||
|
||||
typedef struct _frame_ack_record {
|
||||
uint16_t last_ack; //last ack'd fragment recevied from the rx
|
||||
uint16_t total_frags; //total number of fragments associated with the sequence number
|
||||
uint16_t seq_num; //sequence number this ack corresponds to
|
||||
} FrameAckRecord;
|
||||
|
||||
typedef struct _send_ack_metadata{
|
||||
uint8_t data[GENERIC_FRAG_ACK_DATA_SIZE];
|
||||
uint8_t sender_id;
|
||||
} SendAckMetaData;
|
||||
|
||||
typedef struct _frame_compare {
|
||||
/**
|
||||
* @brief Uses aging based priority scheduling (linearly increasing priority with time)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// #define RIP_BROADCAST_INTERVAL 3000 //temp broadcast every 3 seconds (3000ms)
|
||||
#define RIP_TTL_START 180 //seconds
|
||||
#define RIP_MS_TO_SEC 1000 //1000 ms to 1 sec
|
||||
#define RIP_MAX_SEM_WAIT 30
|
||||
#define RIP_MAX_SEM_WAIT_MS 30
|
||||
#define RIP_FLUSH_COUNT 8 //flush after 8*30 seconds = 240 seconds
|
||||
|
||||
#define RIP_DISCOVERY_MESSAGE_SIZE 1
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
WIP
|
||||
|
||||
The RMT component uses the ESP32 RMT library/API to perform the physical layer part of the network stack. This component simply encodes/decodes an array of characters (specifically `uint8_t`) and either transmits them via the corresponding GPIO pin (transmitting) or up to the caller (receiving).
|
||||
|
||||
The related ESP32-S3 latest documentation on RMT can be found [here](https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/rmt.html).
|
||||
|
||||
# About
|
||||
@@ -10,20 +12,37 @@ This component is a wrapper around the ESP32-S3 RMT API and allows the operation
|
||||
|
||||
## Encoding Method
|
||||
|
||||
RMT uses the Ethernet Manchester encoding method, where a bit 1 is encoded as a falling edge transition and a bit 0 is encoded as a rising edge transition.
|
||||
RMT uses the Ethernet Manchester encoding (Ethernet Standard) method, where a bit 1 is encoded as a falling edge transition and a bit 0 is encoded as a rising edge transition.
|
||||
|
||||
Specific timings are defined in `RMTSymbols.h`, which includes bit timings, resolution HZ, and symbol definitions.
|
||||
|
||||
Note that using a high resolution frequency correlates with a higher rate of CRC corruption on the receiver board.
|
||||
|
||||
### Mathematical Defintions Used
|
||||
|
||||
Basis functions: $\phi_1(t) = \sqrt{\frac{2}{T_s}}\operatorname{rect}\left(\frac{t-3T_s/4}{T_s/2}\right)$ representing a bit 0 and $\phi_2(t) = \sqrt{\frac{2}{T_s}}\operatorname{rect}\left(\frac{t-T_s/4}{T_s/2}\right)$ representing a bit 1, using $T_s$ and the symbol/bit duration. The encoded symbols are $\vec{s_1} = \left[A, 0\right]$ and $\vec{s_2} = \left[0, A\right]$, where $A$ is some voltage.
|
||||
|
||||
This leads to a bit error probability of:
|
||||
$P_e(\vec{s_1}) = P_e(\vec{s_2}) \leq Q\left(\sqrt{\frac{E_{s}}{N_0}}\right)$, where $P_e(\vec{s_i}) \leq \sum_{i=1, i\neq k}^2Q\left(\frac{\lvert\lvert\vec{s_i}-\vec{s_k}\rvert\rvert}{\sqrt{2N_0}}\right)$ and $Q(\cdot)$ is the CDF of the standard Gaussian distribution.
|
||||
|
||||
A visual representation of the above:
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
This component is not meant to be used directly by the user (should only be exclusively be used by the Link Layer, found in `components/dataLink`).
|
||||
|
||||
For specific details, see `dataLink/DataLinkManager.cpp`.
|
||||
For specific details, see [`dataLink/DataLinkManager.cpp`](https://git.uwaterloo.ca/capstone-group2/firmware/-/blob/main/components/dataLink/DataLinkManager.cpp?ref_type=heads).
|
||||
|
||||
## Channel Configurations
|
||||
|
||||
RMT relies on the ESP32-S3's GPIO pins. This RMT component statically sets a predefined GPIO pin to either a TX or RX for a channel.
|
||||
|
||||
On the ESP32-S3, the pins are assigned as follows:
|
||||
|
||||

|
||||
|
||||
For TX/RX pin definitions, see `RMTManager.h`. For example, `tx_gpio[]` is a `4` length array. Each index in the array represents one half of a pair that represents the TX/RX channel on the physical layer.
|
||||
|
||||
## RMT Internal Async Jobs
|
||||
|
||||
@@ -384,7 +384,7 @@ esp_err_t RMTManager::send(uint8_t* data, size_t size, rmt_transmit_config_t* co
|
||||
}
|
||||
if (data == nullptr || size == 0 || size > (RMT_SYMBOL_BLOCK_SIZE*4)) {
|
||||
// printf("send() error: data pointer NULL or size 0\n");
|
||||
ESP_LOGE(DEBUG_TAG, "send() error: data pointer NULL or size 0");
|
||||
ESP_LOGE(DEBUG_TAG, "send() error: data pointer NULL or size 0. size: %d", size);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (config == nullptr) {
|
||||
|
||||
BIN
components/rmt/images/manchester_math.png
Normal file
BIN
components/rmt/images/manchester_math.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
BIN
components/rmt/images/rmt_pin_layouts.png
Normal file
BIN
components/rmt/images/rmt_pin_layouts.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 152 KiB |
@@ -4,43 +4,34 @@ file(GLOB_RECURSE ALL_SRCS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/*.S"
|
||||
)
|
||||
|
||||
if(DEFINED RMT_TEST AND RMT_TEST EQUAL 1)
|
||||
message(STATUS "Building for testing RMT/Link Layer Functionality with main_rmt_test.cpp")
|
||||
list(REMOVE_ITEM ALL_SRCS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
|
||||
)
|
||||
idf_build_set_property(COMPILE_DEFINITIONS RMT_TEST APPEND)
|
||||
else()
|
||||
list(REMOVE_ITEM ALL_SRCS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
|
||||
)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${ALL_SRCS}
|
||||
PRIV_REQUIRES esp_psram spi_flash nvs_flash esp_event rpc constants config rmt esp_driver_gptimer dataLink flatbuffers esp_driver_ledc
|
||||
INCLUDE_DIRS "include")
|
||||
|
||||
if(DEFINED BOARD_NAME AND BOARD_NAME)
|
||||
message(STATUS "Building for board name: " ${BOARD_NAME})
|
||||
idf_build_set_property(COMPILE_DEFINITIONS BOARD_${BOARD_NAME} APPEND)
|
||||
if(DEFINED SRC_BOARD AND SRC_BOARD)
|
||||
message(STATUS "Building for source board id: " ${SRC_BOARD})
|
||||
idf_build_set_property(COMPILE_DEFINITIONS SRC_BOARD=${SRC_BOARD} APPEND)
|
||||
endif()
|
||||
if(DEFINED TIME_TEST AND TIME_TEST)
|
||||
message(STATUS "Building with TIME_TEST: " ${TIME_TEST})
|
||||
idf_build_set_property(COMPILE_DEFINITIONS TIME_TEST APPEND)
|
||||
|
||||
if(DEFINED RECEIVER_BOARD AND RECEIVER_BOARD)
|
||||
message(STATUS "Building for receiver board id: " ${RECEIVER_BOARD})
|
||||
idf_build_set_property(COMPILE_DEFINITIONS RECEIVER_BOARD=${RECEIVER_BOARD} APPEND)
|
||||
endif()
|
||||
if(DEFINED MANCHESTER_40 AND MANCHESTER_40)
|
||||
message(STATUS "Building with MANCHESTER_40: " ${MANCHESTER_40})
|
||||
idf_build_set_property(COMPILE_DEFINITIONS MANCHESTER_40 APPEND)
|
||||
|
||||
if(DEFINED RECEIVE_ONLY AND RECEIVE_ONLY)
|
||||
message(STATUS "Building for receive only")
|
||||
idf_build_set_property(COMPILE_DEFINITIONS RECEIVE_ONLY=${RECEIVE_ONLY} APPEND)
|
||||
endif()
|
||||
if(DEFINED NRZ_INVERTED AND NRZ_INVERTED)
|
||||
message(STATUS "Building with NRZ_INVERTED: " ${NRZ_INVERTED})
|
||||
idf_build_set_property(COMPILE_DEFINITIONS NRZ_INVERTED APPEND)
|
||||
endif()
|
||||
if(DEFINED NRZ_INVERTED_10 AND NRZ_INVERTED_10)
|
||||
message(STATUS "Building with NRZ_INVERTED_10: " ${NRZ_INVERTED_10})
|
||||
idf_build_set_property(COMPILE_DEFINITIONS NRZ_INVERTED_10 APPEND)
|
||||
endif()
|
||||
if(DEFINED NRZ_INVERTED_20 AND NRZ_INVERTED_20)
|
||||
message(STATUS "Building with NRZ_INVERTED_20: " ${NRZ_INVERTED_20})
|
||||
idf_build_set_property(COMPILE_DEFINITIONS NRZ_INVERTED_20 APPEND)
|
||||
endif()
|
||||
if(DEFINED NRZ_INVERTED_2 AND NRZ_INVERTED_2)
|
||||
message(STATUS "Building with NRZ_INVERTED_2: " ${NRZ_INVERTED_2})
|
||||
idf_build_set_property(COMPILE_DEFINITIONS NRZ_INVERTED_2 APPEND)
|
||||
endif()
|
||||
if(DEFINED NRZ_INVERTED_40_HZ AND NRZ_INVERTED_40_HZ)
|
||||
message(STATUS "Building with NRZ_INVERTED_40_HZ: " ${NRZ_INVERTED_40_HZ})
|
||||
idf_build_set_property(COMPILE_DEFINITIONS NRZ_INVERTED_40_HZ APPEND)
|
||||
endif()
|
||||
if(DEFINED VERIFY_RECEIVE AND VERIFY_RECEIVE)
|
||||
message(STATUS "Building with VERIFY_RECEIVE: " ${VERIFY_RECEIVE})
|
||||
idf_build_set_property(COMPILE_DEFINITIONS VERIFY_RECEIVE APPEND)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -1,3 +1,58 @@
|
||||
# Main
|
||||
|
||||
Use `main.cpp` to start the control loop.
|
||||
|
||||
# Data Link Sample Code
|
||||
|
||||
See `main_rmt_test.cpp` for more details for Data Link Layer usage.
|
||||
See `main_rmt_test.cpp` for more details for Data Link Layer usage.
|
||||
|
||||
To run the sample code, add the compiler variable `RMT_TEST=1` when compiling: `idf.py build -D RMT_TEST=1`. To run the main program, simply set `RMT_TEST` to 0 or perform a `fullclean` and rebuild without the compiler variable.
|
||||
|
||||
## Testing with Multiple Boards
|
||||
|
||||
With `main_rmt_test.cpp`, flash one or more boards, ensuring that `BOARD_ID` are unique and `RECEIVER_BOARD_ID` is a board that exists and is connected on the same network.
|
||||
|
||||
### Example Usage:
|
||||
|
||||
Board A: `idf.py -B ./build_A build -D SRC_BOARD=1 -D RECEIVER_BOARD=69 -D RMT_TEST=1 flash monitor -p COM3`
|
||||
|
||||
Board B: `idf.py -B ./build_B build -D SRC_BOARD=69 -D RECEIVER_BOARD=1 -D RMT_TEST=1 flash monitor -p COM4`
|
||||
|
||||
### Receive Only
|
||||
|
||||
A board can be set to only receive frames by setting the compiler flag `RECEIVE_ONLY=1` (and conversely unset by setting to `0`).
|
||||
|
||||
## VSCode Intellisense Parsing
|
||||
|
||||
With the preprocessor if blocks, intellisense may gray out certain code blocks. To change this behaviour, modify your C/C++ configuration under `Defines` or modify your `.vscode/c_cpp_properties.json` file itself to make certain code blocks visible.
|
||||
|
||||
## Expected Behaviour
|
||||
|
||||
When monitoring both boards, you should see `Received message '%.*s' on channel %d from board %d seq num %d` when a board received a message (usually after the message `Sent %zu B sized in %" PRIu64 " us from channel %d` on the other board). When RIP frames are broadcasted, the other boards should receive that RIP frame (usually accompanied with a debug message) and broadcast their own table.
|
||||
|
||||
The specific message being sent can vary depending on the setup (determined at compile time). Simply changing the `snprintf` on the `send_buf` and/or `FrameType` argument of `send()` will change the message/frames being sent (from the perspective of the user).
|
||||
|
||||
# cat
|
||||
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⡷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⡿⠋⠈⠻⣮⣳⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣴⣾⡿⠋⠀⠀⠀⠀⠙⣿⣿⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣿⡿⠟⠛⠉⠀⠀⠀⠀⠀⠀⠀⠈⠛⠛⠿⠿⣿⣷⣶⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⣾⡿⠟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠻⠿⣿⣶⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⣀⣠⣤⣤⣀⡀⠀⠀⣀⣴⣿⡿⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠿⣿⣷⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⣄⠀⠀
|
||||
⢀⣤⣾⡿⠟⠛⠛⢿⣿⣶⣾⣿⠟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠿⣿⣷⣦⣀⣀⣤⣶⣿⡿⠿⢿⣿⡀⠀
|
||||
⣿⣿⠏⠀⢰⡆⠀⠀⠉⢿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠻⢿⡿⠟⠋⠁⠀⠀⢸⣿⠇⠀
|
||||
⣿⡟⠀⣀⠈⣀⡀⠒⠃⠀⠙⣿⡆⠀⠀⠀⠀⠀⠀⠀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠇⠀
|
||||
⣿⡇⠀⠛⢠⡋⢙⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⠀⠀
|
||||
⣿⣧⠀⠀⠀⠓⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠛⠋⠀⠀⢸⣧⣤⣤⣶⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⡿⠀⠀
|
||||
⣿⣿⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠻⣷⣶⣶⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⠁⠀⠀
|
||||
⠈⠛⠻⠿⢿⣿⣷⣶⣦⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⡏⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠉⠙⠛⠻⠿⢿⣿⣷⣶⣦⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠿⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⡄⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠙⠛⠻⠿⢿⣿⣷⣶⣦⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣿⡄⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⠛⠿⠿⣿⣷⣶⣶⣤⣤⣀⡀⠀⠀⠀⢀⣴⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⡿⣄
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⠛⠿⠿⣿⣷⣶⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣹
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⠀⠀⠀⠀⠀⠀⢸⣧
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣆⠀⠀⠀⠀⠀⠀⢀⣀⣠⣤⣶⣾⣿⣿⣿⣿⣤⣄⣀⡀⠀⠀⠀⣿
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⢿⣻⣷⣶⣾⣿⣿⡿⢯⣛⣛⡋⠁⠀⠀⠉⠙⠛⠛⠿⣿⣿⡷⣶⣿
|
||||
|
||||
good kitty
|
||||
@@ -1,3 +1,4 @@
|
||||
#if !defined(RMT_TEST) || (defined(RMT_TEST) && RMT_TEST == 0)
|
||||
// #include <cstdio>
|
||||
// #include <memory>
|
||||
|
||||
@@ -19,3 +20,4 @@ extern "C" [[noreturn]] void app_main(void) {
|
||||
"metadata_tx", 3096, loop_manager.get(), 3, nullptr);
|
||||
loop_manager->control_loop();
|
||||
}
|
||||
#endif
|
||||
@@ -1,292 +1,313 @@
|
||||
// //Used for link layer testing (change name to main.cpp to use)
|
||||
// #include <cstdio>
|
||||
// #include <memory>
|
||||
#if defined(RMT_TEST) && RMT_TEST == 1
|
||||
//Used for link layer testing (change name to main.cpp to use)
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
|
||||
// #include "sdkconfig.h"
|
||||
// #include "freertos/FreeRTOS.h"
|
||||
// #include "freertos/task.h"
|
||||
// #include "esp_flash.h"
|
||||
// #include "nvs_flash.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_flash.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
// #include "RMTManager.h"
|
||||
// #include "DataLinkManager.h"
|
||||
#include "RMTManager.h"
|
||||
#include "DataLinkManager.h"
|
||||
|
||||
// #include <esp_netif.h>
|
||||
// #include <esp_event.h>
|
||||
// #include <freertos/semphr.h>
|
||||
// #include "driver/gptimer.h"
|
||||
// #include "esp_log.h"
|
||||
#include <esp_netif.h>
|
||||
#include <esp_event.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include "driver/gptimer.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
// #define DATA_SIZE_TEST 270
|
||||
// //current board id
|
||||
// #define BOARD_ID 69
|
||||
// //board id to send to
|
||||
// #define RECEIVER_BOARD_ID 1
|
||||
#define DATA_SIZE_TEST 600
|
||||
|
||||
// struct TaskArgs{
|
||||
// DataLinkManager* link_layer_obj;
|
||||
// uint8_t task_id;
|
||||
// uint8_t receiver_id;
|
||||
// QueueHandle_t receive_queue;
|
||||
// };
|
||||
//current board id
|
||||
#ifdef SRC_BOARD
|
||||
#define BOARD_ID SRC_BOARD
|
||||
#else
|
||||
#define BOARD_ID 1
|
||||
#endif
|
||||
|
||||
// struct ReceviedFrame{
|
||||
// uint8_t buf[MAX_CONTROL_DATA_LEN + CONTROL_FRAME_OVERHEAD]; //max 41B
|
||||
// size_t len;
|
||||
// FrameHeader header;
|
||||
// };
|
||||
//board id to send to
|
||||
#ifdef RECEIVER_BOARD
|
||||
#define RECEIVER_BOARD_ID RECEIVER_BOARD
|
||||
#else
|
||||
#define RECEIVER_BOARD_ID 69
|
||||
#endif
|
||||
|
||||
// void receive_frames(void* arg){
|
||||
// TaskArgs* args = (TaskArgs*)arg;
|
||||
struct TaskArgs{
|
||||
DataLinkManager* link_layer_obj;
|
||||
uint8_t task_id;
|
||||
uint8_t receiver_id;
|
||||
QueueHandle_t receive_queue;
|
||||
};
|
||||
|
||||
struct ReceviedFrame{
|
||||
uint8_t buf[DATA_SIZE_TEST + GENERIC_FRAME_OVERHEAD];
|
||||
size_t len;
|
||||
FrameHeader header;
|
||||
};
|
||||
|
||||
void receive_frames(void* arg){
|
||||
TaskArgs* args = (TaskArgs*)arg;
|
||||
|
||||
// DataLinkManager* obj = args->link_layer_obj;
|
||||
DataLinkManager* obj = args->link_layer_obj;
|
||||
|
||||
// if (obj == nullptr){
|
||||
// ESP_LOGE("thread", "bad pointer\n");
|
||||
// vTaskDelete(NULL); //should never get here
|
||||
// }
|
||||
if (obj == nullptr){
|
||||
ESP_LOGE("thread", "bad pointer\n");
|
||||
vTaskDelete(NULL); //should never get here
|
||||
}
|
||||
|
||||
// QueueHandle_t shared_queue = (QueueHandle_t)args->receive_queue;
|
||||
QueueHandle_t shared_queue = (QueueHandle_t)args->receive_queue;
|
||||
|
||||
// uint8_t curr_channel = args-> task_id;
|
||||
uint8_t curr_channel = args-> task_id;
|
||||
|
||||
// printf("RX JOB for task %d starting...\n", curr_channel);
|
||||
// esp_err_t res;
|
||||
printf("RX JOB for task %d starting...\n", curr_channel);
|
||||
esp_err_t res;
|
||||
|
||||
// uint8_t recv_buf[DATA_SIZE_TEST];
|
||||
// memset(recv_buf, 0, DATA_SIZE_TEST);
|
||||
uint8_t recv_buf[DATA_SIZE_TEST];
|
||||
memset(recv_buf, 0, DATA_SIZE_TEST);
|
||||
|
||||
// ReceviedFrame recv_frame = {};
|
||||
ReceviedFrame recv_frame = {};
|
||||
|
||||
// FrameHeader header = {};
|
||||
FrameHeader header = {};
|
||||
|
||||
// while(true){
|
||||
// res = obj->async_receive(recv_buf, sizeof(recv_buf), &header, curr_channel);
|
||||
// vTaskDelay(pdMS_TO_TICKS(10));
|
||||
// if (res != ESP_OK){
|
||||
// // ESP_LOGE("thread", "Failed to receive message on thread %d", curr_channel);
|
||||
// if (res != ESP_ERR_NOT_FOUND) {
|
||||
// recv_frame.len = 0;
|
||||
// if (xQueueSendToBack(shared_queue, (void*)&recv_frame, (TickType_t) 10) != pdPASS){
|
||||
// ESP_LOGE("RX Job", "Failed to push received frame onto shared queue for channel %d", curr_channel);
|
||||
// }
|
||||
// }
|
||||
// continue;
|
||||
// } else {
|
||||
// // printf("Successfully receive message\n");
|
||||
// }
|
||||
while(true){
|
||||
res = obj->async_receive(recv_buf, sizeof(recv_buf), &header, curr_channel);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
if (res != ESP_OK){
|
||||
// ESP_LOGE("thread", "Failed to receive message on thread %d", curr_channel);
|
||||
if (res != ESP_ERR_NOT_FOUND) {
|
||||
recv_frame.len = 0;
|
||||
if (xQueueSendToBack(shared_queue, (void*)&recv_frame, (TickType_t) 10) != pdPASS){
|
||||
ESP_LOGE("RX Job", "Failed to push received frame onto shared queue for channel %d", curr_channel);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
// printf("Successfully receive message\n");
|
||||
}
|
||||
|
||||
// if (header.data_len == 0){
|
||||
// continue;
|
||||
// }
|
||||
if (header.data_len == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
// recv_frame.len = header.data_len;
|
||||
// memcpy((void*)recv_frame.buf, (void*)recv_buf, header.data_len);
|
||||
// recv_frame.header = header;
|
||||
recv_frame.len = header.data_len;
|
||||
memcpy((void*)recv_frame.buf, (void*)recv_buf, header.data_len);
|
||||
recv_frame.header = header;
|
||||
|
||||
// if (xQueueSendToBack(shared_queue, (void*)&recv_frame, (TickType_t) 10) != pdPASS){
|
||||
// ESP_LOGE("RX Job", "Failed to push received frame onto shared queue for channel %d", curr_channel);
|
||||
// }
|
||||
if (xQueueSendToBack(shared_queue, (void*)&recv_frame, (TickType_t) 10) != pdPASS){
|
||||
ESP_LOGE("RX Job", "Failed to push received frame onto shared queue for channel %d", curr_channel);
|
||||
}
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
// void multi_transceiver(void* arg) {
|
||||
// TaskArgs* args = (TaskArgs*)arg;
|
||||
void multi_transceiver(void* arg) {
|
||||
TaskArgs* args = (TaskArgs*)arg;
|
||||
|
||||
// DataLinkManager* obj = args->link_layer_obj;
|
||||
DataLinkManager* obj = args->link_layer_obj;
|
||||
|
||||
// if (obj == nullptr){
|
||||
// ESP_LOGE("thread", "bad pointer\n");
|
||||
// vTaskDelete(NULL); //should never get here
|
||||
// }
|
||||
if (obj == nullptr){
|
||||
ESP_LOGE("thread", "bad pointer\n");
|
||||
vTaskDelete(NULL); //should never get here
|
||||
}
|
||||
|
||||
// xTaskCreate(receive_frames, "receive_frame_job", 4096, arg, 5, NULL);
|
||||
xTaskCreate(receive_frames, "receive_frame_job", 4096, arg, 5, NULL);
|
||||
|
||||
// uint8_t dest_board_id = args->receiver_id; //using a dummy number for now - there is no board with id 2 right now
|
||||
uint8_t dest_board_id = args->receiver_id; //using a dummy number for now - there is no board with id 2 right now
|
||||
|
||||
// const char* message = "this is some other data that should not be corrupted.";
|
||||
const char* message = "this is some other data that should not be corrupted.";
|
||||
const char* generic_frame_additional_message = "This is some other extra data that can be sent that also shouldn't be corrupted while transmitting.";
|
||||
const char* generic_frame_second_additional_message = "At this point we have reached 241 bytes. This is some other data that we can send using generic frames but this will be fragmented. however, this data shouldn't be corrupted and be sent as if it was sent all at once. total message size right here is 469 bytes:)";
|
||||
|
||||
// uint8_t curr_channel = args->task_id;
|
||||
// QueueHandle_t shared_queue = (QueueHandle_t)args->receive_queue;
|
||||
uint8_t curr_channel = args->task_id;
|
||||
QueueHandle_t shared_queue = (QueueHandle_t)args->receive_queue;
|
||||
|
||||
// uint8_t send_buf[DATA_SIZE_TEST];
|
||||
// uint8_t recv_buf[DATA_SIZE_TEST];
|
||||
// memset(recv_buf, 0, DATA_SIZE_TEST);
|
||||
// memset(send_buf, 0, DATA_SIZE_TEST);
|
||||
uint8_t send_buf[DATA_SIZE_TEST];
|
||||
uint8_t recv_buf[DATA_SIZE_TEST];
|
||||
memset(recv_buf, 0, DATA_SIZE_TEST);
|
||||
memset(send_buf, 0, DATA_SIZE_TEST);
|
||||
|
||||
// size_t recv_len = 0;
|
||||
// uint8_t iteration = 0;
|
||||
// esp_err_t res = ESP_OK;
|
||||
size_t recv_len = 0;
|
||||
uint8_t iteration = 0;
|
||||
esp_err_t res = ESP_OK;
|
||||
|
||||
// gptimer_handle_t gptimer = NULL;
|
||||
// gptimer_config_t timer_config = {
|
||||
// .clk_src = GPTIMER_CLK_SRC_DEFAULT,
|
||||
// .direction = GPTIMER_COUNT_UP,
|
||||
// .resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us
|
||||
// };
|
||||
// ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
|
||||
// ESP_ERROR_CHECK(gptimer_enable(gptimer));
|
||||
// ESP_ERROR_CHECK(gptimer_start(gptimer));
|
||||
// uint64_t start_count = 0, end_count = 0;
|
||||
gptimer_handle_t gptimer = NULL;
|
||||
gptimer_config_t timer_config = {
|
||||
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
|
||||
.direction = GPTIMER_COUNT_UP,
|
||||
.resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us
|
||||
};
|
||||
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
|
||||
ESP_ERROR_CHECK(gptimer_enable(gptimer));
|
||||
ESP_ERROR_CHECK(gptimer_start(gptimer));
|
||||
uint64_t start_count = 0, end_count = 0;
|
||||
|
||||
// uint32_t num_incorrect = 0;
|
||||
// uint32_t total_transactions = 0;
|
||||
uint32_t num_incorrect = 0;
|
||||
uint32_t total_transactions = 0;
|
||||
|
||||
// RIPRow_public_matrix matrix[RIP_MAX_ROUTES];
|
||||
// size_t matrix_size = RIP_MAX_ROUTES;
|
||||
RIPRow_public_matrix matrix[RIP_MAX_ROUTES];
|
||||
size_t matrix_size = RIP_MAX_ROUTES;
|
||||
|
||||
// for (int i = 0; i < RIP_MAX_ROUTES; i++){
|
||||
// RIPRow_public* table = (RIPRow_public*)pvPortMalloc(sizeof(RIPRow_public)*RIP_MAX_ROUTES);
|
||||
// matrix[i] = {
|
||||
// .table = table,
|
||||
// .size = RIP_MAX_ROUTES
|
||||
// };
|
||||
// }
|
||||
for (int i = 0; i < RIP_MAX_ROUTES; i++){
|
||||
RIPRow_public* table = (RIPRow_public*)pvPortMalloc(sizeof(RIPRow_public)*RIP_MAX_ROUTES);
|
||||
matrix[i] = {
|
||||
.table = table,
|
||||
.size = RIP_MAX_ROUTES
|
||||
};
|
||||
}
|
||||
|
||||
// ReceviedFrame recv_frame = {};
|
||||
// printf("task %d starting...\n", curr_channel);
|
||||
// vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||
ReceviedFrame recv_frame = {};
|
||||
printf("task %d starting...\n", curr_channel);
|
||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||
|
||||
// bool receive_only = false;
|
||||
bool receive_only = false;
|
||||
#if defined(RECEIVE_ONLY) && RECEIVE_ONLY
|
||||
receive_only = true;
|
||||
#else
|
||||
receive_only = false;
|
||||
#endif
|
||||
|
||||
// while(1){
|
||||
// if(!receive_only){
|
||||
// vTaskDelay(1000 / portTICK_PERIOD_MS); // wait 1 second before trying to send again
|
||||
while(1){
|
||||
if(!receive_only){
|
||||
printf("waiting for 3 seconds...\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(3000)); // wait 3 second before trying to send again
|
||||
|
||||
// snprintf(reinterpret_cast<char*>(send_buf), sizeof(send_buf), "This is a message from board %d sending on channel %d. %s", BOARD_ID, curr_channel, message);
|
||||
snprintf(reinterpret_cast<char*>(send_buf), sizeof(send_buf), "This is a message from board %d sending on channel %d. %s %s %s", BOARD_ID, curr_channel, message, generic_frame_additional_message, generic_frame_second_additional_message);
|
||||
// snprintf(reinterpret_cast<char*>(send_buf), sizeof(send_buf), "This is a message from board %d sending on channel %d. %s %s", BOARD_ID, curr_channel, message, generic_frame_additional_message);
|
||||
// snprintf(reinterpret_cast<char*>(send_buf), sizeof(send_buf), "This is a message from board %d sending on channel %d. %s", BOARD_ID, curr_channel, message);
|
||||
|
||||
// ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &start_count));
|
||||
// res = obj->send(dest_board_id, send_buf, strlen(reinterpret_cast<char*>(send_buf)), FrameType::DEBUG_CONTROL_TYPE, 0x0);
|
||||
// ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &end_count));
|
||||
ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &start_count));
|
||||
res = obj->send(dest_board_id, send_buf, strlen(reinterpret_cast<char*>(send_buf)), FrameType::MISC_GENERIC_TYPE, 0x0);
|
||||
ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &end_count));
|
||||
|
||||
// // snprintf(reinterpret_cast<char*>(send_buf), sizeof(send_buf), "%s RANDOM", mej.kssage); //modifying the data while it transmits shouldn't affect the actual transmission here
|
||||
// snprintf(reinterpret_cast<char*>(send_buf), sizeof(send_buf), "%s RANDOM", mej.kssage); //modifying the data while it transmits shouldn't affect the actual transmission here
|
||||
|
||||
// if (res != ESP_OK){
|
||||
// ESP_LOGE("thread", "Failed to send message on thread %d. Error: 0x%x", curr_channel, res);
|
||||
// continue;
|
||||
// } else {
|
||||
// // printf("Successfully sent message %s\n", send_buf);
|
||||
// printf("Sent %zu B sized in %" PRIu64 " us from channel %d\n", strlen(reinterpret_cast<char*>(send_buf)) + CONTROL_FRAME_OVERHEAD, end_count-start_count, curr_channel);
|
||||
// }
|
||||
// }
|
||||
if (res != ESP_OK){
|
||||
ESP_LOGE("thread", "Failed to send message on thread %d. Error: 0x%x", curr_channel, res);
|
||||
continue;
|
||||
} else {
|
||||
// printf("Successfully sent message %s\n", send_buf);
|
||||
printf("Sent %zu B sized in %" PRIu64 " us from channel %d\n", strlen(reinterpret_cast<char*>(send_buf)) + CONTROL_FRAME_OVERHEAD, end_count-start_count, curr_channel);
|
||||
}
|
||||
}
|
||||
|
||||
// //wait on a queue for a few ms (if there's nothing, just send another frame. otherwise pop from queue and read it)
|
||||
// if (xQueueReceive(shared_queue, (void*)&recv_frame, (TickType_t) 50) != pdPASS){
|
||||
// memset(send_buf, 0, 256);
|
||||
// continue; //nothing or failed to pop from queue
|
||||
// }
|
||||
|
||||
// total_transactions++;
|
||||
// if (recv_frame.len == 0){
|
||||
// //receive fail
|
||||
// num_incorrect++;
|
||||
// } else {
|
||||
// // res = obj->print_frame_info(recv_frame.buf, recv_frame.len, recv_buf, DATA_SIZE_TEST);
|
||||
//wait on a queue for a few ms (if there's nothing, just send another frame. otherwise pop from queue and read it)
|
||||
printf("waiting for rx\n");
|
||||
if (xQueueReceive(shared_queue, (void*)&recv_frame, pdMS_TO_TICKS(36000)) != pdPASS){
|
||||
memset(send_buf, 0, DATA_SIZE_TEST);
|
||||
printf("didn't receive anything in time\n");
|
||||
continue; //nothing or failed to pop from queue
|
||||
}
|
||||
|
||||
total_transactions++;
|
||||
if (recv_frame.len == 0){
|
||||
//receive fail
|
||||
num_incorrect++;
|
||||
} else {
|
||||
// res = obj->print_frame_info(recv_frame.buf, recv_frame.len, recv_buf, DATA_SIZE_TEST);
|
||||
|
||||
// if (res != ESP_OK){
|
||||
// num_incorrect++;
|
||||
// // printf("Received %ld bad frames on tx/rx round %ld for thread %d\n", num_incorrect, total_transactions, curr_channel);
|
||||
// } else {
|
||||
// // printf("Header information:\n");
|
||||
// // printf("Preamble\tTX ID\tRX ID\tSeq Num\tType/Flag\tFrag Info\tData Len\tCRC\n");
|
||||
// // printf("%X\t%d\t%d\t%d\t%X\t%ld\t%d\t%d\n",recv_frame.header.preamble, recv_frame.header.sender_id, recv_frame.header.receiver_id, recv_frame.header.seq_num,
|
||||
// // recv_frame.header.type_flag, recv_frame.header.frag_info, recv_frame.header.data_len, recv_frame.header.crc_16);
|
||||
// // printf("Received message '%.*s' on channel %d\n", recv_frame.len, recv_frame.buf, curr_channel);
|
||||
// }
|
||||
// }
|
||||
if (res != ESP_OK){
|
||||
num_incorrect++;
|
||||
// printf("Received %ld bad frames on tx/rx round %ld for thread %d\n", num_incorrect, total_transactions, curr_channel);
|
||||
} else {
|
||||
// printf("Header information:\n");
|
||||
// printf("Preamble\tTX ID\tRX ID\tSeq Num\tFrag Info\tType/Flag\tData Len\tCRC\n");
|
||||
// printf("0x%02X\t\t%-12d %-13d\t%-15d\t0x%02lX\t%d\t%-10d\t0x%04X\n",recv_frame.header.preamble, recv_frame.header.sender_id, recv_frame.header.receiver_id, recv_frame.header.seq_num,
|
||||
// recv_frame.header.frag_info, recv_frame.header.type_flag, recv_frame.header.data_len, recv_frame.header.crc_16);
|
||||
printf("Received message '%.*s' on channel %d from board %d seq num %d\n", recv_frame.len, recv_frame.buf, curr_channel, recv_frame.header.sender_id, recv_frame.header.seq_num);
|
||||
}
|
||||
}
|
||||
|
||||
// printf("Total received packets: %ld\tTotal packets corrupted: %ld\n", total_transactions, num_incorrect);
|
||||
// printf("Total received packets: %ld\tTotal packets corrupted: %ld\n", total_transactions, num_incorrect);
|
||||
|
||||
// // vTaskDelay(1000 / portTICK_PERIOD_MS); // wait 1 second before trying to send again
|
||||
// //reset temp buffers
|
||||
// memset(recv_buf, 0, DATA_SIZE_TEST);
|
||||
// memset(send_buf, 0, DATA_SIZE_TEST);
|
||||
// vTaskDelay(1000 / portTICK_PERIOD_MS); // wait 1 second before trying to send again
|
||||
//reset temp buffers
|
||||
memset(recv_buf, 0, DATA_SIZE_TEST);
|
||||
memset(send_buf, 0, DATA_SIZE_TEST);
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
// while(true){
|
||||
// vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
// }
|
||||
while(true){
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
// void print_binary(unsigned char c) {
|
||||
// for (int i = 7; i >= 0; i--) {
|
||||
// printf("%d", (c >> i) & 1);
|
||||
// }
|
||||
// }
|
||||
void print_binary(unsigned char c) {
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
printf("%d", (c >> i) & 1);
|
||||
}
|
||||
}
|
||||
|
||||
// void print_string_binary(const char *str) {
|
||||
// while (*str) {
|
||||
// print_binary((unsigned char)*str);
|
||||
// printf(" "); // space between bytes for readability
|
||||
// str++;
|
||||
// }
|
||||
// printf("\n");
|
||||
// }
|
||||
void print_string_binary(const char *str) {
|
||||
while (*str) {
|
||||
print_binary((unsigned char)*str);
|
||||
printf(" "); // space between bytes for readability
|
||||
str++;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// extern "C" [[noreturn]] void app_main(void) {
|
||||
// esp_err_t ret = nvs_flash_init();
|
||||
// if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
// ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
// ret = nvs_flash_init();
|
||||
// }
|
||||
// ESP_ERROR_CHECK(ret);
|
||||
extern "C" [[noreturn]] void app_main(void) {
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
// esp_netif_init();
|
||||
// esp_event_loop_create_default();
|
||||
esp_netif_init();
|
||||
esp_event_loop_create_default();
|
||||
|
||||
// printf("finished esp init\n");
|
||||
printf("finished esp init\n");
|
||||
|
||||
// printf("Hello world!\n");
|
||||
printf("Hello world!\n");
|
||||
|
||||
// // uint8_t iteration = 0;
|
||||
// // const char* message = "THIS IS A TEXT MESSAGE";
|
||||
// uint8_t num_channels = 4;
|
||||
// std::unique_ptr<DataLinkManager> obj = std::make_unique<DataLinkManager>(BOARD_ID, num_channels);
|
||||
// uint8_t iteration = 0;
|
||||
// const char* message = "THIS IS A TEXT MESSAGE";
|
||||
uint8_t num_channels = 4;
|
||||
std::unique_ptr<DataLinkManager> obj = std::make_unique<DataLinkManager>(BOARD_ID, num_channels);
|
||||
|
||||
if (obj->ready() != ESP_OK){
|
||||
for (int i = 5; i >= 0; i--) {
|
||||
printf("Restarting in %d seconds...\n", i);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
printf("Restarting now.\n");
|
||||
fflush(stdout);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
// // uint8_t dest_board_id = 2; //using a dummy number for now - there is no board with id 2 right now
|
||||
// uint8_t dest_board_id = 2; //using a dummy number for now - there is no board with id 2 right now
|
||||
|
||||
// // esp_err_t res;
|
||||
// esp_err_t res;
|
||||
|
||||
// // uint8_t send_buf[256];
|
||||
// // uint8_t recv_buf[256];
|
||||
// // size_t recv_len = 0;
|
||||
// uint8_t send_buf[256];
|
||||
// uint8_t recv_buf[256];
|
||||
// size_t recv_len = 0;
|
||||
|
||||
// // uint8_t curr_channel = 0;
|
||||
// uint8_t curr_channel = 0;
|
||||
|
||||
// DataLinkManager* obj_to_send = obj.release();
|
||||
DataLinkManager* obj_to_send = obj.release();
|
||||
|
||||
// TaskArgs args[4] = {};
|
||||
TaskArgs args[4] = {};
|
||||
|
||||
// for (uint8_t i = 0; i < 1; i++){
|
||||
// args[i].link_layer_obj = obj_to_send;
|
||||
// args[i].task_id = i;
|
||||
// args[i].receiver_id = RECEIVER_BOARD_ID;
|
||||
// args[i].receive_queue = xQueueCreate(10, sizeof(ReceviedFrame)); //queue storing up to 10 control frames
|
||||
// xTaskCreate(multi_transceiver, "multi_transceiver", 4096, static_cast<void*>(&args[i]), 5, NULL);
|
||||
// vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||
// }
|
||||
for (uint8_t i = 0; i < 1; i++){
|
||||
args[i].link_layer_obj = obj_to_send;
|
||||
args[i].task_id = i;
|
||||
args[i].receiver_id = RECEIVER_BOARD_ID;
|
||||
args[i].receive_queue = xQueueCreate(10, sizeof(ReceviedFrame)); //queue storing up to 10 control frames
|
||||
xTaskCreate(multi_transceiver, "multi_transceiver", 8192, static_cast<void*>(&args[i]), 5, NULL);
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
// printf("Tasks have been created\n");
|
||||
printf("Tasks have been created\n");
|
||||
|
||||
// while(true){
|
||||
// //do nothing
|
||||
// vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
// }
|
||||
|
||||
// for (int i = 5; i >= 0; i--) {
|
||||
// printf("Restarting in %d seconds...\n", i);
|
||||
// vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
// }
|
||||
// printf("Restarting now.\n");
|
||||
// fflush(stdout);
|
||||
// esp_restart();
|
||||
|
||||
// while(true){
|
||||
// //dummy wait
|
||||
// vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
// }
|
||||
// }
|
||||
while(true){
|
||||
//do nothing
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
#endif //RMT_TEST
|
||||
Reference in New Issue
Block a user