Files
esp-protocols/components/esp_mqtt_cxx/include/esp_mqtt.hpp

297 lines
8.4 KiB
C++
Raw Normal View History

/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <string_view>
#ifndef __cpp_exceptions
#error MQTT class can only be used when __cpp_exceptions is enabled. Enable CONFIG_COMPILER_CXX_EXCEPTIONS in Kconfig
#endif
#include <optional>
#include <variant>
#include <utility>
#include <memory>
#include <string>
#include "esp_exception.hpp"
#include "esp_mqtt_client_config.hpp"
#include "mqtt_client.h"
namespace idf::mqtt {
constexpr auto *TAG = "mqtt_client_cpp";
struct MQTTException : ESPException {
using ESPException::ESPException;
};
/**
* @brief QoS for publish and subscribe
*
* Sets the QoS as:
* AtMostOnce : Best effort delivery of messages. Message loss can occur.
* AtLeastOnce : Guaranteed delivery of messages. Duplicates can occur.
* ExactlyOnce : Guaranteed delivery of messages exactly once.
*
* @note
* When subscribing to a topic the QoS means the maximum QoS that should be sent to
* client on this topic
*/
enum class QoS { AtMostOnce = 0, AtLeastOnce = 1, ExactlyOnce = 2 };
/**
* @brief Sets if a message must be retained.
*
* Retained messages are delivered to future subscribers that match the topic name.
*
*/
enum class Retain : bool { NotRetained = false, Retained = true };
/**
* @brief Message class template to publish.
*
*/
template <typename T> struct Message {
T data; /*!< Data for publish. Should be a contiguous type*/
QoS qos = QoS::AtLeastOnce; /*!< QoS for the message*/
Retain retain = Retain::NotRetained; /*!< Retention mark for the message.*/
};
/**
* @brief Message type that holds std::string for data
*
*/
using StringMessage = Message<std::string>;
[[nodiscard]] bool filter_is_valid(std::string::const_iterator first, std::string::const_iterator last);
/**
* @brief Filter for mqtt topic subscription.
*
* Topic filter.
*
*/
class Filter {
public:
/**
* @brief Constructs the topic filter from the user filter
* @throws std::domain_error if the filter is invalid.
*
* @param user_filter Filter to be used.
*/
explicit Filter(std::string user_filter);
/**
* @brief Get the filter string used.
*
* @return Reference to the topic filter.
*/
const std::string &get();
/**
* @brief Checks the filter string against a topic name.
*
* @param topic_begin Iterator to the beginning of the sequence.
* @param topic_end Iterator to the end of the sequence.
*
* @return true if the topic name match the filter
*/
[[nodiscard]] bool match(std::string::const_iterator topic_begin, std::string::const_iterator topic_end) const noexcept;
/**
* @brief Checks the filter string against a topic name.
*
* @param topic topic name
*
* @return true if the topic name match the filter
*/
[[nodiscard]] bool match(const std::string &topic) const noexcept;
/**
* @brief Checks the filter string against a topic name.
*
* @param begin Char array with topic name.
* @param size Size of given topic name.
*
* @return true if the topic name match the filter
*/
[[nodiscard]] bool match(char *begin, int size) const noexcept;
private:
/**
* @brief Advance the topic to the next level.
*
* An mqtt topic ends with a /. This function is used to iterate in topic levels.
*
* @return Iterator to the start of the topic.
*/
[[nodiscard]] std::string::const_iterator advance(std::string::const_iterator begin, std::string::const_iterator end) const;
std::string filter;
};
/**
* @brief Message identifier to track delivery.
*
*/
enum class MessageID : int {};
/**
* @brief Base class for MQTT client
*
* Should be inherited to provide event handlers.
*/
class Client {
public:
/**
* @brief Constructor of the client
*
* @param broker Configuration for broker connection
* @param credentials client credentials to be presented to the broker
* @param config Mqtt client configuration
*/
Client(const BrokerConfiguration &broker, const ClientCredentials &credentials, const Configuration &config);
/**
* @brief Constructs Client using the same configuration used for
* `esp_mqtt_client`
* @param config config struct to `esp_mqtt_client`
*/
Client(const esp_mqtt_client_config_t &config);
/**
* @brief Subscribe to topic
*
* @param topic_filter MQTT topic filter
* @param qos QoS subscription, defaulted as QoS::AtLeastOnce
*
* @return Optional MessageID. In case of failure std::nullopt is returned.
*/
std::optional<MessageID> subscribe(const std::string &topic_filter, QoS qos = QoS::AtLeastOnce);
/**
* @brief publish message to topic
*
* @tparam Container Type for data container. Must be a contiguous memory.
* @param topic Topic name
* @param message Message struct containing data, qos and retain
* configuration.
*
* @return Optional MessageID. In case of failure std::nullopt is returned.
*/
template <class Container>
std::optional<MessageID> publish(const std::string &topic, const Message<Container> &message)
{
return publish(topic, std::begin(message.data), std::end(message.data), message.qos, message.retain);
}
/**
* @brief publish message to topic
*
* @tparam InputIt Input data iterator type.
* @param topic Topic name
* @param first, last Iterator pair of data to publish
* @param qos Set qos message
* @param retain Set if message should be retained
*
* @return Optional MessageID. In case of failure std::nullopt is returned.
*/
template <class InputIt>
std::optional<MessageID> publish(const std::string &topic, InputIt first, InputIt last, QoS qos = QoS::AtLeastOnce, Retain retain = Retain::NotRetained)
{
auto size = std::distance(first, last);
auto res = esp_mqtt_client_publish(handler.get(), topic.c_str(), &(*first), size, static_cast<int>(qos),
static_cast<int>(retain));
if (res < 0) {
return std::nullopt;
}
return MessageID{res};
}
virtual ~Client() = default;
protected:
/**
* @brief Helper type to be used as custom deleter for std::unique_ptr.
*/
struct MqttClientDeleter {
void operator()(esp_mqtt_client *client_handler)
{
esp_mqtt_client_destroy(client_handler);
}
};
/**
* @brief Type of the handler for the underlying mqtt_client handler.
* It uses std::unique_ptr for lifetime management
*/
using ClientHandler = std::unique_ptr<esp_mqtt_client, MqttClientDeleter>;
/**
* @brief esp_mqtt_client handler
*
*/
ClientHandler handler;
/**
* @brief Called if there is an error event
*
* @param event mqtt event data
*/
virtual void on_error(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an disconnection event
*
* @param event mqtt event data
*/
virtual void on_disconnected(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an subscribed event
*
* @param event mqtt event data
*/
virtual void on_subscribed(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an unsubscribed event
*
* @param event mqtt event data
*/
virtual void on_unsubscribed(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an published event
*
* @param event mqtt event data
*/
virtual void on_published(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an before connect event
*
* @param event mqtt event data
*/
virtual void on_before_connect(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an connected event
*
* @param event mqtt event data
*
*/
virtual void on_connected(const esp_mqtt_event_handle_t event) = 0;
/**
* @brief Called if there is an data event
*
* @param event mqtt event data
*
*/
virtual void on_data(const esp_mqtt_event_handle_t event) = 0;
private:
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id,
void *event_data) noexcept;
void init(const esp_mqtt_client_config_t &config);
};
} // namespace idf::mqtt