1 - include/AclLog.h

include/AclLog.h File Reference

Namespaces

Name
Acl
Acl::AclLog
A namespace for logging utilities.

Classes

Name
classAcl::AclLog::ThreadNameFormatterFlag
classAcl::AclLog::FileLocationFormatterFlag
A custom flag formatter which logs the source file location between a par of “[]”, in case the location is provided with the log call.

Source code

// Copyright (c) 2024, Ateliere. All rights reserved.

#pragma once

#include <filesystem>
#include <string>

#include <spdlog/details/log_msg.h>
#include <spdlog/fmt/fmt.h>
#include <spdlog/formatter.h>
#include <spdlog/pattern_formatter.h>
#include <sys/prctl.h>

namespace Acl {

namespace AclLog {

enum class Level {
    kTrace,    // Detailed diagnostics (for development only)
    kDebug,    // Messages intended for debugging only
    kInfo,     // Messages about normal behavior (default log level)
    kWarning,  // Warnings (functionality intact)
    kError,    // Recoverable errors (functionality impaired)
    kCritical, // Unrecoverable errors (application must stop)
    kOff       // Turns off all logging
};

void init(const std::string& name);

void initControlMessagesLog(const std::string& name);

void setLevel(Level level);

void logControlMessage(const std::string& origin, const std::string& controlMessage);

AclLog::Level getLogLevel();

size_t getMaxFileSize();

size_t getMaxLogRotations();

std::filesystem::path getLogFileFullPath(const std::string& name);

inline std::string getThreadName() {
    static const size_t RECOMMENDED_BUFFER_SIZE = 20;
    static thread_local std::string name;

    if (name.empty()) {
        char buffer[RECOMMENDED_BUFFER_SIZE];
        int retval = prctl(PR_GET_NAME, buffer);
        if (retval == -1) {
            throw spdlog::spdlog_ex("Failed to get thread name: ", errno);
        }
        name = std::string(buffer);
    }
    return name;
}

class ThreadNameFormatterFlag : public spdlog::custom_flag_formatter {
public:
    void format(const spdlog::details::log_msg&, const std::tm&, spdlog::memory_buf_t& dest) override {
        std::string threadName = getThreadName();
        dest.append(threadName.data(), threadName.data() + threadName.size());
    }

    [[nodiscard]] std::unique_ptr<custom_flag_formatter> clone() const override {
        return spdlog::details::make_unique<ThreadNameFormatterFlag>();
    }
};

class FileLocationFormatterFlag : public spdlog::custom_flag_formatter {
public:
    void format(const spdlog::details::log_msg& msg, const std::tm&, spdlog::memory_buf_t& dest) override {
        if (!msg.source.empty()) {
            using namespace spdlog::details;

            dest.push_back('[');
            const char* filename = short_filename_formatter<null_scoped_padder>::basename(msg.source.filename);
            fmt_helper::append_string_view(filename, dest);
            dest.push_back(':');
            fmt_helper::append_int(msg.source.line, dest);
            dest.push_back(']');
        }
    }

    [[nodiscard]] std::unique_ptr<custom_flag_formatter> clone() const override {
        return spdlog::details::make_unique<FileLocationFormatterFlag>();
    }
};

} // namespace AclLog

} // namespace Acl

2 - include/ControlDataAddress.h

include/ControlDataAddress.h File Reference

Namespaces

Name
Acl

Classes

Name
classAcl::ControlDataAddress
A class representing an address within the control protocol. The address consists of an internal list of UUIDs, which all represent a component that needs to be passed to reach the final address. An address might end with a wildcard, which is represented by the omni UUID (i.e. all digits set to 0xF) and will then match all addresses with the same UUID sequence in the start.
structfmt::formatter< Acl::ControlDataAddress >

Source code

// Copyright (c) 2024, Ateliere. All rights reserved.

#pragma once

#include <cstdint>
#include <sstream>
#include <string>
#include <vector>

#include <fmt/format.h>

#include "UUID.h"

namespace Acl {

class ControlDataAddress final {
public:
    ControlDataAddress() = default;

    explicit ControlDataAddress(const UUID& destinationUUID);

    [[nodiscard]] size_t size() const;

    [[nodiscard]] bool empty() const;

    bool extend(const UUID& uuid);

    bool extend(const ControlDataAddress& address);

    [[nodiscard]] bool hasWildcard() const;

    [[nodiscard]] bool currentUuidIsWildcard() const;

    [[nodiscard]] bool currentUuidMatch(const UUID& uuid) const;

    [[nodiscard]] bool fullAddressMatch(const ControlDataAddress& other) const;

    [[nodiscard]] std::optional<UUID> getCurrentUuid() const;

    std::optional<UUID> moveToAndGetNextUuid();

    [[nodiscard]] std::vector<uint8_t> pack() const;

    static std::optional<ControlDataAddress> unpack(const std::vector<uint8_t>::const_iterator& packedBegin,
                                                    const std::vector<uint8_t>::const_iterator& packedEnd);

    static std::optional<ControlDataAddress> unpack(const uint8_t* packedBegin, const uint8_t* packedEnd);

    [[nodiscard]] std::string toString() const;

    bool operator==(const ControlDataAddress& other) const;

    bool operator!=(const ControlDataAddress& other) const;

    friend std::ostream& operator<<(std::ostream& stream, const ControlDataAddress& address);

private:
    std::vector<UUID> mAddress;
};

std::ostream& operator<<(std::ostream& stream, const ControlDataAddress& address);

} // namespace Acl

template <> struct fmt::formatter<Acl::ControlDataAddress> : formatter<std::string> {
    template <typename FormatContext> auto format(const Acl::ControlDataAddress& address, FormatContext& ctx) {
        return formatter<std::string>::format(address.toString(), ctx);
    }
};

3 - include/ControlDataCommon.h

include/ControlDataCommon.h File Reference

Namespaces

Name
Acl
Acl::ControlDataCommon

Classes

Name
structAcl::ControlDataCommon::Response
A response from a ControlDataReceiver to a request. The UUID tells which receiver the response is sent from.
structAcl::ControlDataCommon::StatusMessage
A status message from a ControlDataReceiver. The UUID tells which receiver the message is sent from.
structAcl::ControlDataCommon::ConnectionEvent
A connection related event.
structfmt::formatter< Acl::ControlDataCommon::EventType >

Source code

// Copyright (c) 2024, Ateliere. All rights reserved.

#pragma once

#include <cstdint>
#include <string>
#include <vector>

#include "ControlDataAddress.h"
#include "UUID.h"

namespace Acl::ControlDataCommon {

struct Response {
    std::string mMessage;          
    uint64_t mRequestId = 0;       
    UUID mFromUUID;                
    ControlDataAddress mRecipient; 
};

struct StatusMessage {
    std::string mMessage;          
    UUID mFromUUID;                
    ControlDataAddress mRecipient; 
};

enum class EventType : uint8_t {
    kDisconnect, 
    kConnect     
};

struct ConnectionEvent {
    EventType mEventType = EventType::kDisconnect; 
    ControlDataAddress mAddress;                   
    UUID mEventNode;                               
};

} // namespace Acl::ControlDataCommon

template <> struct fmt::formatter<Acl::ControlDataCommon::EventType> : formatter<std::string> {
    template <typename FormatContext> auto format(const Acl::ControlDataCommon::EventType type, FormatContext& ctx) {
        std::string value;
        switch (type) {
        case Acl::ControlDataCommon::EventType::kDisconnect:
            value = "disconnect";
            break;
        case Acl::ControlDataCommon::EventType::kConnect:
            value = "connect";
            break;
        }
        return formatter<std::string>::format(value, ctx);
    }
};

4 - include/ControlDataSender.h

include/ControlDataSender.h File Reference

Namespaces

Name
Acl

Classes

Name
classAcl::ControlDataSender
A ControlDataSender can send control signals to one or more receivers using a network connection. A single ControlDataSender can connect to multiple receivers, all identified by a UUID. The class is controlled using an ISystemControllerInterface; this interface is responsible for setting up connections to receivers. The ControlDataSender can send asynchronous requests to (all) the receivers and get a response back. Each response is identified with a request ID as well as the UUID of the responding receiver. The ControlDataSender can also receive status messages from the receivers.
structAcl::ControlDataSender::Settings
Settings for a ControlDataSender.

Source code

// Copyright (c) 2024, Ateliere. All rights reserved.

#pragma once

#include <functional>
#include <memory>
#include <vector>

#include <ISystemControllerInterface.h>

#include "ControlDataCommon.h"

namespace Acl {

class ControlDataSender final {
public:
    enum class SendRequestStatus {
        kSuccess,             
        kFailed,              
        kSendFailedForSome,   
        kSenderNotConfigured, 
        kNoConnectedReceiver, 
        kInternalError        
    };

    struct Settings {
        std::function<void(const ControlDataCommon::Response&)>
            mResponseCallback; // Callback for response messages from receivers
        std::function<void(const ControlDataCommon::StatusMessage&)>
            mStatusMessageCallback; // Callback for status messages from receivers
        std::function<void(const ControlDataCommon::ConnectionEvent&)>
            mConnectionEventCallback; // Callback for connection events that has been detected by the sender, or passed
                                      // to it from a connected receiver
    };

    ControlDataSender();

    ~ControlDataSender();

    [[nodiscard]] bool configure(const std::shared_ptr<ISystemControllerInterface>& controllerInterface,
                                 const Settings& settings) const;

    SendRequestStatus sendRequestToReceivers(std::string_view request,
                                             uint64_t& requestId,
                                             const UUID& requester = UUID::kNilUUID,
                                             int8_t hops = -1) const;

    [[nodiscard]] std::vector<UUID> getDirectlyConnectedReceivers() const;

    static std::string getVersion();

    // ControlDataSender is not copyable
    ControlDataSender(ControlDataSender const&) = delete;            // Copy construct
    ControlDataSender& operator=(ControlDataSender const&) = delete; // Copy assign

private:
    class Impl;
    std::unique_ptr<Impl> pImpl;
};

} // namespace Acl

5 - include/ISystemControllerInterface.h

include/ISystemControllerInterface.h File Reference

Namespaces

Name
Acl

Classes

Name
classAcl::ISystemControllerInterface
An ISystemControllerInterface is the interface between a component and the System controller controlling the component. The interface allows for two-way communication between the component and the system controller by means of sending requests and getting responses. Classes deriving from the ISystemControllerInterface should provide the component side implementation of the communication with the system controller. This interface can be inherited and implemented by developers to connect to custom system controllers, or to directly control a component programmatically, without the need for connecting to a remote server.
structAcl::ISystemControllerInterface::Response
A response to a request, consists of a status code and an (optional) parameters JSON object.
structAcl::ISystemControllerInterface::Callbacks
A struct containing the callbacks that needs to be registered by the component using this interface.
structfmt::formatter< Acl::ISystemControllerInterface::StatusCode >

Source code

// Copyright (c) 2024, Ateliere. All rights reserved.

#pragma once

#include <functional>
#include <json.hpp>
#include <optional>
#include <string>

#include <fmt/format.h>

#include "UUID.h"

namespace Acl {

class ISystemControllerInterface {
public:
    enum class StatusCode : uint32_t {
        SUCCESS = 3001, // Accept / Success

        TOO_MANY_REQUESTS = 3101, // Too many requests, try again later

        UUID_ALREADY_REGISTERED = 3201, // UUID is already registered
        FORMAT_ERROR = 3202,            // Message formatting error
        ALREADY_CONFIGURED = 3203,      // The requested thing to configure is already configured
        OUT_OF_RESOURCES = 3204,  // Out of resources (CPU/GPU close to max utilization, all available slots used, etc.)
        NOT_FOUND = 3205,         // The requested thing was not found
        INTERNAL_ERROR = 3206,    // Internal error when trying to serve the request
        CONNECTION_FAILED = 3207, // Connection failure
        TIMEOUT_EXCEEDED = 3208,  // Timeout exceeded
        KEY_MISMATCH = 3209,      // Key mismatch (might be a timeout, 3007 in the future)
        UNKNOWN_REQUEST = 3210,   // The name of the request was not known
        MALFORMED_REQUEST = 3211, // The request is not correctly formatted
        ALREADY_IN_USE = 3212,    // The requested resource is already in use
        VERSION_MISMATCH = 3213,  // The version of the request is not supported

        // None, yet
    };

    struct Response {
        StatusCode mCode;
        nlohmann::json mParameters; // Can be empty
    };

    struct Callbacks {
        std::function<Response(const std::string&, const nlohmann::json&)>
            mRequestCallback; // Callback called when then controller has sent a request
        std::function<void(uint32_t, const std::string&, const std::error_code&)>
            mConnectionClosedCallback; // Callback called when the connection to the controller is closed
    };

    virtual ~ISystemControllerInterface() = default;

    virtual std::optional<std::string> sendMessage(const std::string& messageTitle,
                                                   const nlohmann::json& parameters) = 0;

    virtual bool registerRequestCallback(const Callbacks& callbacks) = 0;

    virtual bool connect() = 0;

    virtual bool disconnect() = 0;

    [[nodiscard]] virtual bool isConnected() const = 0;

    [[nodiscard]] virtual UUID getUUID() const = 0;
};

} // namespace Acl

template <> struct fmt::formatter<Acl::ISystemControllerInterface::StatusCode> : formatter<std::uint32_t> {
    template <typename FormatContext>
    auto format(Acl::ISystemControllerInterface::StatusCode code, FormatContext& ctx) {
        return formatter<std::uint32_t>::format(static_cast<uint32_t>(code), ctx);
    }
};

6 - include/SystemControllerConnection.h

include/SystemControllerConnection.h File Reference

Namespaces

Name
Acl

Classes

Name
classAcl::SystemControllerConnection
An implementation of the ISystemControllerInterface for a System controller residing in a remote server. The connection to the server uses a Websocket.
structAcl::SystemControllerConnection::Settings
Settings for a SystemControllerConnection.
structfmt::formatter< Acl::SystemControllerConnection::ComponentType >

Source code

// Copyright (c) 2024, Ateliere. All rights reserved.

#pragma once

#include <chrono>

#include "ISystemControllerInterface.h"
#include "json.hpp"

namespace Acl {

class SystemControllerConnection final : public ISystemControllerInterface {
public:
    enum class ComponentType : uint32_t {
        kIngest,
        kPipeline,
        kControlPanel,
    };

    struct Settings {
        std::string mSystemControllerIP;      // IP of the server
        uint16_t mSystemControllerPort;       // Port of the server
        std::string mSystemControllerPostfix; // Postfix of the address that the backend uses if any
        std::string mPSK;    // The pre shared key used for authorization with the system controller server
        UUID mUUID;          // The UUID of the device using this library
        ComponentType mType; // The component type of the component using this SystemControllerConnection
        std::string mName;   // The component name (optional)
        std::string mMyIP;   // The external IP of the system the component is running on. Will be sent to the system
                             // controller server in the announce message (optional)
        std::chrono::milliseconds mConnectTimeout{
            3000};           // Max time to wait on an announcement response from the server during connection
        bool mEnableHTTPS;   // Enable the communication between the system controller and a component encrypted
        bool mInsecureHTTPS; // Disable the verification of the TLS certificate if requested.
        std::string mCustomCaCertFile; // Custom CA certificate
    };

    SystemControllerConnection();
    ~SystemControllerConnection() override;

    bool configure(const Settings& settings);

    bool connect() override;

    [[nodiscard]] bool isConnected() const override;

    [[nodiscard]] UUID getUUID() const override;

    std::optional<std::string> sendMessage(const std::string& messageTitle, const nlohmann::json& parameters) override;

    bool disconnect() override;

    bool registerRequestCallback(const Callbacks& callbacks) override;

    SystemControllerConnection(SystemControllerConnection const&) = delete;            // Copy construct
    SystemControllerConnection(SystemControllerConnection&&) = delete;                 // Move construct
    SystemControllerConnection& operator=(SystemControllerConnection const&) = delete; // Copy assign
    SystemControllerConnection& operator=(SystemControllerConnection&&) = delete;      // Move assign

private:
    class Impl;
    std::unique_ptr<Impl> pImpl;
};

} // namespace Acl

template <> struct fmt::formatter<Acl::SystemControllerConnection::ComponentType> : formatter<std::string> {
    template <typename FormatContext>
    auto format(Acl::SystemControllerConnection::ComponentType type, FormatContext& ctx) {
        std::string value;
        switch (type) {
        case Acl::SystemControllerConnection::ComponentType::kIngest:
            value = "ingest";
            break;
        case Acl::SystemControllerConnection::ComponentType::kPipeline:
            value = "pipeline";
            break;
        case Acl::SystemControllerConnection::ComponentType::kControlPanel:
            value = "controlpanel";
            break;
        }

        return formatter<std::string>::format(value, ctx);
    }
};

7 - include/UUID.h

include/UUID.h File Reference

Namespaces

Name
Acl

Classes

Name
classAcl::UUID
A class holding a UUID, stored as a sequence of bytes. This class only supports version 4 variant 1 of the UUID standard.
structfmt::formatter< Acl::UUID >

Functions

Name
UUID()
Default constructor, returns a nil UUID.
constexprUUID(const std::array< uint8_t, kUUIDSize > & bytes)
Construct a UUID from a sequence of bytes.
UUIDgenerateRandom()
Create a new, randomly generated UUID.
std::optional< UUID >fromString(const std::string & uuid)
Parse a UUID from a string.
std::optional< UUID >fromVector(const std::vector< uint8_t >::const_iterator & start, const std::vector< uint8_t >::const_iterator & end)
Read a UUID from a vector of uint8s.
std::optional< UUID >fromPointer(const uint8_t * start, const uint8_t * end)
Read a UUID from a pointer.
std::stringtoString() const
std::stringtoBitString() const
booloperator==(const UUID & other) const
Compare this UUID to another UUID.
booloperator!=(const UUID & other) const
Compare this UUID to another UUID for inequality.
booloperator<(const UUID & other) const
Less than operator implementation.
std::array< uint8_t, kUUIDSize >::const_iteratorbegin() const
std::array< uint8_t, kUUIDSize >::const_iteratorend() const
constexpr size_tsize() const

Attributes

Name
constexpr size_tkUUIDSize
const UUIDkNilUUID
const UUIDkOmniUUID

Functions Documentation

function UUID

UUID()

Default constructor, returns a nil UUID.

function UUID

explicit constexpr UUID(
    const std::array< uint8_t, kUUIDSize > & bytes
)

Construct a UUID from a sequence of bytes.

Note: This will accept UUIDs that are not valid version 4 UUIDs.

function generateRandom

static UUID generateRandom()

Create a new, randomly generated UUID.

Return: A new, randomly generated UUID

function fromString

static std::optional< UUID > fromString(
    const std::string & uuid
)

Parse a UUID from a string.

Parameters:

  • uuid The string representation of the UUID

Return: An optional containing the UUID on success, or nullopt in case the parsing failed

function fromVector

static std::optional< UUID > fromVector(
    const std::vector< uint8_t >::const_iterator & start,
    const std::vector< uint8_t >::const_iterator & end
)

Read a UUID from a vector of uint8s.

Parameters:

  • start Start iterator to read the UUID from
  • end End iterator to read the UUID from, must be 16 bytes after start

Return: An optional containing the UUID on success, or nullopt in case the parsing failed

function fromPointer

static std::optional< UUID > fromPointer(
    const uint8_t * start,
    const uint8_t * end
)

Read a UUID from a pointer.

Parameters:

  • start Start pointer to read the UUID from
  • end End pointer to read the UUID from, must be 16 bytes after start

Return: An optional containing the UUID on success, or nullopt in case the parsing failed

function toString

std::string toString() const

Return: A string representation of the UUID formatted as hex values

function toBitString

std::string toBitString() const

Return: A string representation of the UUID formatted as bits

function operator==

bool operator==(
    const UUID & other
) const

Compare this UUID to another UUID.

Parameters:

  • other The other UUID to compare this UUID to

Return: True if the UUIDs are identical, false otherwise

function operator!=

bool operator!=(
    const UUID & other
) const

Compare this UUID to another UUID for inequality.

Parameters:

  • other The other UUID to compare this UUID to

Return: True if the UUIDs are not equal, false in case they are identical

function operator<

bool operator<(
    const UUID & other
) const

Less than operator implementation.

Parameters:

  • other The other UUID to compare this UUID to

Return: True if this UUID should be sorted before the other UUID

function begin

std::array< uint8_t, kUUIDSize >::const_iterator begin() const

Return: Begin iterator for the UUID byte sequence

function end

std::array< uint8_t, kUUIDSize >::const_iterator end() const

Return: End iterator for the UUID byte sequence

function size

constexpr size_t size() const

Return: The size in bytes of the UUID. Will always return 16

Attributes Documentation

variable kUUIDSize

static constexpr size_t kUUIDSize = 16;

variable kNilUUID

static const UUID kNilUUID;

variable kOmniUUID

static const UUID kOmniUUID;

Source code

// Copyright (c) 2024, Ateliere. All rights reserved.

#pragma once

#include <array>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>

#include <fmt/format.h>

namespace Acl {

class UUID {
public:
    static constexpr size_t kUUIDSize = 16;
    // Predefined UUID values
    static const UUID kNilUUID;
    static const UUID kOmniUUID;

    UUID();

    constexpr explicit UUID(const std::array<uint8_t, kUUIDSize>& bytes)
        : mUUID{bytes} {
    }

    static UUID generateRandom();

    static std::optional<UUID> fromString(const std::string& uuid);

    static std::optional<UUID> fromVector(const std::vector<uint8_t>::const_iterator& start,
                                          const std::vector<uint8_t>::const_iterator& end);

    static std::optional<UUID> fromPointer(const uint8_t* start, const uint8_t* end);

    [[nodiscard]] std::string toString() const;

    [[nodiscard]] std::string toBitString() const;

    bool operator==(const UUID& other) const;

    bool operator!=(const UUID& other) const;

    bool operator<(const UUID& other) const;

    [[nodiscard]] std::array<uint8_t, kUUIDSize>::const_iterator begin() const;

    [[nodiscard]] std::array<uint8_t, kUUIDSize>::const_iterator end() const;

    [[nodiscard]] constexpr size_t size() const {
        return mUUID.size();
    }

private:
    std::array<uint8_t, kUUIDSize> mUUID{};
} __attribute__((packed));

static_assert(sizeof(UUID) == 16, "UUID has unexpected size");

std::ostream& operator<<(std::ostream& stream, const UUID& uuid);

} // namespace Acl

template <> struct fmt::formatter<Acl::UUID> : formatter<std::string> {
    template <typename FormatContext> auto format(const Acl::UUID& uuid, FormatContext& ctx) {
        return formatter<std::string>::format(uuid.toString(), ctx);
    }
};