pbesa 2.1__py3-none-any.whl → 4.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 (75) hide show
  1. pbesa/__init__.py +4 -3
  2. pbesa/cognitive.py +475 -0
  3. pbesa/kernel/__init__.py +4 -6
  4. pbesa/kernel/adapter.py +165 -0
  5. pbesa/kernel/agent.py +558 -0
  6. pbesa/kernel/io/__init__.py +2 -2
  7. pbesa/kernel/io/system_file.py +38 -0
  8. pbesa/kernel/io/tcp_server.py +23 -0
  9. pbesa/kernel/util.py +217 -0
  10. pbesa/kernel/world.py +43 -0
  11. pbesa/mas.py +565 -0
  12. pbesa/remote/__init__.py +5 -0
  13. pbesa/remote/adm_listener.py +44 -0
  14. pbesa/{middleware/remote/AdmListenerHandler.py → remote/adm_listener_handler.py} +74 -57
  15. pbesa/remote/exceptions.py +18 -0
  16. pbesa/remote/remote_adm.py +42 -0
  17. pbesa/{middleware/remote/RemoteAdmHandler.py → remote/remote_adm_handler.py} +109 -83
  18. pbesa/social/__init__.py +4 -0
  19. pbesa/social/collaborative_team.py +299 -0
  20. pbesa/social/delegator.py +81 -0
  21. pbesa/social/delegator_team.py +334 -0
  22. pbesa/social/dialog.py +153 -0
  23. pbesa/social/dispatcher_team.py +319 -0
  24. pbesa/social/templates.py +18 -0
  25. pbesa/social/worker.py +188 -0
  26. {pbesa-2.1.dist-info → pbesa-4.0.0.dist-info}/LICENSE +21 -21
  27. {pbesa-2.1.dist-info → pbesa-4.0.0.dist-info}/LICENSE.txt +21 -21
  28. pbesa-4.0.0.dist-info/METADATA +8 -0
  29. pbesa-4.0.0.dist-info/RECORD +32 -0
  30. {pbesa-2.1.dist-info → pbesa-4.0.0.dist-info}/WHEEL +5 -5
  31. {pbesa-2.1.dist-info → pbesa-4.0.0.dist-info}/top_level.txt +0 -0
  32. pbesa/engine/__init__.py +0 -2
  33. pbesa/engine/bdi/BDIAg.py +0 -42
  34. pbesa/engine/bdi/BDILevel.py +0 -10
  35. pbesa/engine/bdi/BDIMachine.py +0 -116
  36. pbesa/engine/bdi/Goal.py +0 -28
  37. pbesa/engine/bdi/GoalExe.py +0 -36
  38. pbesa/engine/bdi/__init__.py +0 -5
  39. pbesa/engine/rational/ActionExe.py +0 -35
  40. pbesa/engine/rational/Brain.py +0 -10
  41. pbesa/engine/rational/RationalAg.py +0 -43
  42. pbesa/engine/rational/__init__.py +0 -3
  43. pbesa/kernel/adapter/Adapter.py +0 -26
  44. pbesa/kernel/adapter/FileAdapter.py +0 -23
  45. pbesa/kernel/adapter/__init__.py +0 -2
  46. pbesa/kernel/agent/Action.py +0 -19
  47. pbesa/kernel/agent/Agent.py +0 -91
  48. pbesa/kernel/agent/BehaviorExe.py +0 -33
  49. pbesa/kernel/agent/Channel.py +0 -10
  50. pbesa/kernel/agent/World.py +0 -15
  51. pbesa/kernel/agent/__init__.py +0 -5
  52. pbesa/kernel/io/SystemFile.py +0 -13
  53. pbesa/kernel/io/TCPServer.py +0 -4
  54. pbesa/kernel/system/Adm.py +0 -189
  55. pbesa/kernel/system/Directory.py +0 -36
  56. pbesa/kernel/system/__init__.py +0 -2
  57. pbesa/kernel/util/HashTable.py +0 -62
  58. pbesa/kernel/util/Queue.py +0 -231
  59. pbesa/kernel/util/__init__.py +0 -2
  60. pbesa/middleware/__init__.py +0 -3
  61. pbesa/middleware/adapter/GameAdapter.py +0 -64
  62. pbesa/middleware/adapter/MongoAdapter.py +0 -47
  63. pbesa/middleware/adapter/RESTAdapter.py +0 -52
  64. pbesa/middleware/adapter/SubProcessAdapter.py +0 -30
  65. pbesa/middleware/adapter/WSSAdapter.py +0 -89
  66. pbesa/middleware/adapter/WSSNJAdapter.py +0 -51
  67. pbesa/middleware/adapter/WSSNJHandler.py +0 -23
  68. pbesa/middleware/adapter/__init__.py +0 -7
  69. pbesa/middleware/remote/AdmListener.py +0 -17
  70. pbesa/middleware/remote/RemoteAdm.py +0 -17
  71. pbesa/middleware/remote/__init__.py +0 -4
  72. pbesa/middleware/web/WebAgTK.py +0 -15
  73. pbesa/middleware/web/__init__.py +0 -0
  74. pbesa-2.1.dist-info/METADATA +0 -25
  75. pbesa-2.1.dist-info/RECORD +0 -54
pbesa/kernel/agent.py ADDED
@@ -0,0 +1,558 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ ----------------------------------------------------------
4
+ ------------------------- PBESA --------------------------
5
+ ----------------------------------------------------------
6
+
7
+ @autor AKEN & SIDRE
8
+ @version 4.0.0
9
+ @date 09/08/24
10
+ """
11
+
12
+ # --------------------------------------------------------
13
+ # Define resources
14
+ # --------------------------------------------------------
15
+
16
+ import logging
17
+ import traceback
18
+ from threading import Thread
19
+ from abc import ABC, abstractmethod
20
+ from time import time as _time
21
+
22
+ try:
23
+ import threading as _threading
24
+ except ImportError:
25
+ import dummy_threading as _threading
26
+ from collections import deque
27
+ import heapq
28
+
29
+ # ----------------------------------------------------------
30
+ # Defines system component exceptions
31
+ # ----------------------------------------------------------
32
+
33
+ class AgentException(Exception):
34
+ """ Base class for exceptions of agent """
35
+ pass
36
+
37
+ class ActionException(Exception):
38
+ """ Base class for exceptions of agent """
39
+ pass
40
+
41
+ # --------------------------------------------------------
42
+ # Define component
43
+ # --------------------------------------------------------
44
+
45
+ __all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue']
46
+
47
+ class Empty(Exception):
48
+ "Exception raised by Queue.get(block=0)/get_nowait()."
49
+ pass
50
+
51
+ class Full(Exception):
52
+ "Exception raised by Queue.put(block=0)/put_nowait()."
53
+ pass
54
+
55
+ class Queue:
56
+ """Create a queue object with a given maximum size.
57
+ If maxsize is <= 0, the queue size is infinite.
58
+ """
59
+ def __init__(self, maxsize=0):
60
+ self.maxsize = maxsize
61
+ self._init(maxsize)
62
+ # mutex must be held whenever the queue is mutating. All methods
63
+ # that acquire mutex must release it before returning. mutex
64
+ # is shared between the three conditions, so acquiring and
65
+ # releasing the conditions also acquires and releases mutex.
66
+ self.mutex = _threading.Lock()
67
+ # Notify not_empty whenever an item is added to the queue; a
68
+ # thread waiting to get is notified then.
69
+ self.not_empty = _threading.Condition(self.mutex)
70
+ # Notify not_full whenever an item is removed from the queue;
71
+ # a thread waiting to put is notified then.
72
+ self.not_full = _threading.Condition(self.mutex)
73
+ # Notify all_tasks_done whenever the number of unfinished tasks
74
+ # drops to zero; thread waiting to join() is notified to resume
75
+ self.all_tasks_done = _threading.Condition(self.mutex)
76
+ self.unfinished_tasks = 0
77
+
78
+ def task_done(self):
79
+ """Indicate that a formerly enqueued task is complete.
80
+ Used by Queue consumer threads. For each get() used to fetch a task,
81
+ a subsequent call to task_done() tells the queue that the processing
82
+ on the task is complete.
83
+ If a join() is currently blocking, it will resume when all items
84
+ have been processed (meaning that a task_done() call was received
85
+ for every item that had been put() into the queue).
86
+ Raises a ValueError if called more times than there were items
87
+ placed in the queue.
88
+ """
89
+ self.all_tasks_done.acquire()
90
+ try:
91
+ unfinished = self.unfinished_tasks - 1
92
+ if unfinished <= 0:
93
+ if unfinished < 0:
94
+ raise ValueError('task_done() called too many times')
95
+ self.all_tasks_done.notify_all()
96
+ self.unfinished_tasks = unfinished
97
+ finally:
98
+ self.all_tasks_done.release()
99
+
100
+ def join(self):
101
+ """Blocks until all items in the Queue have been gotten and processed.
102
+ The count of unfinished tasks goes up whenever an item is added to the
103
+ queue. The count goes down whenever a consumer thread calls task_done()
104
+ to indicate the item was retrieved and all work on it is complete.
105
+ When the count of unfinished tasks drops to zero, join() unblocks.
106
+ """
107
+ self.all_tasks_done.acquire()
108
+ try:
109
+ while self.unfinished_tasks:
110
+ self.all_tasks_done.wait()
111
+ finally:
112
+ self.all_tasks_done.release()
113
+
114
+ def qsize(self):
115
+ """Return the approximate size of the queue (not reliable!)."""
116
+ self.mutex.acquire()
117
+ n = self._qsize()
118
+ self.mutex.release()
119
+ return n
120
+
121
+ def empty(self):
122
+ """Return True if the queue is empty, False otherwise (not reliable!)."""
123
+ self.mutex.acquire()
124
+ n = not self._qsize()
125
+ self.mutex.release()
126
+ return n
127
+
128
+ def full(self):
129
+ """Return True if the queue is full, False otherwise (not reliable!)."""
130
+ self.mutex.acquire()
131
+ n = 0 < self.maxsize == self._qsize()
132
+ self.mutex.release()
133
+ return n
134
+
135
+ def put(self, item, block=True, timeout=None):
136
+ """Put an item into the queue.
137
+ If optional args 'block' is true and 'timeout' is None (the default),
138
+ block if necessary until a free slot is available. If 'timeout' is
139
+ a non-negative number, it blocks at most 'timeout' seconds and raises
140
+ the Full exception if no free slot was available within that time.
141
+ Otherwise ('block' is false), put an item on the queue if a free slot
142
+ is immediately available, else raise the Full exception ('timeout'
143
+ is ignored in that case).
144
+ """
145
+ self.not_full.acquire()
146
+ try:
147
+ if self.maxsize > 0:
148
+ if not block:
149
+ if self._qsize() == self.maxsize:
150
+ raise Full
151
+ elif timeout is None:
152
+ while self._qsize() == self.maxsize:
153
+ self.not_full.wait()
154
+ elif timeout < 0:
155
+ raise ValueError("'timeout' must be a non-negative number")
156
+ else:
157
+ endtime = _time() + timeout
158
+ while self._qsize() == self.maxsize:
159
+ remaining = endtime - _time()
160
+ if remaining <= 0.0:
161
+ raise Full
162
+ self.not_full.wait(remaining)
163
+ self._put(item)
164
+ self.unfinished_tasks += 1
165
+ self.not_empty.notify()
166
+ finally:
167
+ self.not_full.release()
168
+
169
+ def put_nowait(self, item):
170
+ """Put an item into the queue without blocking.
171
+ Only enqueue the item if a free slot is immediately available.
172
+ Otherwise raise the Full exception.
173
+ """
174
+ return self.put(item, False)
175
+
176
+ def get(self, block=True, timeout=None):
177
+ """Remove and return an item from the queue.
178
+ If optional args 'block' is true and 'timeout' is None (the default),
179
+ block if necessary until an item is available. If 'timeout' is
180
+ a non-negative number, it blocks at most 'timeout' seconds and raises
181
+ the Empty exception if no item was available within that time.
182
+ Otherwise ('block' is false), return an item if one is immediately
183
+ available, else raise the Empty exception ('timeout' is ignored
184
+ in that case).
185
+ """
186
+ self.not_empty.acquire()
187
+ try:
188
+ if not block:
189
+ if not self._qsize():
190
+ raise Empty
191
+ elif timeout is None:
192
+ while not self._qsize():
193
+ self.not_empty.wait()
194
+ elif timeout < 0:
195
+ raise ValueError("'timeout' must be a non-negative number")
196
+ else:
197
+ endtime = _time() + timeout
198
+ while not self._qsize():
199
+ remaining = endtime - _time()
200
+ if remaining <= 0.0:
201
+ raise Empty
202
+ self.not_empty.wait(remaining)
203
+ item = self._get()
204
+ self.not_full.notify()
205
+ return item
206
+ finally:
207
+ self.not_empty.release()
208
+
209
+ def get_nowait(self):
210
+ """Remove and return an item from the queue without blocking.
211
+ Only get an item if one is immediately available. Otherwise
212
+ raise the Empty exception.
213
+ """
214
+ return self.get(False)
215
+
216
+ # Override these methods to implement other queue organizations
217
+ # (e.g. stack or priority queue).
218
+ # These will only be called with appropriate locks held
219
+
220
+ # Initialize the queue representation
221
+ def _init(self, maxsize):
222
+ self.queue = deque()
223
+
224
+ def _qsize(self, len=len):
225
+ return len(self.queue)
226
+
227
+ # Put a new item in the queue
228
+ def _put(self, item):
229
+ self.queue.append(item)
230
+
231
+ # Get an item from the queue
232
+ def _get(self):
233
+ return self.queue.popleft()
234
+
235
+
236
+ class PriorityQueue(Queue):
237
+ '''Variant of Queue that retrieves open entries in priority order (lowest first).
238
+ Entries are typically tuples of the form: (priority number, data).
239
+ '''
240
+
241
+ def _init(self, maxsize):
242
+ self.queue = []
243
+
244
+ def _qsize(self, len=len):
245
+ return len(self.queue)
246
+
247
+ def _put(self, item, heappush=heapq.heappush):
248
+ heappush(self.queue, item)
249
+
250
+ def _get(self, heappop=heapq.heappop):
251
+ return heappop(self.queue)
252
+
253
+
254
+ class LifoQueue(Queue):
255
+ '''Variant of Queue that retrieves most recently added entries first.'''
256
+
257
+ def _init(self, maxsize):
258
+ self.queue = []
259
+
260
+ def _qsize(self, len=len):
261
+ return len(self.queue)
262
+
263
+ def _put(self, item):
264
+ self.queue.append(item)
265
+
266
+ def _get(self):
267
+ return self.queue.pop()
268
+
269
+ # --------------------------------------------------------
270
+ # Define component
271
+ # --------------------------------------------------------
272
+
273
+ class Channel():
274
+ """ Channel class """
275
+
276
+ def __init__(self, queue: Queue) -> None:
277
+ """ Constructor
278
+ :param queue: Queue
279
+ """
280
+ self.queue = queue
281
+ super().__init__()
282
+
283
+ def send_event(self, event:any) -> None:
284
+ """ Send event
285
+ :param event: Event
286
+ """
287
+ self.queue.put(event)
288
+
289
+ # --------------------------------------------------------
290
+ # Define component
291
+ # --------------------------------------------------------
292
+
293
+ class BehaviorExe(Thread):
294
+ """ Behavior executor component """
295
+
296
+ def __init__(self, queue:Queue) -> None:
297
+ """ Constructor
298
+ @param queue: Queue
299
+ """
300
+ self.__let = False
301
+ self.__alive = True
302
+ self.__stop_agent = Queue(1)
303
+ self.__queue = queue
304
+ super().__init__()
305
+
306
+ def run(self) -> None:
307
+ """ Run """
308
+ while self.__alive:
309
+ evt = self.__queue.get()
310
+ if not self.__let:
311
+ self.__stop_agent.get()
312
+ try:
313
+ if self.__alive:
314
+ evt['action'].execute(evt['data'])
315
+ except Exception as e:
316
+ traceback.print_exc()
317
+ self.__let = False
318
+ self.__queue.task_done()
319
+
320
+ def set_let(self, val:bool) -> None:
321
+ """ Set let
322
+ @param val: Value
323
+ """
324
+ self.__let = val
325
+
326
+ def notify_let(self) -> None:
327
+ """ Notify let"""
328
+ self.__stop_agent.put(None)
329
+
330
+ def set_alive(self, val:bool) -> None:
331
+ """ Set alive
332
+ @param val: Value
333
+ """
334
+ self.__alive = val
335
+
336
+ def finalize(self) -> None:
337
+ """ Finalize """
338
+ self.__let = False
339
+ self.__queue.put(None)
340
+ self.__stop_agent.put(None)
341
+
342
+ # --------------------------------------------------------
343
+ # Define Action component
344
+ # --------------------------------------------------------
345
+
346
+ class Action(ABC):
347
+ """ Represents the reaction to the occurrence of an event """
348
+
349
+ def __init__(self) -> None:
350
+ """ Constructor """
351
+ self.id = None
352
+ self.log = None
353
+ self.adm = None
354
+ self.agent = None
355
+ super().__init__()
356
+
357
+ @abstractmethod
358
+ def execute(self, data:any) -> None:
359
+ """ Execute
360
+ @param data: Data
361
+ """
362
+ pass
363
+
364
+ def set_agent(self, agent) -> None:
365
+ """ Set agent
366
+ @param agent: Agent
367
+ """
368
+ from ..mas import Adm
369
+ self.adm = Adm()
370
+ self.agent = agent
371
+ self.log = agent.log
372
+
373
+ # --------------------------------------------------------
374
+ # Define Agent component
375
+ # --------------------------------------------------------
376
+
377
+ class Agent(ABC):
378
+ """ Represents a basic agent """
379
+
380
+ def __init__(self, agent_id:str) -> None:
381
+ """
382
+ Agent constructor method.
383
+ @param agentID Unic agent ID
384
+ """
385
+ if agent_id and isinstance(agent_id, str):
386
+ self.id = agent_id
387
+ self.state = {}
388
+ self.__events_table = {}
389
+ self.__channels_table = {}
390
+ self.__worker_list = []
391
+ self.__channel_list = []
392
+ self.__behaviors = {}
393
+ self._social = False
394
+ self.log = None
395
+ self.__build_agent()
396
+ from ..mas import Adm
397
+ Adm().add_agent(self)
398
+ super().__init__()
399
+ else:
400
+ raise AgentException('[Fatal, __init__]: The agent ID must be a str')
401
+
402
+ def __build_agent(self) -> None:
403
+ """ Build the agent structure """
404
+ self.setup()
405
+ if len(self.__behaviors) > 0:
406
+ for key, beh in self.__behaviors.items():
407
+ queue = Queue(100)
408
+ channel = Channel(queue)
409
+ worker = BehaviorExe(queue)
410
+ self.__channels_table[key] = {'channel' : channel, 'worker': worker}
411
+ self.__worker_list.append(worker)
412
+ self.__channel_list.append(channel)
413
+ for evts in beh:
414
+ try:
415
+ evts['action'].set_agent(self)
416
+ self.__events_table[evts['event']] = {'behavior' : key, 'action': evts['action']}
417
+ except:
418
+ raise AgentException('[Fatal, buildAgent]: The action must be instantiated: %s' % str(evts['action']))
419
+ else:
420
+ raise AgentException('[Fatal, buildAgent]: Agent behaviors must be defined')
421
+
422
+ @abstractmethod
423
+ def setup(self) -> None:
424
+ """ Method to create and initialize the agent structure """
425
+ pass
426
+
427
+ @abstractmethod
428
+ def shutdown(self) -> None:
429
+ """ Method to free up the resources taken by the agent """
430
+ pass
431
+
432
+ def send_event(self, event:any, data:any) -> None:
433
+ """
434
+ Method that registers an event to the agent.
435
+ @param event Envent
436
+ @param Data event
437
+ @exceptions AgentException
438
+ """
439
+ if event in self.__events_table:
440
+ behavior = self.__events_table[event]
441
+ channel = self.__channels_table[behavior['behavior']]
442
+ evt = {'event': event, 'data': data, 'action': behavior['action']}
443
+ channel['channel'].send_event(evt)
444
+ else:
445
+ raise AgentException('[Warn, send_event]: The agent has not registered the event %s' % event)
446
+
447
+ def start(self) -> None:
448
+ """ Start the agent """
449
+ for w in self.__worker_list:
450
+ w.set_let(True)
451
+ w.start()
452
+
453
+ def wait(self) -> None:
454
+ """ Wait for the agent to finish """
455
+ for w in self.__worker_list:
456
+ w.set_let(False)
457
+
458
+ def finalize(self) -> None:
459
+ """ Finalize the agent """
460
+ for w in self.__worker_list:
461
+ w.set_alive(False)
462
+ w.finalize()
463
+
464
+ def kill(self) -> None:
465
+ """ Remove the agent from the system """
466
+ from ..mas import Adm
467
+ if 'persistence' in Adm().conf:
468
+ self.persist()
469
+ self.shutdown()
470
+ self.id = None
471
+ self.log = None
472
+ self.state = None
473
+ self.__events_table = None
474
+ self.__channels_table = None
475
+ self.finalize()
476
+ self.__worker_list = None
477
+ self.__channel_list = None
478
+ self.__behaviors = None
479
+
480
+ def to_dto(self) -> str:
481
+ """ Convert the agent to a DTO """
482
+ dto = {
483
+ 'command': 'MOVE',
484
+ 'class': self.__class__.__name__,
485
+ 'path': self.__module__,
486
+ 'id': self.id,
487
+ 'state': self.state
488
+ }
489
+ rtn = str(dto)
490
+ rtn = rtn.replace("'", "\"")
491
+ return rtn
492
+
493
+ def add_behavior(self, behavior:str) -> None:
494
+ """
495
+ Add the new behavior to the agent's behavior.
496
+ @param behavior New behavior
497
+ """
498
+ self.__behaviors[behavior] = []
499
+
500
+ def bind_action(self, behavior:str, event:any, action:Action) -> None:
501
+ """
502
+ Link behavior to event with action.
503
+ @param behavior Behavior
504
+ @param event Event link to behavior
505
+ @param action Action link to event
506
+ @exceptions AgentException
507
+ """
508
+ if behavior in self.__behaviors:
509
+ self.__behaviors[behavior].append({
510
+ 'event': event,
511
+ 'action': action
512
+ })
513
+ else:
514
+ raise AgentException('[Fatal, bindAction]: The behavior "%s" is not associated with the agent. Must be added before behavior' % behavior)
515
+
516
+ def set_up_logger(self, logger_name:str, logger_file:str, level:int) -> None:
517
+ """
518
+ Inicia un componente de seguimiento de la aplicacion.
519
+ @param loggerName nombre del log
520
+ @param loggerFile ruta del archivo
521
+ """
522
+ l = logging.getLogger(logger_name)
523
+ formatter = logging.Formatter('[PBESA]: %(asctime)s %(name)-12s %(lineno)d %(levelname)-8s %(message)s')
524
+ fileHandler = logging.FileHandler(logger_file, 'w', 'utf-8')
525
+ fileHandler.setFormatter(formatter)
526
+ streamHandler = logging.StreamHandler()
527
+ streamHandler.setFormatter(formatter)
528
+ l.setLevel(level)
529
+ l.addHandler(fileHandler)
530
+ l.addHandler(streamHandler)
531
+
532
+ def active_logger(self, logger:str, level:int=logging.INFO) -> None:
533
+ """ Activa el log de la aplicacion """
534
+ if not level:
535
+ level = logging.INFO
536
+ self.set_up_logger(logger, '%s.log' % logger, level)
537
+ self.log = logging.getLogger(logger)
538
+
539
+ def suscribe_logger(self, logger:str) -> None:
540
+ """ Suscribe el log de la aplicacion
541
+ @param logger Nombre del log
542
+ """
543
+ self.log = logging.getLogger(logger)
544
+
545
+ def persist(self) -> None:
546
+ """ Persist the agent state
547
+ @exceptions AgentException
548
+ """
549
+ from ..mas import Adm
550
+ db = Adm().get_db_connection()
551
+ db[self.id].delete_many({})
552
+ db[self.id].insert_one(self.state)
553
+
554
+ def is_social(self) -> bool:
555
+ """ Check if the agent is social
556
+ @return bool
557
+ """
558
+ return self._social
@@ -1,2 +1,2 @@
1
- from .SystemFile import SystemFile
2
- from .TCPServer import TCPServer
1
+ from .system_file import SystemFile
2
+ from .tcp_server import TCPServer
@@ -0,0 +1,38 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ ----------------------------------------------------------
4
+ -------------------------- PBESA -------------------------
5
+ ----------------------------------------------------------
6
+
7
+ @autor AKEN
8
+ @version 4.0.0
9
+ @date 08/08/24
10
+ """
11
+
12
+ # --------------------------------------------------------
13
+ # Define resources
14
+ # --------------------------------------------------------
15
+
16
+ import json
17
+
18
+ # --------------------------------------------------------
19
+ # Define component
20
+ # --------------------------------------------------------
21
+
22
+ class SystemFile(object):
23
+ """ System file class """
24
+
25
+ # Path
26
+ path = None
27
+
28
+ def __init__(self, path) -> None:
29
+ """ Constructor
30
+ :param path: Path
31
+ """
32
+ self.path = path
33
+
34
+ def read_json_file(self) -> dict:
35
+ """ Read JSON file """
36
+ with open(self.path) as f:
37
+ data = json.load(f)
38
+ return data
@@ -0,0 +1,23 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ ----------------------------------------------------------
4
+ -------------------------- PBESA -------------------------
5
+ ----------------------------------------------------------
6
+
7
+ @autor AKEN
8
+ @version 4.0.0
9
+ @date 08/08/24
10
+ """
11
+
12
+ # --------------------------------------------------------
13
+ # Define resources
14
+ # --------------------------------------------------------
15
+
16
+ import socketserver
17
+
18
+ # --------------------------------------------------------
19
+ # Define component
20
+ # --------------------------------------------------------
21
+
22
+ class TCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
23
+ pass