# Multi-Device Pipelines

A single [Pipeline](https://docs.luxonis.com/software-v3/depthai/depthai-components/pipeline.md) can span several
[devices](https://docs.luxonis.com/software-v3/depthai/depthai-components/device.md). Every device node is created on the device
it should run on, host nodes consume the streams of all devices alike, and a link between nodes of two different devices is
relayed through the host automatically. The pipeline starts, monitors and stops all of its devices together, so a multi-camera
application is written like a single-device one: one pipeline, one `start()`, one set of queues.

Pipeline nodes:
- Device A
- Device B
- Host
- Camera
- Camera
- Sync
- Your code

Pipeline connections:
- Camera -> Sync
- Camera -> Sync
- Sync -> Your code

Two devices in one pipeline. The `Camera` nodes run on their devices, the
[Sync](https://docs.luxonis.com/software-v3/depthai/depthai-components/nodes/sync.md) node runs on the host and groups the frames
of both devices into one `MessageGroup`.

> **Note**
> Multi-device pipelines (`Pipeline.addDevice()`, device state tracking and the cross-device calibration graph) were added in DepthAI v3.11.0 and are supported on RVC4 devices only.

## How to place it

Create the pipeline without an implicit device, add the devices, and pass the device as the second argument of `create()` for
every device node. Host nodes are created as usual.

#### Python

```python
import depthai as dai

with dai.Pipeline(createImplicitDevice=False) as pipeline:
    deviceA = pipeline.addDevice("192.168.1.11")  # device ID, IP address or name
    deviceB = pipeline.addDevice("192.168.1.12")

    cameraA = pipeline.create(dai.node.Camera, deviceA).build(dai.CameraBoardSocket.CAM_A)
    cameraB = pipeline.create(dai.node.Camera, deviceB).build(dai.CameraBoardSocket.CAM_A)

    sync = pipeline.create(dai.node.Sync)
    sync.setRunOnHost(True)  # group the frames on the host
    cameraA.requestOutput((640, 400)).link(sync.inputs["A"])
    cameraB.requestOutput((640, 400)).link(sync.inputs["B"])
    queue = sync.out.createOutputQueue()

    pipeline.start()
    while pipeline.isRunning():
        group = queue.get()
        frameA, frameB = group["A"], group["B"]
```

#### C++

```cpp
dai::Pipeline pipeline(false);  // no implicit device
auto deviceA = pipeline.addDevice("192.168.1.11");  // device ID, IP address or name
auto deviceB = pipeline.addDevice("192.168.1.12");

auto cameraA = pipeline.create<dai::node::Camera>(deviceA)->build(dai::CameraBoardSocket::CAM_A);
auto cameraB = pipeline.create<dai::node::Camera>(deviceB)->build(dai::CameraBoardSocket::CAM_A);

auto sync = pipeline.create<dai::node::Sync>();
sync->setRunOnHost(true);  // group the frames on the host
cameraA->requestOutput(std::make_pair(640, 400))->link(sync->inputs["A"]);
cameraB->requestOutput(std::make_pair(640, 400))->link(sync->inputs["B"]);
auto queue = sync->out.createOutputQueue();

pipeline.start();
while(pipeline.isRunning()) {
    auto group = queue->get<dai::MessageGroup>();
    auto frameA = group->get<dai::ImgFrame>("A");
    auto frameB = group->get<dai::ImgFrame>("B");
}
```

`addDevice()` accepts a device ID, an IP address or a device name, a `DeviceInfo`, or an already constructed `Device`. It connects
to the device immediately and returns the `Device` object you then pass to `create()`.

## Devices in a pipeline

Every pipeline has a default device, also called the master. Nodes created without an explicit device run on it, and the pipeline
methods without a device argument (`getCalibrationData()`, `setBoardConfig()`, ...) act on it. There are two ways to get one:

 * `dai.Pipeline()` connects to the first available device and makes it the default device. Further devices are added with
   `addDevice()`.
 * `dai.Pipeline(createImplicitDevice=False)` starts without any device. The first device added with `addDevice()`, or the first
   device passed to `create()`, becomes the default device.

A device also joins the pipeline implicitly when it is first passed to `create()`, so an existing `dai.Device` object can be used
without calling `addDevice()`. `getDevices()` lists all devices of the pipeline, the default device first and the others in the
order they were added.

| Method | Description |
| --- | --- |
| `addDevice(idOrIpOrName)`, `addDevice(deviceInfo)`, `addDevice(device)` | Add a device to the pipeline and return its `Device`
object. Adding the same device again is a no-op. |
| `create(NodeType, device, ...)` | Create a device node on the given device. Only device nodes accept a device; a host node is
created without one. |
| `getDefaultDevice()`, `getDevices()` | The default device, and all devices of the pipeline. |
| `getCalibrationData(device)`, `setCalibrationData(device, handler)`, `setBoardConfig(device, config)`,
`setCameraTuningBlobPath(device, path)`, `setXLinkChunkSize(device, size)` | Per-device variants of the single-device pipeline
methods. |

Two restrictions apply: one physical device can be part of a pipeline only once (two `Device` objects for the same device ID are
rejected), and devices cannot be added to a running pipeline.

## Linking across devices

Links are made with `output.link(input)` exactly as in a single-device pipeline; the pipeline works out where the data has to
travel when it is built.

 * Device to host. Any host node can consume outputs of several devices. After the pipeline is built, `Input.getSourceDevice()`
   tells a host node which device feeds an input (or `None` for a host producer), and `inputs.getSourceDevices()` does the same
   for a whole input map. This is how a custom node can label its tiles by device or detect that a device went offline.
 * Device to device. An output of a node on device A can be linked to an input of a node on device B. At build time the pipeline
   inserts a relay through the host for every such link and logs an info line for it. The relay never blocks the producing device:
   its queue holds 8 messages and drops the oldest when the consuming device lags behind.

Pipeline nodes:
- Device A
- Host
- Device B
- Camera
- relay (automatic)
- ImageManip

Pipeline connections:
- Camera -> relay (automatic)
- relay (automatic) -> ImageManip

A `Camera` on device A linked to an `ImageManip` on device B. The frames travel device A → host → device B; the relay nodes are
added by the pipeline and are not visible in the user graph.

```python
camera = pipeline.create(dai.node.Camera, deviceA).build(dai.CameraBoardSocket.CAM_A)
manip = pipeline.create(dai.node.ImageManip, deviceB)
manip.initialConfig.setOutputSize(320, 200)
camera.requestOutput((640, 400)).link(manip.inputImage)  # relayed through the host
```

## Synchronizing frames across devices

Every device captures on its own clock, so grouping frames of several devices needs a common time base. DepthAI offers three
levels:

| Method | What is aligned | Typical sync threshold |
| --- | --- | --- |
| Host timestamps (default) | Nothing on the sensors; the host clock synchronization of every device makes `getTimestamp()`
comparable across devices and a host `Sync` node pairs the closest frames. | one frame period (the example uses 20 ms) |
| [FSYNC](https://docs.luxonis.com/hardware/platform/deploy/data-sync/hardware-sync.md) | Exposure start, through a wire between
the devices. One device strobes, the others lock to it. | 1 ms |
| [PTP](https://docs.luxonis.com/hardware/platform/deploy/data-sync/ptp-sync.md) (Ethernet) | Exposure, through a shared network
clock. Enable `TIME_PTP` frame sync on every camera and let the `Sync` node compare the `SYSTEM` timestamps, which the PTP clock
aligns. | 1 ms |

In all three cases the grouping itself is done by a
[Sync](https://docs.luxonis.com/software-v3/depthai/depthai-components/nodes/sync.md) node running on the host, as in the example
above. The node emits a `MessageGroup` only when the spread of the timestamps in the group is within `setSyncThreshold()`.

```python
sync = pipeline.create(dai.node.Sync)
sync.setRunOnHost(True)
sync.setSyncThreshold(timedelta(milliseconds=1))
sync.setTimestampSource(dai.node.Sync.TimestampSource.SYSTEM)  # PTP-aligned timestamps

for device in pipeline.getDevices():
    camera = pipeline.create(dai.node.Camera, device).build(dai.CameraBoardSocket.CAM_A, sensorFps=30)
    camera.initialControl.setFrameSyncMode(dai.CameraControl.FrameSyncMode.TIME_PTP)
    camera.requestOutput((1280, 800)).link(sync.inputs[device.getDeviceId()])
```

## Device loss

Losing one device does not stop a multi-device pipeline by default. Each device of a running pipeline is in one of four states,
available through `pipeline.getDeviceState(device)` or `device.getDeviceState()`:

| `DeviceState` | Meaning |
| --- | --- |
| `RUNNING` | Connected and streaming. |
| `DISCONNECTED` | The connection was lost; reconnection has not started yet. |
| `RECONNECTING` | The device is being reconnected (see `Device.setMaxReconnectionAttempts()`). |
| `FAILED` | Gone for good: closed, or the reconnection attempts are exhausted. |

While a device is being reconnected its streams go quiet and the rest of the pipeline keeps running. When a device reaches
`FAILED`, the pipeline keeps running in the default permissive mode and stops only when the failed device was the last device
alive or when it consumed streams of other devices (a device-to-device link makes the consuming device essential). With
`setStopOnDeviceLoss(True)` (strict mode) the pipeline stops as soon as any device that runs a node is lost for good; to stop on
the first disconnect instead, disable reconnection on the devices with `setMaxReconnectionAttempts(0)`. A stopped pipeline closes
its output queues, so a blocking `queue.get()` raises `dai.MessageQueue.QueueException` in Python.

`setDeviceStateCallback()` reports every transition. The callback runs on the monitor thread of the affected device, so it must
not block and must not stop or start the pipeline itself.

```python
pipeline.setStopOnDeviceLoss(False)  # default: keep running without the lost device
pipeline.setDeviceStateCallback(lambda device, state: print(device.getDeviceId(), state))

pipeline.start()
while pipeline.isRunning():
    try:
        frame = queue.get()
    except dai.MessageQueue.QueueException:
        break  # the pipeline stopped itself after a device loss
    for device in pipeline.getDevices():
        if pipeline.getDeviceState(device) != dai.DeviceState.RUNNING:
            print(device.getDeviceId(), "is offline")
```

## Common coordinate system

For a visual introduction and camera setup guidance, see [Multi-Device
Calibration](https://docs.luxonis.com/hardware/platform/depth/multi-device-calibration.md).

Every frame carries an
[ImgTransformation](https://docs.luxonis.com/software-v3/depthai/depthai-components/messages/img_transformations.md) whose
extrinsics describe the camera pose with respect to the [origin
camera](https://docs.luxonis.com/software-v3/depthai/depthai-components/coordinate-systems.md) of its own device. The target of
these extrinsics is identified by `toCameraSocket` and `toDeviceId`, so `CAM_A` of device A and `CAM_A` of device B are different
reference frames. Fusing data of several devices (point clouds, panoramas, detections) therefore needs the poses between the
devices.

DepthAI expresses them as a multi-device calibration graph: a list of `MultiDeviceExtrinsics` edges, each mapping the origin of
one device onto the origin of another device. The graph is estimated with the
[MultiDeviceCalibration](https://docs.luxonis.com/software-v3/depthai/depthai-components/host_nodes/multi_device_calibration.md)
node, stored as a JSON file by the `MultiDeviceCalibrationHandler`, and handed to the pipeline:

```python
handler = dai.beta.MultiDeviceCalibrationHandler("multi_device_calibration.json")
pipeline.setMultiDeviceCalibration(handler.getGraph())
```

With the graph set, every device of the graph rebases the extrinsics of the frames of its `Camera` and `ToF` nodes to the common
origin of the graph, the lowest (device ID, socket) pair of the connected devices. Downstream nodes such as
[PointCloud](https://docs.luxonis.com/software-v3/depthai/depthai-components/host_nodes/pointcloud.md) and
[Stitching](https://docs.luxonis.com/software-v3/depthai/depthai-components/host_nodes/stitching.md) read the extrinsics from the
frames, so their outputs land in that common frame without further configuration. Devices that are not part of the graph keep
publishing their local extrinsics.

 * `setMultiDeviceCalibration(graph)` validates the graph structure (a forest of edges between device origins, no cycles, one
   origin socket per device). On a built pipeline it also checks the graph against the local calibration of every device and
   pushes it to the devices at runtime, so streaming nodes switch to the new graph; if a device rejects it, the devices updated
   before are rolled back and the pipeline keeps the previous graph.
 * `getMultiDeviceCalibration()` returns the current graph or `None`, `clearMultiDeviceCalibration()` removes it and the devices
   publish their local extrinsics again.

> **Beta API**
> `dai.beta.node.MultiDeviceCalibration`, `dai.beta.MultiDeviceCalibrationHandler` and `Pipeline.setMultiDeviceCalibration()` are a [Beta feature](https://docs.luxonis.com/software-v3/depthai/examples/beta-features.md): the API may still change between releases.

## Starting and stopping

`pipeline.start()` uploads the pipeline to all devices and starts them in parallel; with FSYNC the master devices are started
before the slaves. If any device fails to start, the devices that already started are rolled back and `start()` raises.
`pipeline.stop()` closes all devices in parallel. Everything in between works as in a single-device pipeline: output queues,
`pipeline.isRunning()`, the Python context manager.

## Examples

 * [Multi-device frame
   sync](https://github.com/luxonis/depthai-core/blob/main/examples/python/MultiDevice/multi_device_frame_sync.py) - `CAM_A` of
   every device grouped by a host `Sync` node, with host, FSYNC or PTP synchronization selectable on the command line.
 * [Multi-device camera
   sync](https://github.com/luxonis/depthai-core/blob/main/examples/python/MultiDevice/multi_device_cam_sync.py) - The minimal
   two-device pipeline with existing `Device` objects.
 * [Multi-device stream](https://github.com/luxonis/depthai-core/blob/main/examples/python/MultiDevice/multi_device_stream.py) - A
   custom host node composing a mosaic of all devices, labelling offline devices and optionally stopping on device loss.
 * [Device-to-device
   relay](https://github.com/luxonis/depthai-core/blob/main/examples/python/MultiDevice/device_to_device_relay.py) - A `Camera` on
   one device linked to an `ImageManip` on another.
 * [Multi-device
   calibration](https://github.com/luxonis/depthai-core/blob/main/examples/python/MultiDeviceCalibration/multi_device_calibration.py)
   - Estimating the calibration graph between two devices.

The C++ versions live in
[examples/cpp/Misc/MultiDevice](https://github.com/luxonis/depthai-core/tree/main/examples/cpp/Misc/MultiDevice) and
[examples/cpp/MultiDeviceCalibration](https://github.com/luxonis/depthai-core/tree/main/examples/cpp/MultiDeviceCalibration).

## Reference

### dai::Pipeline

Kind: class

Represents the pipeline, set of nodes and connections between them.

#### PipelineAutoCalibrationMode AutoCalibrationMode

Kind: typedef

#### PipelineImpl::NodeConnectionMap NodeConnectionMap

Kind: typedef

#### PipelineImpl * impl()

Kind: function

#### const PipelineImpl * impl()

Kind: function

#### std::vector< std::shared_ptr< Node > > getSourceNodes()

Kind: function

#### Pipeline(bool createImplicitDevice)

Kind: function

Creates a pipeline parameters: createImplicitDevice: If true, creates a default device (default = true)

#### Pipeline(std::shared_ptr< Device > device)

Kind: function

Creates a pipeline with specified device

#### Pipeline(std::shared_ptr< PipelineImpl > pimpl)

Kind: function

Creates a pipeline with specified device

#### GlobalProperties getGlobalProperties()

Kind: function

return: Global properties of current pipeline

#### void setGlobalProperties(GlobalProperties globalProperties)

Kind: function

Sets global properties of pipeline

#### void setDefaultDeviceProperties(DeviceProperties deviceProperties)

Kind: function

Sets default device properties

#### void setDefaultDevicePropertiesRef(DeviceProperties * deviceProperties)

Kind: function

Sets default device properties reference. The properties should live at least as long as the pipeline.

#### std::optional< DeviceProperties > getDefaultDeviceProperties()

Kind: function

Gets a copy of default device properties. If pipeline is in host only mode, returns host properties, otherwise returns device
properties

#### PipelineSchema getPipelineSchema(SerializationType type, bool includePipelineDebugging)

Kind: function

return: Pipeline schema

#### PipelineSchema getDevicePipelineSchema(SerializationType type, bool includePipelineDebugging)

Kind: function

return: Device pipeline schema (without host only nodes and connections)

#### void serialize(PipelineSchema & schema, Assets & assets, std::vector< std::uint8_t > & assetStorage)

Kind: function

#### nlohmann::json serializeToJson(bool includeAssests)

Kind: function

Returns whole pipeline represented as JSON.

#### std::shared_ptr< N > create(Args &&... args)

Kind: function

Creates and adds a node to the pipeline. Node is specified by template argument N

#### std::enable_if_t< std::is_base_of< DeviceNode , N >::value, std::shared_ptr< N > > create(std::shared_ptr< Device > device, Args &&... args)

Kind: function

Creates a device node on the specified device and adds it to the pipeline. The device becomes part of the pipeline on first use.
Only device nodes can be created this way - creating a host node with a device is a compile-time error.

#### std::enable_if_t< std::is_base_of< DeviceNode , N >::value, std::shared_ptr< N > > createForDevice(std::shared_ptr< Device > device, Args &&... args)

Kind: function

Creates a device node on the specified device and adds it to the pipeline. Same as create(device, ...) - kept as an explicitly
named alternative.

#### void add(std::shared_ptr< Node > node)

Kind: function

Adds an existing node to the pipeline

#### void remove(std::shared_ptr< Node > node)

Kind: function

Removes a node from pipeline.

#### std::vector< std::shared_ptr< Node > > getAllNodes()

Kind: function

Get a vector of all nodes.

#### std::shared_ptr< const Node > getNode(Node::Id id)

Kind: function

Get node with id if it exists, nullptr otherwise.

#### std::shared_ptr< Node > getNode(Node::Id id)

Kind: function

Get node with id if it exists, nullptr otherwise.

#### std::vector< Node::Connection > getConnections()

Kind: function

Get all connections.

#### NodeConnectionMap getConnectionMap()

Kind: function

#### const AssetManager & getAssetManager()

Kind: function

Get pipelines AssetManager as reference.

#### AssetManager & getAssetManager()

Kind: function

Get pipelines AssetManager as reference.

#### void setOpenVINOVersion(OpenVINO::Version version)

Kind: function

Set a specific OpenVINO version to use with this pipeline.

#### void setCalibrationData(CalibrationHandler calibrationDataHandler)

Kind: function

Sets the calibration in pipeline which overrides the calibration data in eeprom parameters: calibrationDataHandler:
CalibrationHandler object which is loaded with calibration information.

#### CalibrationHandler getCalibrationData()

Kind: function

gets the calibration data which is set through pipeline return: the calibrationHandler with calib data in the pipeline

#### bool isCalibrationDataAvailable()

Kind: function

check if calib data is available on the device return: true - calib data is available return: false - calib data is not available

#### std::optional< EepromData > getEepromData()

Kind: function

gets the eeprom data from the pipeline return: eepromData from the the pipeline

#### void setEepromData(std::optional< EepromData > eepromData)

Kind: function

Sets the eeprom data in pipeline parameters: eepromData: EepromData object that is loaded in the pipeline.

#### void setMultiDeviceCalibration(std::vector< MultiDeviceExtrinsics > graph)

Kind: function

Sets the cross-device calibration graph shared by all devices in the pipeline. The graph is validated with
beta::MultiDeviceCalibrationHandler . Before the pipeline is built it is only stored and travels to the devices with the pipeline
schema. On a built pipeline it is additionally checked against every assigned device's local calibration and pushed to all
assigned devices at runtime, so already streaming Camera and ToF nodes switch to the new graph. parameters: std::invalid_argument:
when the graph structure is invalid; std::runtime_error: when a device's local calibration does not match the graph or a device
rejects the update; devices updated before the failure are restored to the previous graph and the pipeline keeps the previous
graph parameters: graph: Directed cross-device calibration edges.

#### std::optional< std::vector< MultiDeviceExtrinsics > > getMultiDeviceCalibration()

Kind: function

Gets the cross-device calibration graph from the pipeline return: the graph when one is set, std::nullopt otherwise

#### void clearMultiDeviceCalibration()

Kind: function

Removes any cross-device calibration graph from the pipeline. On a built pipeline the removal is pushed to all assigned devices,
which then publish their local calibration metadata again. parameters: std::runtime_error: when a device rejects the update

#### uint32_t getEepromId()

Kind: function

Gets the eeprom id from the pipeline return: eeprom id from the pipeline

#### void setCameraTuningBlobPath(const fs::path & path)

Kind: function

Set a camera IQ (Image Quality) tuning blob, used for all cameras.

#### void setCameraTuningBlobPath(CameraBoardSocket socket, const fs::path & path)

Kind: function

Set a camera IQ (Image Quality) tuning blob, used for specific board socket.

#### void setXLinkChunkSize(int sizeBytes)

Kind: function

Set chunk size for splitting device-sent XLink packets, in bytes. A larger value could increase performance, with 0 disabling
chunking. A negative value won't modify the device defaults - configured per protocol, currently 64*1024 for both USB and
Ethernet.

#### void setSippBufferSize(int sizeBytes)

Kind: function

SIPP (Signal Image Processing Pipeline ) internal memory pool. SIPP is a framework used to schedule HW filters, e.g. ISP, Warp,
Median filter etc. Changing the size of this pool is meant for advanced use cases, pushing the limits of the HW. By default memory
is allocated in high speed CMX memory. Setting to 0 will allocate in DDR 256 kilobytes. Units are bytes.

#### void setSippDmaBufferSize(int sizeBytes)

Kind: function

SIPP (Signal Image Processing Pipeline ) internal DMA memory pool. SIPP is a framework used to schedule HW filters, e.g. ISP,
Warp, Median filter etc. Changing the size of this pool is meant for advanced use cases, pushing the limits of the HW. Memory is
allocated in high speed CMX memory Units are bytes.

#### void setBoardConfig(BoardConfig board)

Kind: function

Sets board configuration.

#### void setAutoCalibrationMode(AutoCalibrationMode mode)

Kind: function

Sets implicit automatic calibration policy for this pipeline.

#### AutoCalibrationMode getAutoCalibrationMode()

Kind: function

Gets implicit automatic calibration policy for this pipeline.

#### BoardConfig getBoardConfig()

Kind: function

Gets board configuration.

#### Device::Config getDeviceConfig()

Kind: function

Get device configuration needed for this pipeline.

#### bool isRunning()

Kind: function

#### bool isBuilt()

Kind: function

#### void build()

Kind: function

#### void buildDevice()

Kind: function

#### void start()

Kind: function

#### void wait()

Kind: function

#### void stop()

Kind: function

#### void processTasks(bool waitForTasks, double timeoutSeconds)

Kind: function

#### void run()

Kind: function

#### std::shared_ptr< Device > getDefaultDevice()

Kind: function

#### std::shared_ptr< const Device > getDefaultDevice()

Kind: function

#### std::shared_ptr< Device > addDevice(std::shared_ptr< Device > device)

Kind: function

Add a device to the pipeline. The first device added to a pipeline created without an implicit device is promoted to the default
device (master), so nodes created without an explicit device run on it. parameters: device: Already constructed device to add
return: The added device

#### std::shared_ptr< Device > addDevice(const DeviceInfo & deviceInfo)

Kind: function

Construct a device from the given device info and add it to the pipeline. parameters: deviceInfo: Device info to construct the
device from return: The constructed device

#### std::shared_ptr< Device > addDevice(const std::string & idOrIpOrName)

Kind: function

Construct a device from a device id, IP address or name and add it to the pipeline. parameters: idOrIpOrName: Device id, IP
address or name to construct the device from return: The constructed device

#### std::vector< std::shared_ptr< Device > > getDevices()

Kind: function

Get all devices that are part of this pipeline - the default device (master) first, then the rest in registration order. Devices
become part of the pipeline explicitly via addDevice or implicitly on first use by a node.

#### DeviceState getDeviceState(const std::shared_ptr< Device > & device)

Kind: function

Get the pipeline-level state of a device: RUNNING, DISCONNECTED, RECONNECTING or FAILED. Losing a device does not stop the
pipeline (its streams go idle) unless it was the last device alive or a device consuming other devices' streams. See
setStopOnDeviceLoss to stop on any device loss.

#### void setDeviceStateCallback(std::function< void(std::shared_ptr< Device >, DeviceState)> callback)

Kind: function

Set a callback invoked on every device state transition. The callback is invoked from the affected device's monitor thread; do not
block in it and do not call pipeline stop/start from it directly.

#### void setStopOnDeviceLoss(bool stop)

Kind: function

Stop the pipeline when any device that runs a node is lost for good (strict mode). Off by default (permissive mode): the pipeline
keeps running, the lost device's streams go idle and it stops only when the last device alive or a device consuming other devices'
streams is lost. A device is lost for good when its state becomes FAILED - after the reconnection attempts are exhausted. To stop
as soon as a device disconnects, disable reconnection on the devices (Device::setMaxReconnectionAttempts(0)). Can be changed while
the pipeline runs. parameters: stop: True to stop the pipeline on any device loss

#### bool getStopOnDeviceLoss()

Kind: function

Get whether the pipeline stops when any device that runs a node is lost for good.

#### void setCameraTuningBlobPath(const std::shared_ptr< Device > & device, const fs::path & path)

Kind: function

Set a camera IQ (Image Quality) tuning blob for all cameras of the given pipeline device.

#### void setCameraTuningBlobPath(const std::shared_ptr< Device > & device, CameraBoardSocket socket, const fs::path & path)

Kind: function

Set a camera IQ (Image Quality) tuning blob for a specific board socket of the given pipeline device.

#### void setXLinkChunkSize(const std::shared_ptr< Device > & device, int sizeBytes)

Kind: function

Set chunk size for splitting device-sent XLink packets for the given pipeline device; see the no-device overload.

#### void setSippBufferSize(const std::shared_ptr< Device > & device, int sizeBytes)

Kind: function

Set the SIPP buffer size of the given pipeline device; see the no-device overload.

#### void setSippDmaBufferSize(const std::shared_ptr< Device > & device, int sizeBytes)

Kind: function

Set the SIPP DMA buffer size of the given pipeline device; see the no-device overload.

#### void setBoardConfig(const std::shared_ptr< Device > & device, const BoardConfig & board)

Kind: function

Sets the board configuration for the given pipeline device. Board configuration is applied when a device boots, so for an already
connected device it only takes effect on flows that (re)boot with this pipeline's configuration.

#### BoardConfig getBoardConfig(const std::shared_ptr< Device > & device)

Kind: function

Gets the board configuration of the given pipeline device.

#### Device::Config getDeviceConfig(const std::shared_ptr< Device > & device)

Kind: function

Get device configuration needed for this pipeline, for the given pipeline device.

#### void setCalibrationData(const std::shared_ptr< Device > & device, const CalibrationHandler & calibrationDataHandler)

Kind: function

Sets the calibration of the given pipeline device, overriding the one in eeprom.

#### bool isCalibrationDataAvailable(const std::shared_ptr< Device > & device)

Kind: function

Check if calibration data is available on the given pipeline device.

#### CalibrationHandler getCalibrationData(const std::shared_ptr< Device > & device)

Kind: function

Gets the calibration data of the given pipeline device.

#### void setEepromData(const std::shared_ptr< Device > & device, const std::optional< EepromData > & eepromData)

Kind: function

Sets the eeprom data of the given pipeline device.

#### std::optional< EepromData > getEepromData(const std::shared_ptr< Device > & device)

Kind: function

Gets the eeprom data of the given pipeline device.

#### uint32_t getEepromId(const std::shared_ptr< Device > & device)

Kind: function

Gets the eeprom id of the given pipeline device.

#### void setDeviceProperties(const std::shared_ptr< Device > & device, const DeviceProperties & deviceProperties)

Kind: function

Sets device properties of the given pipeline device.

#### DeviceProperties getDeviceProperties(const std::shared_ptr< Device > & device)

Kind: function

Gets device properties of the given pipeline device.

#### std::string getTelemetryPipelineId()

Kind: function

#### void addTask(std::function< void()> task)

Kind: function

#### void enableHolisticRecord(const RecordConfig & config)

Kind: function

Record and Replay.

#### void enableHolisticReplay(const std::string & pathToRecording)

Kind: function

#### bool isHolisticRecordEnabled()

Kind: function

#### bool isHolisticReplayEnabled()

Kind: function

#### void enablePipelineDebugging(bool enable)

Kind: function

Pipeline debugging.

#### bool isPipelineDebuggingEnabled()

Kind: function

#### std::shared_ptr< MessageQueue > getPipelineStateOut()

Kind: function

#### std::shared_ptr< InputQueue > getPipelineStateRequest()

Kind: function

#### PipelineStateApi getPipelineState()

Kind: function

### Need assistance?

Head over to [Discussion Forum](https://discuss.luxonis.com/) for technical support or any other questions you might have.
