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.
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/METADATA +3 -2
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/RECORD +35 -29
- 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 +111 -38
- 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/python/server.py.jinja2 +138 -18
- 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.26.dist-info}/WHEEL +0 -0
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/entry_points.txt +0 -0
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/licenses/LICENSE +0 -0
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/top_level.txt +0 -0
@@ -7,15 +7,31 @@ This is the Server for the {{stinger.name}} interface.
|
|
7
7
|
|
8
8
|
import json
|
9
9
|
import logging
|
10
|
-
|
10
|
+
import threading
|
11
|
+
from dataclasses import dataclass, field
|
11
12
|
logging.basicConfig(level=logging.DEBUG)
|
12
13
|
|
13
|
-
from typing import Callable, Dict, Any, Optional, List
|
14
|
+
from typing import Callable, Dict, Any, Optional, List, Generic, TypeVar
|
14
15
|
from connection import BrokerConnection
|
15
16
|
from method_codes import *
|
16
17
|
import {{stinger.get_enum_module_name()}} as {{stinger.get_enum_module_alias()}}
|
17
18
|
{%macro method_type_annotation(method) %}Callable[[{%if method.arg_list | length > 0%}{%for arg in method.arg_list%}{{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}{%else%}None{%endif%}], {{method.return_value_python_type}}]{%endmacro%}
|
18
19
|
|
20
|
+
T = TypeVar('T')
|
21
|
+
|
22
|
+
@dataclass
|
23
|
+
class PropertyControls(Generic[T]):
|
24
|
+
value: T | None = None
|
25
|
+
mutex = threading.Lock()
|
26
|
+
version: int = -1
|
27
|
+
subscription_id: int | None = None
|
28
|
+
callbacks: List[Callable[[T], None]] = field(default_factory=list)
|
29
|
+
|
30
|
+
@dataclass
|
31
|
+
class MethodControls:
|
32
|
+
subscription_id: int | None = None
|
33
|
+
callback: Optional[Callable] = None
|
34
|
+
|
19
35
|
class {{stinger.python.server_class_name}}:
|
20
36
|
|
21
37
|
def __init__(self, connection: BrokerConnection):
|
@@ -25,18 +41,15 @@ class {{stinger.python.server_class_name}}:
|
|
25
41
|
self._conn = connection
|
26
42
|
self._conn.set_message_callback(self._receive_message)
|
27
43
|
self._conn.set_last_will(topic="{{stinger.interface_info.0}}", payload=None, qos=1, retain=True)
|
28
|
-
{%for prop_name,
|
29
|
-
self._property_{{prop_name}} =
|
30
|
-
self._conn.subscribe("{{
|
31
|
-
self.changed_value_callback_for_{{pprop_name}} = None
|
32
|
-
self._publish_interface_info()
|
44
|
+
{%for prop_name, prop in stinger.properties.items()%}
|
45
|
+
self._property_{{prop_name|snake_case}}: PropertyControls[{%if prop.arg_list | length == 1%}{{prop.arg_list[0].python_class}}{%else%}{{prop.python_class}}{%endif%}, {%for arg in prop.arg_list %}{{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}] = PropertyControls()
|
46
|
+
self._property_{{prop_name|snake_case}}.subscription_id = self._conn.subscribe("{{prop.update_topic}}")
|
33
47
|
{%endfor-%}
|
34
|
-
{%-for method in stinger.methods.
|
35
|
-
self.
|
36
|
-
{
|
37
|
-
{%for method_name, method in stinger.methods.items()-%}
|
38
|
-
self._{{method_name|snake_case}}_method_handler: Optional[{{method_type_annotation(method)}}] = None
|
48
|
+
{%-for method_name, method in stinger.methods.items()%}
|
49
|
+
self._method_{{method_name|snake_case}} = MethodControls()
|
50
|
+
self._method_{{method_name|snake_case}}.subscription_id = self._conn.subscribe("{{method.topic}}")
|
39
51
|
{%endfor%}
|
52
|
+
self._publish_interface_info()
|
40
53
|
|
41
54
|
def _receive_message(self, topic: str, payload: str, properties: Dict[str, Any]):
|
42
55
|
""" This is the callback that is called whenever any message is received on a subscribed topic.
|
@@ -44,7 +57,7 @@ class {{stinger.python.server_class_name}}:
|
|
44
57
|
self._logger.debug("Received message to %s", topic)
|
45
58
|
{%if stinger.methods | length > 0 -%}
|
46
59
|
{%for method_name, method in stinger.methods.items()-%}
|
47
|
-
{%if not loop.first%}el{%endif%}if self._conn.is_topic_sub(topic, "{{method.topic}}"):
|
60
|
+
{%if not loop.first%}el{%endif%}if (properties.get('SubscriptionId', -1) == self._method_{{method_name|snake_case}}.subscription_id) or self._conn.is_topic_sub(topic, "{{method.topic}}"):
|
48
61
|
try:
|
49
62
|
payload_obj = json.loads(payload)
|
50
63
|
except json.decoder.JSONDecodeError:
|
@@ -52,7 +65,25 @@ class {{stinger.python.server_class_name}}:
|
|
52
65
|
else:
|
53
66
|
self._process_{{method_name | snake_case}}_call(topic, payload_obj, properties)
|
54
67
|
{%endfor%}
|
55
|
-
{%-
|
68
|
+
{%-endif%}
|
69
|
+
{%for prop_name, prop in stinger.properties.items()%}
|
70
|
+
{%if not loop.first%}el{%endif%}if (properties.get('SubscriptionId', -1) == self._property_{{prop_name|snake_case}}.subscription_id) or self._conn.is_topic_sub(topic, "{{prop.update_topic}}"):
|
71
|
+
{%if prop.arg_list | length > 1 -%}
|
72
|
+
prop_value = {{prop.python_class}}.model_validate_json(payload)
|
73
|
+
{%else-%}
|
74
|
+
payload_obj = json.loads(payload)
|
75
|
+
prop_value = {{prop.arg_list[0].python_class}}(payload_obj["{{prop.arg_list[0].name}}"])
|
76
|
+
{%endif-%}
|
77
|
+
with self._property_{{prop_name|snake_case}}.mutex:
|
78
|
+
self._property_{{prop_name|snake_case}}.value = prop_value
|
79
|
+
self._property_{{prop_name|snake_case}}.version += 1
|
80
|
+
for callback in self._property_{{prop_name|snake_case}}.callbacks:
|
81
|
+
{%-if prop.arg_list | length > 1%}
|
82
|
+
callback({%for arg in prop.arg_list %}prop_value.{{arg.name|snake_case}}{%if not loop.last%}, {%endif%}{%endfor%})
|
83
|
+
{%-else%}
|
84
|
+
callback(prop_value)
|
85
|
+
{%-endif%}
|
86
|
+
{%endfor%}
|
56
87
|
|
57
88
|
def _publish_interface_info(self):
|
58
89
|
self._conn.publish("{{stinger.interface_info.0}}", '''{{stinger.interface_info.1 | tojson}}''', qos=1, retain=True)
|
@@ -78,8 +109,8 @@ class {{stinger.python.server_class_name}}:
|
|
78
109
|
def handle_{{method_name | snake_case}}(self, handler: {{method_type_annotation(method)}}):
|
79
110
|
""" This is a decorator to decorate a method that will handle the '{{method_name}}' method calls.
|
80
111
|
"""
|
81
|
-
if self.
|
82
|
-
self.
|
112
|
+
if self._method_{{method_name|snake_case}}.callback is None and handler is not None:
|
113
|
+
self._method_{{method_name|snake_case}}.callback = handler
|
83
114
|
else:
|
84
115
|
raise Exception("Method handler already set")
|
85
116
|
|
@@ -90,7 +121,7 @@ class {{stinger.python.server_class_name}}:
|
|
90
121
|
correlation_id = properties.get('CorrelationData') # type: Optional[bytes]
|
91
122
|
response_topic = properties.get('ResponseTopic') # type: Optional[str]
|
92
123
|
self._logger.info("Correlation Data %s", correlation_id)
|
93
|
-
if self.
|
124
|
+
if self._method_{{method_name|snake_case}}.callback is not None:
|
94
125
|
method_args = [] # type: List[Any]
|
95
126
|
{%for arg in method.arg_list -%}
|
96
127
|
if "{{arg.name}}" in payload:
|
@@ -114,7 +145,7 @@ class {{stinger.python.server_class_name}}:
|
|
114
145
|
return_json = ""
|
115
146
|
debug_msg = None # type: Optional[str]
|
116
147
|
try:
|
117
|
-
return_struct = self.
|
148
|
+
return_struct = self._method_{{method_name|snake_case}}.callback(*method_args)
|
118
149
|
self._logger.debug("Return value is %s", return_struct)
|
119
150
|
{%if method.return_value is false%}
|
120
151
|
return_json = "{}"
|
@@ -147,7 +178,65 @@ class {{stinger.python.server_class_name}}:
|
|
147
178
|
self._conn.publish(response_topic, return_json, qos=1, retain=False,
|
148
179
|
correlation_id=correlation_id, return_value=return_code, debug_info=debug_msg)
|
149
180
|
{%endfor%}
|
181
|
+
{%for prop_name, prop in stinger.properties.items()%}
|
182
|
+
@property
|
183
|
+
def {{prop_name | snake_case}}(self) -> {{prop.python_class}}|None:
|
184
|
+
""" This property returns the last received value for the '{{prop_name}}' property.
|
185
|
+
"""
|
186
|
+
with self._property_{{prop_name|snake_case}}_mutex:
|
187
|
+
return self._property_{{prop_name|snake_case}}
|
150
188
|
|
189
|
+
@{{prop_name | snake_case}}.setter
|
190
|
+
def {{prop_name | snake_case}}(self, {%if prop.arg_list | length > 1%}value{%else%}{{prop.arg_list[0].name|snake_case}}{%endif%}: {{prop.python_class}}):
|
191
|
+
""" This property sets (publishes) a new value for the '{{prop_name}}' property.
|
192
|
+
"""
|
193
|
+
if not isinstance({%if prop.arg_list | length > 1%}value{%else%}{{prop.arg_list[0].name|snake_case}}{%endif%}, {{prop.python_class}}):
|
194
|
+
raise ValueError(f"The value must be {{prop.python_class}}.")
|
195
|
+
|
196
|
+
{%if prop.arg_list | length > 1%}
|
197
|
+
payload = value.model_dump_json()
|
198
|
+
{%else%}
|
199
|
+
payload = json.dumps({ "{{prop.arg_list[0].name}}": {{prop.arg_list[0].name|snake_case}} })
|
200
|
+
{%endif%}
|
201
|
+
|
202
|
+
if {%if prop.arg_list | length > 1%}value{%else%}{{prop.arg_list[0].name|snake_case}}{%endif%} != self._property_{{prop_name|snake_case}}.value:
|
203
|
+
with self._property_{{prop_name|snake_case}}.mutex:
|
204
|
+
self._property_{{prop_name|snake_case}}.value = {%if prop.arg_list | length > 1%}value{%else%}{{prop.arg_list[0].name|snake_case}}{%endif%}
|
205
|
+
self._property_{{prop_name|snake_case}}.version += 1
|
206
|
+
self._conn.publish("{{prop.value_topic}}", payload, qos=1, retain=True)
|
207
|
+
for callback in self._property_{{prop_name|snake_case}}.callbacks:
|
208
|
+
{%-if prop.arg_list | length > 1%}
|
209
|
+
callback({%for arg in prop.arg_list %}value.{{arg.name|snake_case}}{%if not loop.last%}, {%endif%}{%endfor%})
|
210
|
+
{%-else%}
|
211
|
+
callback({{prop.arg_list[0].name|snake_case}})
|
212
|
+
{%-endif%}
|
213
|
+
|
214
|
+
def set_{{prop_name | snake_case}}(self, {%for arg in prop.arg_list %}{{arg.name|snake_case}}: {{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}):
|
215
|
+
""" This method sets (publishes) a new value for the '{{prop_name}}' property.
|
216
|
+
"""
|
217
|
+
{%-for arg in prop.arg_list%}
|
218
|
+
if not isinstance({{arg.name|snake_case}}, {{arg.python_class}}){%if arg.optional%} and {{arg.name|snake_case}} is not None{%endif%}:
|
219
|
+
raise ValueError(f"The '{{arg.name|snake_case}}' value must be {{arg.python_type}}.")
|
220
|
+
{%-endfor%}
|
221
|
+
|
222
|
+
{%if prop.arg_list | length > 1%}
|
223
|
+
obj = stinger_types.{{prop.python_local_type}}({%for arg in prop.arg_list%}
|
224
|
+
{{arg.name}}={{arg.name|snake_case}},
|
225
|
+
{%endfor%}
|
226
|
+
)
|
227
|
+
{%else%}
|
228
|
+
obj = {{prop.arg_list[0].name | snake_case}}
|
229
|
+
{%endif%}
|
230
|
+
|
231
|
+
# Use the property.setter to do that actual work.
|
232
|
+
self.{{prop_name|snake_case}} = obj
|
233
|
+
|
234
|
+
def on_{{prop_name | snake_case}}_updates(self, handler: Callable[[{%for arg in prop.arg_list %}{{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}], None]):
|
235
|
+
""" This method registers a callback to be called whenever a new '{{prop_name}}' property update is received.
|
236
|
+
"""
|
237
|
+
if handler is not None:
|
238
|
+
self._property_{{prop_name|snake_case}}.callbacks.append(handler)
|
239
|
+
{%endfor%}
|
151
240
|
|
152
241
|
class {{stinger.python.server_class_name}}Builder:
|
153
242
|
"""
|
@@ -159,6 +248,9 @@ class {{stinger.python.server_class_name}}Builder:
|
|
159
248
|
{%for method_name, method in stinger.methods.items()%}
|
160
249
|
self._{{method_name|snake_case}}_method_handler: Optional[{{method_type_annotation(method)}}] = None
|
161
250
|
{%-endfor%}
|
251
|
+
{%for prop_name, property in stinger.properties.items()%}
|
252
|
+
self._{{prop_name|snake_case}}_property_callbacks: List[Callable[[{%for arg in property.arg_list %}{{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}], None]] = []
|
253
|
+
{%-endfor%}
|
162
254
|
{%for method_name, method in stinger.methods.items()%}
|
163
255
|
def handle_{{method_name | snake_case}}(self, handler: {{method_type_annotation(method)}}):
|
164
256
|
if self._{{method_name|snake_case}}_method_handler is None and handler is not None:
|
@@ -166,12 +258,22 @@ class {{stinger.python.server_class_name}}Builder:
|
|
166
258
|
else:
|
167
259
|
raise Exception("Method handler already set")
|
168
260
|
{%endfor%}
|
261
|
+
{%for prop_name, prop in stinger.properties.items()%}
|
262
|
+
def on_{{prop_name | snake_case}}_updates(self, handler: Callable[[{%for arg in prop.arg_list %}{{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}], None]):
|
263
|
+
""" This method registers a callback to be called whenever a new '{{prop_name}}' property update is received.
|
264
|
+
"""
|
265
|
+
self._{{prop_name|snake_case}}_property_callbacks.append(handler)
|
266
|
+
{%endfor%}
|
169
267
|
def build(self) -> {{stinger.python.server_class_name}}:
|
170
268
|
new_server = {{stinger.python.server_class_name}}(self._conn)
|
171
269
|
{%for method_name, method in stinger.methods.items()%}
|
172
270
|
if self._{{method_name|snake_case}}_method_handler is not None:
|
173
271
|
new_server.handle_{{method_name|snake_case}}(self._{{method_name|snake_case}}_method_handler)
|
174
272
|
{%-endfor%}
|
273
|
+
{%for prop_name, prop in stinger.properties.items()%}
|
274
|
+
for callback in self._{{prop_name|snake_case}}_property_callbacks:
|
275
|
+
new_server.on_{{prop_name|snake_case}}_updates(callback)
|
276
|
+
{%endfor%}
|
175
277
|
return new_server
|
176
278
|
|
177
279
|
if __name__ == '__main__':
|
@@ -187,6 +289,18 @@ if __name__ == '__main__':
|
|
187
289
|
conn = {{broker.class_name}}({%if broker.hostname is none%}'localhost', 1883{%endif%})
|
188
290
|
server = {{stinger.python.server_class_name}}(conn)
|
189
291
|
|
292
|
+
{%for prop_name, prop in stinger.properties.items()-%}
|
293
|
+
{%if prop.arg_list | length > 1%}
|
294
|
+
server.{{prop_name | snake_case}} = {{prop.python_class}}(
|
295
|
+
{%for arg in prop.arg_list%}
|
296
|
+
{{arg.name}}={{arg.get_random_example_value()}},
|
297
|
+
{%endfor%}
|
298
|
+
)
|
299
|
+
{%else%}
|
300
|
+
server.{{prop_name | snake_case}} = {{prop.arg_list[0].get_random_example_value()}}
|
301
|
+
{%endif%}
|
302
|
+
{%endfor%}
|
303
|
+
|
190
304
|
{%for method_name, method in stinger.methods.items()%}
|
191
305
|
@server.handle_{{method_name | snake_case}}
|
192
306
|
def {{method_name | snake_case}}({%for arg in method.arg_list%}{{arg.name}}: {{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}) -> {{method.return_value_python_type}}:
|
@@ -195,6 +309,12 @@ if __name__ == '__main__':
|
|
195
309
|
return {{method.get_return_value_random_example_value('python')}}
|
196
310
|
{%endfor%}
|
197
311
|
|
312
|
+
{%for prop_name, prop in stinger.properties.items()%}
|
313
|
+
@server.on_{{prop_name | snake_case}}_updates
|
314
|
+
def on_{{prop_name | snake_case}}_update({%for arg in prop.arg_list %}{{arg.name}}: {{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}):
|
315
|
+
print(f"Received update for '{{prop_name}}' property: {%for arg in prop.arg_list %}{ {{arg.name}}= }{%if not loop.last%}, {%endif%}{%endfor%}")
|
316
|
+
{%endfor%}
|
317
|
+
|
198
318
|
print("Ctrl-C will stop the program.")
|
199
319
|
|
200
320
|
while True:
|
@@ -18,7 +18,7 @@ use serde_json;
|
|
18
18
|
use {{stinger.rust.common_package_name}}::{MethodResultCode, *};
|
19
19
|
|
20
20
|
use std::sync::{Arc, Mutex};
|
21
|
-
use tokio::sync::{broadcast, mpsc{%if stinger.methods|length > 0 -%}, oneshot{%endif-%}};
|
21
|
+
use tokio::sync::{broadcast, mpsc{%if stinger.methods|length > 0 -%}, oneshot{%endif-%}{%if stinger.properties|length > 0 -%}, watch{%endif-%}};
|
22
22
|
use tokio::task::JoinError;
|
23
23
|
|
24
24
|
/// This struct is used to store all the MQTTv5 subscription ids
|
@@ -56,6 +56,7 @@ pub struct {{stinger.name | UpperCamelCase}}Properties {
|
|
56
56
|
{%-else%}
|
57
57
|
pub {{prop_name | snake_case}}: Arc<Mutex<Option<{{prop.arg_list[0].rust_type}}>>>,
|
58
58
|
{%endif%}
|
59
|
+
{{prop_name | snake_case}}_tx_channel: watch::Sender<Option<{%if prop.arg_list | length > 1 %}{{prop.rust_type}}{% else %}{{prop.arg_list[0].rust_type}}{% endif %}>>,
|
59
60
|
{%-endfor%}
|
60
61
|
}
|
61
62
|
{%endif%}
|
@@ -125,9 +126,10 @@ impl {{stinger.rust.client_struct_name}} {
|
|
125
126
|
{%-for prop_name, prop in stinger.properties.items()%}
|
126
127
|
{%if prop.arg_list | length > 1 -%}
|
127
128
|
{{prop_name | snake_case}}: Arc::new(Mutex::new(None)),
|
128
|
-
{
|
129
|
+
{%-else%}
|
129
130
|
{{prop_name | snake_case}}: Arc::new(Mutex::new(None)),
|
130
|
-
{%-endif
|
131
|
+
{%-endif-%}
|
132
|
+
{{prop_name | snake_case}}_tx_channel: watch::channel(None).0,
|
131
133
|
{%-endfor%}
|
132
134
|
};
|
133
135
|
{%endif%}
|
@@ -248,6 +250,20 @@ impl {{stinger.rust.client_struct_name}} {
|
|
248
250
|
}
|
249
251
|
}
|
250
252
|
{%endfor%}
|
253
|
+
{%for prop_name, prop in stinger.properties.items()%}
|
254
|
+
/// Watch for changes to the `{{prop_name}}` property.
|
255
|
+
/// This returns a watch::Receiver that can be awaited on for changes to the property value.
|
256
|
+
pub fn watch_{{prop_name | snake_case}}(&self) -> watch::Receiver<Option<{%if prop.arg_list | length > 1 %}{{prop.rust_type}}{% else %}{{prop.arg_list[0].rust_type}}{% endif %}>> {
|
257
|
+
self.properties.{{prop_name | snake_case}}_tx_channel.subscribe()
|
258
|
+
}
|
259
|
+
{%if not prop.read_only %}
|
260
|
+
pub fn set_{{prop_name | snake_case}}(&mut self, value: {%if prop.arg_list | length > 1 %}{{prop.rust_type}}{% else %}{{prop.arg_list[0].rust_type}}{% endif %}) -> Result<(), MethodResultCode> {
|
261
|
+
let data = {%if prop.arg_list | length > 1 %}value{% else %}value{% endif %};
|
262
|
+
let _publish_result = self.mqttier_client.publish_structure("{{prop.update_topic}}".to_string(), &data);
|
263
|
+
Ok(())
|
264
|
+
}
|
265
|
+
{%endif%}
|
266
|
+
{%endfor%}
|
251
267
|
|
252
268
|
/// Starts the tasks that process messages received.
|
253
269
|
pub async fn run_loop(&self) -> Result<(), JoinError> {
|
@@ -262,8 +278,9 @@ impl {{stinger.rust.client_struct_name}} {
|
|
262
278
|
let mut guard = self.msg_streamer_rx.lock().expect("Mutex was poisoned");
|
263
279
|
guard.take().expect("msg_streamer_rx should be Some")
|
264
280
|
};
|
265
|
-
|
281
|
+
{%if stinger.signals | length > 0 %}
|
266
282
|
let sig_chans = self.signal_channels.clone();
|
283
|
+
{%endif%}
|
267
284
|
let sub_ids = self.subscription_ids.clone();
|
268
285
|
{%-if stinger.properties|length > 0%}
|
269
286
|
let props = self.properties.clone();
|
@@ -292,7 +309,9 @@ impl {{stinger.rust.client_struct_name}} {
|
|
292
309
|
{%if not loop.first%}else {%endif%}if msg.subscription_id == sub_ids.{{prop_name | snake_case}}_property_value {
|
293
310
|
let pl: {{prop.rust_type}} = serde_json::from_slice(&msg.payload).expect("Failed to deserialize");
|
294
311
|
let mut guard = props.{{prop_name | snake_case}}.lock().expect("Mutex was poisoned");
|
295
|
-
*guard = Some(pl);
|
312
|
+
*guard = Some(pl.clone());
|
313
|
+
// Notify any watchers of the property that it has changed.
|
314
|
+
let _ = props.{{prop_name | snake_case}}_tx_channel.send(Some(pl));
|
296
315
|
}
|
297
316
|
{%endfor%}
|
298
317
|
}
|
@@ -4,31 +4,64 @@ on the next generation.
|
|
4
4
|
|
5
5
|
It contains enumerations used by the {{stinger.name}} interface.
|
6
6
|
*/
|
7
|
+
use std::any::Any;
|
7
8
|
use futures::{executor::block_on};
|
8
9
|
use mqttier::MqttierClient;
|
9
|
-
use {{stinger.rust.server_package_name}}::{{stinger.rust.server_struct_name}};
|
10
|
+
use {{stinger.rust.server_package_name}}::{ {{stinger.rust.server_struct_name}}{%if stinger.methods|length>0%}, {{stinger.name | UpperCamelCase }}MethodHandlers{%endif%} };
|
10
11
|
use tokio::time::{sleep, Duration};
|
11
|
-
|
12
|
+
{%if stinger.methods|length > 0%}
|
13
|
+
//use {{stinger.rust.server_package_name}}::handler::{{stinger.name | UpperCamelCase }}MethodHandlers;
|
14
|
+
//use {{stinger.rust.server_package_name}}::init::Initializable;
|
15
|
+
use std::sync::{Arc, Mutex};{%endif%}
|
12
16
|
#[allow(unused_imports)]
|
13
17
|
use {{stinger.rust.common_package_name}}::payloads::{MethodResultCode, *};
|
14
18
|
|
15
|
-
{%
|
16
|
-
|
17
|
-
|
18
|
-
{%-if method.return_value_type == 'struct'%}
|
19
|
-
let rv = {{method.return_value_rust_type}} {
|
20
|
-
{%for arg in method.return_value-%}
|
21
|
-
{{arg.name}}: {{arg.get_random_example_value(lang="rust")}},
|
22
|
-
{%endfor%}
|
23
|
-
};
|
24
|
-
Ok(rv)
|
25
|
-
{%elif method.return_value_type is not false %}
|
26
|
-
Ok({{method.return_value.get_random_example_value(lang="rust")}})
|
27
|
-
{%else%}
|
28
|
-
Ok(())
|
29
|
-
{%-endif%}
|
19
|
+
{%if stinger.methods|length > 0%}
|
20
|
+
struct {{stinger.name | UpperCamelCase }}MethodImpl {
|
21
|
+
server: Option<{{stinger.rust.server_struct_name}}>,
|
30
22
|
}
|
31
|
-
|
23
|
+
|
24
|
+
impl {{stinger.name | UpperCamelCase }}MethodImpl {
|
25
|
+
fn new() -> Self {
|
26
|
+
Self {
|
27
|
+
server: None,
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
impl {{stinger.name | UpperCamelCase }}MethodHandlers for {{stinger.name | UpperCamelCase }}MethodImpl {
|
33
|
+
|
34
|
+
fn initialize(&mut self, server: {{stinger.rust.server_struct_name}}) -> Result<(), MethodResultCode> {
|
35
|
+
self.server = Some(server.clone());
|
36
|
+
Ok(())
|
37
|
+
}
|
38
|
+
|
39
|
+
{%for method_name, method in stinger.methods.items()%}
|
40
|
+
fn handle_{{method_name|snake_case}}(&self, {%for arg in method.arg_list%}_{{arg.name|snake_case}}: {{arg.rust_type}}{%if not loop.last%}, {%endif%}{%endfor%}) -> Result<{{method.return_value_rust_type}}, MethodResultCode> {
|
41
|
+
println!("Handling {{method_name}}");
|
42
|
+
{%-if method.return_value_type == 'struct'%}
|
43
|
+
let rv = {{method.return_value_rust_type}} {
|
44
|
+
{%for arg in method.return_value-%}
|
45
|
+
{{arg.name}}: {{arg.get_random_example_value(lang="rust")}},
|
46
|
+
{%endfor%}
|
47
|
+
};
|
48
|
+
Ok(rv)
|
49
|
+
{%elif method.return_value_type is not false %}
|
50
|
+
Ok({{method.return_value.get_random_example_value(lang="rust")}})
|
51
|
+
{%else%}
|
52
|
+
Ok(())
|
53
|
+
{%-endif%}
|
54
|
+
}
|
55
|
+
{%endfor%}
|
56
|
+
|
57
|
+
fn as_any(&self) -> &dyn Any {
|
58
|
+
self
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
{%endif%}
|
64
|
+
|
32
65
|
|
33
66
|
|
34
67
|
#[tokio::main]
|
@@ -40,8 +73,13 @@ async fn main() {
|
|
40
73
|
block_on(async {
|
41
74
|
{%set broker = stinger.get_example_broker()%}
|
42
75
|
let mut connection = MqttierClient::new("localhost", 1883, None).unwrap();
|
76
|
+
{%if stinger.methods|length > 0%}
|
77
|
+
let handlers: Arc<Mutex<Box<dyn {{stinger.name | UpperCamelCase }}MethodHandlers>>> = Arc::new(Mutex::new(Box::new({{stinger.name | UpperCamelCase }}MethodImpl::new())));
|
78
|
+
let mut server = {{stinger.rust.server_struct_name}}::new(&mut connection, handlers.clone()).await;
|
79
|
+
{%else%}
|
43
80
|
let mut server = {{stinger.rust.server_struct_name}}::new(&mut connection).await;
|
44
|
-
|
81
|
+
{%endif%}
|
82
|
+
|
45
83
|
{%for prop_name, prop in stinger.properties.items()%}
|
46
84
|
println!("Setting initial value for property '{{prop_name}}'");
|
47
85
|
{%-if prop.arg_list | length == 1 %}
|
@@ -55,9 +93,7 @@ async fn main() {
|
|
55
93
|
server.set_{{prop_name | snake_case}}(new_value).await;
|
56
94
|
{%endif%}
|
57
95
|
{%endfor%}{# property initialization -#}
|
58
|
-
|
59
|
-
server.set_method_handler_for_{{method_name|snake_case}}({{method_name|snake_case}}_handler);
|
60
|
-
{%endfor%}
|
96
|
+
|
61
97
|
{%for sig_name, sig in stinger.signals.items()%}
|
62
98
|
sleep(Duration::from_secs(1)).await;
|
63
99
|
println!("Emitting signal '{{sig_name}}'");
|
@@ -77,7 +113,7 @@ async fn main() {
|
|
77
113
|
server.set_{{prop_name | snake_case}}(new_value).await;
|
78
114
|
{%endif%}
|
79
115
|
{%endfor%}{# property initialization -#}
|
80
|
-
let _server_loop_task = server.
|
116
|
+
let _server_loop_task = server.run_loop().await;
|
81
117
|
});
|
82
118
|
// Ctrl-C to stop
|
83
119
|
}
|