stinger-ipc 0.0.1__py3-none-any.whl → 0.0.3__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 (52) hide show
  1. {stinger_ipc-0.0.1.dist-info → stinger_ipc-0.0.3.dist-info}/METADATA +4 -4
  2. stinger_ipc-0.0.3.dist-info/RECORD +52 -0
  3. stinger_ipc-0.0.3.dist-info/entry_points.txt +4 -0
  4. stingeripc/args.py +6 -5
  5. stingeripc/asyncapi.py +249 -167
  6. stingeripc/components.py +301 -136
  7. stingeripc/connection.py +2 -1
  8. stingeripc/exceptions.py +1 -2
  9. stingeripc/interface.py +8 -4
  10. stingeripc/lang_symb.py +68 -0
  11. stingeripc/templates/cpp/CMakeLists.txt.jinja2 +26 -0
  12. stingeripc/templates/cpp/examples/client_main.cpp.jinja2 +47 -0
  13. stingeripc/templates/cpp/examples/server_main.cpp.jinja2 +35 -0
  14. stingeripc/templates/cpp/include/broker.hpp.jinja2 +132 -0
  15. stingeripc/templates/cpp/include/client.hpp.jinja2 +53 -0
  16. stingeripc/templates/cpp/include/enums.hpp.jinja2 +17 -0
  17. stingeripc/templates/cpp/include/ibrokerconnection.hpp.jinja2 +42 -0
  18. stingeripc/templates/cpp/include/return_types.hpp.jinja2 +14 -0
  19. stingeripc/templates/cpp/include/server.hpp.jinja2 +44 -0
  20. stingeripc/templates/cpp/include/structs.hpp.jinja2 +13 -0
  21. stingeripc/templates/cpp/src/broker.cpp.jinja2 +243 -0
  22. stingeripc/templates/cpp/src/client.cpp.jinja2 +202 -0
  23. stingeripc/templates/cpp/src/server.cpp.jinja2 +170 -0
  24. stingeripc/templates/markdown/index.md.jinja2 +142 -0
  25. stingeripc/templates/python/__init__.py.jinja2 +1 -0
  26. stingeripc/templates/python/client.py.jinja2 +309 -0
  27. stingeripc/templates/python/connection.py.jinja2 +164 -0
  28. stingeripc/templates/python/interface_types.py.jinja2 +48 -0
  29. stingeripc/templates/python/method_codes.py.jinja2 +30 -0
  30. stingeripc/templates/python/pyproject.toml.jinja2 +9 -0
  31. stingeripc/templates/python/server.py.jinja2 +214 -0
  32. stingeripc/templates/rust/Cargo.toml.jinja2 +4 -0
  33. stingeripc/templates/rust/client/Cargo.toml.jinja2 +25 -0
  34. stingeripc/templates/rust/client/examples/client.rs.jinja2 +53 -0
  35. stingeripc/templates/rust/client/src/lib.rs.jinja2 +247 -0
  36. stingeripc/templates/rust/connection/Cargo.toml.jinja2 +21 -0
  37. stingeripc/templates/rust/connection/examples/pub_and_recv.rs.jinja2 +44 -0
  38. stingeripc/templates/rust/connection/src/handler.rs.jinja2 +0 -0
  39. stingeripc/templates/rust/connection/src/lib.rs.jinja2 +262 -0
  40. stingeripc/templates/rust/connection/src/payloads.rs.jinja2 +131 -0
  41. stingeripc/templates/rust/server/Cargo.toml.jinja2 +19 -0
  42. stingeripc/templates/rust/server/examples/server.rs.jinja2 +83 -0
  43. stingeripc/templates/rust/server/src/lib.rs.jinja2 +272 -0
  44. stingeripc/tools/__init__.py +0 -0
  45. stingeripc/tools/markdown_generator.py +25 -0
  46. stingeripc/tools/python_generator.py +41 -0
  47. stingeripc/tools/rust_generator.py +50 -0
  48. stingeripc/topic.py +11 -8
  49. stinger_ipc-0.0.1.dist-info/RECORD +0 -13
  50. {stinger_ipc-0.0.1.dist-info → stinger_ipc-0.0.3.dist-info}/WHEEL +0 -0
  51. {stinger_ipc-0.0.1.dist-info → stinger_ipc-0.0.3.dist-info}/licenses/LICENSE +0 -0
  52. {stinger_ipc-0.0.1.dist-info → stinger_ipc-0.0.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,243 @@
1
+
2
+ #include <exception>
3
+ #include <string>
4
+ #include <functional>
5
+ #include <boost/interprocess/sync/scoped_lock.hpp>
6
+ #include <boost/lexical_cast.hpp>
7
+ #include <boost/format.hpp>
8
+ #include <boost/algorithm/string.hpp>
9
+ #include <mosquitto.h>
10
+ #include <mqtt_protocol.h>
11
+ #include <iostream>
12
+
13
+ #include "broker.hpp"
14
+
15
+ using namespace std;
16
+
17
+ MqttConnection::MqttConnection(const std::string& host, int port, const std::string& clientId)
18
+ : _mosq(NULL), _host(host), _port(port), _clientId(clientId)
19
+ {
20
+ boost::mutex::scoped_lock lock(_mutex);
21
+
22
+ if (mosquitto_lib_init() != MOSQ_ERR_SUCCESS) {
23
+ throw std::runtime_error("Mosquitto lib init problem");
24
+ };
25
+ _mosq = mosquitto_new(_clientId.c_str(), false, (void*)this);
26
+
27
+ mosquitto_connect_callback_set(_mosq, [](struct mosquitto *mosq, void *user, int i)
28
+ {
29
+ MqttConnection *thisClient = static_cast<MqttConnection*>(user);
30
+ cout << "Connected to " << thisClient->_host << endl;
31
+ boost::mutex::scoped_lock lock(thisClient->_mutex);
32
+ while(!thisClient->_subscriptions.empty())
33
+ {
34
+ auto sub = thisClient->_subscriptions.front();
35
+ cout << "Subscribing to " << sub._topic << endl;
36
+ mosquitto_subscribe(mosq, NULL, sub._topic.c_str(), sub._qos);
37
+ thisClient->_subscriptions.pop();
38
+ }
39
+ while(!thisClient->_msgQueue.empty())
40
+ {
41
+ MqttMessage& msg = thisClient->_msgQueue.front();
42
+ cout << "Sending message to " << msg._topic << endl;
43
+ int mid;
44
+ mosquitto_property *propList = NULL;
45
+ if (msg._optCorrelationId)
46
+ {
47
+ mosquitto_property_add_string(&propList, MQTT_PROP_CORRELATION_DATA, msg._optCorrelationId->c_str());
48
+ }
49
+ if (msg._optResponseTopic)
50
+ {
51
+ mosquitto_property_add_string(&propList, MQTT_PROP_RESPONSE_TOPIC, msg._optResponseTopic->c_str());
52
+ }
53
+ mosquitto_publish_v5(mosq, &mid, msg._topic.c_str(), msg._payload.size(), msg._payload.c_str(), msg._qos, msg._retain, propList);
54
+ mosquitto_property_free_all(&propList);
55
+ thisClient->_sendMessages[mid] = msg._pSentPromise;
56
+ thisClient->_msgQueue.pop();
57
+ }
58
+ });
59
+
60
+ mosquitto_message_v5_callback_set(_mosq, [](struct mosquitto *mosq, void *user, const struct mosquitto_message *mmsg, const mosquitto_property *props)
61
+ {
62
+ MqttConnection *thisClient = static_cast<MqttConnection*>(user);
63
+ cout << "Fowarding message (" << mmsg->topic << ") to " << thisClient->_messageCallbacks.size() << " callbacks" << endl;
64
+ std::string topic(mmsg->topic);
65
+ 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;
69
+ const mosquitto_property *prop;
70
+ for (prop = props; prop != NULL; prop = mosquitto_property_next(prop))
71
+ {
72
+ if (mosquitto_property_identifier(prop) == MQTT_PROP_CORRELATION_DATA)
73
+ {
74
+ void *correlation_data;
75
+ uint16_t correlation_data_len;
76
+ if (mosquitto_property_read_binary(prop, MQTT_PROP_CORRELATION_DATA, &correlation_data, &correlation_data_len, false))
77
+ {
78
+ optCorrelationId = std::string(static_cast<char*>(correlation_data), correlation_data_len);
79
+ }
80
+ }
81
+ else if (mosquitto_property_identifier(prop) == MQTT_PROP_RESPONSE_TOPIC)
82
+ {
83
+ char *responseTopic = NULL;
84
+ if (mosquitto_property_read_string(prop, MQTT_PROP_RESPONSE_TOPIC, &responseTopic, false))
85
+ {
86
+ optResponseTopic = std::string(responseTopic);
87
+ free(responseTopic);
88
+ }
89
+ }
90
+ else if (mosquitto_property_identifier(prop) == MQTT_PROP_USER_PROPERTY)
91
+ {
92
+ char *name = NULL;
93
+ char *value = NULL;
94
+ if (mosquitto_property_read_string_pair(prop, MQTT_PROP_USER_PROPERTY, &name, &value, false))
95
+ {
96
+ if (strcmp(name, "ReturnValue") == 0)
97
+ {
98
+ int returnValueInt = boost::lexical_cast<int>(value);
99
+ optResultCode = static_cast<MethodResultCode>(returnValueInt);
100
+ }
101
+ free(name);
102
+ free(value);
103
+ }
104
+ }
105
+ }
106
+ for (auto& cb : thisClient->_messageCallbacks)
107
+ {
108
+ cout << "Calling callback" << endl;
109
+ cb(topic, payload, optCorrelationId, optResponseTopic, optResultCode);
110
+ }
111
+ });
112
+
113
+ mosquitto_publish_callback_set(_mosq, [](struct mosquitto *mosq, void *user, int mid)
114
+ {
115
+ MqttConnection *thisClient = static_cast<MqttConnection*>(user);
116
+ auto found = thisClient->_sendMessages.find(mid);
117
+ if (found != thisClient->_sendMessages.end())
118
+ {
119
+ found->second->set_value(true);
120
+ thisClient->_sendMessages.erase(found);
121
+ }
122
+ });
123
+
124
+ Connect();
125
+ mosquitto_loop_start(_mosq);
126
+ }
127
+
128
+ MqttConnection::~MqttConnection()
129
+ {
130
+ boost::mutex::scoped_lock lock(_mutex);
131
+ mosquitto_loop_stop(_mosq, true);
132
+ mosquitto_disconnect(_mosq);
133
+ mosquitto_destroy(_mosq);
134
+ mosquitto_lib_cleanup();
135
+
136
+ }
137
+
138
+ void MqttConnection::Connect()
139
+ {
140
+ mosquitto_int_option(_mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5);
141
+ mosquitto_connect(_mosq, _host.c_str(), _port, 120);
142
+ }
143
+
144
+ boost::future<bool> MqttConnection::Publish(
145
+ const std::string& topic,
146
+ const std::string& payload,
147
+ unsigned qos,
148
+ bool retain,
149
+ boost::optional<std::string> optCorrelationId,
150
+ boost::optional<std::string> optResponseTopic,
151
+ boost::optional<MethodResultCode> optResultCode)
152
+ {
153
+ int mid;
154
+ mosquitto_property *propList = NULL;
155
+ mosquitto_property_add_string(&propList, MQTT_PROP_CONTENT_TYPE, "application/json");
156
+ if (optCorrelationId)
157
+ {
158
+ mosquitto_property_add_binary(&propList, MQTT_PROP_CORRELATION_DATA, (void*)optCorrelationId->c_str(), optCorrelationId->size());
159
+ }
160
+ if (optResponseTopic)
161
+ {
162
+ mosquitto_property_add_string(&propList, MQTT_PROP_RESPONSE_TOPIC, optResponseTopic->c_str());
163
+ }
164
+ if (optResultCode)
165
+ {
166
+ std::string resultCodeStr = std::to_string(static_cast<int>(*optResultCode));
167
+ mosquitto_property_add_string_pair(&propList, MQTT_PROP_USER_PROPERTY, "ReturnValue", resultCodeStr.c_str());
168
+ }
169
+ int rc = mosquitto_publish_v5(_mosq, &mid, topic.c_str(), payload.size(), payload.c_str(), qos, retain, propList);
170
+ mosquitto_property_free_all(&propList);
171
+ if (rc == MOSQ_ERR_NO_CONN)
172
+ {
173
+ std::cout << "Delayed published queued to: " << topic << std::endl;
174
+ MqttConnection::MqttMessage msg(topic, payload, qos, retain, optCorrelationId, optResponseTopic);
175
+ auto future = msg.getFuture();
176
+ boost::mutex::scoped_lock lock(_mutex);
177
+ _msgQueue.push(std::move(msg));
178
+ return future;
179
+ }
180
+ else if (rc == MOSQ_ERR_SUCCESS)
181
+ {
182
+ std::cout << "Published to: " << topic << " | " << payload << std::endl;
183
+ auto pPromise = std::make_shared<boost::promise<bool>>();
184
+ auto future = pPromise->get_future();
185
+ boost::mutex::scoped_lock lock(_mutex);
186
+ _sendMessages[mid] = std::move(pPromise);
187
+ return future;
188
+ }
189
+ else
190
+ {
191
+ std::cout << "RC " << rc << std::endl;
192
+ }
193
+ throw std::runtime_error("Unhandled rc");
194
+ }
195
+
196
+ void MqttConnection::Subscribe(const std::string& topic, int qos)
197
+ {
198
+ std::cout << "Subscribing to " << topic << endl;
199
+ int rc = mosquitto_subscribe(_mosq, NULL, topic.c_str(), qos);
200
+ if (rc == MOSQ_ERR_NO_CONN)
201
+ {
202
+ MqttConnection::MqttSubscription sub(topic, qos);
203
+ boost::mutex::scoped_lock lock(_mutex);
204
+ _subscriptions.push(sub);
205
+ }
206
+ else if (rc == MOSQ_ERR_SUCCESS)
207
+ {
208
+ std::cout << "Subscribed to " << topic << std::endl;
209
+ }
210
+ }
211
+
212
+ 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)
214
+ {
215
+ boost::mutex::scoped_lock lock(_mutex);
216
+ _messageCallbacks.push_back(cb);
217
+ cout << "Message callback set" << endl;
218
+ }
219
+
220
+ bool MqttConnection::TopicMatchesSubscription(const std::string& topic, const std::string& subscr) const
221
+ {
222
+ bool result;
223
+ int rc = mosquitto_topic_matches_sub(subscr.c_str(), topic.c_str(), &result);
224
+ if (rc != MOSQ_ERR_SUCCESS)
225
+ {
226
+ throw std::runtime_error("Mosquitto error");
227
+ }
228
+ return result;
229
+ }
230
+
231
+ std::string MqttConnection::GetClientId() const
232
+ {
233
+ return _clientId;
234
+ }
235
+
236
+ {%for broker in stinger.brokers.values()%}
237
+ {{broker.class_name}}::{{broker.class_name}}({%if broker.hostname is none%}const std::string& host, int port, {%endif%}const std::string& clientId)
238
+ : MqttConnection({%if broker.hostname is none%}host{%else%}"{{broker.hostname}}"{%endif%}, {%if broker.port is none%}port{%else%}{{broker.port}}{%endif%}, clientId)
239
+ {
240
+
241
+ }
242
+
243
+ {%endfor%}
@@ -0,0 +1,202 @@
1
+
2
+ #include <vector>
3
+ #include <iostream>
4
+ #include <boost/format.hpp>
5
+ #include <boost/algorithm/string.hpp>
6
+ #include <boost/lexical_cast.hpp>
7
+ #include <boost/functional/hash.hpp>
8
+ #include <boost/uuid/uuid_io.hpp>
9
+ #include <boost/uuid/uuid_generators.hpp>
10
+ #include <rapidjson/stringbuffer.h>
11
+ #include <rapidjson/writer.h>
12
+ #include <rapidjson/error/en.h>
13
+ #include <rapidjson/document.h>
14
+
15
+ #include "client.hpp"
16
+ #include "enums.hpp"
17
+ #include "ibrokerconnection.hpp"
18
+
19
+
20
+ constexpr const char {{stinger.name | UpperCamelCase}}Client::NAME[];
21
+ constexpr const char {{stinger.name | UpperCamelCase}}Client::INTERFACE_VERSION[];
22
+
23
+ {{stinger.name | UpperCamelCase}}Client::{{stinger.name | UpperCamelCase}}Client(std::shared_ptr<IBrokerConnection> broker) : _broker(broker)
24
+ {
25
+ _broker->AddMessageCallback([this](const std::string& topic, const std::string& payload, const boost::optional<std::string> optCorrelationId, const boost::optional<std::string> unusedRespTopic, const boost::optional<MethodResultCode> optResultCode)
26
+ {
27
+ _receiveMessage(topic, payload, optCorrelationId, optResultCode);
28
+ });
29
+
30
+ {%-for sig_name, sig in stinger.signals.items() %}
31
+ _broker->Subscribe("{{sig.topic}}", 1);
32
+ {%endfor%}
33
+ {%-for method_name, method in stinger.methods.items() %}
34
+ { // Restrict scope
35
+ std::stringstream responseTopicStringStream;
36
+ responseTopicStringStream << boost::format("{{method.response_topic('%1%')}}") % _broker->GetClientId();
37
+ _broker->Subscribe(responseTopicStringStream.str(), 2);
38
+ }
39
+ {%-endfor%}
40
+ }
41
+
42
+ void {{stinger.name | UpperCamelCase}}Client::_receiveMessage(
43
+ const std::string& topic,
44
+ const std::string& payload,
45
+ const boost::optional<std::string> optCorrelationId,
46
+ const boost::optional<MethodResultCode> optResultCode)
47
+ {
48
+ {%-for sig_name, sig in stinger.signals.items() %}
49
+ if (_broker->TopicMatchesSubscription(topic, "{{sig.topic}}"))
50
+ {
51
+ //Log("Handling {{sig_name}} signal");
52
+ rapidjson::Document doc;
53
+ try {
54
+ if (_{{sig_name | camelCase}}Callback)
55
+ {
56
+ rapidjson::ParseResult ok = doc.Parse(payload.c_str());
57
+ if (!ok)
58
+ {
59
+ //Log("Could not JSON parse {{sig_name}} signal payload.");
60
+ throw std::runtime_error(rapidjson::GetParseError_En(ok.Code()));
61
+ }
62
+
63
+ if (!doc.IsObject()) {
64
+ throw std::runtime_error("Received payload is not an object");
65
+ }
66
+
67
+ {%for arg in sig.arg_list%}
68
+ {{arg.cpp_temp_type}} temp{{arg.name}};
69
+ { // Scoping
70
+ rapidjson::Value::ConstMemberIterator itr = doc.FindMember("{{arg.name}}");
71
+ if (itr != doc.MemberEnd() && itr->value.Is{{arg.cpp_rapidjson_type}}()) {
72
+ {%if arg.arg_type.name.lower() == 'primitive'%}
73
+ temp{{arg.name}} = itr->value.Get{{arg.cpp_rapidjson_type}}();
74
+ {%elif arg.arg_type.name.lower() == 'enum'%}
75
+ temp{{arg.name}} = static_cast<{{arg.cpp_data_type}}>(itr->value.Get{{arg.cpp_rapidjson_type}}());
76
+ {%endif%}
77
+ } else {
78
+ {%-if arg.optional%}
79
+ temp{{arg.name}} = boost::none;
80
+ {%else%}
81
+ throw std::runtime_error("Received payload doesn't have required value/type");
82
+ {%endif%}
83
+ }
84
+ }
85
+ {%endfor%}
86
+
87
+ _{{sig_name | camelCase}}Callback({%for arg in sig.arg_list%}temp{{arg.name}}{%if not loop.last%}, {%endif%}{%endfor%});
88
+ }
89
+ }
90
+ catch (const boost::bad_lexical_cast&)
91
+ {
92
+ // We couldn't find an integer out of the string in the topic name,
93
+ // so we are dropping the message completely.
94
+ // TODO: Log this failure
95
+ }
96
+ }
97
+ {%-endfor%}
98
+ {%-for method_name, method in stinger.methods.items() %}
99
+ {%if not loop.first%}else {%endif%}if (_broker->TopicMatchesSubscription(topic, "{{method.response_topic('+')}}") && optCorrelationId)
100
+ {
101
+ std::cout << "Matched topic for {{method_name}} response" << std::endl;
102
+ _handle{{method_name|UpperCamelCase}}Response(topic, payload, *optCorrelationId);
103
+ }
104
+ {%-endfor%}
105
+ }
106
+
107
+
108
+ {%-for sig_name, sig in stinger.signals.items() %}
109
+ void {{stinger.name | UpperCamelCase}}Client::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)
110
+ {
111
+ _{{sig_name | camelCase}}Callback = cb;
112
+ }
113
+ {%-endfor%}
114
+
115
+ {%for method_name, method in stinger.methods.items()%}
116
+ boost::future<{{method.return_value_cpp_class}}> {{stinger.name | UpperCamelCase}}Client::{{method_name | camelCase}}({%for arg in method.arg_list%}{{arg.cpp_type}} {{arg.name}}{%if not loop.last%}, {%endif%}{%endfor%})
117
+ {
118
+ auto correlationId = boost::uuids::random_generator()();
119
+ const std::string correlationIdStr = boost::lexical_cast<std::string>(correlationId);
120
+ _pending{{method_name|UpperCamelCase}}MethodCalls[correlationId] = boost::promise<{{method.return_value_cpp_class}}>();
121
+
122
+ rapidjson::Document doc;
123
+ doc.SetObject();
124
+
125
+ {%macro addToDoc(arg)%}
126
+ {%-if arg.arg_type.name.lower() == 'primitive'%}
127
+ {%-if arg.optional %}if ({{arg.name}}) {%endif-%}
128
+ {%-if arg.type.name.lower() == 'string'%}
129
+ { // restrict scope
130
+ rapidjson::Value tempStringValue;
131
+ tempStringValue.SetString({{arg.name}}{%-if arg.optional %}->{%else%}.{%endif-%}c_str(), {{arg.name}}.size(), doc.GetAllocator());
132
+ doc.AddMember("{{arg.name}}", tempStringValue, doc.GetAllocator());
133
+ }
134
+ {%-else%}
135
+ doc.AddMember("{{arg.name}}", {%-if arg.optional %}*{%endif-%}{{arg.name}}, doc.GetAllocator());
136
+ {%-endif%}
137
+ {%elif arg.arg_type.name.lower() == 'enum'%}
138
+ doc.AddMember("{{arg.name}}", static_cast<int>({{arg.name}}), doc.GetAllocator());
139
+ {%-endif-%}
140
+ {%endmacro%}
141
+ {%for arg in method.arg_list%}
142
+ {{addToDoc(arg)}}
143
+ {%endfor%}
144
+ rapidjson::StringBuffer buf;
145
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buf);
146
+ doc.Accept(writer);
147
+ std::stringstream responseTopicStringStream;
148
+ responseTopicStringStream << boost::format("{{method.response_topic('%1%')}}") % _broker->GetClientId();
149
+ _broker->Publish("{{method.topic}}", buf.GetString(), 2, false, correlationIdStr, responseTopicStringStream.str(), MethodResultCode::SUCCESS);
150
+
151
+ return _pending{{method_name|UpperCamelCase}}MethodCalls[correlationId].get_future();
152
+ }
153
+
154
+ void {{stinger.name | UpperCamelCase}}Client::_handle{{method_name|UpperCamelCase}}Response(
155
+ const std::string& topic,
156
+ const std::string& payload,
157
+ const std::string &correlationId)
158
+ {
159
+ std::cout << "In response handler for " << topic << " with correlationId=" << correlationId << std::endl;
160
+ rapidjson::Document doc;
161
+ rapidjson::ParseResult ok = doc.Parse(payload.c_str());
162
+ if (!ok)
163
+ {
164
+ //Log("Could not JSON parse {{method_name}} signal payload.");
165
+ throw std::runtime_error(rapidjson::GetParseError_En(ok.Code()));
166
+ }
167
+
168
+ if (!doc.IsObject()) {
169
+ throw std::runtime_error("Received payload is not an object");
170
+ }
171
+
172
+ boost::uuids::uuid correlationIdUuid = boost::lexical_cast<boost::uuids::uuid>(correlationId);
173
+ auto promiseItr = _pending{{method_name|UpperCamelCase}}MethodCalls.find(correlationIdUuid);
174
+ if (promiseItr != _pending{{method_name|UpperCamelCase}}MethodCalls.end())
175
+ {
176
+ {%macro unpack_rv(arg)-%}
177
+ rapidjson::Value::ConstMemberIterator {{arg.name}}Itr = doc.FindMember("{{arg.name}}");
178
+ {%if arg.arg_type.name.lower() == 'primitive'-%}
179
+ {{arg.cpp_type}} {{arg.name}} = {{arg.name}}Itr->value.Get{{arg.cpp_rapidjson_type}}();
180
+ {%elif arg.arg_type.name.lower() == 'enum'-%}
181
+ {{arg.cpp_type}} {{arg.name}} = static_cast<{{arg.cpp_type}}>({{arg.name}}Itr->value.GetInt());
182
+ {%endif-%}
183
+ {%endmacro-%}
184
+ {%if method.return_value_type == 'struct'-%}
185
+ {%for item in method.return_value%}
186
+ {{unpack_rv(item)}}
187
+ {%-endfor%}
188
+ {{method.return_value_cpp_class}} returnValue { //initializer list
189
+ {%for item in method.return_value%}
190
+ {{item.name}}{%if not loop.last%},{%endif%}
191
+ {%-endfor%}
192
+ };
193
+ promiseItr->second.set_value(returnValue);
194
+ {%-else%}
195
+ {{-unpack_rv(method.return_value)}}
196
+ promiseItr->second.set_value({{method.return_value_property_name}});
197
+ {%-endif%}
198
+ }
199
+
200
+ std::cout << "End of response handler for " << topic << std::endl;
201
+ }
202
+ {%endfor%}
@@ -0,0 +1,170 @@
1
+
2
+ #include <vector>
3
+ #include <iostream>
4
+ #include <boost/format.hpp>
5
+ #include <boost/algorithm/string.hpp>
6
+ #include <boost/lexical_cast.hpp>
7
+ #include <boost/functional/hash.hpp>
8
+ #include <rapidjson/stringbuffer.h>
9
+ #include <rapidjson/writer.h>
10
+ #include <rapidjson/error/en.h>
11
+ #include <rapidjson/document.h>
12
+
13
+ #include "server.hpp"
14
+ #include "enums.hpp"
15
+ #include "ibrokerconnection.hpp"
16
+
17
+
18
+ constexpr const char {{stinger.name | UpperCamelCase}}Server::NAME[];
19
+ constexpr const char {{stinger.name | UpperCamelCase}}Server::INTERFACE_VERSION[];
20
+
21
+ {{stinger.name | UpperCamelCase}}Server::{{stinger.name | UpperCamelCase}}Server(std::shared_ptr<IBrokerConnection> broker) : _broker(broker) {
22
+ _broker->AddMessageCallback([this](const std::string& topic, const std::string& payload, const boost::optional<std::string> optCorrelationId, const boost::optional<std::string> optResponseTopic, const boost::optional<MethodResultCode> unusedRc)
23
+ {
24
+ _receiveMessage(topic, payload, optCorrelationId, optResponseTopic);
25
+ });
26
+ {%for method in stinger.methods.values()%}
27
+ _broker->Subscribe("{{method.topic}}", 2);
28
+ {%endfor%}
29
+ }
30
+
31
+ void {{stinger.name | UpperCamelCase}}Server::_receiveMessage(
32
+ const std::string& topic,
33
+ const std::string& payload,
34
+ const boost::optional<std::string> optCorrelationId,
35
+ const boost::optional<std::string> optResponseTopic)
36
+ {
37
+ {%for method_name, method in stinger.methods.items()%}
38
+ if (_broker->TopicMatchesSubscription(topic, "{{method.topic}}"))
39
+ {
40
+ std::cout << "Message matched topic {{method.topic}}\n";
41
+ rapidjson::Document doc;
42
+ try {
43
+ if (_{{method_name | camelCase}}Handler)
44
+ {
45
+ rapidjson::ParseResult ok = doc.Parse(payload.c_str());
46
+ if (!ok)
47
+ {
48
+ //Log("Could not JSON parse {{sig_name}} signal payload.");
49
+ throw std::runtime_error(rapidjson::GetParseError_En(ok.Code()));
50
+ }
51
+
52
+ if (!doc.IsObject()) {
53
+ throw std::runtime_error("Received payload is not an object");
54
+ }
55
+
56
+ _call{{method_name | UpperCamelCase}}Handler(topic, doc, optCorrelationId, optResponseTopic);
57
+ }
58
+ }
59
+ catch (const boost::bad_lexical_cast&)
60
+ {
61
+ // We couldn't find an integer out of the string in the topic name,
62
+ // so we are dropping the message completely.
63
+ // TODO: Log this failure
64
+ }
65
+ }
66
+ {%endfor%}
67
+ }
68
+
69
+ {%for sig_name, sig in stinger.signals.items()%}
70
+ boost::future<bool> {{stinger.name | UpperCamelCase}}Server::emit{{sig_name | UpperCamelCase}}Signal({%for arg in sig.arg_list%}{{arg.cpp_type}} {{arg.name}}{%if not loop.last%}, {%endif%}{%endfor%})
71
+ {
72
+ rapidjson::Document doc;
73
+ doc.SetObject();
74
+ {%macro addToDoc(arg)%}
75
+ {%-if arg.optional%}if ({{arg.name}}){%endif%}
76
+ {%-if arg.arg_type.name.lower() == 'primitive'%}
77
+ {%-if arg.type.name.lower() == 'string'%}
78
+ { // restrict scope
79
+ rapidjson::Value tempStringValue;
80
+ tempStringValue.SetString({{arg.name}}{%-if arg.optional%}->{%else%}.{%endif%}c_str(), {{arg.name}}.size(), doc.GetAllocator());
81
+ doc.AddMember("{{arg.name}}", tempStringValue, doc.GetAllocator());
82
+ }
83
+ {%-else%}
84
+ doc.AddMember("{{arg.name}}", {%-if arg.optional%}*{%endif%}{{arg.name}}, doc.GetAllocator());
85
+ {%-endif%}
86
+ {%-elif arg.arg_type.name.lower() == 'enum'%}
87
+ doc.AddMember("{{arg.name}}", static_cast<int>({%-if arg.optional%}*{%endif%}{{arg.name}}), doc.GetAllocator());
88
+ {%-endif-%}
89
+ {%-endmacro%}
90
+ {%-for arg in sig.arg_list%}
91
+ {{addToDoc(arg)}}
92
+ {%endfor%}
93
+ rapidjson::StringBuffer buf;
94
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buf);
95
+ doc.Accept(writer);
96
+ return _broker->Publish("{{sig.topic}}", buf.GetString(), 1, false, boost::none, boost::none, boost::none);
97
+ }
98
+ {%endfor%}
99
+
100
+ {%for method_name, method in stinger.methods.items()%}
101
+ void {{stinger.name | UpperCamelCase}}Server::register{{method_name | UpperCamelCase}}Handler(std::function<{{method.return_value_cpp_class}}({%for arg in method.arg_list%}{{arg.cpp_type}}{%if not loop.last%}, {%endif%}{%endfor%})> func)
102
+ {
103
+ std::cout << "Registered method to handle {{method.topic}}\n";
104
+ _{{method_name | camelCase}}Handler = func;
105
+ }
106
+ {%endfor%}
107
+
108
+ {%for method_name, method in stinger.methods.items()%}
109
+ void {{stinger.name | UpperCamelCase}}Server::_call{{method_name | UpperCamelCase}}Handler(
110
+ const std::string& topic,
111
+ const rapidjson::Document& doc,
112
+ const boost::optional<std::string> optCorrelationId,
113
+ const boost::optional<std::string> optResponseTopic) const
114
+ {
115
+ std::cout << "Handling call to {{method_name}}\n";
116
+ if (_{{method_name | camelCase}}Handler) {
117
+ {%for arg in method.arg_list%}
118
+ {{arg.cpp_temp_type}} temp{{arg.name|UpperCamelCase}};
119
+ { // Scoping
120
+ rapidjson::Value::ConstMemberIterator itr = doc.FindMember("{{arg.name}}");
121
+ if (itr != doc.MemberEnd() && itr->value.Is{{arg.cpp_rapidjson_type}}()) {
122
+ {%if arg.arg_type.name.lower() == 'primitive'%}
123
+ temp{{arg.name|UpperCamelCase}} = itr->value.Get{{arg.cpp_rapidjson_type}}();
124
+ {%elif arg.arg_type.name.lower() == 'enum'%}
125
+ temp{{arg.name|UpperCamelCase}} = static_cast<{{arg.cpp_type}}>(itr->value.Get{{arg.cpp_rapidjson_type}}());
126
+ {%endif%}
127
+ } else {
128
+ throw std::runtime_error("Received payload doesn't have required value/type");
129
+ }
130
+ }
131
+ {%endfor%}
132
+
133
+ {{method.return_value_cpp_class}} ret = _{{method_name | camelCase}}Handler({%for arg in method.arg_list%}temp{{arg.name|UpperCamelCase}}{%if not loop.last%}, {%endif%}{%endfor%});
134
+
135
+ if (optResponseTopic)
136
+ {
137
+ rapidjson::Document responseJson;
138
+ responseJson.SetObject();
139
+ {%macro add_arg_to_json(arg, value) %}
140
+ // add the {{arg.name}} (a/n {{arg.arg_type.name}}) to the json
141
+ rapidjson::Value returnValue{{arg.name|UpperCamelCase}};
142
+ {%-if arg.arg_type.name.lower() == 'primitive' %}
143
+ {%-if arg.type.name.lower() == 'string' %}
144
+ returnValue{{arg.name|UpperCamelCase}}.SetString({{value}}.c_str(), {{value}}.size(), responseJson.GetAllocator());
145
+ {%-else%}
146
+ returnValue{{arg.name|UpperCamelCase}}.Set{{method.return_value.cpp_rapidjson_type | UpperCamelCase}}({{value}});
147
+ {%-endif%} {# value type string/primitive #}
148
+ {%-elif arg.arg_type.name.lower() == 'enum'%}
149
+ returnValue{{arg.name|UpperCamelCase}}.SetInt(static_cast<int>({{value}}));
150
+ {%-endif%} {# value or enum#}
151
+ responseJson.AddMember("{{arg.name}}", returnValue{{arg.name|UpperCamelCase}}, responseJson.GetAllocator());
152
+ {%endmacro%}
153
+
154
+ {%-if method.return_value_type == 'struct'%}
155
+ // Return type is a struct of values that need added to json
156
+ {%for member in method.return_value-%}
157
+ {{add_arg_to_json(member, "ret."+member.name)}}
158
+ {%endfor%}
159
+ {%-else%}
160
+ {{add_arg_to_json(method.return_value, "ret")}}
161
+ {%-endif%}
162
+
163
+ rapidjson::StringBuffer buf;
164
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buf);
165
+ responseJson.Accept(writer);
166
+ _broker->Publish(*optResponseTopic, buf.GetString(), 2, false, optCorrelationId, boost::none, MethodResultCode::SUCCESS);
167
+ }
168
+ }
169
+ }
170
+ {%endfor%}