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.
Files changed (78) hide show
  1. naeural_client/__init__.py +13 -0
  2. naeural_client/_ver.py +13 -0
  3. naeural_client/base/__init__.py +6 -0
  4. naeural_client/base/distributed_custom_code_presets.py +44 -0
  5. naeural_client/base/generic_session.py +1763 -0
  6. naeural_client/base/instance.py +616 -0
  7. naeural_client/base/payload/__init__.py +1 -0
  8. naeural_client/base/payload/payload.py +66 -0
  9. naeural_client/base/pipeline.py +1499 -0
  10. naeural_client/base/plugin_template.py +5209 -0
  11. naeural_client/base/responses.py +209 -0
  12. naeural_client/base/transaction.py +157 -0
  13. naeural_client/base_decentra_object.py +143 -0
  14. naeural_client/bc/__init__.py +3 -0
  15. naeural_client/bc/base.py +1046 -0
  16. naeural_client/bc/chain.py +0 -0
  17. naeural_client/bc/ec.py +324 -0
  18. naeural_client/certs/__init__.py +0 -0
  19. naeural_client/certs/r9092118.ala.eu-central-1.emqxsl.com.crt +22 -0
  20. naeural_client/code_cheker/__init__.py +1 -0
  21. naeural_client/code_cheker/base.py +520 -0
  22. naeural_client/code_cheker/checker.py +294 -0
  23. naeural_client/comm/__init__.py +2 -0
  24. naeural_client/comm/amqp_wrapper.py +338 -0
  25. naeural_client/comm/mqtt_wrapper.py +539 -0
  26. naeural_client/const/README.md +3 -0
  27. naeural_client/const/__init__.py +9 -0
  28. naeural_client/const/base.py +101 -0
  29. naeural_client/const/comms.py +80 -0
  30. naeural_client/const/environment.py +26 -0
  31. naeural_client/const/formatter.py +7 -0
  32. naeural_client/const/heartbeat.py +111 -0
  33. naeural_client/const/misc.py +20 -0
  34. naeural_client/const/payload.py +190 -0
  35. naeural_client/default/__init__.py +1 -0
  36. naeural_client/default/instance/__init__.py +4 -0
  37. naeural_client/default/instance/chain_dist_custom_job_01_plugin.py +54 -0
  38. naeural_client/default/instance/custom_web_app_01_plugin.py +118 -0
  39. naeural_client/default/instance/net_mon_01_plugin.py +45 -0
  40. naeural_client/default/instance/view_scene_01_plugin.py +28 -0
  41. naeural_client/default/session/mqtt_session.py +72 -0
  42. naeural_client/io_formatter/__init__.py +2 -0
  43. naeural_client/io_formatter/base/__init__.py +1 -0
  44. naeural_client/io_formatter/base/base_formatter.py +80 -0
  45. naeural_client/io_formatter/default/__init__.py +3 -0
  46. naeural_client/io_formatter/default/a_dummy.py +51 -0
  47. naeural_client/io_formatter/default/aixp1.py +113 -0
  48. naeural_client/io_formatter/default/default.py +22 -0
  49. naeural_client/io_formatter/io_formatter_manager.py +96 -0
  50. naeural_client/logging/__init__.py +1 -0
  51. naeural_client/logging/base_logger.py +2056 -0
  52. naeural_client/logging/logger_mixins/__init__.py +12 -0
  53. naeural_client/logging/logger_mixins/class_instance_mixin.py +92 -0
  54. naeural_client/logging/logger_mixins/computer_vision_mixin.py +443 -0
  55. naeural_client/logging/logger_mixins/datetime_mixin.py +344 -0
  56. naeural_client/logging/logger_mixins/download_mixin.py +421 -0
  57. naeural_client/logging/logger_mixins/general_serialization_mixin.py +242 -0
  58. naeural_client/logging/logger_mixins/json_serialization_mixin.py +481 -0
  59. naeural_client/logging/logger_mixins/pickle_serialization_mixin.py +301 -0
  60. naeural_client/logging/logger_mixins/process_mixin.py +63 -0
  61. naeural_client/logging/logger_mixins/resource_size_mixin.py +81 -0
  62. naeural_client/logging/logger_mixins/timers_mixin.py +501 -0
  63. naeural_client/logging/logger_mixins/upload_mixin.py +260 -0
  64. naeural_client/logging/logger_mixins/utils_mixin.py +675 -0
  65. naeural_client/logging/small_logger.py +93 -0
  66. naeural_client/logging/tzlocal/__init__.py +20 -0
  67. naeural_client/logging/tzlocal/unix.py +231 -0
  68. naeural_client/logging/tzlocal/utils.py +113 -0
  69. naeural_client/logging/tzlocal/win32.py +151 -0
  70. naeural_client/logging/tzlocal/windows_tz.py +718 -0
  71. naeural_client/plugins_manager_mixin.py +273 -0
  72. naeural_client/utils/__init__.py +2 -0
  73. naeural_client/utils/comm_utils.py +44 -0
  74. naeural_client/utils/dotenv.py +75 -0
  75. naeural_client-2.0.0.dist-info/METADATA +365 -0
  76. naeural_client-2.0.0.dist-info/RECORD +78 -0
  77. naeural_client-2.0.0.dist-info/WHEEL +4 -0
  78. 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,3 @@
1
+ ## Inter-repor / inter-project universal constants
2
+
3
+ The purpose of this sub-package is to provide portable constant libraries that can be used cross-project and cross repository
@@ -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"