# Events

This example shows how to use the EventsManager to send custom events, including file data and RGB camera frames, with associated
tags and metadata.

> EventsManager node is currently in "early access preview". Please use the
> [depthai-core develop](https://github.com/luxonis/depthai-core/tree/develop)
> branch to have access to the latest features and fixes.

This example requires the DepthAI v3 API, see [installation instructions](https://docs.luxonis.com/software-v3/depthai.md).

## Pipeline

### examples/events.pipeline.json

```json
{"pipeline": {"connections": [{"node1Id": 0, "node1Output": "0", "node1OutputGroup": "dynamicOutputs", "node2Id": 1, "node2Input": "in", "node2InputGroup": ""}], "globalProperties": {"calibData": null, "cameraTuningBlobSize": null, "cameraTuningBlobUri": "", "leonCssFrequencyHz": 700000000.0, "leonMssFrequencyHz": 700000000.0, "pipelineName": null, "pipelineVersion": null, "sippBufferSize": 18432, "sippDmaBufferSize": 16384, "xlinkChunkSize": -1}, "nodes": [[1, {"alias": "", "id": 1, "ioInfo": [[["", "in"], {"blocking": true, "group": "", "id": 4, "name": "in", "queueSize": 3, "type": 3, "waitForMessage": false}]], "logLevel": 3, "name": "XLinkOut", "parentId": -1, "properties": {"maxFpsLimit": -1.0, "metadataOnly": false, "streamName": "__x_0_0"}}], [0, {"alias": "", "id": 0, "ioInfo": [[["dynamicOutputs", "0"], {"blocking": false, "group": "dynamicOutputs", "id": 3, "name": "0", "queueSize": 8, "type": 0, "waitForMessage": false}], [["", "raw"], {"blocking": false, "group": "", "id": 2, "name": "raw", "queueSize": 8, "type": 0, "waitForMessage": false}], [["", "mockIsp"], {"blocking": true, "group": "", "id": 1, "name": "mockIsp", "queueSize": 8, "type": 3, "waitForMessage": false}], [["", "inputControl"], {"blocking": true, "group": "", "id": 0, "name": "inputControl", "queueSize": 3, "type": 3, "waitForMessage": false}]], "logLevel": 3, "name": "Camera", "parentId": -1, "properties": {"boardSocket": 0, "cameraName": "", "fps": -1.0, "imageOrientation": -1, "initialControl": {"aeLockMode": false, "aeMaxExposureTimeUs": 100663297, "aeRegion": {"height": 0, "priority": 3545344224, "width": 0, "x": 4163, "y": 1}, "afRegion": {"height": 0, "priority": 0, "width": 0, "x": 23088, "y": 0}, "antiBandingMode": 81, "autoFocusMode": 3, "awbLockMode": false, "awbMode": 40, "brightness": -86, "captureIntent": 211, "chromaDenoise": 0, "cmdMask": 0, "contrast": 43, "controlMode": 48, "effectMode": 90, "enableHdr": false, "expCompensation": -41, "expManual": {"exposureTimeUs": 23088, "frameDurationUs": 23088, "sensitivityIso": 3545344332}, "frameSyncMode": 0, "lensPosAutoInfinity": 96, "lensPosAutoMacro": 169, "lensPosition": 0, "lensPositionRaw": 0.0, "lowPowerNumFramesBurst": 0, "lowPowerNumFramesDiscard": 0, "lumaDenoise": 0, "miscControls": [], "saturation": 25, "sceneMode": 169, "sharpness": 0, "strobeConfig": {"activeLevel": 136, "enable": 0, "gpioNumber": -81}, "strobeTimings": {"durationUs": 4, "exposureBeginOffsetUs": 23088, "exposureEndOffsetUs": 17}, "wbColorTemp": 0}, "isp3aFps": 0, "mockIspHeight": -1, "mockIspWidth": -1, "numFramesPoolIsp": 3, "numFramesPoolPreview": 4, "numFramesPoolRaw": 3, "numFramesPoolStill": 4, "numFramesPoolVideo": 4, "outputRequests": [{"enableUndistortion": null, "fps": {"value": null}, "resizeMode": 0, "size": {"value": {"index": 0, "value": [256, 256]}}, "type": null}], "resolutionHeight": -1, "resolutionWidth": -1}}]]}}
```

## Source code

#### Python

```python
#!/usr/bin/env python3

import cv2
import depthai as dai
import numpy as np
import time

# Callback functions
def uploadSuccessCallback(sendSnapResult):
    assert (sendSnapResult.uploadStatus == dai.SendSnapCallbackStatus.SUCCESS)
    print(f"Successfully uploaded Snap: ({sendSnapResult.snapName}, {sendSnapResult.snapTimestamp}, {sendSnapResult.snapHubID}) to the hub.")

def uploadFailureCallback(sendSnapResult):
    assert (sendSnapResult.uploadStatus != dai.SendSnapCallbackStatus.SUCCESS)
    print(f"Upload of Snap: ({sendSnapResult.snapName}, {sendSnapResult.snapTimestamp}, {sendSnapResult.snapLocalID}) to the hub has failed.")

    status = sendSnapResult.uploadStatus
    if status == dai.SendSnapCallbackStatus.FILE_BATCH_PREPARATION_FAILED:
        print("File batch preparation failed!")
    elif status == dai.SendSnapCallbackStatus.GROUP_CONTAINS_REJECTED_FILES:
        print("Snap's file group contains rejected files!")
    elif status == dai.SendSnapCallbackStatus.FILE_UPLOAD_FAILED:
        print("File upload was unsuccessful!")
    elif status == dai.SendSnapCallbackStatus.SEND_EVENT_FAILED:
        print("Snap could not be sent to the hub, following successful file upload!")

# Create pipeline
with dai.Pipeline() as pipeline:
    # Set your Hub team's api-key using the environment variable DEPTHAI_HUB_API_KEY. Or pass the key when creating the EventsManager instance.
    eventMan = dai.EventsManager("")

    cameraNode = pipeline.create(dai.node.Camera).build()
    detectionNetwork = pipeline.create(dai.node.DetectionNetwork).build(cameraNode, dai.NNModelDescription("yolov6-nano"))
    labelMap = detectionNetwork.getClasses()

    # Create output queues
    qRgb = detectionNetwork.passthrough.createOutputQueue()
    qDet = detectionNetwork.out.createOutputQueue()

    pipeline.start()

    # nn data, being the bounding box locations, are in <0..1> range - they need to be normalized with frame width/height
    def frameNorm(frame, bbox):
        normVals = np.full(len(bbox), frame.shape[0])
        normVals[::2] = frame.shape[1]
        return (np.clip(np.array(bbox), 0, 1) * normVals).astype(int)

    print("Press 's' to send a snap, and 'q' to quit")
    while pipeline.isRunning():
        key = cv2.waitKey(1)
        if key == ord("q"):
            pipeline.stop()
            break

        inRgb: dai.ImgFrame = qRgb.get()
        inDet: dai.ImgDetections = qDet.get()
        if inRgb is None or inDet is None:
            continue

        # Display the video stream and detections
        color = (255, 0, 0)
        frame = inRgb.getCvFrame()
        if frame is not None:
            for detection in inDet.detections:
                bbox = frameNorm(
                    frame,
                    (detection.xmin, detection.ymin, detection.xmax, detection.ymax),
                )
                cv2.putText(
                    frame,
                    labelMap[detection.label],
                    (bbox[0] + 10, bbox[1] + 20),
                    cv2.FONT_HERSHEY_TRIPLEX,
                    0.5,
                    255,
                )
                cv2.putText(
                    frame,
                    f"{int(detection.confidence * 100)}%",
                    (bbox[0] + 10, bbox[1] + 40),
                    cv2.FONT_HERSHEY_TRIPLEX,
                    0.5,
                    255,
                )
                cv2.rectangle(frame, (bbox[0], bbox[1]), (bbox[2], bbox[3]), color, 2)
            # Show the frame
            cv2.imshow("rgb", frame)

        # Trigger sendSnap()
        if key == ord("s"):
            localSnapID = eventMan.sendSnap("ImageDetection", None, inRgb, inDet, ["EventsExample", "Python"], {"key_0" : "value_0", "key_1" : "value_1"}, 
                              uploadSuccessCallback, uploadFailureCallback)
            if localSnapID != None:
                print(f"Snap with a localID: {localSnapID} has been successfully added to the EventsManager")
            else:
                print("Snap was not successfully added to the EventsManager")

    cv2.destroyAllWindows()
    # Wait for pending snaps to be uploaded
    if eventMan.waitForPendingUploads(timeoutMs=10000):
        print("Pending uploads have been successfully uploaded")
    else:
        print("Pending uploads were discarded due to timeout (10s) or dropped connection")
```

#### C++

```cpp
#include <iostream>
#include <opencv2/opencv.hpp>
#include <string>

#include "depthai/depthai.hpp"
#include "depthai/utility/EventsManager.hpp"

// Helper function to normalize frame coordinates
cv::Rect frameNorm(const cv::Mat& frame, const dai::Point2f& topLeft, const dai::Point2f& bottomRight) {
    float width = frame.cols, height = frame.rows;
    return cv::Rect(cv::Point(topLeft.x * width, topLeft.y * height), cv::Point(bottomRight.x * width, bottomRight.y * height));
}

// Callback functions
void uploadSuccessCallback(dai::utility::SendSnapCallbackResult sendSnapResult) {
    std::cout << "Successfully uploaded Snap: (" << sendSnapResult.snapName << ", " << sendSnapResult.snapTimestamp << ", "
              << sendSnapResult.snapHubID.value_or("") << ") to the hub." << std::endl;
}

void uploadFailureCallback(dai::utility::SendSnapCallbackResult sendSnapResult) {
    std::cout << "Upload of Snap: (" << sendSnapResult.snapName << ", " << sendSnapResult.snapTimestamp << ", " << sendSnapResult.snapLocalID
              << ") to the hub has failed." << std::endl;

    switch(sendSnapResult.uploadStatus) {
        case dai::utility::SendSnapCallbackStatus::FILE_BATCH_PREPARATION_FAILED:
            std::cout << "File batch preparation failed!" << std::endl;
            break;
        case dai::utility::SendSnapCallbackStatus::GROUP_CONTAINS_REJECTED_FILES:
            std::cout << "Snap's file group contains rejected files!" << std::endl;
            break;
        case dai::utility::SendSnapCallbackStatus::FILE_UPLOAD_FAILED:
            std::cout << "File upload was unsuccessful!" << std::endl;
            break;
        case dai::utility::SendSnapCallbackStatus::SEND_EVENT_FAILED:
            std::cout << "Snap could not be sent to the hub, following successful file upload!" << std::endl;
            break;
        default:
            break;
    }
}

int main() {
    dai::Pipeline pipeline(true);

    // Set your Hub team's api-key using the environment variable DEPTHAI_HUB_API_KEY. Or pass the key when creating the EventsManager instance.
    auto eventsManager = std::make_shared<dai::utility::EventsManager>("");

    auto camRgb = pipeline.create<dai::node::Camera>()->build();
    auto detectionNetwork = pipeline.create<dai::node::DetectionNetwork>();

    dai::NNModelDescription modelDescription;
    modelDescription.model = "yolov6-nano";
    detectionNetwork->build(camRgb, modelDescription);
    auto labelMap = detectionNetwork->getClasses();

    // Create output queues
    auto qRgb = detectionNetwork->passthrough.createOutputQueue();
    auto qDet = detectionNetwork->out.createOutputQueue();

    pipeline.start();

    std::cout << "Press 's' to send a snap, and 'q' to quit" << std::endl;
    while(pipeline.isRunning()) {
        auto key = cv::waitKey(1);
        if(key == 'q') {
            pipeline.stop();
            break;
        }

        auto inRgb = qRgb->get<dai::ImgFrame>();
        auto inDet = qDet->get<dai::ImgDetections>();
        if(inRgb == nullptr || inDet == nullptr) {
            continue;
        }

        // Display the video stream and detections
        cv::Mat frame = inRgb->getCvFrame();
        if(!frame.empty()) {
            // Display detections
            for(const auto& detection : inDet->detections) {
                auto bbox = frameNorm(frame, dai::Point2f(detection.xmin, detection.ymin), dai::Point2f(detection.xmax, detection.ymax));

                // Draw label
                cv::putText(
                    frame, labelMap.value()[detection.label], cv::Point(bbox.x + 10, bbox.y + 20), cv::FONT_HERSHEY_TRIPLEX, 0.5, cv::Scalar(255, 255, 255));

                // Draw confidence
                cv::putText(frame,
                            std::to_string(static_cast<int>(detection.confidence * 100)) + "%",
                            cv::Point(bbox.x + 10, bbox.y + 40),
                            cv::FONT_HERSHEY_TRIPLEX,
                            0.5,
                            cv::Scalar(255, 255, 255));

                // Draw rectangle
                cv::rectangle(frame, bbox, cv::Scalar(255, 0, 0), 2);
            }

            // Show the frame
            cv::imshow("rgb", frame);
        }

        // Trigger sendSnap()
        if(key == 's') {
            auto localSnapID = eventsManager->sendSnap("ImageDetection",
                                                       std::nullopt,
                                                       inRgb,
                                                       inDet,
                                                       {"EventsExample", "C++"},
                                                       {{"key_0", "value_0"}, {"key_1", "value_1"}},
                                                       uploadSuccessCallback,
                                                       uploadFailureCallback);

            if(localSnapID.has_value()) {
                std::cout << "Snap with a localID: " << localSnapID.value() << " has been successfully added to the EventsManager" << std::endl;
            } else {
                std::cout << "Snap was not successfully added to the EventsManager" << std::endl;
            }
        }
    }

    cv::destroyAllWindows();
    // Wait for pending snaps to be uploaded
    if(eventsManager->waitForPendingUploads(10000)) {
        std::cout << "Pending uploads have been successfully uploaded" << std::endl;
    } else {
        std::cout << "Pending uploads were discarded due to timeout (10s) or dropped connection" << std::endl;
    }

    return EXIT_SUCCESS;
}
```

### Need assistance?

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