# Write a First Module

## Prerequisites

- A working installation of DV on your computer
  - **macOS** Make sure to also install the `dv-runtime` package via brew
  - **Windows** Development on Windows is not yet supported
- Knowledge of C++ programming
- A working C++ toolchain for your system, including *cmake*
- Your C++ editor or IDE of choice

## Start a new project

A demo module that counts events can be found on Gitlab. *(https://gitlab.com/inivation/dv/dv-example-cpp)*.

### Clone the start module repository with git

```bash
git clone https://gitlab.com/inivation/dv/dv-example-cpp.git
```

### Rename project

After downloading, let's rename the project first. First, rename the project folder

```bash
mv dv-example-cpp my-first-module
```

after that, rename the project name in the `CMakeLists.txt` file by changing the line `PROJECT(dv-example-cpp C CXX)`
with your favourite text editor. For example, you could change it to `PROJECT(my-first-module C CXX)`.

## Command Line Build

To build the demo module from terminal, use the following commands:

```bash
cd my-first-module
cmake .
make -j2 -s
sudo make install
```

If everything goes well, you should end up with a file `my-first-module.(so|dylib)` in the `modules` directory.

## IDE

### QT Creator

Click *File -> Open File or Project...* and select the projects *CMakeLists.txt*. Configure the project by selecting the
builds you want to create and specify their location or use the default.

### CLion

Click *File -> Open* and select the project root directory. CLion should automatically detect the project as a CMake
project and try to run `cmake` in the project folder.

### Eclipse

Eclipse does not support *cmake* natively yet. It does however support *Makefile* projects. Since *cmake* essentially
generates a *Makefile*, we can run cmake in the command line and open the resulting project in Eclipse.

Run cmake in the project directory

```
cmake .
```

Now, you can import it into Eclipse with *File -> Import... -> Existing Code as Makefile Project*.

### Other

The project is a simple *CMake* project. If your IDE supports CMake, it should be capable of opening the project out of
the box.

### Run configuration

Set your IDEs run configuration to launch `dv-runtime`. `dv-runtime` is (most likely) in `/usr/bin` (Linux) or
`/usr/local/bin` (macOS).

![QT Creator run configuration](/assets/development-configuration/qtcreatorRunconfig.png)

*Run configuration for QT Creator (Projects -> Build & Run -> Run).*

![Clion run configuration](/assets/development-configuration/clion-run-configuration.png) *Run configuration for CLion.*

To have DV load modules automatically when running the project within an IDE, add
`DV_MODULES_PATH=$PATH_TO_MODULE_PROJECT` to __Environment variables__. (\$PATH_TO_MODULE_PROJECT is a path to the
project root directory of your new module.)

### Set up DV

To iteratively test a module, you will have to set up DV the following way

1. Launch DV
1. Disable *Connect to -> Manage local runtime instances*. This tells DV to not start its own runtime, and just wait for
   someone else to start a runtime in its behalf.
1. Select *Shutdown* if DV asks to shut down the current instance.
1. DV is now waiting for a runtime to be started externally, and will connect as soon as one is started
   ![DV waiting for connection](/assets/dv-screenshots/waiting-for-connection.png)
1. Start your runtime by clicking the play button in your IDE. Make sure to have set up your IDE run configuration as
   described above.
1. As soon as DV connects to the runtime, select the *Structure* tab, click on *Add module* and select *Modify module
   search path*.
1. Click on *Add path* and add the path where you compiled your modules project directory. Click on *Done*.
   ![Adding a module search path](/assets/dv-screenshots/add-module-search-path.png)
1. Click on `Add module` again. If your module compiled succesfully, it should show up in the list of available modules
   to add. Click on your module name. ![Adding user module](/assets/dv-screenshots/add-user-module.png)
1. Connect the input and outputs of your module. Drag the connections from outputs into your modules inputs, and drag
   connections from your modules outputs to other inputs.
   ![Adding user module](/assets/dv-screenshots/connect-up-module.png)
1. Click the *Play* Button on your module to start it.
1. If your module is working, you should see your expected outputs.

## Adapt the module

### Getting event data from an input

Event data gets passed between modules in packets. The size of event packets is decided by the interval the preceeding
module emits the packets. The `run` function gets executed whenever there is new data available, or, when nothing
happens, periodically.

Every module has inputs. Inputs are defined in the `initInputs` function. The demo module we just compiled before, has
an *events* input called "events". To access the most recently arrived data packet on this input and iterate over the
events, override the `run` function as such:

```c++
void run() override {
    auto inData = inputs.getEventInput("events").events();

    for (const auto &event : inData) {
        // do something with the events
    }
}
```

### Getting meta data from an input

Input do not only provide raw data, they also provide (static) meta information, such as width and height dimensions.
For example, to get the width and height of the event input, write:

```C++
int width = inputs.getEventInput("events").sizeX();
int height = inputs.getEventInput("events").sizeY();
```

There is a simple counting algorithm already implemented in the example project

### Adding outputs

Modules can not only take data, they can also output data to subsequent modules. Module outputs are defined in the
static `initOutputs` function. Since the example module we worked on so far, does not have an `initOutputs` function, we
add it like so

```C++
static void initOutputs(dv::OutputDefinitionList &out) {
    out.addEventOutput("events");
}
```

The `out.addEventOutput("events");` line adds an output of the event type, with the name "events" to the module.
Contrary to the input case, we're not entierly done yet. Any output you define in your module has to be assigned its
required meta data. For example, an event output needs to have an assigned width and height to it. It is common that you
would want to setup the output with the same dimensions as an input to the module. Since the information to what is
actually connected to an input is only available at run time, we do not perform the setup call in the `initOutputs`
function, but rather in the classes constructor.

Since our class does not have a constuctor yet, we create one like so:

```C++
ExampleModule() {
    outputs.getEventOutput("events").setup(inputs.getEventInput("events"));
}
```

The line in the constructor body sets up the event output named "events" to the same parameters as the event input named
"events". To set the output up with different parameters, you can call a different setup function like

```C++
outputs.getEventOutput("events").setup(346, 260, "Data from example module");
```

### Adding config options

Config options are configurable parameters that are displayed in the gui for easy access. Config options are defined in
the `initConfigOptions` function. In our example app, the `initConfigOptions` function already exists. The value of a
config option can be accessed in the run function by calling `config.getInt` (or similar for other datatypes).

```C++
static void initConfigOptions(dv::RuntimeConfig &config) {
	config.add(
		"printInterval", dv::ConfigOption::intOption(
							 "Interval in number of events between consecutive printing of the event number.", 10000));
}
```

**A note on performance:** Looking up the value of a config option requires an access to a hashmap with a string key.
This is an efficient operation, but in case of event-by-event processing, it still adds considerable overhead. To solve
this issue, one can override a function `void configUpdate()` which gets called whenever a config value is changed by
the user. Use this function to look up config values and copy them into local variables to improve performance in the
`run` function. Example:

```C++
void configUpdate() override {
    printInterval = config.getInt("printInterval");
}
```

## Example: Create a refractorary period filter

*The complete code for this example can be accessed at
[gitlab.com/inivation/dv/dv-tutorial-code](https://gitlab.com/inivation/dv/dv-tutorial-code)*

Time to make our example module do something useful. A refractory period filter limits the maximum firing rate of a
pixel. Whenever an event at a pixel passes through the filter, all subsequent events are discarded until the refractory
period is over.

### Add private data members

Let's change the private data members of our class to store the data we need to function

```C++
private:
    // user selectable refractory period in microseconds
    long refractoryPeriod;
    // a matrix storing the last firing times for every pixel
    dv::TimeMat lastFiringTimes;
```

The first integer `refractoryPeriod` stores a configuration value, where the user can set the refractory period to a
user defined value. The second member `lastFiringTimes` is of the `dv::TimeMat` type. `dv::TimeMat` is a simple matrix
type that stores a 2D array of 64bit integers. We usually use the OpenCV `Mat` types for handling 2D data. OpenCV does
not provide a 64bit integer type, which is why we provide the `dv::TimeMat` type. Make sure to include

```C++
#include <dv-sdk/processing.hpp>
```

### Set inputs and outputs

Our refractory period filter needs exactly one event input and one event output. We define them as follows:

```C++
static void initInputs(dv::InputDefinitionList &in) {
    in.addEventInput("events");
}

static void initOutputs(dv::OutputDefinitionList &out) {
    out.addEventOutput("events");
}
```

### Set description

Let's set the description of the refractory period filter

```C++
static const char *initDescription() {
    return "This module filters events by applying a refractory period to the event timestamps.";
}
```

### Set configuration options

Our refractory period filter should have exactly one configuration option, the option to set the refractory period. The
type of the option is long and we set a default of 10ms with sensible range from 1ms to 1000ms.

```C++
static void initConfigOptions(dv::RuntimeConfig &config) {
	config.add(
		"refractoryPeriod", dv::ConfigOption::longOption("Refractory period to apply to events (in ms)", 10, 1, 1000));

	config.setPriorityOptions({"refractoryPeriod"});
}
```

The `setPriorityOptions` call only makes sure that the config option is exposed to the right side bar of the gui by
default. If you do not specify this, you access the option in the gui by clicking on the black plus icon.

### Set constructor

The job of the constructor is all about initializing private data members as well as outputs. We initialize the
`lastFiringTimes` array as well as the output to the same dimensions as the *events* input.

```C++
ExampleModule() : refractoryPeriod(0), lastFiringTimes(inputs.getEventInput("events").size()) {
	outputs.getEventOutput("events").setup(inputs.getEventInput("events"));
}
```

### Define the config update

The `configUpdate` function gets called at the start (before run) as well as whenever the config is changed. In our
case, only when the user changes the *refractoryPeriod* configuration. In the function, we look up the new value and
store it in the private data member. We could look up the value in the run function as well, but since config changes
are quite rare, it makes sense to only do it when there is a change.

```C++
void configUpdate() override {
    refractoryPeriod = config.getLong("refractoryPeriod") * 1000;
}
```

We multiply the value by 1000, to convert the value from miliseconds to microseconds.

### Implement run function

The `run` function is where the actual processing happens. Our refractory period filter is pretty simple:

```C++
void run() override {
    auto input = inputs.getEventInput("events");
    auto output = outputs.getEventOutput("events");

    for (const auto &event : input.events()) {
        if ((event.timestamp() - lastFiringTimes.at(event.y(), event.x())) > refractoryPeriod) {
            lastFiringTimes.at(event.y(), event.x()) = event.timestamp();
            output << event;
        }
    }
    output << dv::commit;
}
```

If the time between the current event and the last firing time at that position is larger than the refractory period,
update the last firing time for that pixel and append the event to the output.

After we appended all events the output, calling `output << dv::commit;` sends the events out in a packet to the next
module.

**Congratulations, you built your first useful module!**

*The complete code for this example can be accessed at
[gitlab.com/inivation/dv/dv-tutorial-code](https://gitlab.com/inivation/dv/dv-tutorial-code)*

## Resources

- Minimal example module: *[gitlab.com/inivation/dv/dv-example-cpp](https://gitlab.com/inivation/dv/dv-example-cpp)*
- Module with header / code separation:
  *[gitlab.com/inivation/dv/dv-tutorial-code/-/tree/master/color-paint-example](https://gitlab.com/inivation/dv/dv-tutorial-code/-/tree/master/color-paint-example)*
