# Xavia SDK user manual

This document is the full, human readable user manual for the Xavia SDK.
It is targeted to c++ developers that need to create a system to interact with a Xavia Sensor.
Please also examine the [examples manual](../examples/examples.md) and the [API reference](XaviaSDK_API_reference.pdf).
This manual will not explain the point cloud itself and how the data behaves in different sceneries.
For those aspects, please read the main 'Xavia user manual'.
For information on how to use this SDK in combination with the addon camera, please see the [separate camera manual](ManualCamera.md).

- [Xavia SDK user manual](#xavia-sdk-user-manual)
  - [Introduction](#introduction)
  - [Dependencies](#dependencies)
  - [Installation](#installation)
    - [CMake](#cmake)
    - [Other build systems](#other-build-systems)
  - [Deployment](#deployment)
  - [Usage](#usage)
    - [General code structure](#general-code-structure)
    - [Sensor creation and configuration](#sensor-creation-and-configuration)
    - [Sensor control](#sensor-control)
    - [Working with point clouds](#working-with-point-clouds)
    - [Errors and Events](#errors-and-events)
    - [System power management](#system-power-management)
    - [Multi tenant](#multi-tenant)
    - [Examples](#examples)
  - [Development support](#development-support)
    - [Testability](#testability)
    - [Debugging](#debugging)
  - [Errors and questions](#errors-and-questions)

## Introduction

The Xavia SDK in C++ is created using C++17.
It is tested with the g++ (7 and up), cl (MSVC 19.39) and clang compilers on Windows x86_64 (Windows 11 on Intel CPU), Linux x86_64 (ubuntu 24, WSL on Intel CPU) and Linux arm64 (ubuntu 24 on Jetson Xavier NX).
The SDK supports CMake integration, but can be used with any build system.

## Dependencies

The runtime SDK is build on the following libraries:

- asio
- plog
- pthread (UNIX only)

The first two are included in the SDK binaries should not create concerns.
The pthread library on UNIX should be installed by default on your Linux systems.

## Installation

The installation is also explained into the ReadMe.md file in the source of the SDK download.

Download and unpack the SDK archive for your platform into your development environment.

### CMake

Use CMake version 3.20 or newer.
Include the SDK by adding the following code to the dependencies section of your CMakeLists.txt file:

```cmake
find_package(Xavia_SDK REQUIRED)
```

Depending on where you unpacked the SDK archive, you might need to supply hints on where to find the SDK.
This can be done by defining the variable `set(Xavia_SDK_DIR "<UnpackFolder>/cmake/)`, where `<UnpackFolder>` is the path to where you unpacked the SDK archive, including the archive name.

This will create a number of targets, the most important one being `xavia::SDK`.
Link this target to the relevant executable or library in your code to make the SDK available:

```cmake
target_link_libraries(MyLib PRIVATE xavia::SDK)
```

In the files of that library you will then be able to include the public header files of the SDK.
All of these are in a subfolder `xavia`.
Hence the include command is:

```c++
#include <xavia/iSensor.h>
```

### Other build systems

When not using CMake or (versions before 3), you will need to manually include the headers and link the libraries.
These files can be all be found in the root of the archive:

- `<root>/include` contains the folder `xavia` and underneath that the public header files.
  It is advised to setup the system such that the `xavia` folder needs to be mentioned in the include statement: `#include <xavia/iSensor.h>`.
- `<root>/lib` contains the static libraries (.lib and .a) for inclusion during the build process, and shared libraries for Linux (.so) for runtime inclusion.
- `<root>/bin` contains the shared libraries for Windows (.dll) for runtime inclusion.

On unix you will need to link to pthread in order to build.

## Deployment

The SDK supports both shared and static inclusion.
When including statically, no extra files are required during deployment.
For dynamic inclusion, the file `Xavia_SDK.dll` or `Xavia_SDK.so` needs to be bundled with the executable.

It is not necessary to deploy any of the other files in the SDK archive to the target device for executing your application.

## Usage

### General code structure

The SDK partially acts as a framework and usage is split into two parts.

In the first part you should configure the system and the framework.
The default configuration already allows to simply start and stop the sensor.
By providing callback methods to the factory, the SDK will call these callbacks on specific events to allow you to interact with the system.
This part of the SDK works on the rhythm of the hardware.
You are in this configuration part for as long as you are interacting with the factory.

In the second part, you control the sensor state and you can move the sensor from 'idle' to 'running' and back.
Additionally, it is possible to query the sensor for its state and relevant properties.
This second part starts from the moment you acquire a `Sensor` object by calling the `SensorFactory::Build()` method.

In code this can be recognized as follows:

```c++
int main()
{
  xavia::SDK::SensorFactory factory;
  {
    // PART 1: CONFIGURATION
  }

  auto sensor = factory.Build();
  {
    // PART 2: RUNTIME
  }
}
```

It is possible to switch as often between these parts as desired.
It is also possible to reuse the same factory to create multiple sensors objects.

> ! Warning. Although it is possible to create multiple sensor objects that connect to the same Xavia sensor at the same time, this is not advised.
> Each such object will take up further resources and requires a separate communication channel, slowing down the overall performance.
> Within the same application, create only a single sensor object and use a `shared_ptr` to distribute it.

#### Destruction

All the objects used and returned by the SDK are smart pointers, values or references.
No raw pointers are used.
All these objects are also following RAII principles.
The consequence is that you do not need to worry about any of the memory allocated by the SDK or the objects provided by it.
Simply allow all to go out of scope for automatic cleanup.

### Sensor creation and configuration

In the 'configuration' part of your application, you should setup the interaction between your code and the SDK.
This is done through callbacks.
In this sense, the SDK acts as a framework that runs its own thread and calls your code from that thread.
This thread is managers the TCP connection to the Xavia sensor.

The sensor factory provides the following callback options:

- `SetPointCloudCallback`
- `SetErrorCallback`
- `SetAlertCallback`

The actual response you will get on each of these callbacks is explained in separate sections later in this manual.

When all configuration has been completed, you can instantiate the `Sensor` object using the `Build` method.
This method has 2 optional arguments: the ip address of Xavia and the port number.
The following variations are all legal:

```c++
factory.Build();
factory.Build("10.10.100.11");
factory.Build("10.10.100.11", 4000);
```

Since these are the default values, all three will have the same effect.
The `Build` function creates the actual connection to the Xavia sensor.
As such it will only succeed if the sensor is detectable in the network and powered on.
In the case it is not, the function might take several seconds (up to 15 seconds) before reporting the error.
Errors are reported by throwing exceptions.

### Sensor control

In the 'runtime' part of your application you can control the Xavia sensor by calling methods on the `ISensor` object:

- `Start`
- `Stop`
- `GetSerialNumber`
- `GetCurrentState`
- `GetErrors`
- `Reboot`
- `Poweroff`

All of these are synchronous operations that will execute either locally or remotely.
Synchronous in this sense means that the method call will only return after either a timeout or a response from the sensor.
For functions like `Start`,`Stop`, `Reboot` and `Poweroff` this response only indicates that the sensor accepted the command into the queue but it is probably not yet executed.
The `GetSerialNumber` and `GetCurrentState` will be discussed below, but do not directly interact with the sensor.
Instead they serve information from the SDK cache.
The `GetErrors` function does synchronize with the hardware and retrieves the latest information.

#### Start/Stop

The start action enables the laser output, starts the measurement and starts streaming the point clouds from the sensor to all clients.
The stop action does the inverse action: stops laser output, stops measurement and stops streaming point clouds.
Both actions will block your thread until either a timeout occurs, or the sensor acknowledged receiving the command.
Therefor it is good practice to encompass both in a try catch block:

```c++
try
{
  sensor->Start(); // or Stop()
}
catch(xavia::sdk::Exception& e)
{
  //..
}
```

The function has an optional `timeoutMs` argument.
You can provide the maximum amount of time in milliseconds the SDK waits for the response from the sensor.
If the timeout is omitted the default value of 5 seconds is used.
Timeouts can happen on `Stop` if the sensor is also streaming other messages at 1kHz and the outgoing buffer was full when the message was supposed to be transmitted.
In this case it is safest to synchronize the state to make certain that the stop really failed:

```c++
try
{
  sensor->Stop();
}
catch(xavia::sdk::Exception& e)
{
  case UNABLE_TO_STOP:
    sensor->GetErrors(); // synchronizes
    if(sensor->GetCurrentState() == xavia::sdk::SensorState::IDLE)
    {
      // stop worked, the sensor is idle
      break
    }
    else
    {
      // Error failed to stop
    }
    break;
}
```

The errors `ACTION_ILLEGAL_SENSOR_RUNNING` and `ACTION_ILLEGAL_NOT_RUNNING` will be returned if the sensor was already
started or stopped respectively.
This means that calling `Start` on a sensor that is already running is considered an error.
But the error will not compromise the sensor.
It will refuse the command and continue in state it was.

> (i) Note: All clients are considered equal by Xavia. When multiple clients connect, and one sends the Stop command, streaming will stop for all clients.
> This can happen for example when using the web viewer together with your own application.
> See the [multi-tenant section](#multi-tenant).

#### Sensor state

Xavia has 4 states:

- **Uninitialized**: This state can only occur if there is an internal error in the hardware which prevents the on board software from communicating with the certain components. Normally this case should never occur.
- **Idle**: The default startup state. The sensor is ready and waiting for the `Start` command
- **Running**: The sensor is measuring, the lasers are on.
- **Error**: The sensor experienced an error and is unable to operate normally. Advised action is to reboot the sensor.

The idle and running states will be encountered most often.
Use the `Start` and `Stop` functions to switch between them.

The error state can happen for a number of reasons: an internal error, overheating, under voltage, ...
When the system is in an error state, it will remain that way.
The only recovery from an error state is by rebooting the system.

### Working with point clouds

Once the sensor is in running state it will stream point clouds to all connected clients.
The SDK will automatically receive all these point clouds and handle them.
To receive these point cloud objects, a point cloud callback should be registered.
To do so, use the `SetPointCloudCallback` function of the `SensorFactory` before building the Sensor object:

```c++
factory.SetPointCloudCallback(callback);
```

This callback has only one argument and no return.
Additionally it should not throw exceptions.
For example:

```c++
void MyCallback(std::unique_ptr<xavia::sdk::IPointCloud> pc)
{
  // do something with pc
};
```

You receive the point clouds as a mutable, unique pointer.
As such their lifetime is now connected to the scope of this function unless you decide otherwise.

#### Point cloud data

A few explanation of the point cloud data and the meaning of different fields is part of the full product user manual.
Here only a short summary is provided, focused more on the technical side then the physical interpretation of the data.
The point cloud object provides access to the following fields:

- `GetXMm()`, `GetYMm()`, `GetZMm()`: returns the reconstructed (x,y,z) coordinates expressed in mm with the optical origin of the sensor.
- `GetDistanceMm()`: provides the radial distance from the sensor origin to the point in mm. This is calculated from the time of flight information. This field is always larger than 0. If this field is -1, then no valid time of flight measurement was received.
- `GetReflectivity()`: a distance corrected measure for the amount of laser light that returns. It is a number larger than 0. If it says 10%, it means that the returned light is equal to the return of a 10% Lambertian target at the measured distance.
- `GetAmbient()`: provides a measurement of the background light. This means IR light from other sources that the lidar illuminated the scene.
- `GetConfidence()`: returns the confidence of the measurement. This is a value between 0 and 1 where 1 would mean 100% confident. You should expect to see values around 0.995 and 0.999 in this field.

For the (x,y,z) coordinates, the coordinate system is a right-handed system, with the x-direction equal to the viewing direction of the sensor.
The z-direction is upwards (when the XenomatiX logo is on top) and the y-direction is to the left hand side.

The point cloud information is communicated line per line.
The standard Xavia has a point cloud frame of 56 lines, and each line provides 192 measurements.
Lines can be identified from the `MetaData::lineNumber` field.
The vectors returned by e.g. `GetDistanceMm()` contain the distance value for all 192 measurements in this line.
For future compatibility the number of measurements in a line can be read from either `GetDistanceMm().size()`, or from `GetNrPoints()`.

56 lines together cover the entire field of view.
That combination is called a 'frame'.
Frames can be identified by the `MetaData::frameNumber` field.
It starts at 0 and increments for every frame that is recorded by the hardware.
The maximum line number is 4'294'967'295, after which it will overflow back to 0.
This will happen after 2485 days of continuous measurement (6,8 years) for the 20Hz version.

An important note is that within a frame, the lines are reported from line number 0 to 55.
This is from the bottom towards the top of the field of view.

##### Metadata

Besides the point cloud data itself, the point cloud object also provides metadata.
Metadata are counters and timestamps related to the point cloud information.

Timestamps are uint64_t POSIX numbers indicating the number of microseconds since 1/1/1970.
The line-timestamp reported is the internal clock of Xavia at the moment of the exposure of that specific line.
The frame-timestamp is equal to the line timestamp of the first exposed line (line 0) of the frame.
There is around 893 us between each line.

The metadata also reports a return index and a trigger counter.
The return index will be used in the future to indicate multiple returns.
When this becomes active, the sensor will send multiple lines with the same line number, but different return index.
The meaning of each index will depend on the selected mode.
Each such line will trigger a new callback.

The trigger counter will be active when the sensor is put into slave mode.
In this case, some external signal will be used to start the exposure of the sensor.
If this signal is lower than the maximum framerate of the sensor, then the trigger counter will equal the frame number.
However, if triggering happens faster than the sensor can perform measurements, then the trigger counter will grow faster than the frame number.
In other words, if the frame number increments by 1, but the trigger counter increments by 2, it means that the system received a trigger but was not ready to act on it and that trigger did not result in a measurement.
On the other hand, if both the trigger counter and frame number increment by 2, it means that the trigger did result in a measurement, but the system was unable to stream the data or the sdk failed to receive it properly.

##### Probes

The point cloud object also contains a `OnboardProbes` section that gives access to a temperature and a humidity readout.
Both are measured from hardware probes inside the sensor.
The temperature probe is reported in degrees Celsius.
The humidity is reported as the relative humidity.

##### IMU data

> ! Note: the IMU is not supported in the first version of Xavia.

Each point cloud also carries the IMU data.
The IMU is part of Xavia and measures the acceleration and angular motion of the device.
This can be used to either correct for motion blur, better synchronize with the RGB image, or to help slam algorithms.
It will provide the acceleration on the three axes expressed in m/s^2.
It will also provide the angular motion for yaw, pitch and roll expressed in degrees per second.

#### Performance

To use the Xavia to its fullest, some performance considerations are in order.

##### Real time processing

It is important to note that he Xavia works at a framerate of either 20Hz or 10Hz, depending on the options your purchased.
At 20Hz and 56 lines per frame, the sensor will output 1120 lines per second.
This will also result in your callback being called 1120 times per second.
In order to not miss any data, the callback should be handled as a real time part of the software.
Meaning that it should complete its work within 893 microseconds.
If the callback takes more time than that, then eventually the internal buffers will fill up and data will be skipped.

In the 10Hz version, the 56 lines of a frame will still arrive every 893 microseconds, but a 50 ms pause will be present before the next burst of data comes.

The choice for streaming line by line instead of aggregating on the sensor and streaming full frames stems from the idea that some data is better than no data.

- Your code can already start interpreting the first lines before the final lines are on the system. This can save milliseconds in response time.
  For example, the bottom half of the FoV (which on a vehicle typically contains the ground and closest objects) can already be processed 25 ms in advance of the full frame arriving.
- Even if a single line is missing, the other 98% of the frame can still be enough to draw conclusions and run your algorithms.

The SDK has an internal buffer to help manage variations in the network communication or in the callback response.
The default size of this buffer equals two full frames.
This should be less than 0.5MB of ram.
The size of this buffer is configurable through `SensorFactory::SetPointCloudBufferSize(size)`.
Lowering this value can save ram, at the danger of missing lines.
We don't advise to go below 3 or many lines will be missed.
Any value above 20 should be sufficient to capture all lines.

> ! Warning: a large buffer is no long-term solution for a slow callback. If the callback is too slow, the buffer will prevent lines to be dropped,
> but the data provided to the callback will become increasingly stale. After a while, the buffer will still overflow and lines will be missed.

The advise is to use the callback only to move the data into a new thread to do the calculations.
In this thread either aggregate the lines into a frame if that is the desired methodology, or use a multi-threaded computation pipeline to process multiple lines at once, keeping each step below the 893 microseconds.

##### Vectorization

A feature of this SDK is that the data is provided in a vectorized manner.
This shows in return value of the `GetXMm()` methods and its friends.
The `std::vector` container provides the data in consecutive memory.
This allows user to optimally use `std::memcpy` to transfer data, but it also allows the compiler/user to optimally use caching and simd operations.
This also supports the memory organization that many blas libraries prefer.

#### Exporting and importing data

With access to data from the point cloud it is possible to support any output format.
However, to ease development, this SDK provides the proprietary XenomatiX format.
For this, two functions exist: `Serialize()` and `Deserialize()`.
Their usage can be explained by the following example:

```c++
void OnNewPointCloud(xavia::sdk::IPointCloud& pc)
{
  auto buffer = pc.Serialize(); // returns the XenomatiX format
  std::ofstream file("output.xpc", std::ios::binary);
  file.write(buffer.data(), buffer.size());
}

std::unique_ptr<xavia::sdk::IPointCloud> ReadPointCloud(std::string filename)
{
  auto pc = xavia::sdk::CreatePointCloud(); // creates empty point cloud
  
  // read from file
  std::ifstream file(filename,std::ios::binary| std::ios::ate);
  std::streamsize size = file.tellg();
  std::vector<char> buffer(size);
  file.read(buffer.data(), size);

  // interpret
  pc.Deserialize(buffer); // pc will be equal to the pc from the 'OnNewPointCloud' function.
  return pc;
}
```

The serialized format is equal to the ethernet communication, as such it is also possible to use the `Deserialize` function to interpret the bytes from e.g. a network capture.

### Errors and Events

Errors can occur in two scenarios: 1) you asked for an action and it is unable to comply, 2) something happened in the system and it reports the error.
The first category is triggered by a user action.
They are all communicated by throwing exceptions.
This is a non-exhaustive list of possible errors:

- **UNABLE_TO_CONNECT**: SDK failed to connect to target IP address. Check your network connection and that the system is powered.
- **ACTION_ILLEGAL_SENSOR_RUNNING**: You cannot perform this action while the sensor is in running state (e.g. Start)
- **ACTION_ILLEGAL_SENSOR_NOT_INITIALISED**: You cannot perform this action while the sensor is not initialized (e.g. Start)
- **UNABLE_TO_START**: Sensor failed to acknowledge the start command
- **UNABLE_TO_STOP**: Sensor failed to acknowledge the stop command
- **ACTION_ILLEGAL_NOT_RUNNING**: You cannot perform the action while the sensor is in idle state (e.g. Stop)
- **COMMAND_BUFFER_FULL**: The command was received by the system, but the current command buffer is full. Try again later.
- **UNKNOWN_MESSAGE_ID**: This system does not recognize the command you provided.
- **UNSUPPORTED_COMMAND**: The provided command is not supported by the current system.
- **UNKNOWN_ERROR**: Error that does not fall in any of the above categories. Use the `.what()` method of the exception or the logs to get more information.

Some extra explanation on some of these errors:

- Command buffer full: The Xavia has an internal queue of 6 commands.
  If more commands are send while the previous command are still being processed, it will report that the buffer is full.
  In this case your command is not accepted and the system will not execute it.
  Try to send it again when the buffer has room.

- UNKNOWN_MESSAGE_ID and UNSUPPORTED_COMMAND will be very rare.
  These errors can only occur when mixing an old sensor with a newer SDK.
  Or if you try to use the SDK for an unsupported XenomatiX sensor.

Besides the errors above, it is also possible that something happens on the system and an error is pushed.
In this case the error is not caused by the call to a function and the sdk cannot throw an exception.
These errors are pushed to the Event callback if you provide any (not obligatory).
The following events are currently defined:

- **HARDWARE_CRITICAL**: An unspecified critical hardware error occurred. Usually this means a broken component.
- **POWER_CRITICAL**: The measured power on the main input, or in one of the components was out of bounds. The sensor will shut down to protect itself.
- **TEMPERATURE_CRITICAL**: The measured temperature is out of range. This either indicates that the sensor is overheating, under cooled, or that a temperature sensor is broken. In all cases, the sensor will take actions to protect itself and shut down.
- **EYE_SAFETY_ALERT**: The sensor measured an unexpected and potentially unsafe laser output and prevented laser output before it can do any harm.
  The sensor will stay in this error state until rebooted.
- **HUMIDITY_ALERT**: The measured humidity inside the sensor is out of bounds and the sensor was stopped to protect itself.
- **VOLTAGE_ALERT**: An invalid voltage was measured. Correct the input voltage you provide and make sure that your power system can support the load.
- **HOUSING_REMOVED**: This event indicates that the sensor is or was opened. To protect users certain components and laser output have been disabled. Replace the housing and restart the sensor to resolve the warning.

These events (sometimes also called Alerts) are communicated to the callback together with a `state` flag.
If this flag is true, it means that the alert is raised. If the flag is false, then the alert is released.
For example: if the sensor overheats the `temperature_critical` alert is issued with status true.
If after a while the sensor cools down, the alert will get triggered again but with the status false.

### System power management

The SDK allows to power off and reboot the Xavia sensor.
In order to do so, you first need to create a connection with the system.
Once the `Sensor` object has been build and connection was successful, the `Poweroff` and `Reboot` commands become available.
In code this looks like:

```c++
xavia::SDK::SensorFactory factory;
auto sensor = factory.Build();
sensor->Reboot(); // or sensor->Poweroff();
```

In both cases, the SDK will issue the command to the sensor over the TCP/IP connection.
Assuming the sensor is responsive and has room in the command queue, it will respond with the message 'command queued'.
The SDK will consider this success and the call to `sensor->Reboot()` will return.
At this point the command on the Xavia sensor is simply queued.
Depending on the queue and the other commands, it might take some time before the command is actually executed.
When the Xavia executes the command to shutdown it will signal the operating system to stop all activities and gracefully shutdown.
At this point the Xavia sensor will broadcast the message that it is 'shutting down' to all clients.
If you connected an event callback (see [the Errors and events section](#errors-and-events)), this callback will get triggered on the `xavia::SDK::Alert::shutting_down` event.

Due to the asynchronous nature of the commands, this can generate different behavior on different execution runs.
Therefore, the safest action is to assume the system will go down and consider the sensor object to become invalid from the moment you use either the `Poweroff` or `Reboot` method.
Destroy the sensor object (let it go out of scope, or use `sensor.reset()`).
When rebooting, wait some time for the hardware to reboot and try to use the factory to build a new sensor object.
The emphasis is on 'try' because this might fail if the system is not yet ready to accept client connections.

In the case the sensor does not react positively on the TCP command, both `Poweroff` and `Reboot` methods will throw an exception.
In this case you can assume that the sensor is not powering down.

### Multi tenant

The Xavia system is a multi tenant system, which means that multiple clients connect to the same sensor instance.
None of these clients are considered more important than the other.
The sensor will handle each received command in the order they arrive.
The consequence is that if any of the clients issues e.g. a `Start` command, the sensor will obey.
For all important scenarios (start, stop, shutting down, ...) the sensor will broadcast an event alerting all the clients of the state change.
If needed you can query the system to synchronize with its state by using the `GetErrors()` function (see [the section on sensor state](#sensor-state)).

Additionally, the SDK will not take automatic actions.
For example: destroying the `Sensor` object will not send a Stop command to the Xavia sensor.
It will keep on working, even if no clients are listening.
Similarly, the SDK will not issue a Start command when connecting.
Instead

You can use this, if so desired, in a micro services like architecture where different clients act on different signals, or take different actions.
E.g. one client can handle all the point clouds and events, while another system can create a temporary client just to start and stop the sensor(s).
Or a central system can be used to create a sensor object simply to stop and shut down all Xavia sensor in your network.
Each individual application will then receive the shut down signal from their own sensor.

Another example is the interaction with the build-in Point cloud viewer.
This viewer also offers start, stop, power off, and reboot buttons.
You can use this:

- to check if your own Start and Stop commands truly change the system state correctly.
- to check if your application can handle the sensor starting/stopping based on alternative sources.

#### Security

The current version of the sensor and SDK do not contain any security measures.
Messages are not encrypted during transmission.
No authentication is required for commanding the system.

### Examples

To further support your development, the SDK contains a number of examples.
These examples can be used as a starting point for your code or to act as an implementation example.
All the examples use and link the SDK in the same way your application should use it.
Each example focusses on a specific functionality and is provided with detailed comments.
The ideal application combines the functionalities of the individual examples into a robust system.

The examples can be built easily using cmake and will then be available as a target.
Either include the `examples` subfolder into your project or build it directly:

```bash
cd Xavia_SDK-etc.
cmake -S ./examples -B ./examples/build -G {"Visual Studio 17 2022", "Ninja", etc.}
cmake --build ./examples/build --target xavia::example_basic
```

The above shows how to build the `xavia::example_basic` target.
This will create an executable `xSDK_example_basic`.
More example targets are available, for the full list, please review the [examples manual](../examples/examples.md).

## Development support

As fellow developers we understand that creating an application is more than just writing code and getting it to work.
Professional software development requires thinking about code quality, testability and readability as well.
This SDK has been designed with your quality of life in mind, as will be explained in the following paragraphs.

### Testability

In order for you to achieve 99.99% coverage, we provide all the interfaces for our objects.
This means that it is possible to mock the sensor and the point clouds.

For example, using the [gmock](https://github.com/google/googletest) framework from google:

```c++
#include <xavia/iSensor.h>
#include <gmock/gmock.h> 

class MockSensor : public xavia::SDK::ISensor {
 public:
   MOCK_METHOD(void, Start, (const std::size_t& timeoutMS), (override));
   MOCK_METHOD(void, Stop, (const std::size_t& timeoutMS), (override));
   MOCK_METHOD(SerialNumber, GetSerialNumber, (), (const, override));
   MOCK_METHOD(SensorState,  GetCurrentState, (), (const, override));
   MOCK_METHOD(std::vector<SensorError>, GetErrors, (const std::size_t& timeoutMS), (override));
   MOCK_METHOD(void, Reboot,(), (override));
   MOCK_METHOD(void, Poweroff,(), (override));
}
```

creates a fully controllable mock xavia sensor on which you can define expectations and put behavior.
In a similar way the full point cloud can be mocked.

The combination of both should allow you to write unit tests for most of your code.

### Debugging

Despite all the testing, bugs can still happen.
It is not possible for you to step into our code during debugging due to the pre-built nature of the SDK.
However, to aid understanding in what is going wrong, the SDK can output logs.
Two options are available:

- `xavia::SDK::SetLogOutputFolder()` provides a way to designate a folder in which the SDK will create a file.
  Each execution a new file will be defined using the system time.
- `xavia::SDK::SetLogCallBack()` provides a way to capture the SDK log message into your own application.
  The callback will receive a `severity` and `std::wstring` field.
  This allows you to push these log files to your own file or e.g. send them to a cloud or central system.

If neither of these are provided, the SDK will log to the `std::cout` by default.
It is possible to provide both a folder/cout output and the callback method.

For more information on the logging, please examine the corresponding example.

> ! Important: Set the preferred logging method before calling any SDK function.
> On the first log message, the logging instance is initialized and the logging method can no longer be changed after that.

## Errors and questions

If, after reading this manual and the examples, you still cannot find the answer to your questions, please contact us at [xavia.support@xenomatix.com](xavia.support@xenomatix.com).
