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.
- pbesa/__init__.py +4 -3
- pbesa/cognitive.py +475 -0
- pbesa/kernel/__init__.py +4 -6
- pbesa/kernel/adapter.py +165 -0
- pbesa/kernel/agent.py +558 -0
- pbesa/kernel/io/__init__.py +2 -2
- pbesa/kernel/io/system_file.py +38 -0
- pbesa/kernel/io/tcp_server.py +23 -0
- pbesa/kernel/util.py +217 -0
- pbesa/kernel/world.py +43 -0
- pbesa/mas.py +565 -0
- pbesa/remote/__init__.py +5 -0
- pbesa/remote/adm_listener.py +44 -0
- pbesa/{middleware/remote/AdmListenerHandler.py → remote/adm_listener_handler.py} +74 -57
- pbesa/remote/exceptions.py +18 -0
- pbesa/remote/remote_adm.py +42 -0
- pbesa/{middleware/remote/RemoteAdmHandler.py → remote/remote_adm_handler.py} +109 -83
- pbesa/social/__init__.py +4 -0
- pbesa/social/collaborative_team.py +299 -0
- pbesa/social/delegator.py +81 -0
- pbesa/social/delegator_team.py +334 -0
- pbesa/social/dialog.py +153 -0
- pbesa/social/dispatcher_team.py +319 -0
- pbesa/social/templates.py +18 -0
- pbesa/social/worker.py +188 -0
- {pbesa-2.1.dist-info → pbesa-4.0.0.dist-info}/LICENSE +21 -21
- {pbesa-2.1.dist-info → pbesa-4.0.0.dist-info}/LICENSE.txt +21 -21
- pbesa-4.0.0.dist-info/METADATA +8 -0
- pbesa-4.0.0.dist-info/RECORD +32 -0
- {pbesa-2.1.dist-info → pbesa-4.0.0.dist-info}/WHEEL +5 -5
- {pbesa-2.1.dist-info → pbesa-4.0.0.dist-info}/top_level.txt +0 -0
- pbesa/engine/__init__.py +0 -2
- pbesa/engine/bdi/BDIAg.py +0 -42
- pbesa/engine/bdi/BDILevel.py +0 -10
- pbesa/engine/bdi/BDIMachine.py +0 -116
- pbesa/engine/bdi/Goal.py +0 -28
- pbesa/engine/bdi/GoalExe.py +0 -36
- pbesa/engine/bdi/__init__.py +0 -5
- pbesa/engine/rational/ActionExe.py +0 -35
- pbesa/engine/rational/Brain.py +0 -10
- pbesa/engine/rational/RationalAg.py +0 -43
- pbesa/engine/rational/__init__.py +0 -3
- pbesa/kernel/adapter/Adapter.py +0 -26
- pbesa/kernel/adapter/FileAdapter.py +0 -23
- pbesa/kernel/adapter/__init__.py +0 -2
- pbesa/kernel/agent/Action.py +0 -19
- pbesa/kernel/agent/Agent.py +0 -91
- pbesa/kernel/agent/BehaviorExe.py +0 -33
- pbesa/kernel/agent/Channel.py +0 -10
- pbesa/kernel/agent/World.py +0 -15
- pbesa/kernel/agent/__init__.py +0 -5
- pbesa/kernel/io/SystemFile.py +0 -13
- pbesa/kernel/io/TCPServer.py +0 -4
- pbesa/kernel/system/Adm.py +0 -189
- pbesa/kernel/system/Directory.py +0 -36
- pbesa/kernel/system/__init__.py +0 -2
- pbesa/kernel/util/HashTable.py +0 -62
- pbesa/kernel/util/Queue.py +0 -231
- pbesa/kernel/util/__init__.py +0 -2
- pbesa/middleware/__init__.py +0 -3
- pbesa/middleware/adapter/GameAdapter.py +0 -64
- pbesa/middleware/adapter/MongoAdapter.py +0 -47
- pbesa/middleware/adapter/RESTAdapter.py +0 -52
- pbesa/middleware/adapter/SubProcessAdapter.py +0 -30
- pbesa/middleware/adapter/WSSAdapter.py +0 -89
- pbesa/middleware/adapter/WSSNJAdapter.py +0 -51
- pbesa/middleware/adapter/WSSNJHandler.py +0 -23
- pbesa/middleware/adapter/__init__.py +0 -7
- pbesa/middleware/remote/AdmListener.py +0 -17
- pbesa/middleware/remote/RemoteAdm.py +0 -17
- pbesa/middleware/remote/__init__.py +0 -4
- pbesa/middleware/web/WebAgTK.py +0 -15
- pbesa/middleware/web/__init__.py +0 -0
- pbesa-2.1.dist-info/METADATA +0 -25
- pbesa-2.1.dist-info/RECORD +0 -54
pbesa/mas.py
ADDED
@@ -0,0 +1,565 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
------------------------------------------------------------
|
4
|
+
-------------------------- PBESA ---------------------------
|
5
|
+
------------------------------------------------------------
|
6
|
+
|
7
|
+
@autor AKEN & SIDRE
|
8
|
+
@version 3.0.1
|
9
|
+
@date 27/07/20
|
10
|
+
"""
|
11
|
+
|
12
|
+
# --------------------------------------------------------
|
13
|
+
# Define resources
|
14
|
+
# --------------------------------------------------------
|
15
|
+
|
16
|
+
import gc
|
17
|
+
import json
|
18
|
+
import base64
|
19
|
+
import socket
|
20
|
+
import traceback
|
21
|
+
from time import sleep
|
22
|
+
from pymongo import MongoClient
|
23
|
+
from .kernel.agent import Agent
|
24
|
+
from .kernel.agent import Queue
|
25
|
+
from .remote.remote_adm import RemoteAdm
|
26
|
+
from .remote.adm_listener import AdmListener
|
27
|
+
from .kernel.adapter import Adapter, FileAdapter
|
28
|
+
|
29
|
+
# ----------------------------------------------------------
|
30
|
+
# Defines system component exceptions
|
31
|
+
# ----------------------------------------------------------
|
32
|
+
|
33
|
+
class SystemException(Exception):
|
34
|
+
""" Base class for exceptions of system """
|
35
|
+
pass
|
36
|
+
|
37
|
+
# --------------------------------------------------------
|
38
|
+
# Define Component
|
39
|
+
# --------------------------------------------------------
|
40
|
+
|
41
|
+
class Directory(object):
|
42
|
+
""" Directory class """
|
43
|
+
|
44
|
+
class __Directory:
|
45
|
+
""" Directory singleton class """
|
46
|
+
|
47
|
+
def __init__(self) -> None:
|
48
|
+
""" Directory constructor """
|
49
|
+
self.container_list = []
|
50
|
+
self.agent_list = []
|
51
|
+
self.gateway = None
|
52
|
+
self.check_to_list = None
|
53
|
+
|
54
|
+
def add_container(self, container:str) -> None:
|
55
|
+
""" Add container to container list
|
56
|
+
@param container Container to add
|
57
|
+
"""
|
58
|
+
self.container_list.append(container)
|
59
|
+
|
60
|
+
def add_agent(self, agent:Agent) -> None:
|
61
|
+
""" Add agent to agent list
|
62
|
+
@param agent Agent to add
|
63
|
+
"""
|
64
|
+
self.agent_list.append(agent)
|
65
|
+
|
66
|
+
def get_containers(self) -> list:
|
67
|
+
""" Get container list
|
68
|
+
@return list Container list
|
69
|
+
"""
|
70
|
+
return self.container_list
|
71
|
+
|
72
|
+
def get_agents(self) -> list:
|
73
|
+
""" Get agent list
|
74
|
+
@return list Agent list
|
75
|
+
"""
|
76
|
+
return self.agent_list
|
77
|
+
|
78
|
+
def set_agent_list(self, agent_list: list) -> None:
|
79
|
+
""" Set agent list
|
80
|
+
@param agentList Agent list
|
81
|
+
"""
|
82
|
+
self.agent_list = agent_list
|
83
|
+
|
84
|
+
def get_agent(self, agent_id: str) -> Agent:
|
85
|
+
""" Get agent by id
|
86
|
+
@param agentID Agent ID
|
87
|
+
@return Agent Agent
|
88
|
+
"""
|
89
|
+
for agent in self.agent_list:
|
90
|
+
if agent_id == agent['agent']:
|
91
|
+
return agent
|
92
|
+
return None
|
93
|
+
|
94
|
+
def reset_agent_list(self) -> None:
|
95
|
+
""" Reset agent list """
|
96
|
+
self.agent_list = []
|
97
|
+
|
98
|
+
def remove_agent(self, agent: Agent) -> None:
|
99
|
+
""" Remove agent from agent list """
|
100
|
+
for ag in self.agent_list:
|
101
|
+
if ag['agent'] == agent:
|
102
|
+
self.agent_list.remove(ag)
|
103
|
+
|
104
|
+
def set_check_list(self, gateway:str, check_to_list:list) -> None:
|
105
|
+
""" Set check list
|
106
|
+
@param gateway Gateway
|
107
|
+
@param checkToList Check list
|
108
|
+
"""
|
109
|
+
self.gateway = gateway
|
110
|
+
self.check_to_list = check_to_list
|
111
|
+
|
112
|
+
def check_full(self, container_name:str) -> None:
|
113
|
+
""" Check if container name is in check list
|
114
|
+
@param containerName Container name
|
115
|
+
"""
|
116
|
+
if self.check_to_list:
|
117
|
+
if container_name in self.check_to_list:
|
118
|
+
self.check_to_list.remove(container_name)
|
119
|
+
if len(self.check_to_list) == 0:
|
120
|
+
self.gateway.put(None)
|
121
|
+
else:
|
122
|
+
raise SystemException('Container name: "%s" does not match' % container_name)
|
123
|
+
|
124
|
+
# --------------------------------------------------------
|
125
|
+
# Singleton
|
126
|
+
# --------------------------------------------------------
|
127
|
+
|
128
|
+
# Singleton instance
|
129
|
+
instance = None
|
130
|
+
|
131
|
+
def __new__(cls) -> object:
|
132
|
+
""" Create new instance """
|
133
|
+
if not Directory.instance:
|
134
|
+
Directory.instance = Directory.__Directory()
|
135
|
+
return Directory.instance
|
136
|
+
|
137
|
+
def __getattr__(self, name: str) -> object:
|
138
|
+
""" Get attribute
|
139
|
+
@param name Attribute name
|
140
|
+
@return object Attribute
|
141
|
+
"""
|
142
|
+
return getattr(self.instance, name)
|
143
|
+
|
144
|
+
def __setattr__(self, name:str) -> object:
|
145
|
+
""" Set attribute
|
146
|
+
@param name Attribute name
|
147
|
+
@return object Attribute
|
148
|
+
"""
|
149
|
+
return setattr(self.instance, name)
|
150
|
+
|
151
|
+
# --------------------------------------------------------
|
152
|
+
# Define component
|
153
|
+
# --------------------------------------------------------
|
154
|
+
|
155
|
+
class Adm(object):
|
156
|
+
""" Represents the agent container manager """
|
157
|
+
|
158
|
+
class __Adm:
|
159
|
+
""" Singleton design pattern """
|
160
|
+
|
161
|
+
def __init__(self) -> None:
|
162
|
+
""" Default constructor """
|
163
|
+
self.val = None
|
164
|
+
self.conf = None
|
165
|
+
self.adapters = {}
|
166
|
+
self.agents_table = {}
|
167
|
+
self.container_list = []
|
168
|
+
self.__db = None
|
169
|
+
self.__client = None
|
170
|
+
|
171
|
+
def __str__(self) -> str:
|
172
|
+
""" To string """
|
173
|
+
return repr(self) + self.val
|
174
|
+
|
175
|
+
def connect_database(self) -> None:
|
176
|
+
"""Connects to mongo database"""
|
177
|
+
database_server = 'localhost'
|
178
|
+
database_port = 27017
|
179
|
+
if 'database_server' in self.conf['persistence']:
|
180
|
+
database_server = self.conf['persistence']['database_server']
|
181
|
+
if 'database_port' in self.conf['persistence']:
|
182
|
+
database_port = self.conf['persistence']['database_port']
|
183
|
+
try:
|
184
|
+
self.__client = MongoClient(database_server, database_port)
|
185
|
+
self.__db = self.__client[self.conf['persistence']['database_name']]
|
186
|
+
# Check if exist mas structure
|
187
|
+
collist = self.__db.list_collection_names()
|
188
|
+
if not "mas" in collist:
|
189
|
+
self.__db.create_collection("mas")
|
190
|
+
self.__db.create_collection("agents")
|
191
|
+
self.__db.create_collection("agenda")
|
192
|
+
self.__db.create_collection("work_memory")
|
193
|
+
except:
|
194
|
+
name = self.conf['persistence']['database_name']
|
195
|
+
raise SystemException(f'[Error, connect_database]: Cannot connect to database: {name}')
|
196
|
+
|
197
|
+
def start(self) -> None:
|
198
|
+
""" Default administrator startup """
|
199
|
+
self.conf = {
|
200
|
+
"user" : "local",
|
201
|
+
"host" : "localhost",
|
202
|
+
"port" : 8080,
|
203
|
+
"remote" : None
|
204
|
+
}
|
205
|
+
|
206
|
+
def start_by_conf(self, conf:dict) -> None:
|
207
|
+
"""
|
208
|
+
Administrator startup by configuration.
|
209
|
+
@param conf configuration dictionary or
|
210
|
+
configuration file path
|
211
|
+
@exceptions SystemException
|
212
|
+
"""
|
213
|
+
if conf:
|
214
|
+
# Sets local everoment from dictionary
|
215
|
+
if not isinstance(conf, str):
|
216
|
+
if 'user' in conf and 'host' in conf and 'port' in conf:
|
217
|
+
if conf['user'] and conf['host'] and conf['port']:
|
218
|
+
self.conf = conf
|
219
|
+
else:
|
220
|
+
raise SystemException('[Warn, startByConf]: Configuration parameters cannot be null')
|
221
|
+
if 'persistence' in conf:
|
222
|
+
if 'database_name' in conf['persistence'] and conf['persistence']['database_name']:
|
223
|
+
if 'user' in conf and 'host' in conf and 'port' in conf:
|
224
|
+
self.conf = conf
|
225
|
+
else:
|
226
|
+
self.conf = {
|
227
|
+
"user" : "local",
|
228
|
+
"host" : "localhost",
|
229
|
+
"port" : 8080,
|
230
|
+
"remote" : None,
|
231
|
+
"persistence": conf['persistence']
|
232
|
+
}
|
233
|
+
self.connect_database()
|
234
|
+
else:
|
235
|
+
raise SystemException('[Warn, startByConf]: Name of databes is requerried')
|
236
|
+
else:
|
237
|
+
CONF_DIR = conf
|
238
|
+
fa = FileAdapter({'alias':'JsonAdapter', 'type': 'JSON', 'path': CONF_DIR})
|
239
|
+
fa.setup()
|
240
|
+
param = fa.request()
|
241
|
+
self.conf = param['conf']
|
242
|
+
# Sets remote everoment
|
243
|
+
if 'remote' in self.conf and self.conf['remote']:
|
244
|
+
remote = self.conf['remote']
|
245
|
+
if remote['master_mode']:
|
246
|
+
self.remoteAdm = RemoteAdm(self.conf['host'], self.conf['port'])
|
247
|
+
self.remoteAdm.start()
|
248
|
+
else:
|
249
|
+
if 'container_name' in self.conf and 'master_host' in remote and 'master_port' in remote:
|
250
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
251
|
+
attempts = remote['attempts']
|
252
|
+
data = '{"command": "REGISTER", "name":"' + self.conf['container_name'] + '", "host":"' + self.conf['host'] + '", "port":"'+ str(self.conf['port']) +'"}'
|
253
|
+
received = None
|
254
|
+
for x in range(1, attempts):
|
255
|
+
try:
|
256
|
+
sock.connect((remote['master_host'], remote['master_port']))
|
257
|
+
sock.sendall(bytes(data + "\n", "utf-8"))
|
258
|
+
received = str(sock.recv(1024), "utf-8")
|
259
|
+
if received == 'ACK' + "\n":
|
260
|
+
break
|
261
|
+
except:
|
262
|
+
sleep(1)
|
263
|
+
finally:
|
264
|
+
sock.close()
|
265
|
+
if received == 'ACK' + "\n":
|
266
|
+
admListener = AdmListener(self.conf['host'], self.conf['port'])
|
267
|
+
admListener.start()
|
268
|
+
else:
|
269
|
+
raise SystemException('[Warn, startByConf]: The administrator could not connect with the administrator master')
|
270
|
+
else:
|
271
|
+
raise SystemException('[Warn, startByConf]: A parameter is missing. The parameters are: {container_name, master_host, master_port}')
|
272
|
+
else:
|
273
|
+
raise SystemException('[Warn, startByConf]: The parameter "conf" cannot be NoneType')
|
274
|
+
|
275
|
+
def kill_agent(self, agent:Agent) -> None:
|
276
|
+
"""
|
277
|
+
Remove an agent of the system.
|
278
|
+
@param agent Agent to add
|
279
|
+
@exceptions SystemException
|
280
|
+
"""
|
281
|
+
del self.agents_table[agent.id]
|
282
|
+
directory = Directory()
|
283
|
+
directory.remove_agent(agent.id)
|
284
|
+
agent.kill()
|
285
|
+
if self.conf['remote']:
|
286
|
+
remote = self.conf['remote']
|
287
|
+
agents = directory.get_agents()
|
288
|
+
dto = '{"command":"UPDATE", "agents" : ' + json.dumps(agents, ensure_ascii=False) + '}'
|
289
|
+
if remote['mode'] == 'MASTER':
|
290
|
+
containers = directory.get_containers()
|
291
|
+
for ctn in containers:
|
292
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
293
|
+
try:
|
294
|
+
sock.connect( (ctn['ip'], int(ctn['port'])) )
|
295
|
+
sock.sendall(bytes(dto + "\n", "utf-8"))
|
296
|
+
except:
|
297
|
+
raise SystemException('[Warn, killAgent]: Could not update container with IP %s' % ctn['ip'])
|
298
|
+
finally:
|
299
|
+
sock.close()
|
300
|
+
else:
|
301
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
302
|
+
master = remote['master']
|
303
|
+
try:
|
304
|
+
sock.connect((master['master_ip'], master['master_port']))
|
305
|
+
sock.sendall(bytes(dto + "\n", "utf-8"))
|
306
|
+
except:
|
307
|
+
raise SystemException('[Warn, killAgent]: Could not update container master')
|
308
|
+
finally:
|
309
|
+
sock.close()
|
310
|
+
agent = None
|
311
|
+
gc.collect()
|
312
|
+
|
313
|
+
def add_agent(self, agent:Agent) -> None:
|
314
|
+
"""
|
315
|
+
Add an agent to the system.
|
316
|
+
@param agent Agent to add
|
317
|
+
@exceptions SystemException
|
318
|
+
"""
|
319
|
+
if agent.id in self.agents_table:
|
320
|
+
raise SystemException('[Warn, addAgent]: Agent ID: "%s" already exists in container' % agent.id)
|
321
|
+
else:
|
322
|
+
# Register new agent to container
|
323
|
+
self.agents_table[agent.id] = agent
|
324
|
+
host = self.conf['host']
|
325
|
+
port = self.conf['port']
|
326
|
+
if 'remote' in self.conf and self.conf['remote']:
|
327
|
+
remote = self.conf['remote']
|
328
|
+
directory = Directory()
|
329
|
+
agentDTO = {'agent': agent.id, 'host': host, 'port' : port}
|
330
|
+
directory.add_agent(agentDTO)
|
331
|
+
if 'remote' in self.conf and self.conf['remote']:
|
332
|
+
remote = self.conf['remote']
|
333
|
+
if remote['master_mode']:
|
334
|
+
agents = directory.get_agents()
|
335
|
+
dto = '{"command":"UPDATE", "agents": ' + json.dumps(agents, ensure_ascii=False) + '}'
|
336
|
+
containers = directory.get_containers()
|
337
|
+
for ctn in containers:
|
338
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
339
|
+
try:
|
340
|
+
sock.connect( (ctn['host'], int(ctn['port'])) )
|
341
|
+
sock.sendall(bytes(dto + "\n", "utf-8"))
|
342
|
+
except:
|
343
|
+
raise SystemException('[Warn, addAgent]: Could not update container with %s host' % ctn['host'])
|
344
|
+
finally:
|
345
|
+
sock.close()
|
346
|
+
else:
|
347
|
+
dto = '{"command":"ADD", "agent": ' + json.dumps(agentDTO, ensure_ascii=False) + '}'
|
348
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
349
|
+
try:
|
350
|
+
sock.connect((remote['master_host'], remote['master_port']))
|
351
|
+
sock.sendall(bytes(dto + "\n", "utf-8"))
|
352
|
+
except:
|
353
|
+
raise SystemException('[Warn, addAgent]: Could not update container master')
|
354
|
+
finally:
|
355
|
+
sock.close()
|
356
|
+
# Persist agent information
|
357
|
+
if 'persistence' in self.conf:
|
358
|
+
agent_count = self.__db["agents"].count_documents({'agent_id': agent.id})
|
359
|
+
if agent_count == 0:
|
360
|
+
# Create new agent document
|
361
|
+
self.__db["agents"].insert_one({'agent_id': agent.id})
|
362
|
+
self.__db.create_collection(agent.id)
|
363
|
+
# Create new agent state document
|
364
|
+
self.__db[agent.id].insert_one(agent.state)
|
365
|
+
else:
|
366
|
+
# Get current state
|
367
|
+
state_list = self.__db[agent.id].find({})
|
368
|
+
agent.state = state_list[0]
|
369
|
+
|
370
|
+
def send_event(self, agent_id:str, event:any, data:any) -> bool:
|
371
|
+
"""
|
372
|
+
Send an event to another agent.
|
373
|
+
@param agentID Destination agent ID
|
374
|
+
@param event Event to send
|
375
|
+
@param data Event data
|
376
|
+
@return :bool True if the submission was
|
377
|
+
successful | False otherwise
|
378
|
+
@exceptions SystemException
|
379
|
+
"""
|
380
|
+
if agent_id in self.agents_table:
|
381
|
+
ag = self.agents_table[agent_id]
|
382
|
+
ag.send_event(event, data)
|
383
|
+
return True
|
384
|
+
else:
|
385
|
+
if 'remote' in self.conf and self.conf['remote']:
|
386
|
+
remote = self.conf['remote']
|
387
|
+
attempts = remote['attempts']
|
388
|
+
agent = Directory().get_agent(agent_id)
|
389
|
+
received = None
|
390
|
+
if agent:
|
391
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
392
|
+
aux = 'None'
|
393
|
+
if data:
|
394
|
+
data = json.dumps(data)
|
395
|
+
data = data.encode('utf-8')
|
396
|
+
data = base64.b64encode(data)
|
397
|
+
aux = data.decode('utf-8')
|
398
|
+
dto = '{"command": "SENDEVENT", "id":"' + agent_id + '", "event":"' + event + '", "data":"'+ aux +'"}'
|
399
|
+
for it in range(1, attempts):
|
400
|
+
try:
|
401
|
+
sock.connect((agent['host'], agent['port']))
|
402
|
+
sock.sendall(bytes(dto + "\n", "utf-8"))
|
403
|
+
received = str(sock.recv(1024), "utf-8")
|
404
|
+
if received == 'ACK' + "\n":
|
405
|
+
break
|
406
|
+
except:
|
407
|
+
traceback.print_exc()
|
408
|
+
sleep(1)
|
409
|
+
finally:
|
410
|
+
sock.close()
|
411
|
+
if received == 'ACK' + "\n":
|
412
|
+
return True
|
413
|
+
else:
|
414
|
+
raise SystemException('[Warn, send_event]: The event could not be sent to the agent with the ID: %s ' % agent_id)
|
415
|
+
else:
|
416
|
+
raise SystemException('[Warn, send_event]: An agent with the ID %s could not be found in the system' % agent_id)
|
417
|
+
|
418
|
+
def add_adapter(self, id:str, adapter:Adapter) -> None:
|
419
|
+
""" Add an adapter to the system
|
420
|
+
@param id Adapter ID
|
421
|
+
@param adapter Adapter to add
|
422
|
+
"""
|
423
|
+
self.adapters[id] = adapter
|
424
|
+
|
425
|
+
def get_adapter(self, id:str) -> Adapter:
|
426
|
+
""" Get an adapter from the system
|
427
|
+
@param id Adapter ID
|
428
|
+
@return Adapter Adapter
|
429
|
+
"""
|
430
|
+
return self.adapters[id]
|
431
|
+
|
432
|
+
def get_conf(self) -> dict:
|
433
|
+
""" Get configuration
|
434
|
+
@return dict Configuration
|
435
|
+
"""
|
436
|
+
return self.conf
|
437
|
+
|
438
|
+
def add_container(self, container:str) -> None:
|
439
|
+
""" Add a container to the system
|
440
|
+
@param container Container to add
|
441
|
+
"""
|
442
|
+
self.container_list.append(container)
|
443
|
+
|
444
|
+
def move_agent(self, agent_id:str, container:str) -> None:
|
445
|
+
"""
|
446
|
+
Add an agent to the system.
|
447
|
+
@param agent Agent to add
|
448
|
+
@exceptions SystemException
|
449
|
+
"""
|
450
|
+
ag = self.agents_table[agent_id]
|
451
|
+
ag.finalize()
|
452
|
+
dto = ag.toDTO()
|
453
|
+
containers = Directory().get_containers()
|
454
|
+
for ctn in containers:
|
455
|
+
if container == ctn['name']:
|
456
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
457
|
+
try:
|
458
|
+
sock.connect( (ctn['host'], int(ctn['port'])) )
|
459
|
+
sock.sendall(bytes(dto + "\n", "utf-8"))
|
460
|
+
except:
|
461
|
+
raise SystemException('[Error, moveAgent]: Could not update container with IP %s' % ctn['host'])
|
462
|
+
finally:
|
463
|
+
sock.close()
|
464
|
+
break
|
465
|
+
self.agents_table[agent_id] = None
|
466
|
+
Directory().remove_agent(agent_id)
|
467
|
+
|
468
|
+
def set_end_point(self, agent_id:str, socket:socket) -> None:
|
469
|
+
""" Set the endpoint of an agent
|
470
|
+
@param agentID Agent ID
|
471
|
+
@param socket Socket
|
472
|
+
"""
|
473
|
+
ag = self.agents_table[agent_id]
|
474
|
+
ag.set_socket(socket)
|
475
|
+
|
476
|
+
def call_agent(self, agent_id:str, data:any) -> any:
|
477
|
+
"""
|
478
|
+
Call a social agent.
|
479
|
+
@param data Call data
|
480
|
+
"""
|
481
|
+
if agent_id in self.agents_table:
|
482
|
+
ag = self.agents_table[agent_id]
|
483
|
+
if ag.is_social():
|
484
|
+
if ag.is_block():
|
485
|
+
queue = Queue(1)
|
486
|
+
dto = {
|
487
|
+
'dto': data,
|
488
|
+
'gateway': queue
|
489
|
+
}
|
490
|
+
ag.send_event('delegate', dto)
|
491
|
+
result = queue.get()
|
492
|
+
queue.task_done()
|
493
|
+
return result
|
494
|
+
else:
|
495
|
+
raise SystemException('[Warn, callAgent]: The "callAgent" method is only for blocking controllers')
|
496
|
+
else:
|
497
|
+
raise SystemException('[Warn, callAgent]: Only social agents can be invoked in this method')
|
498
|
+
else:
|
499
|
+
raise SystemException('[Warn, callAgent]: An agent with the ID %s could not be found in the system' % agent_id)
|
500
|
+
|
501
|
+
def submit_agent(self, agent_id:str, data:any) -> None:
|
502
|
+
"""
|
503
|
+
Call a social agent.
|
504
|
+
@param data Call data
|
505
|
+
"""
|
506
|
+
if agent_id in self.agents_table:
|
507
|
+
ag = self.agents_table[agent_id]
|
508
|
+
if ag.is_social():
|
509
|
+
if not ag.is_block():
|
510
|
+
ag.send_event('delegate', data)
|
511
|
+
else:
|
512
|
+
raise SystemException('[Warn, submitAgent]: The "submitAgent" method is only for non-blocking controllers')
|
513
|
+
else:
|
514
|
+
raise SystemException('[Warn, submitAgent]: Only social agents can be invoked in this method')
|
515
|
+
else:
|
516
|
+
raise SystemException('[Warn, submitAgent]: An agent with the ID %s could not be found in the system' % agent_id)
|
517
|
+
|
518
|
+
def wait_full(self, container_list) -> None:
|
519
|
+
""" Wait for the container list to be full
|
520
|
+
@param containerList Container list
|
521
|
+
"""
|
522
|
+
gateway = Queue(1)
|
523
|
+
Directory().set_check_list(gateway, container_list)
|
524
|
+
gateway.get()
|
525
|
+
gateway.task_done()
|
526
|
+
|
527
|
+
def destroy(self) -> None:
|
528
|
+
""" Destroy the administrator """
|
529
|
+
for agent in self.agents_table:
|
530
|
+
self.kill_agent(agent)
|
531
|
+
self.val = None
|
532
|
+
self.conf = None
|
533
|
+
self.adapters = None
|
534
|
+
self.agents_table = None
|
535
|
+
self.container_list = None
|
536
|
+
if self.__db:
|
537
|
+
self.__client.close()
|
538
|
+
self.__client = None
|
539
|
+
self.__db = None
|
540
|
+
gc.collect()
|
541
|
+
|
542
|
+
def get_db_connection(self) -> MongoClient:
|
543
|
+
""" Get database connection """
|
544
|
+
return self.__db
|
545
|
+
|
546
|
+
# ----------------------------------------------------
|
547
|
+
# Defines singleton instance
|
548
|
+
# ----------------------------------------------------
|
549
|
+
|
550
|
+
# Singleton instance
|
551
|
+
instance = None
|
552
|
+
|
553
|
+
def __new__(cls) -> __Adm:
|
554
|
+
""" Singleton design pattern """
|
555
|
+
if not Adm.instance:
|
556
|
+
Adm.instance = Adm.__Adm()
|
557
|
+
return Adm.instance
|
558
|
+
|
559
|
+
def __getattr__(self, name: str) -> any:
|
560
|
+
""" Get attribute """
|
561
|
+
return getattr(self.instance, name)
|
562
|
+
|
563
|
+
def __setattr__(self, name: str) -> None:
|
564
|
+
""" Set attribute """
|
565
|
+
return setattr(self.instance, name)
|
pbesa/remote/__init__.py
ADDED
@@ -0,0 +1,44 @@
|
|
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
|
+
from threading import Thread
|
18
|
+
from .adm_listener_handler import AdmListenerHandler
|
19
|
+
|
20
|
+
# --------------------------------------------------------
|
21
|
+
# Define component
|
22
|
+
# --------------------------------------------------------
|
23
|
+
|
24
|
+
class AdmListener(Thread):
|
25
|
+
""" ADM Listener class """
|
26
|
+
|
27
|
+
# IP
|
28
|
+
IP = None
|
29
|
+
# PORT
|
30
|
+
PORT = None
|
31
|
+
|
32
|
+
def __init__(self, ip:str, port:int) -> None:
|
33
|
+
""" Constructor
|
34
|
+
:param ip: IP
|
35
|
+
:param port: PORT
|
36
|
+
"""
|
37
|
+
self.IP = ip
|
38
|
+
self.PORT = port
|
39
|
+
super().__init__()
|
40
|
+
|
41
|
+
def run(self) -> None:
|
42
|
+
""" Run """
|
43
|
+
server = socketserver.TCPServer((self.IP, self.PORT), AdmListenerHandler)
|
44
|
+
server.serve_forever()
|