Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
#include "rv/apollo/secure_matrix.hpp"
#include "rv/tracking/Classification.hpp"

#include <iostream>

namespace rv {
namespace tracking {

Expand Down
5 changes: 0 additions & 5 deletions controller/src/robot_vision/src/rv/tracking/TrackManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

#include "rv/Utils.hpp"
#include "rv/tracking/TrackManager.hpp"
#include <iostream>
#include <omp.h>

namespace rv {
Expand Down Expand Up @@ -354,10 +353,6 @@ void TrackManager::updateTrackerConfig(int camera_frame_rate)
mConfig.mMaxNumberOfUnreliableFrames = std::ceil(camera_frame_rate*mConfig.mMaxUnreliableTime);
mConfig.mNonMeasurementFramesDynamic = std::ceil(camera_frame_rate*mConfig.mNonMeasurementTimeDynamic);
mConfig.mNonMeasurementFramesStatic = std::ceil(camera_frame_rate*mConfig.mNonMeasurementTimeStatic);
std::cout << "Updated parameters for reference camera frame rate = " << camera_frame_rate << "fps" << std::endl;
std::cout << "max_unreliable_frames = " << mConfig.mMaxNumberOfUnreliableFrames << std::endl;
std::cout << "non_measurement_frames_dynamic = " << mConfig.mNonMeasurementFramesDynamic << std::endl;
std::cout << "non_measurement_frames_static = " << mConfig.mNonMeasurementFramesStatic << std::endl;
}

} // namespace tracking
Expand Down
11 changes: 6 additions & 5 deletions docs/design/tracker-service.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ graph LR

**Topics Subscribed:**

| Topic | Description |
| ------------------------------- | ---------------------------------------------------------------------------- |
| `scenescape/data/camera/+` | Detection messages from AI pipeline with bounding boxes in pixel coordinates |
| `scenescape/cmd/scene/update/+` | Config change notifications from Manager API (dynamic mode only) |
| Topic | Description |
| -------------------------- | ---------------------------------------------------------------------------- |
| `scenescape/data/camera/+` | Detection messages from AI pipeline with bounding boxes in pixel coordinates |
| `scenescape/cmd/database` | Database change notifications from Manager API (dynamic mode only) |

**Detection Message:**

Expand Down Expand Up @@ -179,8 +179,9 @@ Scenes fetched from Manager API at startup:

- Set `scenes.source: "api"` or omit `scenes` section (defaults to API mode)
- Requires `infrastructure.manager` with API URL and credentials
- Subscribes to `scenescape/cmd/scene/update/{scene_id}` for change notifications
- Subscribes to `scenescape/cmd/database` for change notifications
- On notification: logs change, exits gracefully (Docker restarts the service which loads new config at startup)
- Fires on any database change: scene create/update/delete, camera changes, region edits, etc.
- Suitable for multi-node deployments with centralized scene management

### Observability
Expand Down
15 changes: 14 additions & 1 deletion sample_data/docker-compose-dl-streamer-example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ secrets:
file: ${SECRETSDIR}/django
controller.auth:
environment: CONTROLLER_AUTH
controller-auth-file:
file: manager/secrets/controller.auth
browser.auth:
file: ${SECRETSDIR}/browser.auth
calibration.auth:
Expand Down Expand Up @@ -458,13 +460,19 @@ services:
depends_on:
broker:
condition: service_started
web:
condition: service_healthy
environment:
- TRACKER_LOG_LEVEL=info
- TRACKER_MQTT_HOST=broker.scenescape.intel.com
- TRACKER_MQTT_PORT=1883
- TRACKER_MQTT_INSECURE=false
- TRACKER_MQTT_TLS_CA_CERT=/run/secrets/certs/scenescape-ca.pem
- TRACKER_MQTT_TLS_VERIFY_SERVER=true
- TRACKER_MANAGER_URL=https://web.scenescape.intel.com
- TRACKER_MANAGER_AUTH_PATH=/run/secrets/controller.auth
- TRACKER_MANAGER_CA_CERT_PATH=/run/secrets/certs/scenescape-ca.pem
- TRACKER_SCENES_SOURCE=api
# Override host proxy settings - Paho MQTT dont respect no_proxy var, so as a WA
# tracker code detects empty vars and unsets them (see mqtt_client.cpp clearEmptyProxyVars)
- http_proxy=
Expand All @@ -474,12 +482,17 @@ services:
secrets:
- source: root-cert
target: certs/scenescape-ca.pem
- source: controller-auth-file
target: /run/secrets/controller.auth
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
restart: always
# Exit 0: graceful stop or non-retryable error (bad auth) β€” stay stopped
# Exit 1: retryable error (broker unavailable) β€” restart
# Exit 99: scene update received β€” restart to reload config
restart: on-failure
mem_limit: ${TRACKER_MEM_LIMIT:-512m}
# Scale: ~1 CPU per 100 tracked objects. Increase TRACKER_CPUS for larger deployments.
cpus: ${TRACKER_CPUS:-2.0}
Expand Down
5 changes: 5 additions & 0 deletions tracker/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ find_package(CLI11 REQUIRED)
find_package(httplib REQUIRED)
find_package(RapidJSON REQUIRED)
find_package(PahoMqttCpp REQUIRED)
find_package(OpenSSL REQUIRED)

#####################################################################
# System-provided packages
Expand Down Expand Up @@ -76,6 +77,8 @@ set(PROJECT_SOURCE_LIST
${CMAKE_CURRENT_SOURCE_DIR}/src/cli.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/config_loader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/scene_loader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/api_scene_loader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/manager_rest_client.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/coordinate_transformer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/healthcheck_server.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/healthcheck_command.cpp
Expand Down Expand Up @@ -122,6 +125,8 @@ target_link_libraries(${PROJECT_NAME}
httplib::httplib
rapidjson
PahoMqttCpp::paho-mqttpp3-static
OpenSSL::SSL
OpenSSL::Crypto
)

#####################################################################
Expand Down
4 changes: 2 additions & 2 deletions tracker/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ format-python:
echo "autopep8 not found. Install: pip install autopep8"; \
exit 1; \
fi
@cd test/service && autopep8 --in-place --indent-size=2 --max-line-length=120 *.py
@cd test/service && autopep8 --in-place --indent-size=2 --max-line-length=120 *.py mocks/*.py
@echo "βœ“ Python files formatted"

install-hooks:
Expand Down Expand Up @@ -111,7 +111,7 @@ lint-python:
echo "autopep8 not found. Install: pip install autopep8"; \
exit 1; \
fi
@cd test/service && autopep8 --diff --exit-code --indent-size=2 --max-line-length=120 *.py && echo "βœ“ Python lint passed"
@cd test/service && autopep8 --diff --exit-code --indent-size=2 --max-line-length=120 *.py mocks/*.py && echo "βœ“ Python lint passed"

lint-trivy:
@echo "Running Trivy security scan..."
Expand Down
2 changes: 1 addition & 1 deletion tracker/conanfile.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ CMakeDeps
CMakeToolchain

[options]
cpp-httplib/*:with_openssl=False
cpp-httplib/*:with_openssl=True
opencv/*:shared=True
opencv/*:tracking=True
opencv/*:video=True
Expand Down
7 changes: 5 additions & 2 deletions tracker/config/tracker.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
"healthcheck": {
"port": 8080
}
},
"manager": {
"url": "https://web.scenescape.intel.com",
"auth_path": "/run/secrets/controller.auth"
}
},
"observability": {
Expand All @@ -29,7 +33,6 @@
"non_measurement_time_static_s": 1.6
},
"scenes": {
"source": "file",
"file_path": "scenes.json"
"source": "api"
}
}
18 changes: 17 additions & 1 deletion tracker/inc/config_loader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#pragma once

#include "scene_loader.hpp"
#include "scenes_config.hpp"

#include <filesystem>
#include <optional>
Expand Down Expand Up @@ -47,12 +47,22 @@ struct TrackerConfig {
bool schema_validation = true;
};

/**
* @brief Manager REST API connection settings.
*/
struct ManagerConfig {
std::string url; ///< Manager API base URL
std::string auth_path; ///< Path to JSON auth file {user, password}
std::optional<std::string> ca_cert_path; ///< CA cert for HTTPS verification
};

/**
* @brief External service connections.
*/
struct InfrastructureConfig {
MqttConfig mqtt;
TrackerConfig tracker;
std::optional<ManagerConfig> manager; ///< Required when scenes.source='api'
};

/**
Expand Down Expand Up @@ -134,6 +144,12 @@ constexpr char TRACKING_NON_MEASUREMENT_TIME_DYNAMIC_S[] =
"/tracking/non_measurement_time_dynamic_s";
constexpr char TRACKING_NON_MEASUREMENT_TIME_STATIC_S[] = "/tracking/non_measurement_time_static_s";

// Manager
constexpr char INFRASTRUCTURE_MANAGER[] = "/infrastructure/manager";
constexpr char INFRASTRUCTURE_MANAGER_URL[] = "/infrastructure/manager/url";
constexpr char INFRASTRUCTURE_MANAGER_AUTH_PATH[] = "/infrastructure/manager/auth_path";
constexpr char INFRASTRUCTURE_MANAGER_CA_CERT_PATH[] = "/infrastructure/manager/ca_cert_path";

// Scenes
constexpr char SCENES_SOURCE[] = "/scenes/source";
constexpr char SCENES_FILE_PATH[] = "/scenes/file_path";
Expand Down
11 changes: 11 additions & 0 deletions tracker/inc/env_vars.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ constexpr const char* NON_MEASUREMENT_TIME_DYNAMIC_S = "TRACKER_NON_MEASUREMENT_
/// seconds, >= 0 - RobotVision tracker parameter
constexpr const char* NON_MEASUREMENT_TIME_STATIC_S = "TRACKER_NON_MEASUREMENT_TIME_STATIC_S";

// Manager API overrides

/// Manager API base URL
constexpr const char* MANAGER_URL = "TRACKER_MANAGER_URL";

/// Path to JSON auth file {user, password}
constexpr const char* MANAGER_AUTH_PATH = "TRACKER_MANAGER_AUTH_PATH";

/// Path to CA certificate for HTTPS verification
constexpr const char* MANAGER_CA_CERT_PATH = "TRACKER_MANAGER_CA_CERT_PATH";

// Scenes overrides

/// "file"|"api"
Expand Down
71 changes: 71 additions & 0 deletions tracker/inc/manager_rest_client.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-FileCopyrightText: 2026 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <chrono>
#include <optional>
#include <string>

namespace tracker {

/**
* @brief Abstract interface for Manager REST API operations.
*
* Enables dependency injection and mock-based testing of components
* that depend on the Manager API (e.g., ApiSceneLoader).
*/
class IManagerRestClient {
public:
virtual ~IManagerRestClient() = default;

/**
* @brief Authenticate with the Manager API.
*
* @param username API username
* @param password API password
* @throws std::runtime_error on connection failure, HTTP error, or auth rejection
*/
virtual void authenticate(const std::string& username, const std::string& password) = 0;

/**
* @brief Fetch all scenes from the Manager API.
*
* @return Raw JSON response body string
* @throws std::runtime_error if not authenticated, connection fails, or HTTP error
*/
virtual std::string fetchScenes() = 0;
};

/**
* @brief HTTP client for Manager REST API.
*
* Handles authentication and scene fetching from the SceneScape Manager.
* Supports HTTPS with CA certificate verification.
*/
class ManagerRestClient : public IManagerRestClient {
public:
/**
* @brief Construct a Manager REST API client.
*
* @param url Manager API base URL (e.g., "https://web.scenescape.intel.com")
* @param ca_cert_path Optional CA certificate path for HTTPS verification
* @param connect_timeout TCP connect timeout (default: 10s)
* @param read_timeout HTTP read timeout (default: 30s)
*/
ManagerRestClient(std::string url, std::optional<std::string> ca_cert_path = std::nullopt,
std::chrono::milliseconds connect_timeout = std::chrono::seconds(10),
std::chrono::milliseconds read_timeout = std::chrono::seconds(30));

void authenticate(const std::string& username, const std::string& password) override;
std::string fetchScenes() override;

private:
std::string url_;
std::optional<std::string> ca_cert_path_;
std::string token_;
std::chrono::milliseconds connect_timeout_;
std::chrono::milliseconds read_timeout_;
};

} // namespace tracker
41 changes: 41 additions & 0 deletions tracker/inc/message_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <atomic>
#include <chrono>
#include <filesystem>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
Expand Down Expand Up @@ -57,9 +58,15 @@ class MessageHandler {
/// Topic pattern for scene output (format with scene_id and thing_type)
static constexpr const char* TOPIC_SCENE_DATA_PATTERN = "scenescape/data/scene/{}/{}";

/// Topic for database update notifications (dynamic mode)
static constexpr const char* TOPIC_DATABASE_UPDATE = "scenescape/cmd/database";

/// Default thing type for output (category from detection)
static constexpr const char* DEFAULT_THING_TYPE = "thing";

/// Callback type for requesting service shutdown (dynamic scene reload)
using ShutdownCallback = std::function<void()>;

/**
* @brief Construct message handler with MQTT client, scene registry, and buffer.
*
Expand All @@ -75,6 +82,18 @@ class MessageHandler {
const TrackingConfig& tracking_config, bool schema_validation = true,
const std::filesystem::path& schema_dir = "/scenescape/schema");

/**
* @brief Enable dynamic mode for database update notifications.
*
* In dynamic mode (scenes.source=api), the handler subscribes to
* scenescape/cmd/database. On receiving an update (scene create, update,
* delete, camera change, etc.), it invokes the shutdown callback to
* trigger a graceful restart.
*
* @param callback Function to call when database update is received
*/
void enableDynamicMode(ShutdownCallback callback);

/**
* @brief Start message handling (subscribe to topics).
*/
Expand Down Expand Up @@ -114,6 +133,17 @@ class MessageHandler {
*/
void handleCameraMessage(const std::string& topic, const std::string& payload);

/**
* @brief Handle database update notification (dynamic mode).
*
* Logs the change and triggers graceful shutdown via shutdown callback.
* Fires on any database change: scene create/update/delete, camera changes, etc.
*
* @param topic MQTT topic (scenescape/cmd/database)
* @param payload Message payload (content is logged but not parsed)
*/
void handleDatabaseUpdateMessage(const std::string& topic, const std::string& payload);

/**
* @brief Extract camera_id from topic.
*
Expand Down Expand Up @@ -157,11 +187,22 @@ class MessageHandler {
static std::unique_ptr<rapidjson::SchemaDocument>
loadSchema(const std::filesystem::path& schema_path);

/**
* @brief Route incoming MQTT message to the appropriate handler.
*
* Routes by topic:
* - scenescape/cmd/database -> handleDatabaseUpdateMessage
* - scenescape/data/camera/ -> handleCameraMessage (default)
*/
void routeMessage(const std::string& topic, const std::string& payload);

std::shared_ptr<IMqttClient> mqtt_client_;
const SceneRegistry& scene_registry_;
TimeChunkBuffer& buffer_;
TrackingConfig tracking_config_;
bool schema_validation_;
bool dynamic_mode_{false};
ShutdownCallback shutdown_callback_;
std::unique_ptr<rapidjson::SchemaDocument> camera_schema_;
std::unique_ptr<rapidjson::SchemaDocument> scene_schema_;

Expand Down
Loading
Loading