Simulator Tutorial
Tip
It is recommended that after each task you try to compile and run your code.
Task 0: Setup
Compile and run SimpleSim
See RoFI Compilation. For this tutorial you will need the desktop suite.
After installing all the prerequisities, run Simplesim using the Quick start SimpleSim example.
Create a folder for your solution
Create a directory for your solution in tutorials/simulator/
(with any name that you like).
Note
You may be able to create the directory anywhere, but right now the easiest
way to include everything is to work inside the RoFI
repository.
Task 1: Create a Configuration
Create a file configuration.json
that will contain a configuration with:
id 12: Universal module
id 42: Pad 6×3
Connect the Universal module by the A-Z
connector to the pad with North
orientation at position:
O O O O O O
X O O O O O
O O O O O O
Note
You can also try to create the configuration in code and serialize it using the library.
Walkthrough
Go to documentation for configuration serialization.
Task 2: Create Hello World for Universal Module
Create a file module_code.cpp
that will print "Hello world {id}!"
with the local module’s id to the standard output.
Tip
Try running more clients (your module executable) and see what it does.
When the client seems to be cycling, it probably couldn’t be assigned any module.
Walkthrough
Create file
module_code.cpp
andCMakeLists.txt
in your solution folder.Add the executable in
CMakeLists.txt
and link libraryrofi::hal
.# CMakeLists.txt add_executable(tutorial-simulation module_code.cpp) target_link_libraries(tutorial-simulation rofi::hal)
Update
tutorials/simulator/CMakeLists.txt
to include your directory (by adding it to the listSOLUTION_DIRS
).Note
This is the only time in this tutorial that you won’t create your own file, but rather change an existing one.
Create the
module_code.cpp
file withmain
function that will print the required information.Hint
Hint
After compiling your code (using rmake
), the executable will be in your path
when you are in RoFI environment.
Task 3: Move Universal Module
Modify module_code.cpp
such that the Universal module will move to the position:
O O O O O O
O O O O O X
O O O O O O
The module should be connected by connector B-Z
with North
orientation.
Tip
The default position of rofi::hal::Connector
is extended.
So you don’t need to extend the connectors of Pad, but you need to retract a connector before connecting.
Note
The RoFI HAL is asynchronous by design.
However this tutorial is not about asynchronous programming,
so you can use std::promise
to synchronize the calls.
Caution
There is no guarantee that the thread in which the callback is called will end.
So use std::promise::set_value()
instead of
std::promise::set_value_at_thread_exit()
.
Walkthrough
This walkthrough will synchronize everything. It may not be the best practice, but the best practice for RoFI HAL in real code will probably be not to use it directly.
Add blocking versions of
rofi::hal::Joint::setPosition()
androfi::hal::RoFI::wait()
.Hint
void blockingFunc() { auto funcPromise = std::promise< void >(); func( [ & ] { funcPromise.set_value(); } ); funcPromise.get_future().get(); }
Add blocking versions of
rofi::hal::Connector::connect()
androfi::hal::Connector::disconnect()
usingrofi::hal::Connector::onEvent()
.Don’t forget to avoid dangling references.
Put it all together.
Tip
You can use
atoms::Angle
to convert degrees to radians.
Task 4: Multiple Modules with Same Code
Create a second Universal module with id 99
connected to the Pad at position Y:
O O O O O O
X O O O O Y
O O O O O O
Update the code in such a way so that both Universal modules meet at the middle and connect to each other and print the connection orientation.
The Universal modules should be connected together with the A-Z
connectors
and they should be connected to the pad at the following positions:
O O O O O O
O X O O Y O
O O O O O O
Try to program the module code in such a way that it works no matter what ids the modules have.
Hint
You may need to think about the orientation in which to connect the second Universal module to the pad in the configuration.
Walkthrough
Add a check that code is run in an Universal Module.
Note
Modules in simulator will get populated by code from the lowest
rofi::hal::RoFI::Id
to the highest.Update code such that both Universal Modules stop in the middle.
Get and print the orientation by
rofi::hal::Connector::getState()
.
Task 5: Module Communication
Update the code in such a way so that after both Universal modules meet in the middle, they:
Select a direction to a side.
Disconnect from each other.
Move in the selected direction.
Hint
You will need to rotate with the gamma joint.
Connect to each other at the new position.
The Universal modules should be again connected together with the
A-Z
connectors. They should now be connected to the pad right next to each other.
Again, try to program the module code in such a way that it works no matter what ids the modules have.
Walkthrough
Add a function that will use
rofi::hal::Connector::onPacket()
and return whether this module is the master or not.Hint
You can use the
rofi::hal::RoFI::Id
since it is always different for different modules.Caution
Make sure that you set the callback before the other module sends the packet.
Use the knowledge whether the current module id the master to split the code where you need to.
Task 6: Packet Filter
Create a packet filter discard_half.py
that discards every second packet
(counted for each sender module separately).
Create a configuration and code to test that it works.
Walkthrough
Create a file
discard_half.py
and create a python functionfilter(packet, sender, receiver)
.The function should return
(new_packet, delay_ms)
. The packet is lost ifdelay_ms == -1
. The behaviour when you return other negative values is not specified.Create a variable to store the number of packets sent by each module and throw away every second packet (by returning
-1
delay).Create a configuration of two modules connected together.
Note
Don’t forget to fix the position of the modules in space.
Add code to test the packet filter.
Run the simulator using the
-p
command line option.