stinger-ipc 0.0.10__py3-none-any.whl → 0.0.26__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/METADATA +3 -2
  2. {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/RECORD +35 -29
  3. stingeripc/asyncapi.py +3 -0
  4. stingeripc/components.py +61 -10
  5. stingeripc/schema/schema.yaml +240 -0
  6. stingeripc/templates/cpp/examples/client_main.cpp.jinja2 +18 -0
  7. stingeripc/templates/cpp/include/broker.hpp.jinja2 +18 -10
  8. stingeripc/templates/cpp/include/client.hpp.jinja2 +80 -11
  9. stingeripc/templates/cpp/include/ibrokerconnection.hpp.jinja2 +24 -4
  10. stingeripc/templates/cpp/include/property_structs.hpp.jinja2 +40 -2
  11. stingeripc/templates/cpp/include/server.hpp.jinja2 +5 -1
  12. stingeripc/templates/cpp/include/structs.hpp.jinja2 +2 -0
  13. stingeripc/templates/cpp/partials/args.jinja2 +11 -0
  14. stingeripc/templates/cpp/partials/deserialize.jinja2 +42 -0
  15. stingeripc/templates/cpp/partials/serialize.jinja2 +18 -0
  16. stingeripc/templates/cpp/src/broker.cpp.jinja2 +46 -24
  17. stingeripc/templates/cpp/src/client.cpp.jinja2 +111 -38
  18. stingeripc/templates/cpp/src/property_structs.cpp.jinja2 +25 -0
  19. stingeripc/templates/cpp/src/server.cpp.jinja2 +17 -25
  20. stingeripc/templates/cpp/src/structs.cpp.jinja2 +13 -0
  21. stingeripc/templates/html/app.js.jinja2 +130 -29
  22. stingeripc/templates/html/index.html.jinja2 +96 -8
  23. stingeripc/templates/html/styles.css.jinja2 +135 -0
  24. stingeripc/templates/markdown/index.md.jinja2 +151 -10
  25. stingeripc/templates/python/server.py.jinja2 +138 -18
  26. stingeripc/templates/rust/client/src/lib.rs.jinja2 +24 -5
  27. stingeripc/templates/rust/server/examples/server.rs.jinja2 +59 -23
  28. stingeripc/templates/rust/server/src/lib.rs.jinja2 +72 -51
  29. stingeripc/tools/cli.py +33 -7
  30. stingeripc/tools/cpp_generator.py +90 -0
  31. stingeripc/tools/rust_generator.py +1 -1
  32. stingeripc/templates/rust/payloads/src/handler.rs.jinja2 +0 -0
  33. {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/WHEEL +0 -0
  34. {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/entry_points.txt +0 -0
  35. {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/licenses/LICENSE +0 -0
  36. {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,7 @@ on the next generation.
4
4
 
5
5
  It contains enumerations used by the {{stinger.name}} interface.
6
6
  */
7
-
7
+ {% import 'partials/args.jinja2' as ar %}
8
8
 
9
9
  #pragma once
10
10
 
@@ -16,10 +16,13 @@ It contains enumerations used by the {{stinger.name}} interface.
16
16
  #include <mutex>
17
17
  #include <rapidjson/document.h>
18
18
  #include <boost/uuid/uuid.hpp>
19
-
19
+ #include <boost/optional.hpp>
20
20
  #include "ibrokerconnection.hpp"
21
21
  #include "{{stinger.cpp.enum_header_file}}"
22
22
  #include "return_types.hpp"
23
+
24
+ {%macro prop_value_type(prop) -%}{%if prop.arg_list|length > 1%}struct {%endif%}{{prop.name | UpperCamelCase}}Property{%endmacro%}
25
+
23
26
  {%if stinger.properties | length > 0 %}#include "{{stinger.cpp.property_struct_header_file}}"{%endif%}
24
27
 
25
28
  class {{stinger.cpp.client_class_name}} {
@@ -34,28 +37,94 @@ public:
34
37
  {{stinger.cpp.client_class_name}}(std::shared_ptr<IBrokerConnection> broker);
35
38
 
36
39
  virtual ~{{stinger.cpp.client_class_name}}() = default;
37
-
40
+ {%if stinger.signals|length > 0 -%}
41
+ // ------------------ SIGNALS --------------------
38
42
  {%for sig_name, sig in stinger.signals.items()%}
39
43
  // Register a callback for the `{{sig_name}}` signal.
40
44
  // The provided method will be called whenever a `{{sig_name}}` is received.
41
45
  void register{{sig_name | UpperCamelCase}}Callback(const std::function<void({%for arg in sig.arg_list%}{{arg.cpp_type}}{%if not loop.last%}, {%endif%}{%endfor%})>& cb);
42
46
  {%endfor%}
43
-
47
+ {%endif%}
48
+ {%if stinger.methods|length > 0 -%}
49
+ // ------------------- METHODS --------------------
44
50
  {%for method_name, method in stinger.methods.items()%}
45
51
  // Calls the `{{method_name}}` method.
46
52
  // Returns a future. When that future resolves, it will have the returned value.
47
53
  boost::future<{{method.return_value_cpp_class}}> {{method_name | camelCase}}({%for arg in method.arg_list%}{{arg.cpp_type}} {{arg.name}}{%if not loop.last%}, {%endif%}{%endfor%});
48
54
  {%endfor%}
49
- private:
55
+ {%endif%}
56
+ {%if stinger.properties|length > 0 -%}
57
+ // ---------------- PROPERTIES ------------------
58
+ {%for prop_name, prop in stinger.properties.items()%}
59
+ // ---{{prop_name}} Property---
60
+
61
+ // Gets the latest value of the `{{prop_name}}` property, if one has been received.
62
+ // If no value has been received yet, an empty optional is returned.
63
+ boost::optional<{{prop_value_type(prop)}}> get{{prop_name | UpperCamelCase}}Property() const;
64
+
65
+ // Add a callback that will be called whenever the `{{prop_name}}` property is updated.
66
+ // The provided method will be called whenever a new value for the `{{prop_name}}` property is received.
67
+ void register{{prop_name | UpperCamelCase}}PropertyCallback(const std::function<void({{ar.declarationParams(prop.arg_list)}})>& cb);
68
+ {% if not prop.read_only %}
69
+ boost::future<bool> update{{prop_name | UpperCamelCase}}Property({{ar.declarationParams(prop.arg_list)}}) const;
70
+ {%endif%}
71
+ {%endfor%}
72
+ {%endif%}
73
+ private:
74
+ // Pointer to the broker connection.
50
75
  std::shared_ptr<IBrokerConnection> _broker;
51
- void _receiveMessage(const std::string& topic, const std::string& payload, const boost::optional<std::string> optCorrelationId, const boost::optional<MethodResultCode> optResultCode);
52
- {%-for method_name, method in stinger.methods.items()%}
53
- std::map<boost::uuids::uuid, boost::promise<{{method.return_value_cpp_class}}>> _pending{{method_name|UpperCamelCase}}MethodCalls;
54
- {%-endfor%}
76
+
77
+ // Internal method for receiving messages from the broker.
78
+ void _receiveMessage(
79
+ const std::string& topic,
80
+ const std::string& payload,
81
+ const MqttProperties& mqttProps);
82
+ {%if stinger.signals|length > 0 %}
83
+ // ------------------ SIGNALS --------------------
55
84
  {%for sig_name, sig in stinger.signals.items()%}
56
- std::function<void({%for arg in sig.arg_list%}{{arg.cpp_type}}{%if not loop.last%}, {%endif%}{%endfor%})> _{{sig_name | camelCase}}Callback;
85
+ // List of callbacks to be called whenever the `{{sig_name}}` signal is received.
86
+ std::vector<std::function<void({%for arg in sig.arg_list%}{{arg.cpp_type}}{%if not loop.last%}, {%endif%}{%endfor%})>> _{{sig_name | lowerCamelCase}}SignalCallbacks;
87
+ std::mutex _{{sig_name | lowerCamelCase}}SignalCallbacksMutex;
88
+
89
+ // MQTT Subscription ID for `{{sig_name}}` signal receptions.
90
+ int _{{sig_name | lowerCamelCase}}SignalSubscriptionId;
57
91
  {%endfor%}
92
+ {%endif%}
93
+ {%if stinger.methods|length > 0 -%}
94
+ // ------------------- METHODS --------------------
95
+
58
96
  {%-for method_name, method in stinger.methods.items()%}
59
- void _handle{{method_name|UpperCamelCase}}Response(const std::string& topic, const std::string& payload, const std::string& optCorrelationId);
97
+ // Holds promises for pending `{{method_name}}` method calls.
98
+ std::map<boost::uuids::uuid, boost::promise<{{method.return_value_cpp_class}}>> _pending{{method_name|UpperCamelCase}}MethodCalls;
99
+
100
+ // This is called internally to process responses to `{{method_name}}` method calls.
101
+ void _handle{{method_name|UpperCamelCase}}Response(const std::string& topic, const std::string& payload, const std::string& correlationId);
60
102
  {%-endfor%}
103
+ {%endif%}
104
+ {%if stinger.properties|length > 0 -%}
105
+ // ---------------- PROPERTIES ------------------
106
+ {%for prop_name, prop in stinger.properties.items()%}
107
+
108
+ // ---{{prop_name}} Property---
109
+
110
+ // Last received value{%if prop.arg_list|length > 1%}s{%endif%} for the `{{prop_name}}` property.
111
+ boost::optional<{{prop_value_type(prop)}}> _{{prop_name | lowerCamelCase}}Property;
112
+
113
+ // This is the property version of the last received `{{prop_name}}` property update.
114
+ int _last{{prop_name | UpperCamelCase}}PropertyVersion = -1;
115
+
116
+ // Mutex for protecting access to the `{{prop_name}}` property and its version.
117
+ mutable std::mutex _{{prop_name | lowerCamelCase}}PropertyMutex;
118
+
119
+ // MQTT Subscription ID for `{{prop_name}}` property updates.
120
+ int _{{prop_name | lowerCamelCase}}PropertySubscriptionId;
121
+
122
+ // Method for parsing a JSON payload that updates the `{{prop_name}}` property.
123
+ void _receive{{prop_name | UpperCamelCase}}PropertyUpdate(const std::string& topic, const std::string& payload, boost::optional<int> optPropertyVersion);
124
+
125
+ // Callbacks registered for changes to the `{{prop_name}}` property.
126
+ std::vector<std::function<void({{ar.declarationParams(prop.arg_list)}})>> _{{prop_name | lowerCamelCase}}PropertyCallbacks;
127
+ std::mutex _{{prop_name | lowerCamelCase}}PropertyCallbacksMutex;
128
+ {%endfor%}
129
+ {%endif%}
61
130
  };
@@ -22,23 +22,43 @@ enum class MethodResultCode
22
22
  {%-endfor%}
23
23
  };
24
24
 
25
+ struct MqttProperties
26
+ {
27
+ MqttProperties()
28
+ : correlationId(boost::none),
29
+ responseTopic(boost::none),
30
+ resultCode(boost::none),
31
+ subscriptionId(boost::none),
32
+ propertyVersion(boost::none)
33
+ {}
34
+ boost::optional<std::string> correlationId;
35
+ boost::optional<std::string> responseTopic;
36
+ boost::optional<MethodResultCode> resultCode;
37
+ boost::optional<int> subscriptionId;
38
+ boost::optional<int> propertyVersion;
39
+ };
40
+
25
41
  class IBrokerConnection
26
42
  {
27
43
  public:
28
44
  /*! Publish to a topic.
29
45
  * Implementations should queue up messages when not connected.
30
46
  */
31
- virtual boost::future<bool> Publish(const std::string& topic, const std::string& payload, unsigned qos, bool retain, boost::optional<std::string> optCorrelationId, boost::optional<std::string> optResponseTopic, boost::optional<MethodResultCode> optResultCode) = 0;
32
-
47
+ virtual boost::future<bool> Publish(const std::string& topic, const std::string& payload, unsigned qos, bool retain, const MqttProperties& mqttProps) = 0;
48
+
33
49
  /*! Subscribe to a topic.
34
50
  * Implementation should queue up subscriptions when not connected.
35
51
  */
36
- virtual void Subscribe(const std::string& topic, int qos) = 0;
52
+ virtual int Subscribe(const std::string& topic, int qos) = 0;
37
53
 
38
54
  /*! Provide a callback to be called on an incoming message.
39
55
  * Implementation should accept this at any time, even when not connected.
40
56
  */
41
- virtual void AddMessageCallback(const std::function<void(const std::string&, const std::string&, const boost::optional<std::string>, const boost::optional<std::string>, const boost::optional<MethodResultCode>)>& cb) = 0;
57
+ virtual void AddMessageCallback(const std::function<void
58
+ (const std::string&,
59
+ const std::string&,
60
+ const MqttProperties&
61
+ )>& cb) = 0;
42
62
 
43
63
  /*! Utility for matching topics.
44
64
  * This probably should be a wrapper around `mosquitto_topic_matches_sub` or similar
@@ -5,6 +5,44 @@ on the next generation.
5
5
  It contains enumerations used by the {{stinger.name}} interface.
6
6
  */
7
7
 
8
- {%for prop_name, prop in stinger.properties.items() %}
9
- // {{prop}}
8
+
9
+ #pragma once
10
+ #include <string>
11
+ #include <rapidjson/document.h>
12
+ #include "{{stinger.cpp.enum_header_file}}"
13
+ #include "structs.hpp"
14
+
15
+ {%for prop_name, prop in stinger.properties.items()%}
16
+ {%- if prop.arg_list| length > 1 %}
17
+ {%-if prop.documentation %}/**
18
+ {{prop.documentation |commentblock(' * ')}}
19
+ */{%endif%}
20
+ struct {{prop.name | UpperCamelCase}}Property {
21
+ static {{prop.name | UpperCamelCase}}Property FromRapidJsonObject(const rapidjson::Value& jsonObj);
22
+ rapidjson::Value ToRapidJsonObject(rapidjson::Document::AllocatorType& allocator) const;
23
+ {%-for arg in prop.arg_list %}
24
+ {%if arg.arg_type.name.lower() == "struct"-%}
25
+ {{arg.cpp_type}} {{arg.name}};{%if arg.description %} ///< {{arg.description}}{%endif%}
26
+ {%-else-%}
27
+ {{arg.cpp_temp_type}} {{arg.name }};{%if arg.description %} ///< {{arg.description}}{%endif%}
28
+ {%-endif-%}
29
+ {%endfor-%}
30
+ {# end for arg #}
31
+ };
32
+ {%elif prop.arg_list| length == 1 %}
33
+ {% set arg = prop.arg_list[0] %}
34
+ {%if prop.documentation %}/**
35
+ * The `{{prop.name}}` property contains a single field:
36
+ {%-if arg.arg_type.name.lower() == "struct"%}
37
+ * {{arg.cpp_type}} {{arg.name}};{%if arg.description %} ///< {{arg.description}}{%endif%}
38
+ {%-else%}
39
+ * {{arg.cpp_temp_type}} {{arg.name }};{%if arg.description %} ///< {{arg.description}}{%endif%}
40
+ {%-endif%}
41
+ *
42
+ * Because there is only one field, no outer-structure is needed.
43
+ *
44
+ {{prop.documentation |commentblock(' * ')}}
45
+ */{%endif%}
46
+ typedef {%if arg.arg_type.name.lower() == "struct"%}{{arg.cpp_type}}{%else%}{{arg.cpp_temp_type}}{%endif%} {{prop.name | UpperCamelCase}}Property;
47
+ {%endif%}
10
48
  {%endfor%}
@@ -41,7 +41,11 @@ public:
41
41
  {%endfor%}
42
42
  private:
43
43
  std::shared_ptr<IBrokerConnection> _broker;
44
- void _receiveMessage(const std::string& topic, const std::string& payload, const boost::optional<std::string> optCorrelationId, const boost::optional<std::string> optResponseTopic);
44
+ void _receiveMessage(
45
+ const std::string& topic,
46
+ const std::string& payload,
47
+ const MqttProperties& mqttProps
48
+ );
45
49
 
46
50
  {%for method_name, method in stinger.methods.items()%}
47
51
  void _call{{method_name | UpperCamelCase}}Handler(const std::string& topic, const rapidjson::Document& doc, boost::optional<std::string> clientId, boost::optional<std::string> correlationId) const;
@@ -8,10 +8,12 @@ It contains enumerations used by the {{stinger.name}} interface.
8
8
  #pragma once
9
9
  #include <string>
10
10
  #include <boost/optional.hpp>
11
+ #include <rapidjson/document.h>
11
12
  #include "{{stinger.cpp.enum_header_file}}"
12
13
 
13
14
  {%for istruct_name, istruct in stinger.structs.items() %}
14
15
  struct {{istruct_name | UpperCamelCase }} {
16
+ static {{istruct_name | UpperCamelCase }} FromRapidJsonObject(const rapidjson::Value& jsonObj);
15
17
  {%-for value in istruct.members %}
16
18
  {{value.cpp_temp_type}} {{value.name}};
17
19
  {%-endfor%}
@@ -0,0 +1,11 @@
1
+ {%macro methodParams(arglist)%}
2
+ {%- for arg in arglist -%}
3
+ {{ arg.cpp_type }} {{ arg.name }}{% if not loop.last %}, {% endif %}
4
+ {%- endfor -%}
5
+ {%-endmacro%}
6
+
7
+ {%macro declarationParams(arglist)%}
8
+ {%- for arg in arglist -%}
9
+ {{ arg.cpp_type }}{% if not loop.last %}, {% endif %}
10
+ {%- endfor -%}
11
+ {%-endmacro%}
@@ -0,0 +1,42 @@
1
+ {%macro deserialize_value(target, arg)%}
2
+ {%-if arg.arg_type.name.lower() == 'primitive'%}
3
+ {{target}} = itr->value.Get{{arg.cpp_rapidjson_type}}();
4
+ {%-elif arg.arg_type.name.lower() == 'enum'%}
5
+ {{target}} = static_cast<{{arg.cpp_data_type}}>(itr->value.Get{{arg.cpp_rapidjson_type}}());
6
+ {%-elif arg.arg_type.name.lower() == 'struct'%}
7
+ {{target}} = {{arg.cpp_type}}::FromRapidJsonObject(itr->value);
8
+ {%endif%}
9
+ {%endmacro%}
10
+
11
+
12
+ {%-macro deserialize(target, arglist, json_obj)%}
13
+ {%-if arglist|length == 1%}
14
+ {%-set arg = arglist[0]%}
15
+ rapidjson::Value::ConstMemberIterator itr = {{json_obj}}.FindMember("{{arg.name}}");
16
+ if (itr != {{json_obj}}.MemberEnd() && itr->value.Is{{arg.cpp_rapidjson_type}}()) {
17
+ {{deserialize_value(target, arg)}}
18
+ } else {
19
+ {%-if arg.optional%}
20
+ {{target}} = boost::none;
21
+ {%-else%}
22
+ throw std::runtime_error("Received payload doesn't have required value/type");
23
+ {%-endif%}
24
+ }
25
+ {%-elif arglist|length > 1%}
26
+ {%-for arg in arglist%}
27
+ {%-set arg_target %}{{target}}.{{arg.name}}{%endset%}
28
+ { // Scoping
29
+ rapidjson::Value::ConstMemberIterator itr = {{json_obj}}.FindMember("{{arg.name}}");
30
+ if (itr != {{json_obj}}.MemberEnd() && itr->value.Is{{arg.cpp_rapidjson_type}}()) {
31
+ {{deserialize_value(arg_target, arg) | indent(8)}}
32
+ } else {
33
+ {%-if arg.optional%}
34
+ {{arg_target}} = boost::none;
35
+ {%-else%}
36
+ throw std::runtime_error("Received payload doesn't have required value/type");
37
+ {%endif%}
38
+ }
39
+ }
40
+ {%-endfor%}
41
+ {%-endif%}
42
+ {%-endmacro%}
@@ -0,0 +1,18 @@
1
+
2
+
3
+ {%-macro addToValue(value, arg, allocator)%}
4
+ {%-if arg.arg_type.name.lower() == 'primitive'%}
5
+ {%-if arg.optional %}if ({{arg.name}}) {%endif-%}
6
+ {%-if arg.type.name.lower() == 'string'%}
7
+ { {%if not arg.optional %}// restrict scope{%endif%}
8
+ rapidjson::Value tempStringValue;
9
+ tempStringValue.SetString({{arg.name}}{%-if arg.optional %}->{%else%}.{%endif-%}c_str(), {{arg.name}}{%-if arg.optional %}->{%else%}.{%endif%}size(), {{allocator}});
10
+ {{value}}.AddMember("{{arg.name}}", tempStringValue, {{allocator}});
11
+ }
12
+ {%-else%}
13
+ {{value}}.AddMember("{{arg.name}}", {%if arg.optional %}*{%endif-%}{{arg.name}}, {{allocator}});
14
+ {%-endif%}
15
+ {%elif arg.arg_type.name.lower() == 'enum'%}
16
+ {{value}}.AddMember("{{arg.name}}", static_cast<int>({%if arg.optional %}*{%endif-%}{{arg.name}}), {{allocator}});
17
+ {%-endif-%}
18
+ {%endmacro%}
@@ -32,8 +32,11 @@ MqttConnection::MqttConnection(const std::string& host, int port, const std::str
32
32
  while(!thisClient->_subscriptions.empty())
33
33
  {
34
34
  auto sub = thisClient->_subscriptions.front();
35
- cout << "Subscribing to " << sub._topic << endl;
36
- mosquitto_subscribe(mosq, NULL, sub._topic.c_str(), sub._qos);
35
+ cout << "Subscribing to " << sub.topic << endl;
36
+ mosquitto_property *propList = NULL;
37
+ mosquitto_property_add_int16(&propList, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, sub.subscriptionId);
38
+ int rc = mosquitto_subscribe_v5(mosq, NULL, sub.topic.c_str(), sub.qos, 0, propList);
39
+ mosquitto_property_free_all(&propList);
37
40
  thisClient->_subscriptions.pop();
38
41
  }
39
42
  while(!thisClient->_msgQueue.empty())
@@ -42,6 +45,7 @@ MqttConnection::MqttConnection(const std::string& host, int port, const std::str
42
45
  cout << "Sending message to " << msg._topic << endl;
43
46
  int mid;
44
47
  mosquitto_property *propList = NULL;
48
+ mosquitto_property_add_string(&propList, MQTT_PROP_CONTENT_TYPE, "application/json");
45
49
  if (msg._optCorrelationId)
46
50
  {
47
51
  mosquitto_property_add_string(&propList, MQTT_PROP_CORRELATION_DATA, msg._optCorrelationId->c_str());
@@ -63,9 +67,7 @@ MqttConnection::MqttConnection(const std::string& host, int port, const std::str
63
67
  cout << "Fowarding message (" << mmsg->topic << ") to " << thisClient->_messageCallbacks.size() << " callbacks" << endl;
64
68
  std::string topic(mmsg->topic);
65
69
  std::string payload(static_cast<char*>(mmsg->payload), mmsg->payloadlen);
66
- boost::optional<std::string> optCorrelationId;
67
- boost::optional<std::string> optResponseTopic;
68
- boost::optional<MethodResultCode> optResultCode;
70
+ MqttProperties mqttProps;
69
71
  const mosquitto_property *prop;
70
72
  for (prop = props; prop != NULL; prop = mosquitto_property_next(prop))
71
73
  {
@@ -75,7 +77,8 @@ MqttConnection::MqttConnection(const std::string& host, int port, const std::str
75
77
  uint16_t correlation_data_len;
76
78
  if (mosquitto_property_read_binary(prop, MQTT_PROP_CORRELATION_DATA, &correlation_data, &correlation_data_len, false))
77
79
  {
78
- optCorrelationId = std::string(static_cast<char*>(correlation_data), correlation_data_len);
80
+ mqttProps.correlationId = std::string(static_cast<char*>(correlation_data), correlation_data_len);
81
+ free(correlation_data);
79
82
  }
80
83
  }
81
84
  else if (mosquitto_property_identifier(prop) == MQTT_PROP_RESPONSE_TOPIC)
@@ -83,7 +86,7 @@ MqttConnection::MqttConnection(const std::string& host, int port, const std::str
83
86
  char *responseTopic = NULL;
84
87
  if (mosquitto_property_read_string(prop, MQTT_PROP_RESPONSE_TOPIC, &responseTopic, false))
85
88
  {
86
- optResponseTopic = std::string(responseTopic);
89
+ mqttProps.responseTopic = std::string(responseTopic);
87
90
  free(responseTopic);
88
91
  }
89
92
  }
@@ -96,17 +99,30 @@ MqttConnection::MqttConnection(const std::string& host, int port, const std::str
96
99
  if (strcmp(name, "ReturnValue") == 0)
97
100
  {
98
101
  int returnValueInt = boost::lexical_cast<int>(value);
99
- optResultCode = static_cast<MethodResultCode>(returnValueInt);
102
+ mqttProps.resultCode = static_cast<MethodResultCode>(returnValueInt);
103
+ }
104
+ else if (strcmp(name, "PropertyVersion") == 0)
105
+ {
106
+ int propertyVersionInt = boost::lexical_cast<int>(value);
107
+ mqttProps.propertyVersion = propertyVersionInt;
100
108
  }
101
109
  free(name);
102
110
  free(value);
103
111
  }
104
112
  }
113
+ else if (mosquitto_property_identifier(prop) == MQTT_PROP_SUBSCRIPTION_IDENTIFIER)
114
+ {
115
+ uint32_t subscriptionId;
116
+ if (mosquitto_property_read_int32(prop, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, &subscriptionId, false))
117
+ {
118
+ mqttProps.subscriptionId = static_cast<int>(subscriptionId);
119
+ }
120
+ }
105
121
  }
106
122
  for (auto& cb : thisClient->_messageCallbacks)
107
123
  {
108
124
  cout << "Calling callback" << endl;
109
- cb(topic, payload, optCorrelationId, optResponseTopic, optResultCode);
125
+ cb(topic, payload, mqttProps);
110
126
  }
111
127
  });
112
128
 
@@ -146,24 +162,22 @@ boost::future<bool> MqttConnection::Publish(
146
162
  const std::string& payload,
147
163
  unsigned qos,
148
164
  bool retain,
149
- boost::optional<std::string> optCorrelationId,
150
- boost::optional<std::string> optResponseTopic,
151
- boost::optional<MethodResultCode> optResultCode)
165
+ const MqttProperties& mqttProps)
152
166
  {
153
167
  int mid;
154
168
  mosquitto_property *propList = NULL;
155
169
  mosquitto_property_add_string(&propList, MQTT_PROP_CONTENT_TYPE, "application/json");
156
- if (optCorrelationId)
170
+ if (mqttProps.correlationId)
157
171
  {
158
- mosquitto_property_add_binary(&propList, MQTT_PROP_CORRELATION_DATA, (void*)optCorrelationId->c_str(), optCorrelationId->size());
172
+ mosquitto_property_add_binary(&propList, MQTT_PROP_CORRELATION_DATA, (void*)mqttProps.correlationId->c_str(), mqttProps.correlationId->size());
159
173
  }
160
- if (optResponseTopic)
174
+ if (mqttProps.responseTopic)
161
175
  {
162
- mosquitto_property_add_string(&propList, MQTT_PROP_RESPONSE_TOPIC, optResponseTopic->c_str());
176
+ mosquitto_property_add_string(&propList, MQTT_PROP_RESPONSE_TOPIC, mqttProps.responseTopic->c_str());
163
177
  }
164
- if (optResultCode)
178
+ if (mqttProps.resultCode)
165
179
  {
166
- std::string resultCodeStr = std::to_string(static_cast<int>(*optResultCode));
180
+ std::string resultCodeStr = std::to_string(static_cast<int>(*mqttProps.resultCode));
167
181
  mosquitto_property_add_string_pair(&propList, MQTT_PROP_USER_PROPERTY, "ReturnValue", resultCodeStr.c_str());
168
182
  }
169
183
  int rc = mosquitto_publish_v5(_mosq, &mid, topic.c_str(), payload.size(), payload.c_str(), qos, retain, propList);
@@ -171,7 +185,7 @@ boost::future<bool> MqttConnection::Publish(
171
185
  if (rc == MOSQ_ERR_NO_CONN)
172
186
  {
173
187
  std::cout << "Delayed published queued to: " << topic << std::endl;
174
- MqttConnection::MqttMessage msg(topic, payload, qos, retain, optCorrelationId, optResponseTopic);
188
+ MqttConnection::MqttMessage msg(topic, payload, qos, retain, mqttProps.correlationId, mqttProps.responseTopic);
175
189
  auto future = msg.getFuture();
176
190
  boost::mutex::scoped_lock lock(_mutex);
177
191
  _msgQueue.push(std::move(msg));
@@ -193,13 +207,16 @@ boost::future<bool> MqttConnection::Publish(
193
207
  throw std::runtime_error("Unhandled rc");
194
208
  }
195
209
 
196
- void MqttConnection::Subscribe(const std::string& topic, int qos)
210
+ int MqttConnection::Subscribe(const std::string& topic, int qos)
197
211
  {
198
- std::cout << "Subscribing to " << topic << endl;
199
- int rc = mosquitto_subscribe(_mosq, NULL, topic.c_str(), qos);
212
+ int subscriptionId = _nextSubscriptionId++;
213
+ mosquitto_property *propList = NULL;
214
+ mosquitto_property_add_int16(&propList, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, subscriptionId);
215
+ int rc = mosquitto_subscribe_v5(_mosq, NULL, topic.c_str(), qos, 0, propList);
216
+ mosquitto_property_free_all(&propList);
200
217
  if (rc == MOSQ_ERR_NO_CONN)
201
218
  {
202
- MqttConnection::MqttSubscription sub(topic, qos);
219
+ MqttConnection::MqttSubscription sub(topic, qos, subscriptionId);
203
220
  boost::mutex::scoped_lock lock(_mutex);
204
221
  _subscriptions.push(sub);
205
222
  }
@@ -207,10 +224,15 @@ void MqttConnection::Subscribe(const std::string& topic, int qos)
207
224
  {
208
225
  std::cout << "Subscribed to " << topic << std::endl;
209
226
  }
227
+ return subscriptionId;
210
228
  }
211
229
 
212
230
  void MqttConnection::AddMessageCallback(
213
- const std::function<void(const std::string&, const std::string&, const boost::optional<std::string>, const boost::optional<std::string>, const boost::optional<MethodResultCode>)>& cb)
231
+ const std::function<void(
232
+ const std::string&,
233
+ const std::string&,
234
+ const MqttProperties&
235
+ )>& cb)
214
236
  {
215
237
  boost::mutex::scoped_lock lock(_mutex);
216
238
  _messageCallbacks.push_back(cb);