From 0958f5f292a65da6a46b9eba9c21f7a2523faa83 Mon Sep 17 00:00:00 2001 From: Takatoshi Kondo Date: Wed, 4 Dec 2024 18:38:50 +0900 Subject: [PATCH] Partial Mr.DOCS support. --- CMakeLists.txt | 1 + README.md | 20 +- doc/modules/ROOT/pages/reference.adoc | 218 ++++++++++++++---- doc/mrdocs.yml | 2 +- include/async_mqtt/protocol/connection.hpp | 49 ++++ .../async_mqtt/protocol/connection_fwd.hpp | 23 +- tool/CMakeLists.txt | 9 + 7 files changed, 263 insertions(+), 59 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ac4f98e9..1e4aae20f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ option(ASYNC_MQTT_BUILD_TOOLS "Enable building tools (broker, bench, etc.." OFF) option(ASYNC_MQTT_BUILD_EXAMPLES "Enable building example applications" OFF) option(ASYNC_MQTT_BUILD_EXAMPLES_SEPARATE "Enable building separate library build example applications(It requires much memory)" OFF) option(ASYNC_MQTT_BUILD_LIB "Enable building separate compilation library" OFF) +option(ASYNC_MQTT_MRDOCS "For Mr.Docs document generation" OFF) # Not implemented yet option(ASYNC_MQTT_USE_STR_CHECK "Enable UTF8 String check" OFF) diff --git a/README.md b/README.md index cab116992..7fced2752 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,24 @@ # async_mqtt -Asynchronous MQTT communication library. +- I/O independent (as known as Sans-I/O) MQTT protocol library for C++17. +- Asynchronous MQTT communication library using the MQTT protocol library and Boost.Asio. Version 10.0.0 [![Actions Status](https://github.com/redboltz/async_mqtt/workflows/CI/badge.svg)](https://github.com/redboltz/async_mqtt/actions)[![codecov](https://codecov.io/gh/redboltz/async_mqtt/branch/main/graph/badge.svg)](https://codecov.io/gh/redboltz/async_mqtt) -This is Boost.Asio oriented asynchronous MQTT communication library. You can use async_mqtt to develop not only your MQTT client application but also your server (e.g. broker). -Based on https://github.com/redboltz/mqtt_cpp experience, there are many improvements. See overview. - -Document is https://github.com/redboltz/async_mqtt/blob/doc/README.adoc - -# Overview +## Document [Latest document](https://redboltz.github.io/async_mqtt/doc/latest/index.html) +## I/O independent protocol library +- connection + - Protocol state machine for MQTT connection. + - All APIs are implemented as synchronous member functions. + - State machine events are implemented as pure virtual functions. +- rv_connection + - Inherits connection. + - State machine events are implemented as the return value of APIs. + - The type of return value is vector of event_variant. + ## Boost.Asio style asynchronous APIs support ### [Completion Token](https://www.boost.org/doc/html/boost_asio/overview/model/completion_tokens.html) is supported diff --git a/doc/modules/ROOT/pages/reference.adoc b/doc/modules/ROOT/pages/reference.adoc index 470a4ba82..e11d6c1b7 100644 --- a/doc/modules/ROOT/pages/reference.adoc +++ b/doc/modules/ROOT/pages/reference.adoc @@ -1,17 +1,47 @@ [#reference] = Reference -[width=100%] +== I/O independent (a.k.a Sans-I/O) MQTT protocol machine + +[%header,width=100%,cols="1,1,1"] |=== -1+| *MQTT Connections* | *Predefined Layers* | *MQTT packets* | *Errors* +|MQTT Protocol +|Timer +|Error -| **Class Templates** +| -xref:reference:async_mqtt/client.adoc[`client`] +**Classes** -xref:reference:async_mqtt/endpoint.adoc[`endpoint`] +**Protocol machines** -xref:reference:async_mqtt/basic_endpoint.adoc[`basic_endpoint`] +xref:reference:async_mqtt/connection.adoc[`connection`] + +xref:reference:async_mqtt/basic_connection.adoc[`basic_connection`] + +xref:reference:async_mqtt/rv_connection.adoc[`rv_connection`] + +xref:reference:async_mqtt/basic_rv_connection.adoc[`basic_rv_connection`] + +**Events** + +xref:reference:async_mqtt/event_variant.adoc[`event_variant`] + +xref:reference:async_mqtt/event/basic_send.adoc[`basic_send`] + +xref:reference:async_mqtt/event/send.adoc[`send`] + +xref:reference:async_mqtt/event/basic_packet_received.adoc[`basic_packet_received`] + +xref:reference:async_mqtt/event/packet_received.adoc[`packet_received`] + +xref:reference:async_mqtt/event/close.adoc[`close`] + +xref:reference:async_mqtt/event/basic_packet_id_released.adoc[`basic_packet_id_released`] + +xref:reference:async_mqtt/event/packet_id_released.adoc[`packet_id_released`] + +xref:reference:async_mqtt/event/timer.adoc[`timer`] **Enums** @@ -19,124 +49,228 @@ xref:reference:async_mqtt/protocol_version.adoc[`protocol_version`] xref:reference:async_mqtt/role.adoc[`role`] +| + +**Enums** + +xref:reference:async_mqtt/timer_kind.adoc[`timer_kind`] + +xref:reference:async_mqtt/timer_op.adoc[`timer_op`] + **Functions** -xref:reference:async_mqtt/setup_log.adoc[`setup_log`] +xref:reference:async_mqtt/timer_kind_to_string.adoc[`timer_kind_to_string`] -xref:reference:async_mqtt/logger.adoc[`log`] +xref:reference:async_mqtt/timer_op_to_string.adoc[`timer_op_to_string`] -| **Types** +| -xref:reference:async_mqtt/protocol/mqtt.adoc[`mqtt`] +**Types** -xref:reference:async_mqtt/protocol/mqtts.adoc[`mqtts`] +xref:reference:async_mqtt/error_code.adoc[`error_code`] -xref:reference:async_mqtt/protocol/ws.adoc[`ws`] +**Enums** -xref:reference:async_mqtt/protocol/wss.adoc[`wss`] +**common** -**Class Templates** +xref:reference:async_mqtt/mqtt_error.adoc[`mqtt_error`] -xref:reference:async_mqtt/layer_customize-03.adoc[`layer_customize(TCP)`] +**v3.1.1** -xref:reference:async_mqtt/layer_customize-08.adoc[`layer_customize(TLS)`] +xref:reference:async_mqtt/connect_return_code.adoc[`connect_return_code`] -xref:reference:async_mqtt/layer_customize-02.adoc[`layer_customize(WS)`] +xref:reference:async_mqtt/suback_return_code.adoc[`suback_return_code`] + +**v5** + +xref:reference:async_mqtt/connect_reason_code.adoc[`connect_reason_code`] + +xref:reference:async_mqtt/disconnect_reason_code.adoc[`disconnect_reason_code`] +xref:reference:async_mqtt/suback_reason_code.adoc[`suback_reason_code`] -**Function Templates** +xref:reference:async_mqtt/unsuback_reason_code.adoc[`unsuback_reason_code`] -xref:reference:async_mqtt/async_underlying_handshake-05.adoc[`async_underlying_handshake(TCP)`] -xref:reference:async_mqtt/async_underlying_handshake-09.adoc[`async_underlying_handshake(TLS)`] -xref:reference:async_mqtt/async_underlying_handshake-06.adoc[`async_underlying_handshake(WS)`] -xref:reference:async_mqtt/async_underlying_handshake-0b.adoc[`async_underlying_handshake(WS with path)`] +xref:reference:async_mqtt/puback_reason_code.adoc[`puback_reason_code`] -| **Class Templates** +xref:reference:async_mqtt/pubrec_reason_code.adoc[`pubrec_reason_code`] -**common** +xref:reference:async_mqtt/pubrel_reason_code.adoc[`pubrel_reason_code`] + +xref:reference:async_mqtt/pubcomp_reason_code.adoc[`pubcomp_reason_code`] + +xref:reference:async_mqtt/auth_reason_code.adoc[`auth_reason_code`] + +|=== + + +[%header,width=100%,cols="1,1,1,1"] +|=== +4+|MQTT packets + +| + +**Classes(1 of 4)** + +**variants** xref:reference:async_mqtt/packet_variant.adoc[`packet_variant`] + xref:reference:async_mqtt/basic_packet_variant.adoc[`basic_packet_variant`] **v3.1.1** xref:reference:async_mqtt/v3_1_1/connect_packet.adoc[`connect_packet`] + xref:reference:async_mqtt/v3_1_1/connack_packet.adoc[`connack_packet`] + xref:reference:async_mqtt/v3_1_1/disconnect_packet.adoc[`disconnect_packet`] + xref:reference:async_mqtt/v3_1_1/subscribe_packet.adoc[`subscribe_packet`] + xref:reference:async_mqtt/v3_1_1/basic_subscribe_packet.adoc[`basic_subscribe_packet`] + xref:reference:async_mqtt/v3_1_1/suback_packet.adoc[`suback_packet`] + xref:reference:async_mqtt/v3_1_1/basic_suback_packet.adoc[`basic_suback_packet`] + xref:reference:async_mqtt/v3_1_1/unsubscribe_packet.adoc[`unsubscribe_packet`] + xref:reference:async_mqtt/v3_1_1/basic_unsubscribe_packet.adoc[`basic_unsubscribe_packet`] + xref:reference:async_mqtt/v3_1_1/unsuback_packet.adoc[`unsuback_packet`] + xref:reference:async_mqtt/v3_1_1/basic_unsuback_packet.adoc[`basic_unsuback_packet`] + +| + +**Classes(2 of 4)** + xref:reference:async_mqtt/v3_1_1/publish_packet.adoc[`publish_packet`] + xref:reference:async_mqtt/v3_1_1/basic_publish_packet.adoc[`basic_publish_packet`] + xref:reference:async_mqtt/v3_1_1/puback_packet.adoc[`puback_packet`] + xref:reference:async_mqtt/v3_1_1/basic_puback_packet.adoc[`basic_puback_packet`] + xref:reference:async_mqtt/v3_1_1/pubrec_packet.adoc[`pubrec_packet`] + xref:reference:async_mqtt/v3_1_1/basic_pubrec_packet.adoc[`basic_pubrec_packet`] + xref:reference:async_mqtt/v3_1_1/pubrel_packet.adoc[`pubrel_packet`] + xref:reference:async_mqtt/v3_1_1/basic_pubrel_packet.adoc[`basic_pubrel_packet`] + xref:reference:async_mqtt/v3_1_1/pubcomp_packet.adoc[`pubcomp_packet`] + xref:reference:async_mqtt/v3_1_1/basic_pubcomp_packet.adoc[`basic_pubcomp_packet`] + xref:reference:async_mqtt/v3_1_1/pingreq_packet.adoc[`pingreq_packet`] + xref:reference:async_mqtt/v3_1_1/pingresp_packet.adoc[`pungresp_packet`] +| + +**Classes(3 of 4)** + **v5** xref:reference:async_mqtt/v5/connect_packet.adoc[`connect_packet`] + xref:reference:async_mqtt/v5/connack_packet.adoc[`connack_packet`] + xref:reference:async_mqtt/v5/disconnect_packet.adoc[`disconnect_packet`] + xref:reference:async_mqtt/v5/subscribe_packet.adoc[`subscribe_packet`] + xref:reference:async_mqtt/v5/basic_subscribe_packet.adoc[`basic_subscribe_packet`] + xref:reference:async_mqtt/v5/suback_packet.adoc[`suback_packet`] + xref:reference:async_mqtt/v5/basic_suback_packet.adoc[`basic_suback_packet`] + xref:reference:async_mqtt/v5/unsubscribe_packet.adoc[`unsubscribe_packet`] + xref:reference:async_mqtt/v5/basic_unsubscribe_packet.adoc[`basic_unsubscribe_packet`] + xref:reference:async_mqtt/v5/unsuback_packet.adoc[`unsuback_packet`] + xref:reference:async_mqtt/v5/basic_unsuback_packet.adoc[`basic_unsuback_packet`] + +| + +**Classes(4 of 4)** + xref:reference:async_mqtt/v5/publish_packet.adoc[`publish_packet`] + xref:reference:async_mqtt/v5/basic_publish_packet.adoc[`basic_publish_packet`] + xref:reference:async_mqtt/v5/puback_packet.adoc[`puback_packet`] + xref:reference:async_mqtt/v5/basic_puback_packet.adoc[`basic_puback_packet`] + xref:reference:async_mqtt/v5/pubrec_packet.adoc[`pubrec_packet`] + xref:reference:async_mqtt/v5/basic_pubrec_packet.adoc[`basic_pubrec_packet`] + xref:reference:async_mqtt/v5/pubrel_packet.adoc[`pubrel_packet`] + xref:reference:async_mqtt/v5/basic_pubrel_packet.adoc[`basic_pubrel_packet`] + xref:reference:async_mqtt/v5/pubcomp_packet.adoc[`pubcomp_packet`] + xref:reference:async_mqtt/v5/basic_pubcomp_packet.adoc[`basic_pubcomp_packet`] + xref:reference:async_mqtt/v5/pingreq_packet.adoc[`pingreq_packet`] + xref:reference:async_mqtt/v5/pingresp_packet.adoc[`pungresp_packet`] + xref:reference:async_mqtt/v5/auth_packet.adoc[`auth_packet`] +|=== -| **Types** -xref:reference:async_mqtt/error_code.adoc[`error_code`] +== Boost.Asio binding of the MQTT protocol machine -**Enums** +[width=100%] +|=== +1+| *MQTT Connections* | *Predefined Layers* -**common** +| -xref:reference:async_mqtt/mqtt_error.adoc[`mqtt_error`] +**Classes** -**v3.1.1** +xref:reference:async_mqtt/client.adoc[`client`] -xref:reference:async_mqtt/connect_return_code.adoc[`connect_return_code`] -xref:reference:async_mqtt/suback_return_code.adoc[`suback_return_code`] +xref:reference:async_mqtt/endpoint.adoc[`endpoint`] -**v5** +xref:reference:async_mqtt/basic_endpoint.adoc[`basic_endpoint`] -xref:reference:async_mqtt/connect_reason_code.adoc[`connect_reason_code`] -xref:reference:async_mqtt/disconnect_reason_code.adoc[`disconnect_reason_code`] -xref:reference:async_mqtt/suback_reason_code.adoc[`suback_reason_code`] -xref:reference:async_mqtt/unsuback_reason_code.adoc[`unsuback_reason_code`] -xref:reference:async_mqtt/puback_reason_code.adoc[`puback_reason_code`] -xref:reference:async_mqtt/pubrec_reason_code.adoc[`pubrec_reason_code`] -xref:reference:async_mqtt/pubrel_reason_code.adoc[`pubrel_reason_code`] -xref:reference:async_mqtt/pubcomp_reason_code.adoc[`pubcomp_reason_code`] -xref:reference:async_mqtt/auth_reason_code.adoc[`auth_reason_code`] +**Functions** + +xref:reference:async_mqtt/setup_log.adoc[`setup_log`] + +xref:reference:async_mqtt/logger.adoc[`log`] + +| + +**Types** + +xref:reference:async_mqtt/protocol/mqtt.adoc[`mqtt`] + +xref:reference:async_mqtt/protocol/mqtts.adoc[`mqtts`] + +xref:reference:async_mqtt/protocol/ws.adoc[`ws`] + +xref:reference:async_mqtt/protocol/wss.adoc[`wss`] + +**Classes** + +xref:reference:async_mqtt/layer_customize-03.adoc[`layer_customize(TCP)`] + +xref:reference:async_mqtt/layer_customize-08.adoc[`layer_customize(TLS)`] + +xref:reference:async_mqtt/layer_customize-02.adoc[`layer_customize(WS)`] |=== diff --git a/doc/mrdocs.yml b/doc/mrdocs.yml index 1eb4aa6b1..7bb237cfd 100644 --- a/doc/mrdocs.yml +++ b/doc/mrdocs.yml @@ -40,4 +40,4 @@ base-url: https://www.github.com/redboltz/async_mqtt/blob/main/include/ verbose: true multipage: true -cmake: '-D CMAKE_CXX_STANDARD=17 -D ASYNC_MQTT_USE_TLS=ON -D ASYNC_MQTT_USE_WS=ON -D ASYNC_MQTT_USE_LOG=ON -D ASYNC_MQTT_BUILD_TOOLS=ON"' +cmake: '-D CMAKE_CXX_STANDARD=17 -D ASYNC_MQTT_USE_TLS=ON -D ASYNC_MQTT_USE_WS=ON -D ASYNC_MQTT_USE_LOG=ON -D ASYNC_MQTT_BUILD_TOOLS=ON -D ASYNC_MQTT_MRDOCS=ON' diff --git a/include/async_mqtt/protocol/connection.hpp b/include/async_mqtt/protocol/connection.hpp index 47975e0d5..01beaeb34 100644 --- a/include/async_mqtt/protocol/connection.hpp +++ b/include/async_mqtt/protocol/connection.hpp @@ -25,6 +25,17 @@ class basic_connection { public: basic_connection(protocol_version ver); + virtual ~basic_connection() = default; + + /** + * @brief packet sending request. + * + * @li if the packet can't send @ref on_error is called. + * @li if the packet is @ref v3_1_1::pingreq_packet or @ref v5::pingreq_packet, + * and Keep Alive is set, then @ref on_timer_op for @ref timer_kind::pingreq_send is called. + * + * @param packet The packet to send. + */ template void send(Packet packet); @@ -32,9 +43,24 @@ class basic_connection { void recv(std::istream& is); + /** + * @brief notify timer is fired + * + * @ref on_close, + */ void notify_timer_fired(timer_kind kind); + /** + * @brief notify the connection is closed + * + * + * @li if the packet can't send @ref on_error is called. + * @li if the packet is @ref v3_1_1::pingreq_packet or @ref v5::pingreq_packet, + * and Keep Alive is set, then @ref on_timer_op for @ref timer_kind::pingreq_send is called. + * + * @param packet The packet to send. + */ void notify_closed(); @@ -178,13 +204,36 @@ class basic_connection { connection_status get_connection_status() const; +#if defined(ASYNC_MQTT_MRDOCS) +public: +#else // defined(ASYNC_MQTT_MRDOCS) private: +#endif // defined(ASYNC_MQTT_MRDOCS) + + /** + * @brief Handler for error notifications. + * + * This function is called when an error occurs in @ref send, @ref recv, @ref notify_timer_fired, + * @ref set_pingreq_send_interval, or @ref release_packet_id **before these functions return**. + * + * @param ec The error_code indicating the error. + */ virtual void on_error(error_code ec) = 0; + + /** + * @brief Handler for error notifications. + * + * This function is called when an error occurs in @ref send, @ref recv, @ref notify_timer_fired, + * @ref set_pingreq_send_interval, or @ref release_packet_id **before these functions return**. + * + * @param ec The error_code indicating the error. + */ virtual void on_send( basic_packet_variant packet, std::optional::type> release_packet_id_if_send_error = std::nullopt ) = 0; + virtual void on_packet_id_release( typename basic_packet_id_type::type packet_id ) = 0; diff --git a/include/async_mqtt/protocol/connection_fwd.hpp b/include/async_mqtt/protocol/connection_fwd.hpp index efedc658e..1d08b1384 100644 --- a/include/async_mqtt/protocol/connection_fwd.hpp +++ b/include/async_mqtt/protocol/connection_fwd.hpp @@ -17,14 +17,23 @@ namespace async_mqtt { * @ingroup connection * @brief MQTT connection * + * I/O independent MQTT protocol state machine. + * @li Manage connection status. + * @li Manage packet identifier. + * @li Manage Topic Alias. + * @li Manage resending packets on reconnection. + * @li Manage PINGREQ/PINGRESP timers. + * @li Manage automatic sending response packets. + * + * All requests and settings are implemented as synchronous functions. + * When caller's action is required, virtual function that is corresponding to + * the action is called **in the synchronous function before the function + * returns**. + * * #### Thread Safety * @li Distinct objects: Safe * @li Shared objects: Unsafe * - * #### Requirements - * @li Header: async_mqtt/protocol/connection.hpp - * @li Convenience header: async_mqtt/all.hpp - * * @tparam Role role for packet sendable checking * @tparam PacketIdBytes MQTT spec is 2. You can use `connection` for that. */ @@ -35,16 +44,12 @@ class basic_connection; * @ingroup connection * @related basic_connection * @brief Type alias of basic_connection (PacketIdBytes=2). - * This is for typical usecase. + * This is for typical usecase (e.g. MQTT client). * * #### Thread Safety * @li Distinct objects: Safe * @li Shared objects: Unsafe * - * #### Requirements - * @li Header: async_mqtt/connection.hpp - * @li Convenience header: async_mqtt/all.hpp - * * @tparam Role role for packet sendable checking */ template diff --git a/tool/CMakeLists.txt b/tool/CMakeLists.txt index 9b74ff46d..6e8187bde 100644 --- a/tool/CMakeLists.txt +++ b/tool/CMakeLists.txt @@ -25,6 +25,15 @@ foreach(source_file ${exec_PROGRAMS}) target_include_directories(${source_file_we} PRIVATE include) target_link_libraries(${source_file_we} async_mqtt_iface) + if(ASYNC_MQTT_MRDOCS) + target_compile_definitions( + ${source_file_we} + PUBLIC + ASYNC_MQTT_MRDOCS + ) + endif() + + if(WIN32 AND ASYNC_MQTT_USE_STATIC_OPENSSL) target_link_libraries(${source_file_we} Crypt32) endif()