Creating Components Part 1: Creating the MathSender
Background
Components are the lifeblood of an F’ deployment. In this tutorial the components are strictly virtual, however in many deployments components will represent unique pieces of hardware, such as sensors and microcontrollers!
In this section
In this section, you will begin creating an active component and write its F Prime Prime (fpp) implementation. You will generate cpp and hpp files using the fpp. Note, that you will implement component behavior in MathSender.cpp
in the next section. Most commonly, the steps to create a new component are the following:
- Construct the FPP model.
- Add the model to the project.
- Build the stub implementation.
- Complete the implementation.
- Write and run unit tests.
Component Description
The MathSender
is an active component which receives parameters, sends parameters, logs events, and sends telemetry.
With the component description in mind, use the following command to create the MathSender
component:
Creating the MathSender
F´ projects conveniently come with a Components/
folder to create components in. It is not required for components to live there, but this tutorial will make use of it.
# In: MathProject/Components/
fprime-util new --component
This command will prompt you for some inputs. Answer the prompts as shown below:
[INFO] Cookiecutter source: using builtin
Component name [MyComponent]: MathSender
Component short description [Example Component for F Prime FSW framework.]: Active component used for sending operations and operands to the MathReceiver.
Component namespace [Component]: MathModule
Select component kind:
1 - active
2 - passive
3 - queued
Choose from 1, 2, 3 [1]: 1
Enable Commands?:
1 - yes
2 - no
Choose from 1, 2 [1]: 1
Enable Telemetry?:
1 - yes
2 - no
Choose from 1, 2 [1]: 1
Enable Events?:
1 - yes
2 - no
Choose from 1, 2 [1]: 1
Enable Parameters?:
1 - yes
2 - no
Choose from 1, 2 [1]: 1
[INFO] Found CMake file at 'MathProject/project.cmake'
Add component Components/MathSender to MathProject/project.cmake at end of file (yes/no)? yes
Generate implementation files (yes/no)? yes
Before doing anything to the files you have just generated, try building:
cd MathSender
fprime-util build
Editing the FPP Model
Now that you have created the component, you can start working on implementing the component behavior. The first part of implementing component behavior is editing the fpp file. The fpp file will specify what goes into the auto-generated cpp and hpp files. Writing the fpp file will not implement component behavior on its own, but it will generate templates for most of what you will write in cpp and hpp files.
In Components/MathSender
, open MathSender.fpp
and entirely replace its contents with the following:
# In: MathSender.fpp
module MathModule {
@ Component for sending a math operation
active component MathSender {
# ----------------------------------------------------------------------
# General ports
# ----------------------------------------------------------------------
@ Port for sending the operation request
output port mathOpOut: OpRequest
@ Port for receiving the result
async input port mathResultIn: MathResult
# ----------------------------------------------------------------------
# Special ports
# ----------------------------------------------------------------------
@ Command receive port
command recv port cmdIn
@ Command registration port
command reg port cmdRegOut
@ Command response port
command resp port cmdResponseOut
@ Event port
event port eventOut
@ Telemetry port
telemetry port tlmOut
@ Text event port
text event port textEventOut
@ Time get port
time get port timeGetOut
# ----------------------------------------------------------------------
# Commands
# ----------------------------------------------------------------------
@ Do a math operation
async command DO_MATH(
val1: F32 @< The first operand
op: MathOp @< The operation
val2: F32 @< The second operand
)
# ----------------------------------------------------------------------
# Events
# ----------------------------------------------------------------------
@ Math command received
event COMMAND_RECV(
val1: F32 @< The first operand
op: MathOp @< The operation
val2: F32 @< The second operand
) \
severity activity low \
format "Math command received: {f} {} {f}"
@ Received math result
event RESULT(
result: F32 @< The math result
) \
severity activity high \
format "Math result is {f}"
# ----------------------------------------------------------------------
# Telemetry
# ----------------------------------------------------------------------
@ The first value
telemetry VAL1: F32
@ The operation
telemetry OP: MathOp
@ The second value
telemetry VAL2: F32
@ The result
telemetry RESULT: F32
}
}
About this Component
The above code defines MathSender
component. The component is active, which means it has its own thread.
Inside the definition of the MathSender
component are several specifiers. We have divided the specifiers into five groups.
-
General ports: These are user-defined ports for application-specific functions. There are two general ports: an output port
mathOpOut
of typeOpRequest
and an input portmathResultIn
of typeMathResult
. Notice that these port specifiers use the ports that you defined. The input port is asynchronous. This means that invoking the port (i.e., sending data on the port) puts a message on a queue. The handler runs later, on the thread of this component. -
Special ports: These are ports that have a special meaning in F Prime. There are ports for registering commands with the dispatcher, receiving commands, sending command responses, emitting event reports, emitting telemetry, and getting the time.
-
Commands: These are commands sent from the ground or from a sequencer and dispatched to this component. There is one command
DO_MATH
for doing a math operation. The command is asynchronous. This means that when the command arrives, it goes on a queue and its handler is later run on the thread of this component -
Events: These are event reports that this component can emit. There are two event reports, one for receiving a command and one for receiving a result.
-
Telemetry: These are channels that define telemetry points that the this component can emit. There are four telemetry channels: three for the arguments to the last command received and one for the last result received.
For more information on defining components, see the FPP User’s Guide.
Generate the Implementation Files
Now you have written the FPP code for the component, but the cpp and hpp files do not yet reflect the changes you have made to the fpp file. To get the cpp and hpp to reflect the specs you have defined in the fpp, you need to use the implement command as shown below:
# In: MathSender
fprime-util impl
Now, In MathSender
, you will see two new files, MathSender.cpp-template
and MathSender.hpp-template
. The template files are the files you just generated using the FPP model. Whenever F’ generates code, it creates new file with the -template
so as to not burn down any old code. In this case, you did not write anything in the original MathSender.cpp
or MathSender.hpp
, so you can use a move command to replace the old code with the new code:
# In: MathSender
mv MathSender.cpp-template MathSender.cpp
mv MathSender.hpp-template MathSender.hpp
Build MathSender to make sure everything worked as expected.
# In: MathSender
fprime-util build
Wait… Shouldn’t You Add this to the Build?
If you’ve been paying attention to the tutorial thus far, you might be getting some warning bells that you have not added your new component to the build. Fear not, when using fprime-util new --component
all of the CMakeLists.txt
and project.cmake
work was done for you! Take a look at both files to verify for yourself.
Summary
You are about two thirds of the way through finishing MathSender
. In the next section you will implement MathSender
’s component behavior.
Next: Creating Components Part 2: Implementing MathSender Behavior