.. _sensor_checker_utility:

Sensor Checker Dynamic Loading System
=====================================

This system implements a plugin-based architecture for sensor parameter validation using dynamic shared object loading. It allows different sensor types to have their own validation logic while maintaining a common interface.

Architecture Overview
---------------------

The system consists of:

1. **Core Interface** (``ISensorCheckerImpl.h``) - Defines the common interface that all sensor checker implementations must follow
2. **Type Definitions** (``SensorCheckerTypes.h``) - Defines the data structures used by the API
3. **Utility Class** (``SensorCheckerUtil.h/cpp``) - Main class that handles dynamic loading and parameter caching
4. **Implementation Libraries** - Separate shared object files for each sensor type
5. **Factory Functions** - C-style functions exported by each implementation library
6. **Python Bindings** - Python interface for easy integration

How Dynamic Loading Works
-------------------------

Library Discovery
~~~~~~~~~~~~~~~~~

When ``SensorCheckerUtil::init()`` is called, it:

- Constructs the library path based on the model information (name, version, schema version, vendor, market)
- Or uses a custom path if provided
- Loads the shared library using platform-specific APIs (``dlopen`` on Linux, ``LoadLibrary`` on Windows)

Symbol Resolution
~~~~~~~~~~~~~~~~~

The system looks for a factory function named ``createSensorCheckerImpl()`` in the loaded library:

.. code-block:: cpp

    extern "C" {
        SENSOR_CHECKER_API omni::sensors::ISensorCheckerImpl* createSensorCheckerImpl();
        SENSOR_CHECKER_API void destroySensorCheckerImpl(omni::sensors::ISensorCheckerImpl* impl);
    }

Implementation Creation
~~~~~~~~~~~~~~~~~~~~~~~

Once the factory function is found, it's called to create an instance of the sensor checker implementation.

Parameter Validation
~~~~~~~~~~~~~~~~~~~~

The implementation's ``validateParams()`` method is called with the provided parameters. It returns ``nullptr`` if valid, or an error string if validation fails.

Package Structure
-----------------

::

    sensor_checker/
    ├── docs/
    │   ├── README.md                     # Markdown documentation
    │   └── sensor_checker_utility.rst    # This RST documentation file
    ├── include/
    │   ├── ISensorCheckerImpl.h          # Common interface for sensor checker implementations
    │   ├── SensorCheckerTypes.h          # Type definitions and enums
    │   └── SensorCheckerUtil.h           # Main utility class header
    ├── python/
    │   ├── __init__.py                   # Python module entry point
    │   └── test_sensor_checker.py        # Python test suite
    └── PACKAGE-LICENSES/                 # Package license files

Data Structures
---------------

ModelInfo
~~~~~~~~~

Contains information about the sensor model:

.. code-block:: cpp

    struct ModelInfo
    {
        const char* modelName;      // Name of the sensor model (e.g., "LidarCore")
        const char* modelVersion;   // Version of the model (e.g., "1.0")
        const char* modelVendor;    // Vendor of the model (e.g., "NVIDIA")
        const char* schemaVersion;  // Version of the schema (e.g., "1.0")
        const char* marketName;     // Market name for the model
    };

Parameters
~~~~~~~~~~

Contains the parameters to be validated:

.. code-block:: cpp

    struct Parameters
    {
        uint32_t numParams;                    // Number of parameters
        const char** paramNames;               // Array of parameter names
        SensorCheckerParamType* dataTypes;     // Array of parameter data types
        uint32_t* paramVectorLengths;          // Array of vector lengths
        const void** paramValues;              // Array of parameter values
    };

AOVInfo
~~~~~~~

Contains information about an Arbitrary Output Variable:

.. code-block:: cpp

    struct AOVInfo
    {
        const char* aovName;        // Name of the AOV
        const void* aovBuffer;      // Buffer containing AOV data
        const void* aovMetadata;    // Metadata for the AOV
    };

Usage Examples
--------------

C++ Usage
~~~~~~~~~

.. code-block:: cpp

    #include "SensorCheckerUtil.h"

    omni::sensors::SensorCheckerUtil checker;

    // Initialize with model information
    omni::sensors::ModelInfo modelInfo = {
        "LidarCore",    // modelName
        "1.0",          // modelVersion
        "NVIDIA",       // modelVendor
        "1.0",          // schemaVersion
        "default"       // marketName
    };
    checker.init(modelInfo);

    // Prepare parameters -- this is an incomplete set just for demonstration
    const char* paramNames[] = {"omni:sensor:Core:numberOfEmitters", "omni:sensor:Core:nearRange"};
    omni::sensors::SensorCheckerParamType dataTypes[] = {
        omni::sensors::SensorCheckerParamType::INT,
        omni::sensors::SensorCheckerParamType::FLOAT
    };
    uint32_t paramVectorLengths[] = {1, 1};
    uint32_t numEmitters = 100U;
    float nearRange = 0.5f;
    const void* paramValues[] = {&numEmitters, &nearRange};

    omni::sensors::Parameters params = {
        2,              // numParams
        paramNames,     // paramNames
        dataTypes,      // dataTypes
        paramVectorLengths, // paramVectorLengths
        paramValues     // paramValues
    };

    // Validate parameters
    const char* error = checker.validateParams(params);

    if (error == nullptr) {
        auto validatedParams = checker.getValidatedParams();
        // Use validated parameters...
    } else {
        std::cout << "Validation failed: " << error << std::endl;
    }

USD File Validation
~~~~~~~~~~~~~~~~~~~

.. code-block:: cpp

    // Validate parameters from a USD file
    const char* error = checker.validateParams("/path/to/sensor.usda", "/sensor_prim");
    if (error == nullptr) {
        std::cout << "USD validation successful" << std::endl;
    } else {
        std::cout << "USD validation failed: " << error << std::endl;
    }

AOV Validation
~~~~~~~~~~~~~~

.. code-block:: cpp

    // First the parameters needed for AOV validation have to be cached and validated
    const char* error = checker.validateParams("/path/to/sensor.usda", "/sensor_prim");
    if (error != nullptr) {
        std::cout << "Parameter validation failed: " << error << std::endl;
        return;
    }

    // Validate AOV (Arbitrary Output Variable)
    omni::sensors::AOVInfo aovInfo = {
        "Coordinates",  // aovName
        aovBuffer,      // aovBuffer
        aovMetadata     // aovMetadata
    };

    // Get the validated parameters for AOV validation
    auto validatedParams = checker.getValidatedParams();
    const char* aovError = checker.validateAOV(aovInfo);
    if (aovError == nullptr) {
        std::cout << "AOV validation successful" << std::endl;
    } else {
        std::cout << "AOV validation failed: " << aovError << std::endl;
    }

Python Usage
~~~~~~~~~~~~

.. code-block:: python

    from omni.sensors.sensor_checker import SensorCheckerUtil, ModelInfo, Parameters, SensorCheckerParamType

    # Create a sensor checker
    checker = SensorCheckerUtil()

    # Initialize with LidarCore model
    model_info = ModelInfo(
        model_name="LidarCore",
        model_version="1.0",
        model_vendor="NVIDIA",
        schema_version="1.0",
        market_name="default"
    )
    checker.init(model_info)

    # USDA path and to be checked prim
    usda_path = "path/to/usda.usda"
    prim_path = "/prim_path"
    # Validate the parameters
    error = checker.validateParams(usda_path, prim_path)
    if error is None:
        print("LidarCore parameters valid")
        validated_params = checker.getValidatedParams()
        print("Validated parameters:", validated_params)
    else:
        print(f"Validation failed: {error}")

Creating New Sensor Checker Implementations
-------------------------------------------

Create Implementation Class
~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: cpp

    class MySensorCheckerImpl : public omni::sensors::ISensorCheckerImpl
    {
    public:
        void init(const omni::sensors::ModelInfo& modelInfo) override;
        const char* validateParams(const omni::sensors::Parameters& params) override;
        const char* validateAOV(const omni::sensors::AOVInfo& aovInfo,
                               const omni::sensors::Parameters& activeParams) override;
    };

Implement Factory Functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: cpp

    extern "C" {
        SENSOR_CHECKER_API omni::sensors::ISensorCheckerImpl* createSensorCheckerImpl()
        {
            return new MySensorCheckerImpl();
        }

        SENSOR_CHECKER_API void destroySensorCheckerImpl(omni::sensors::ISensorCheckerImpl* impl)
        {
            delete impl;
        }
    }

Add to Build System
~~~~~~~~~~~~~~~~~~~

For premake5, add a new project to ``premake5.lua``:

.. code-block:: lua

    project_with_location("sensor_checker_my_sensor")
        group ("sensors/sensor_checker")
        language "C++"
        kind "SharedLib"
        staticruntime "Off"
        exceptionhandling "On"
        targetdir (package_dir.."/bin")
        cppdialect "C++17"

        defines { "SENSOR_CHECKER_EXPORTS" }

        includedirs
        {
            "%{root}/source/utilities/sensors/sensor_checker/include",
        }

        filter { "system:linux" }
            buildoptions { "-fPIC" }
            linkoptions { "-Wl,-rpath,'$$ORIGIN'" }
        filter { "system:windows" }
            disablewarnings { "4251" }
            rtti "On"
        filter {}

        files { "examples/MySensorCheckerImpl.cpp", "examples/MySensorCheckerImpl.h" }

        -- Set output name to match what SensorCheckerUtil expects
        targetname "SensorCheckerMySensor"

Use the Implementation
~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: cpp

    omni::sensors::ModelInfo modelInfo = {
        "MySensor",     // modelName
        "1.0",          // modelVersion
        "MyVendor",     // modelVendor
        "1.0",          // schemaVersion
        "default"       // marketName
    };
    checker.init(modelInfo);
    // or with custom path
    checker.init(modelInfo, "/path/to/libSensorCheckerMySensor.so");

Library Naming Convention
-------------------------

The system expects shared libraries to follow this naming pattern:

- **Linux**: ``libSensorChecker{ModelName}{ModelVendor}.so``
- **Windows**: ``SensorChecker{ModelName}{ModelVendor}.dll``

For example:

- ``libSensorCheckerLidarCoreNVIDIA.so``

Error Handling
--------------

The system gracefully handles:

- Missing libraries (prints error, just no implementation)
- Missing factory functions (unloads library and continues)
- Invalid implementations (calls are safely forwarded)
- Validation errors (returns descriptive error strings)

Platform Support
----------------

- **Linux**: Uses ``dlopen``, ``dlsym``, ``dlclose``
- **Windows**: Uses ``LoadLibrary``, ``GetProcAddress``, ``FreeLibrary``
- **macOS**: Uses the same APIs as Linux

Current Implementations
-----------------------

- **LidarCore**: NVIDIA LiDAR sensor validation
- **WPMDmatApprox**: NVIDIA WPM Radar sensor validation