stinger-ipc 0.0.10__py3-none-any.whl → 0.0.25__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.
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.25.dist-info}/METADATA +3 -2
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.25.dist-info}/RECORD +34 -28
- stingeripc/asyncapi.py +3 -0
- stingeripc/components.py +61 -10
- stingeripc/schema/schema.yaml +240 -0
- stingeripc/templates/cpp/examples/client_main.cpp.jinja2 +18 -0
- stingeripc/templates/cpp/include/broker.hpp.jinja2 +18 -10
- stingeripc/templates/cpp/include/client.hpp.jinja2 +80 -11
- stingeripc/templates/cpp/include/ibrokerconnection.hpp.jinja2 +24 -4
- stingeripc/templates/cpp/include/property_structs.hpp.jinja2 +40 -2
- stingeripc/templates/cpp/include/server.hpp.jinja2 +5 -1
- stingeripc/templates/cpp/include/structs.hpp.jinja2 +2 -0
- stingeripc/templates/cpp/partials/args.jinja2 +11 -0
- stingeripc/templates/cpp/partials/deserialize.jinja2 +42 -0
- stingeripc/templates/cpp/partials/serialize.jinja2 +18 -0
- stingeripc/templates/cpp/src/broker.cpp.jinja2 +46 -24
- stingeripc/templates/cpp/src/client.cpp.jinja2 +109 -36
- stingeripc/templates/cpp/src/property_structs.cpp.jinja2 +25 -0
- stingeripc/templates/cpp/src/server.cpp.jinja2 +17 -25
- stingeripc/templates/cpp/src/structs.cpp.jinja2 +13 -0
- stingeripc/templates/html/app.js.jinja2 +130 -29
- stingeripc/templates/html/index.html.jinja2 +96 -8
- stingeripc/templates/html/styles.css.jinja2 +135 -0
- stingeripc/templates/markdown/index.md.jinja2 +151 -10
- stingeripc/templates/rust/client/src/lib.rs.jinja2 +24 -5
- stingeripc/templates/rust/server/examples/server.rs.jinja2 +59 -23
- stingeripc/templates/rust/server/src/lib.rs.jinja2 +72 -51
- stingeripc/tools/cli.py +33 -7
- stingeripc/tools/cpp_generator.py +90 -0
- stingeripc/tools/rust_generator.py +1 -1
- stingeripc/templates/rust/payloads/src/handler.rs.jinja2 +0 -0
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.25.dist-info}/WHEEL +0 -0
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.25.dist-info}/entry_points.txt +0 -0
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.25.dist-info}/licenses/LICENSE +0 -0
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.25.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
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
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
|
-
|
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,
|
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
|
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
|
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
|
-
|
9
|
-
|
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(
|
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.
|
36
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
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
|
-
|
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 (
|
170
|
+
if (mqttProps.correlationId)
|
157
171
|
{
|
158
|
-
mosquitto_property_add_binary(&propList, MQTT_PROP_CORRELATION_DATA, (void*)
|
172
|
+
mosquitto_property_add_binary(&propList, MQTT_PROP_CORRELATION_DATA, (void*)mqttProps.correlationId->c_str(), mqttProps.correlationId->size());
|
159
173
|
}
|
160
|
-
if (
|
174
|
+
if (mqttProps.responseTopic)
|
161
175
|
{
|
162
|
-
mosquitto_property_add_string(&propList, MQTT_PROP_RESPONSE_TOPIC,
|
176
|
+
mosquitto_property_add_string(&propList, MQTT_PROP_RESPONSE_TOPIC, mqttProps.responseTopic->c_str());
|
163
177
|
}
|
164
|
-
if (
|
178
|
+
if (mqttProps.resultCode)
|
165
179
|
{
|
166
|
-
std::string resultCodeStr = std::to_string(static_cast<int>(*
|
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,
|
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
|
-
|
210
|
+
int MqttConnection::Subscribe(const std::string& topic, int qos)
|
197
211
|
{
|
198
|
-
|
199
|
-
|
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(
|
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);
|