naeural-client 2.0.0__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.
- naeural_client/__init__.py +13 -0
- naeural_client/_ver.py +13 -0
- naeural_client/base/__init__.py +6 -0
- naeural_client/base/distributed_custom_code_presets.py +44 -0
- naeural_client/base/generic_session.py +1763 -0
- naeural_client/base/instance.py +616 -0
- naeural_client/base/payload/__init__.py +1 -0
- naeural_client/base/payload/payload.py +66 -0
- naeural_client/base/pipeline.py +1499 -0
- naeural_client/base/plugin_template.py +5209 -0
- naeural_client/base/responses.py +209 -0
- naeural_client/base/transaction.py +157 -0
- naeural_client/base_decentra_object.py +143 -0
- naeural_client/bc/__init__.py +3 -0
- naeural_client/bc/base.py +1046 -0
- naeural_client/bc/chain.py +0 -0
- naeural_client/bc/ec.py +324 -0
- naeural_client/certs/__init__.py +0 -0
- naeural_client/certs/r9092118.ala.eu-central-1.emqxsl.com.crt +22 -0
- naeural_client/code_cheker/__init__.py +1 -0
- naeural_client/code_cheker/base.py +520 -0
- naeural_client/code_cheker/checker.py +294 -0
- naeural_client/comm/__init__.py +2 -0
- naeural_client/comm/amqp_wrapper.py +338 -0
- naeural_client/comm/mqtt_wrapper.py +539 -0
- naeural_client/const/README.md +3 -0
- naeural_client/const/__init__.py +9 -0
- naeural_client/const/base.py +101 -0
- naeural_client/const/comms.py +80 -0
- naeural_client/const/environment.py +26 -0
- naeural_client/const/formatter.py +7 -0
- naeural_client/const/heartbeat.py +111 -0
- naeural_client/const/misc.py +20 -0
- naeural_client/const/payload.py +190 -0
- naeural_client/default/__init__.py +1 -0
- naeural_client/default/instance/__init__.py +4 -0
- naeural_client/default/instance/chain_dist_custom_job_01_plugin.py +54 -0
- naeural_client/default/instance/custom_web_app_01_plugin.py +118 -0
- naeural_client/default/instance/net_mon_01_plugin.py +45 -0
- naeural_client/default/instance/view_scene_01_plugin.py +28 -0
- naeural_client/default/session/mqtt_session.py +72 -0
- naeural_client/io_formatter/__init__.py +2 -0
- naeural_client/io_formatter/base/__init__.py +1 -0
- naeural_client/io_formatter/base/base_formatter.py +80 -0
- naeural_client/io_formatter/default/__init__.py +3 -0
- naeural_client/io_formatter/default/a_dummy.py +51 -0
- naeural_client/io_formatter/default/aixp1.py +113 -0
- naeural_client/io_formatter/default/default.py +22 -0
- naeural_client/io_formatter/io_formatter_manager.py +96 -0
- naeural_client/logging/__init__.py +1 -0
- naeural_client/logging/base_logger.py +2056 -0
- naeural_client/logging/logger_mixins/__init__.py +12 -0
- naeural_client/logging/logger_mixins/class_instance_mixin.py +92 -0
- naeural_client/logging/logger_mixins/computer_vision_mixin.py +443 -0
- naeural_client/logging/logger_mixins/datetime_mixin.py +344 -0
- naeural_client/logging/logger_mixins/download_mixin.py +421 -0
- naeural_client/logging/logger_mixins/general_serialization_mixin.py +242 -0
- naeural_client/logging/logger_mixins/json_serialization_mixin.py +481 -0
- naeural_client/logging/logger_mixins/pickle_serialization_mixin.py +301 -0
- naeural_client/logging/logger_mixins/process_mixin.py +63 -0
- naeural_client/logging/logger_mixins/resource_size_mixin.py +81 -0
- naeural_client/logging/logger_mixins/timers_mixin.py +501 -0
- naeural_client/logging/logger_mixins/upload_mixin.py +260 -0
- naeural_client/logging/logger_mixins/utils_mixin.py +675 -0
- naeural_client/logging/small_logger.py +93 -0
- naeural_client/logging/tzlocal/__init__.py +20 -0
- naeural_client/logging/tzlocal/unix.py +231 -0
- naeural_client/logging/tzlocal/utils.py +113 -0
- naeural_client/logging/tzlocal/win32.py +151 -0
- naeural_client/logging/tzlocal/windows_tz.py +718 -0
- naeural_client/plugins_manager_mixin.py +273 -0
- naeural_client/utils/__init__.py +2 -0
- naeural_client/utils/comm_utils.py +44 -0
- naeural_client/utils/dotenv.py +75 -0
- naeural_client-2.0.0.dist-info/METADATA +365 -0
- naeural_client-2.0.0.dist-info/RECORD +78 -0
- naeural_client-2.0.0.dist-info/WHEEL +4 -0
- naeural_client-2.0.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,539 @@
|
|
1
|
+
# PAHO
|
2
|
+
# TODO: implement config validation and base config format
|
3
|
+
# TODO: add queue for to_send messages
|
4
|
+
|
5
|
+
# TODO: adding a lock for accessing self._mqttc should solve some of the bugs, but it introduces a new one
|
6
|
+
# basically, when a user thread calls send, they should acquire the lock for the self._mqttc object
|
7
|
+
# and use it to send messages. However, if the mqttc has loop started but did not connect, the lock will
|
8
|
+
# prevent the client from ever connecting.
|
9
|
+
|
10
|
+
import os
|
11
|
+
import traceback
|
12
|
+
from collections import deque
|
13
|
+
from threading import Lock
|
14
|
+
from time import sleep
|
15
|
+
|
16
|
+
import paho.mqtt.client as mqtt
|
17
|
+
from paho.mqtt import __version__ as mqtt_version
|
18
|
+
|
19
|
+
from ..const import BASE_CT, COLORS, COMMS, PAYLOAD_CT
|
20
|
+
from ..utils import resolve_domain_or_ip
|
21
|
+
|
22
|
+
from importlib import resources as impresources
|
23
|
+
from .. import certs
|
24
|
+
|
25
|
+
|
26
|
+
class MQTTWrapper(object):
|
27
|
+
def __init__(self,
|
28
|
+
log,
|
29
|
+
config,
|
30
|
+
recv_buff=None,
|
31
|
+
send_channel_name=None,
|
32
|
+
recv_channel_name=None,
|
33
|
+
comm_type=None,
|
34
|
+
on_message=None,
|
35
|
+
post_default_on_message=None, # callback that gets called after custom or default rcv callback
|
36
|
+
debug_errors=False,
|
37
|
+
connection_name='MqttWrapper',
|
38
|
+
verbosity=1,
|
39
|
+
**kwargs):
|
40
|
+
self.log = log
|
41
|
+
self._config = config
|
42
|
+
self._recv_buff = recv_buff
|
43
|
+
self._mqttc = None
|
44
|
+
self.debug_errors = debug_errors
|
45
|
+
self._thread_name = None
|
46
|
+
self.connected = False
|
47
|
+
self.disconnected = False
|
48
|
+
self.__verbosity = verbosity
|
49
|
+
self._send_to = None
|
50
|
+
self._nr_full_retries = 0
|
51
|
+
self.__nr_dropped_messages = 0
|
52
|
+
self._comm_type = comm_type
|
53
|
+
self.send_channel_name = send_channel_name
|
54
|
+
self.recv_channel_name = recv_channel_name
|
55
|
+
self._disconnected_log = deque(maxlen=10)
|
56
|
+
self._disconnected_counter = 0
|
57
|
+
self._custom_on_message = on_message
|
58
|
+
self._post_default_on_message = post_default_on_message
|
59
|
+
self._connection_name = connection_name
|
60
|
+
self.last_disconnect_log = ''
|
61
|
+
|
62
|
+
self.DEBUG = False
|
63
|
+
|
64
|
+
if self.recv_channel_name is not None and on_message is None:
|
65
|
+
assert self._recv_buff is not None
|
66
|
+
|
67
|
+
self.P(f"Initializing MQTTWrapper using Paho MQTT v{mqtt_version}")
|
68
|
+
super(MQTTWrapper, self).__init__(**kwargs)
|
69
|
+
return
|
70
|
+
|
71
|
+
def P(self, s, color=None, verbosity=1, **kwargs):
|
72
|
+
if verbosity > self.__verbosity:
|
73
|
+
return
|
74
|
+
if color is None or (isinstance(color, str) and color[0] not in ['e', 'r']):
|
75
|
+
color = COLORS.COMM
|
76
|
+
comtype = self._comm_type[:7] if self._comm_type is not None else 'CUSTOM'
|
77
|
+
self.log.P("[MQTWRP][{}] {}".format(comtype, s), color=color, **kwargs)
|
78
|
+
return
|
79
|
+
|
80
|
+
@property
|
81
|
+
def nr_dropped_messages(self):
|
82
|
+
return self.__nr_dropped_messages
|
83
|
+
|
84
|
+
def D(self, s, t=False):
|
85
|
+
_r = -1
|
86
|
+
if self.DEBUG:
|
87
|
+
if self.show_prefixes:
|
88
|
+
msg = "[DEBUG] {}: {}".format(self.__name__, s)
|
89
|
+
else:
|
90
|
+
if self.prefix_log is None:
|
91
|
+
msg = "[D] {}".format(s)
|
92
|
+
else:
|
93
|
+
msg = "[D]{} {}".format(self.prefix_log, s)
|
94
|
+
# endif
|
95
|
+
# endif
|
96
|
+
_r = self.log.P(msg, show_time=t, color='yellow')
|
97
|
+
# endif
|
98
|
+
return _r
|
99
|
+
|
100
|
+
@property
|
101
|
+
def is_secured(self):
|
102
|
+
val = self.cfg_secured
|
103
|
+
if isinstance(val, str):
|
104
|
+
val = val.upper() in ["1", "TRUE", "YES"]
|
105
|
+
return val
|
106
|
+
|
107
|
+
@property
|
108
|
+
def send_channel_name(self):
|
109
|
+
return self._send_channel_name
|
110
|
+
|
111
|
+
@property
|
112
|
+
def recv_channel_name(self):
|
113
|
+
return self._recv_channel_name
|
114
|
+
|
115
|
+
@send_channel_name.setter
|
116
|
+
def send_channel_name(self, x):
|
117
|
+
if isinstance(x, tuple):
|
118
|
+
self._send_channel_name, self._send_to = x
|
119
|
+
else:
|
120
|
+
self._send_channel_name = x
|
121
|
+
return
|
122
|
+
|
123
|
+
@recv_channel_name.setter
|
124
|
+
def recv_channel_name(self, x):
|
125
|
+
self._recv_channel_name = x
|
126
|
+
return
|
127
|
+
|
128
|
+
@property
|
129
|
+
def cfg_node_id(self):
|
130
|
+
return self._config.get(COMMS.EE_ID, self._config.get(COMMS.SB_ID, None))
|
131
|
+
|
132
|
+
@property
|
133
|
+
def cfg_node_addr(self):
|
134
|
+
return self._config.get(COMMS.EE_ADDR)
|
135
|
+
|
136
|
+
@property
|
137
|
+
def cfg_user(self):
|
138
|
+
return self._config[COMMS.USER]
|
139
|
+
|
140
|
+
@property
|
141
|
+
def cfg_pass(self):
|
142
|
+
return self._config[COMMS.PASS]
|
143
|
+
|
144
|
+
@property
|
145
|
+
def cfg_host(self):
|
146
|
+
return self._config[COMMS.HOST]
|
147
|
+
|
148
|
+
@property
|
149
|
+
def cfg_port(self):
|
150
|
+
return self._config[COMMS.PORT]
|
151
|
+
|
152
|
+
@property
|
153
|
+
def cfg_qos(self):
|
154
|
+
return self._config[COMMS.QOS]
|
155
|
+
|
156
|
+
@property
|
157
|
+
def cfg_cert_path(self):
|
158
|
+
return self._config.get(COMMS.CERT_PATH)
|
159
|
+
|
160
|
+
@property
|
161
|
+
def cfg_secured(self):
|
162
|
+
return self._config.get(COMMS.SECURED, 0) # TODO: make 1 later on
|
163
|
+
|
164
|
+
@property
|
165
|
+
def recv_channel_def(self):
|
166
|
+
if self.recv_channel_name is None:
|
167
|
+
return
|
168
|
+
|
169
|
+
cfg = self._config[self.recv_channel_name].copy()
|
170
|
+
topic = cfg[COMMS.TOPIC]
|
171
|
+
lst_topics = []
|
172
|
+
if "{}" in topic:
|
173
|
+
if self.cfg_node_id is not None:
|
174
|
+
lst_topics.append(topic.format(self.cfg_node_id))
|
175
|
+
if self.cfg_node_addr is not None:
|
176
|
+
lst_topics.append(topic.format(self.cfg_node_addr))
|
177
|
+
else:
|
178
|
+
lst_topics.append(topic)
|
179
|
+
|
180
|
+
if len(lst_topics) == 0:
|
181
|
+
raise ValueError("ERROR! No topics to subscribe to")
|
182
|
+
|
183
|
+
cfg[COMMS.TOPIC] = lst_topics
|
184
|
+
return cfg
|
185
|
+
|
186
|
+
@property
|
187
|
+
def send_channel_def(self):
|
188
|
+
if self.send_channel_name is None:
|
189
|
+
return
|
190
|
+
|
191
|
+
cfg = self._config[self.send_channel_name].copy()
|
192
|
+
topic = cfg[COMMS.TOPIC]
|
193
|
+
if self._send_to is not None and "{}" in topic:
|
194
|
+
topic = topic.format(self._send_to)
|
195
|
+
|
196
|
+
assert "{}" not in topic
|
197
|
+
|
198
|
+
cfg[COMMS.TOPIC] = topic
|
199
|
+
return cfg
|
200
|
+
|
201
|
+
@property
|
202
|
+
def connection(self):
|
203
|
+
return self._mqttc
|
204
|
+
|
205
|
+
def __get_client_id(self):
|
206
|
+
mqttc = self._mqttc
|
207
|
+
client_id = str(mqttc._client_id) if mqttc is not None else 'None'
|
208
|
+
return client_id
|
209
|
+
|
210
|
+
def __maybe_set_mqtt_tls(self, mqttc: mqtt.Client):
|
211
|
+
if self.is_secured: # no need to set TLS if not configured with "SECURED" : 1
|
212
|
+
self.P("Setting up secured comms on PORT: {}".format(self.cfg_port))
|
213
|
+
cert_path = str(self.cfg_cert_path)
|
214
|
+
|
215
|
+
if cert_path.upper() in ["", "NONE", "NULL"]:
|
216
|
+
cert_file_name = self.cfg_host + ".crt"
|
217
|
+
cert_file = impresources.files(certs).joinpath(cert_file_name)
|
218
|
+
|
219
|
+
if cert_file.exists():
|
220
|
+
self.P("Using certificate file: {}".format(cert_file_name))
|
221
|
+
mqttc.tls_set(cert_file)
|
222
|
+
else:
|
223
|
+
self.P("No certificate provided, using default TLS")
|
224
|
+
mqttc.tls_set()
|
225
|
+
# end if certificate not provided
|
226
|
+
else:
|
227
|
+
if os.path.exists(cert_path):
|
228
|
+
self.P("Using certificate file: {}".format(cert_path))
|
229
|
+
mqttc.tls_set(cert_path)
|
230
|
+
else:
|
231
|
+
self.P("Certificate file not found: {}".format(cert_path), color='r', verbosity=1)
|
232
|
+
self.P("Using default TLS", verbosity=1)
|
233
|
+
mqttc.tls_set()
|
234
|
+
# end if certificate provided
|
235
|
+
else:
|
236
|
+
self.P("Communication is not secured. SECURED: {}, PORT: {}".format(
|
237
|
+
self.cfg_secured, self.cfg_port), color='r'
|
238
|
+
)
|
239
|
+
# end if secured
|
240
|
+
return
|
241
|
+
|
242
|
+
def __create_mqttc_object(self, comtype, client_uid):
|
243
|
+
client_id = self._connection_name + '_' + comtype + '_' + client_uid
|
244
|
+
if mqtt_version.startswith('2'):
|
245
|
+
mqttc = mqtt.Client(
|
246
|
+
callback_api_version=mqtt.CallbackAPIVersion.VERSION2,
|
247
|
+
client_id=client_id,
|
248
|
+
clean_session=True,
|
249
|
+
)
|
250
|
+
else:
|
251
|
+
mqttc = mqtt.Client(
|
252
|
+
client_id=client_id,
|
253
|
+
clean_session=True
|
254
|
+
)
|
255
|
+
|
256
|
+
mqttc.username_pw_set(
|
257
|
+
username=self.cfg_user,
|
258
|
+
password=self.cfg_pass
|
259
|
+
)
|
260
|
+
|
261
|
+
self.__maybe_set_mqtt_tls(mqttc)
|
262
|
+
|
263
|
+
mqttc.on_connect = self._callback_on_connect
|
264
|
+
mqttc.on_disconnect = self._callback_on_disconnect
|
265
|
+
mqttc.on_message = self._callback_on_message
|
266
|
+
mqttc.on_publish = self._callback_on_publish
|
267
|
+
|
268
|
+
return mqttc
|
269
|
+
|
270
|
+
def __sleep_until_connected(self, max_sleep, sleep_time):
|
271
|
+
for sleep_iter in range(1, int(max_sleep / sleep_time) + 1):
|
272
|
+
sleep(sleep_time)
|
273
|
+
if self.connected:
|
274
|
+
break
|
275
|
+
# endfor
|
276
|
+
return sleep_iter
|
277
|
+
|
278
|
+
def _callback_on_connect(self, client, userdata, flags, rc, *args, **kwargs):
|
279
|
+
self.connected = False
|
280
|
+
if rc == 0:
|
281
|
+
self.connected = True
|
282
|
+
self.P("Conn ok clntid '{}' with code: {}".format(
|
283
|
+
self.__get_client_id(), rc), color='g', verbosity=1)
|
284
|
+
return
|
285
|
+
|
286
|
+
def _callback_on_disconnect(self, client, userdata, rc, *args, **kwargs):
|
287
|
+
"""
|
288
|
+
Tricky callback
|
289
|
+
|
290
|
+
we can piggy-back ride the client with flags:
|
291
|
+
client.connected_flag = False
|
292
|
+
client.disconnect_flag = True
|
293
|
+
"""
|
294
|
+
|
295
|
+
if mqtt_version.startswith('2'):
|
296
|
+
# In version 2, on_disconnect has a different order of parameters, and rc is passed as the 4th parameter
|
297
|
+
# check https://eclipse.dev/paho/files/paho.mqtt.python/html/migrations.html for more info
|
298
|
+
rc = args[0]
|
299
|
+
if rc == 0:
|
300
|
+
self.P('Graceful disconnect (reason_code={})'.format(rc), color='m', verbosity=1)
|
301
|
+
str_error = "Graceful disconnect."
|
302
|
+
else:
|
303
|
+
str_error = mqtt.error_string(rc) + ' (reason_code={})'.format(rc)
|
304
|
+
self.P("Unexpected disconnect for client id '{}': {}".format(
|
305
|
+
self.__get_client_id(), str_error), color='r', verbosity=1)
|
306
|
+
|
307
|
+
if self._disconnected_counter > 0:
|
308
|
+
self.P("Trying to determine IP of target server...", verbosity=1)
|
309
|
+
ok, str_ip, str_domain = resolve_domain_or_ip(self.cfg_host)
|
310
|
+
msg = ' Multiple conn loss ({} disconnects so far), showing previous 10:\n{}'.format(
|
311
|
+
self._disconnected_counter, self.last_disconnect_log
|
312
|
+
)
|
313
|
+
server_port = "***** Please check server connection: {}:{} {} *****".format(
|
314
|
+
self.cfg_host, self.cfg_port,
|
315
|
+
"({}:{})".format(str_ip, self.cfg_port) if (ok and str_ip != str_domain) else ""
|
316
|
+
)
|
317
|
+
msg += "\n\n{}\n{}\n{}".format("*" * len(server_port), server_port, "*" * len(server_port))
|
318
|
+
self.P(msg, color='r', verbosity=1)
|
319
|
+
# endif multiple disconnects
|
320
|
+
self.connected = False
|
321
|
+
self.disconnected = True
|
322
|
+
self._disconnected_log.append((self.log.time_to_str(), str_error))
|
323
|
+
self._disconnected_counter += 1
|
324
|
+
self.last_disconnect_log = '\n'.join([f"* Comm error '{x2}' occurred at {x1}" for x1, x2 in self._disconnected_log])
|
325
|
+
# we need to stop the loop otherwise the client thread will keep working
|
326
|
+
# so we call release->loop_stop
|
327
|
+
|
328
|
+
self.release()
|
329
|
+
return
|
330
|
+
|
331
|
+
def _callback_on_publish(self, client, userdata, mid, *args, **kwargs):
|
332
|
+
return
|
333
|
+
|
334
|
+
def _callback_on_message(self, client, userdata, message, *args, **kwargs):
|
335
|
+
if self._custom_on_message is not None:
|
336
|
+
self._custom_on_message(client, userdata, message)
|
337
|
+
else:
|
338
|
+
try:
|
339
|
+
msg = message.payload.decode('utf-8')
|
340
|
+
self._recv_buff.append(msg)
|
341
|
+
except:
|
342
|
+
# DEBUG TODO: enable here a debug show of the message.payload if
|
343
|
+
# the number of dropped messages rises
|
344
|
+
# TODO: add also to ANY OTHER wrapper
|
345
|
+
self.__nr_dropped_messages += 1
|
346
|
+
# now call the "post-process" callback
|
347
|
+
if self._post_default_on_message is not None:
|
348
|
+
self._post_default_on_message()
|
349
|
+
return
|
350
|
+
|
351
|
+
def get_connection_issues(self):
|
352
|
+
return {x1: x2 for x1, x2 in self._disconnected_log}
|
353
|
+
|
354
|
+
def server_connect(self, max_retries=5):
|
355
|
+
max_sleep = 2
|
356
|
+
sleep_time = 0.01
|
357
|
+
nr_retry = 1
|
358
|
+
has_connection = False
|
359
|
+
exception = None
|
360
|
+
sleep_iter = None
|
361
|
+
comtype = self._comm_type[:7] if self._comm_type is not None else 'CUSTOM'
|
362
|
+
|
363
|
+
while nr_retry <= max_retries:
|
364
|
+
try:
|
365
|
+
# 1. create a unique client id
|
366
|
+
client_uid = self.log.get_unique_id()
|
367
|
+
|
368
|
+
# 2. create the mqtt client object (with callbacks set)
|
369
|
+
self._mqttc = self.__create_mqttc_object(comtype, client_uid)
|
370
|
+
|
371
|
+
# TODO: more verbose logging including when there is no actual exception
|
372
|
+
# 3. connect to the server
|
373
|
+
self._mqttc.connect(host=self.cfg_host, port=self.cfg_port)
|
374
|
+
|
375
|
+
# 4. start the loop in another thread
|
376
|
+
if self._mqttc is not None:
|
377
|
+
self._mqttc.loop_start() # start loop in another thread
|
378
|
+
|
379
|
+
# 5. wait until connected
|
380
|
+
sleep_iter = self.__sleep_until_connected(max_sleep=max_sleep, sleep_time=sleep_time)
|
381
|
+
|
382
|
+
has_connection = self.connected
|
383
|
+
except Exception as e:
|
384
|
+
exception = e
|
385
|
+
if self.debug_errors:
|
386
|
+
self.P(exception, color='r', verbosity=1)
|
387
|
+
self.P(traceback.format_exc(), color='r', verbosity=1)
|
388
|
+
|
389
|
+
# end try-except
|
390
|
+
|
391
|
+
if has_connection:
|
392
|
+
break
|
393
|
+
|
394
|
+
nr_retry += 1
|
395
|
+
# endwhile
|
396
|
+
|
397
|
+
# set thread name ; useful for debugging
|
398
|
+
mqttc = self._mqttc
|
399
|
+
if mqttc is not None and hasattr(mqttc, '_thread') and mqttc._thread is not None:
|
400
|
+
mqttc._thread.name = self._connection_name + '_' + comtype + '_' + client_uid
|
401
|
+
self._thread_name = mqttc._thread.name
|
402
|
+
|
403
|
+
if has_connection:
|
404
|
+
msg = "MQTT conn ok by '{}' in {:.1f}s - {}:{}".format(
|
405
|
+
self._thread_name,
|
406
|
+
sleep_iter * sleep_time,
|
407
|
+
self.cfg_host,
|
408
|
+
self.cfg_port
|
409
|
+
)
|
410
|
+
msg_type = PAYLOAD_CT.STATUS_TYPE.STATUS_NORMAL
|
411
|
+
self._nr_full_retries = 0
|
412
|
+
|
413
|
+
self.P(msg)
|
414
|
+
|
415
|
+
else:
|
416
|
+
reason = exception
|
417
|
+
if reason is None:
|
418
|
+
reason = " max retries in {:.1f}s".format(sleep_iter * sleep_time)
|
419
|
+
|
420
|
+
self._nr_full_retries += 1
|
421
|
+
msg = 'MQTT (Paho) conn to {}:{} failed after {} retr ({} trials) (reason:{})'.format(
|
422
|
+
self.cfg_host,
|
423
|
+
self.cfg_port,
|
424
|
+
nr_retry,
|
425
|
+
self._nr_full_retries,
|
426
|
+
reason
|
427
|
+
)
|
428
|
+
msg_type = PAYLOAD_CT.STATUS_TYPE.STATUS_EXCEPTION
|
429
|
+
self.P(msg, color='r', verbosity=1)
|
430
|
+
|
431
|
+
# endif
|
432
|
+
|
433
|
+
dct_ret = {
|
434
|
+
'has_connection': has_connection,
|
435
|
+
'msg': msg,
|
436
|
+
'msg_type': msg_type
|
437
|
+
}
|
438
|
+
|
439
|
+
# if release was not called from on_disconnect, basically
|
440
|
+
# this method of checking self._mqttc is not None is not
|
441
|
+
# very reliable, as race conditions can occur
|
442
|
+
if self._mqttc is not None and not has_connection:
|
443
|
+
self.release()
|
444
|
+
|
445
|
+
return dct_ret
|
446
|
+
|
447
|
+
def get_thread_name(self):
|
448
|
+
return self._thread_name
|
449
|
+
|
450
|
+
def subscribe(self, max_retries=5):
|
451
|
+
|
452
|
+
if self.recv_channel_name is None:
|
453
|
+
return
|
454
|
+
|
455
|
+
nr_retry = 1
|
456
|
+
has_connection = True
|
457
|
+
exception = None
|
458
|
+
lst_topics = self.recv_channel_def[COMMS.TOPIC]
|
459
|
+
for topic in lst_topics:
|
460
|
+
current_topic_connection = False
|
461
|
+
while nr_retry <= max_retries:
|
462
|
+
try:
|
463
|
+
if self._mqttc is not None:
|
464
|
+
self._mqttc.subscribe(
|
465
|
+
topic=topic,
|
466
|
+
qos=self.cfg_qos
|
467
|
+
)
|
468
|
+
current_topic_connection = True
|
469
|
+
else:
|
470
|
+
has_connection = False
|
471
|
+
except Exception as e:
|
472
|
+
has_connection = False
|
473
|
+
exception = e
|
474
|
+
|
475
|
+
if current_topic_connection:
|
476
|
+
break
|
477
|
+
|
478
|
+
sleep(1)
|
479
|
+
nr_retry += 1
|
480
|
+
# endwhile
|
481
|
+
|
482
|
+
if current_topic_connection:
|
483
|
+
msg = "MQTT (Paho) subscribed to topic '{}'".format(topic)
|
484
|
+
msg_type = PAYLOAD_CT.STATUS_TYPE.STATUS_NORMAL
|
485
|
+
else:
|
486
|
+
msg = "MQTT (Paho) subscribe to '{}' FAILED after {} retries (reason:{})".format(topic, max_retries, exception)
|
487
|
+
msg_type = PAYLOAD_CT.STATUS_TYPE.STATUS_EXCEPTION
|
488
|
+
# endif
|
489
|
+
|
490
|
+
dct_ret = {
|
491
|
+
'has_connection': has_connection,
|
492
|
+
'msg': msg,
|
493
|
+
'msg_type': msg_type
|
494
|
+
}
|
495
|
+
|
496
|
+
return dct_ret
|
497
|
+
|
498
|
+
def receive(self):
|
499
|
+
return
|
500
|
+
|
501
|
+
def send(self, message):
|
502
|
+
mqttc = self._mqttc
|
503
|
+
if mqttc is None:
|
504
|
+
return
|
505
|
+
|
506
|
+
result = mqttc.publish(
|
507
|
+
topic=self.send_channel_def[COMMS.TOPIC],
|
508
|
+
payload=message,
|
509
|
+
qos=self.cfg_qos
|
510
|
+
)
|
511
|
+
|
512
|
+
####
|
513
|
+
self.D("Sent message '{}'".format(message))
|
514
|
+
####
|
515
|
+
|
516
|
+
if result.rc == mqtt.MQTT_ERR_QUEUE_SIZE:
|
517
|
+
raise ValueError('Message is not queued due to ERR_QUEUE_SIZE')
|
518
|
+
|
519
|
+
return
|
520
|
+
|
521
|
+
def release(self):
|
522
|
+
try:
|
523
|
+
mqttc = self._mqttc
|
524
|
+
|
525
|
+
if mqttc is not None:
|
526
|
+
self._mqttc.disconnect()
|
527
|
+
self._mqttc.loop_stop() # stop the loop thread
|
528
|
+
self._mqttc = None
|
529
|
+
self.connected = False
|
530
|
+
msg = 'MQTT (Paho) connection released.'
|
531
|
+
except Exception as e:
|
532
|
+
msg = 'MQTT (Paho) exception while releasing connection: `{}`'.format(str(e))
|
533
|
+
|
534
|
+
self.P(msg)
|
535
|
+
|
536
|
+
# TODO: method should return None; update code in core to reflect this
|
537
|
+
dct_ret = {'msgs': [msg]}
|
538
|
+
|
539
|
+
return dct_ret
|
@@ -0,0 +1,9 @@
|
|
1
|
+
from .misc import COLORS, WEEKDAYS_SHORT
|
2
|
+
from . import comms as COMMS
|
3
|
+
from . import base as BASE_CT
|
4
|
+
from . import payload as PAYLOAD_CT
|
5
|
+
from .formatter import FORMATTER_DATA
|
6
|
+
from .payload import STATUS_TYPE, PAYLOAD_DATA, COMMANDS, NOTIFICATION_CODES
|
7
|
+
from .base import CONFIG_STREAM, BIZ_PLUGIN_DATA, PLUGIN_INFO
|
8
|
+
from . import heartbeat as HB
|
9
|
+
from .environment import ENVIRONMENT
|
@@ -0,0 +1,101 @@
|
|
1
|
+
EE_ID = 'EE_ID'
|
2
|
+
SB_ID = 'SB_ID' # change to SB_ID = EE_ID post mod from sb to ee
|
3
|
+
|
4
|
+
|
5
|
+
class CONFIG_STREAM:
|
6
|
+
K_URL = 'URL'
|
7
|
+
K_TYPE = 'TYPE'
|
8
|
+
K_RECONNECTABLE = 'RECONNECTABLE'
|
9
|
+
K_NAME = 'NAME'
|
10
|
+
K_LIVE_FEED = 'LIVE_FEED'
|
11
|
+
K_PLUGINS = 'PLUGINS'
|
12
|
+
K_INSTANCES = 'INSTANCES'
|
13
|
+
|
14
|
+
K_MODIFIED_BY_ADDR = 'MODIFIED_BY_ADDR'
|
15
|
+
K_MODIFIED_BY_ID = 'MODIFIED_BY_ID'
|
16
|
+
|
17
|
+
K_INITIATOR_ID = 'INITIATOR_ID'
|
18
|
+
K_INITIATOR_ADDR = 'INITIATOR_ADDR'
|
19
|
+
K_SESSION_ID = 'SESSION_ID'
|
20
|
+
K_ALLOWED_PLUGINS = 'ALLOWED_PLUGINS'
|
21
|
+
|
22
|
+
K_PIPELINE_COMMAND = 'PIPELINE_COMMAND'
|
23
|
+
K_USE_LOCAL_COMMS_ONLY = 'USE_LOCAL_COMMS_ONLY'
|
24
|
+
|
25
|
+
METASTREAM = 'MetaStream'
|
26
|
+
|
27
|
+
INITIATOR_ID = K_INITIATOR_ID
|
28
|
+
SESSION_ID = K_SESSION_ID
|
29
|
+
COLLECTED_STREAMS = 'COLLECTED_STREAMS'
|
30
|
+
STREAM_CONFIG_METADATA = 'STREAM_CONFIG_METADATA'
|
31
|
+
CAP_RESOLUTION = 'CAP_RESOLUTION'
|
32
|
+
|
33
|
+
LAST_UPDATE_TIME = 'LAST_UPDATE_TIME'
|
34
|
+
|
35
|
+
URL = K_URL
|
36
|
+
TYPE = K_TYPE
|
37
|
+
RECONNECTABLE = K_RECONNECTABLE
|
38
|
+
NAME = K_NAME
|
39
|
+
LIVE_FEED = K_LIVE_FEED
|
40
|
+
PLUGINS = K_PLUGINS
|
41
|
+
INSTANCES = K_INSTANCES
|
42
|
+
PIPELINE_COMMAND = K_PIPELINE_COMMAND
|
43
|
+
LAST_PIPELINE_COMMAND = 'LAST_' + PIPELINE_COMMAND
|
44
|
+
|
45
|
+
DEFAULT_PLUGINS = 'DEFAULT_PLUGINS'
|
46
|
+
OVERWRITE_DEFAULT_PLUGIN_CONFIG = 'OVERWRITE_DEFAULT_PLUGIN_CONFIG'
|
47
|
+
|
48
|
+
DEFAULT_PLUGIN = 'DEFAULT_PLUGIN'
|
49
|
+
DEFAULT_PLUGIN_SIGNATURE = 'REST_CUSTOM_EXEC_01'
|
50
|
+
DEFAULT_PLUGIN_CONFIG = 'DEFAULT_PLUGIN_CONFIG'
|
51
|
+
|
52
|
+
ALLOWED_PLUGINS = K_ALLOWED_PLUGINS
|
53
|
+
|
54
|
+
MANDATORY = [K_NAME, K_TYPE]
|
55
|
+
|
56
|
+
VOID_STREAM = 'VOID'
|
57
|
+
|
58
|
+
NO_DATA_STREAMS = [VOID_STREAM]
|
59
|
+
|
60
|
+
|
61
|
+
class BIZ_PLUGIN_DATA:
|
62
|
+
INSTANCE_ID = 'INSTANCE_ID'
|
63
|
+
MAX_INPUTS_QUEUE_SIZE = 'MAX_INPUTS_QUEUE_SIZE'
|
64
|
+
COORDS = 'COORDS'
|
65
|
+
POINTS = 'POINTS'
|
66
|
+
INSTANCES = 'INSTANCES'
|
67
|
+
SIGNATURE = 'SIGNATURE'
|
68
|
+
PROCESS_DELAY = 'PROCESS_DELAY'
|
69
|
+
ALLOW_EMPTY_INPUTS = 'ALLOW_EMPTY_INPUTS'
|
70
|
+
RUN_WITHOUT_IMAGE = 'RUN_WITHOUT_IMAGE'
|
71
|
+
|
72
|
+
|
73
|
+
class PLUGIN_INFO:
|
74
|
+
STREAM_ID = 'STREAM_ID'
|
75
|
+
INSTANCE_ID = BIZ_PLUGIN_DATA.INSTANCE_ID
|
76
|
+
SIGNATURE = BIZ_PLUGIN_DATA.SIGNATURE
|
77
|
+
FREQUENCY = 'FREQUENCY'
|
78
|
+
EXEC_TIMESTAMP = 'EXEC_TIMESTAMP'
|
79
|
+
INIT_TIMESTAMP = 'INIT_TIMESTAMP'
|
80
|
+
LAST_CONFIG_TIMESTAMP = 'LAST_CONFIG_TIMESTAMP'
|
81
|
+
FIRST_ERROR_TIME = 'FIRST_ERROR_TIME'
|
82
|
+
LAST_ERROR_TIME = 'LAST_ERROR_TIME'
|
83
|
+
PROC_ITER = 'PROC_ITER'
|
84
|
+
EXEC_ITER = 'EXEC_ITER'
|
85
|
+
OUTSIDE_WORKING_HOURS = 'OUTSIDE_WORKING_HOURS'
|
86
|
+
ACTIVE_PLUGINS_FIELDS = [
|
87
|
+
STREAM_ID,
|
88
|
+
SIGNATURE,
|
89
|
+
INSTANCE_ID,
|
90
|
+
|
91
|
+
FREQUENCY,
|
92
|
+
INIT_TIMESTAMP,
|
93
|
+
EXEC_TIMESTAMP,
|
94
|
+
LAST_CONFIG_TIMESTAMP,
|
95
|
+
FIRST_ERROR_TIME,
|
96
|
+
LAST_ERROR_TIME,
|
97
|
+
OUTSIDE_WORKING_HOURS,
|
98
|
+
]
|
99
|
+
|
100
|
+
|
101
|
+
EE_DATE_TIME_FORMAT = "%Y.%m.%d_%H:%M:%S"
|