Commit 1c18151dc73b5a65151edce7773a41099ff580da

Authored by Etienne Pallier
1 parent 37d5efa2
Exists in dev

Multi-agents : AgentA (sender) sends commands to AgentB (receiver)

- AgentA a son propre scenario de commandes à envoyer à AgentB
- Mode opératoire pour lancer un agent:
	- pour démarrer agentA : ./pyros.py start agentA [-c configfile]
		(ou encore: activer l'environnement virtuel, puis lancer "./AgentA.py
configfile")
	- pour démarrer agentB : ouvrir un autre terminal et taper "./pyros.py
start agentB"
README.md
... ... @@ -71,19 +71,19 @@ Date: 18/03/2019
71 71  
72 72 Author: E. Pallier
73 73  
74   -VERSION: 0.20.20
  74 +VERSION: 0.20.21
75 75  
76 76 Comment:
77   - Multi-agents (3 agents) : AgentA, AgentB, et AgentX s'envoient des commandes (en //)
  77 + Multi-agents (2 agents) : AgentA (sender) sends commands to AgentB (receiver)
78 78  
79   - - AgentA, AgentB, et AgentX ont chacun leur propre scenario
80   - - GROSSE OPTIMISATION : plus besoin du script intermédiaire "start_agent.py" !!!
81   - - pyros.py lance directement "cd src/agent/ ; python AgentX.py"
  79 + - AgentA a son propre scenario de commandes à envoyer à AgentB
82 80 - Mode opératoire pour lancer un agent:
83   - - pour démarrer agentX : ./pyros.py start agentX [-c configfile]
84   - (ou encore: activer l'environnement virtuel, puis lancer "./AgentX.py configfile")
85   - - pour démarrer agentA : ouvrir un autre terminal et taper "./pyros.py start agentA"
86   - - pour utiliser thread ou processus : il suffit de mettre la constante RUN_IN_THREAD de AgentX (ou AgentA) à False ou True
  81 + - pour démarrer agentA : ./pyros.py start agentA [-c configfile]
  82 + (ou encore: activer l'environnement virtuel, puis lancer "./AgentA.py configfile")
  83 + - pour démarrer agentB : ouvrir un autre terminal et taper "./pyros.py start agentB"
  84 + - pour utiliser thread ou processus : il suffit de mettre la constante RUN_IN_THREAD de AgentA (ou AgentB ou AgentX) à False ou True
  85 + - GROSSE OPTIMISATION : plus besoin du script intermédiaire "start_agent.py" !!!
  86 + ==> pyros.py lance directement "cd src/agent/ ; python AgentX.py"
87 87  
88 88 --------------------------------------------------------------------------------------------
89 89 - TECHNICAL DOC: tinyurl.com/pyros-doc
... ...
src/agent/Agent.py
... ... @@ -148,7 +148,7 @@ class Agent:
148 148 # Run this agent in simulator mode
149 149 SIMULATOR_MODE = True
150 150 # Run the assertion tests at the end
151   - SIMULATOR_WITH_TEST = True
  151 + SIMULATOR_WITH_TEST = False
152 152 # Who should I send commands to ?
153 153 SIMULATOR_COMMANDS_DEST = "myself"
154 154 # Default scenario to be executed
... ... @@ -271,6 +271,9 @@ class Agent:
271 271 DEFAULT_CONFIG_FILE_NAME = "config_unit_simulunit1.xml"
272 272 CONFIG_DIR = "config"
273 273  
  274 + # Command to send
  275 + cmdts = None
  276 +
274 277 _agent_survey = None
275 278 _pending_commands = []
276 279 _current_specific_cmd = None
... ... @@ -373,8 +376,8 @@ class Agent:
373 376 # Wait a random number of sec before starting new iteration
374 377 # (to let another agent having the chance to send a command)
375 378 random_waiting_sec = random.randint(0,5)
376   - ##print(f"Waiting {random_waiting_sec} sec before starting new iteration...")
377   - ##time.sleep(random_waiting_sec)
  379 + print(f"Waiting {random_waiting_sec} sec (random) before starting new iteration...")
  380 + time.sleep(random_waiting_sec)
378 381  
379 382 try:
380 383  
... ... @@ -389,7 +392,7 @@ class Agent:
389 392  
390 393 self.update_survey()
391 394  
392   - if self.SIMULATOR_MODE: self.simulator_send_next_command()
  395 + #if self.SIMULATOR_MODE: self.simulator_send_next_command()
393 396  
394 397 # generic cmd in json format
395 398 print("------START COMMMAND PROCESSING------")
... ... @@ -408,7 +411,7 @@ class Agent:
408 411 N=3
409 412 if ((self._iter_num-1) % N) == 0: Command.purge_old_commands_for_agent(self.name)
410 413  
411   - self.waitfor(self.mainloop_waittime)
  414 + #self.waitfor(self.mainloop_waittime)
412 415  
413 416 print("-"*20, "MAIN LOOP ITERATION (END)", "-"*20)
414 417 #print("-"*80)
... ... @@ -420,8 +423,8 @@ class Agent:
420 423 # Exit if max duration is timed out
421 424 if self.MAX_DURATION_SEC:
422 425 #print ("time", time.time()-start_time)
423   - print("Exit because of max duration set to ", self.MAX_DURATION_SEC, "s")
424 426 if time.time()-start_time > self.MAX_DURATION_SEC:
  427 + print("Exit because of max duration set to ", self.MAX_DURATION_SEC, "s")
425 428 self.kill_running_specific_cmd_if_exists()
426 429 break
427 430  
... ... @@ -443,6 +446,46 @@ class Agent:
443 446 """
444 447 self.set_status(self.STATUS_ROUTINE_PROCESS)
445 448  
  449 + if self.cmdts is None:
  450 + self.cmdts = self.simulator_get_next_command_to_send()
  451 + else:
  452 + # Previous cmd was not completed, so, make a copy of it and send it again
  453 + # For this, it is enough to set primary key to None,
  454 + # then the send() command below will save a NEW command
  455 + #self.cmdts = copy.copy(self.cmdts)
  456 + self.cmdts.set_as_pending()
  457 + self.cmdts.id = None
  458 +
  459 + # No more command to send (from simulator), return
  460 + if self.cmdts is None: return
  461 +
  462 + # 1) Send cmd
  463 + print(f"Send command", self.cmdts)
  464 + self.cmdts.send()
  465 + cmdts_is_processed = False
  466 + cmdts_res = None
  467 +
  468 + # 2) Wait for end of cmd execution
  469 + #self.wait_for_execution_of_cmd(self.cmdts)
  470 + while not cmdts_is_processed:
  471 + print(f"Waiting for end of cmd {self.cmdts.name} execution...")
  472 + self.cmdts.refresh_from_db()
  473 + # timeout ?
  474 + if self.cmdts.is_expired(): break
  475 + # skipped or killed ?
  476 + if self.cmdts.is_skipped() or self.cmdts.is_killed(): break
  477 + if self.cmdts.is_executed():
  478 + cmdts_res = self.cmdts.get_result()
  479 + self.cmdts = None
  480 + cmdts_is_processed = True
  481 + time.sleep(1)
  482 +
  483 + # 3) Get cmd result
  484 + if cmdts_is_processed:
  485 + print(f"Cmd executed. Result is '{cmdts_res}'")
  486 + else:
  487 + print("Command was not completed")
  488 +
446 489  
447 490 """
448 491 def purge_commands(self):
... ... @@ -604,11 +647,18 @@ class Agent:
604 647 self._agent_survey.save()
605 648  
606 649  
  650 + def simulator_get_next_command_to_send(self)->Command:
  651 + cmd_name = next(self.SIMULATOR_COMMANDS, None)
  652 + if cmd_name is None: return None
  653 + receiver_agent = self.name if self.SIMULATOR_COMMANDS_DEST=="myself" else self.SIMULATOR_COMMANDS_DEST
  654 + return Command(sender=self.name, receiver=receiver_agent, name=cmd_name)
  655 +
607 656 def simulator_send_next_command(self):
608 657 #self._current_test_cmd = "go_idle" if self._current_test_cmd=="go_active" else "go_active"
609 658 #if self._nb_test_cmds == 4: self._current_test_cmd = "exit"
610 659 cmd_name = next(self.SIMULATOR_COMMANDS, None)
611   - if not cmd_name: return
  660 + #print("next cmd is ", cmd_name)
  661 + if cmd_name is None: return
612 662 #Command.objects.create(sender=self.name, receiver=self.name, name=cmd_name)
613 663 receiver_agent = self.name if self.SIMULATOR_COMMANDS_DEST=="myself" else self.SIMULATOR_COMMANDS_DEST
614 664 Command.objects.create(sender=self.name, receiver=receiver_agent, name=cmd_name)
... ... @@ -616,6 +666,11 @@ class Agent:
616 666 #self._simulator_current_cmd_idx += 1
617 667 #self._nb_test_cmds += 1
618 668  
  669 + """
  670 + def send_command(self, cmd_name):
  671 + receiver_agent = self.name if self.SIMULATOR_COMMANDS_DEST=="myself" else self.SIMULATOR_COMMANDS_DEST
  672 + Command.objects.create(sender=self.name, receiver=receiver_agent, name=cmd_name)
  673 + """
619 674  
620 675 def get_next_valid_command(self)->Command:
621 676 """
... ... @@ -755,9 +810,11 @@ class Agent:
755 810 # Executing command
756 811 if cmd.name == "go_active":
757 812 self.set_active()
  813 + cmd.set_result("I am now active")
758 814 time.sleep(1)
759 815 if cmd.name == "go_idle":
760 816 self.set_idle()
  817 + cmd.set_result("I am now idle")
761 818 time.sleep(1)
762 819 # If cmd is "abort", kill any currently running thread
763 820 if cmd.name in ("abort", "exit"):
... ...
src/agent/AgentA.py
... ... @@ -14,6 +14,9 @@ from Agent import Agent
14 14  
15 15 class AgentA(Agent):
16 16  
  17 + #MAX_DURATION_SEC = None
  18 + MAX_DURATION_SEC = 85
  19 +
17 20 # FOR TEST ONLY
18 21 # Run this agent in simulator mode
19 22 SIMULATOR_MODE = True
... ... @@ -21,19 +24,25 @@ class AgentA(Agent):
21 24 SIMULATOR_WITH_TEST = False
22 25 # Who should I send commands to ?
23 26 #SIMULATOR_COMMANDS_DEST = "myself"
24   - SIMULATOR_COMMANDS_DEST = "AgentX"
  27 + SIMULATOR_COMMANDS_DEST = "AgentB"
25 28 # Scenario to be executed
26 29 SIMULATOR_COMMANDS = [
27 30 "go_active",
  31 +
28 32 "go_idle",
  33 + # Not executed because receiver agent is now "idle"
  34 + #"specific0",
  35 +
  36 + # Executed because receiver agent is now "active"
29 37 "go_active",
30   - "go_idle",
31   - "go_active",
  38 + "specific1",
  39 +
32 40 "go_idle",
33 41 "exit",
34 42 ]
35 43  
36 44  
  45 +
37 46 """
38 47 =================================================================
39 48 FUNCTIONS RUN INSIDE MAIN THREAD
... ...
src/agent/AgentB.py
... ... @@ -21,9 +21,10 @@ class AgentB(Agent):
21 21 SIMULATOR_WITH_TEST = False
22 22 # Who should I send commands to ?
23 23 #SIMULATOR_COMMANDS_DEST = "myself"
24   - SIMULATOR_COMMANDS_DEST = "AgentX"
  24 + SIMULATOR_COMMANDS_DEST = "AgentA"
25 25 # Scenario to be executed
26   - SIMULATOR_COMMANDS = [
  26 + SIMULATOR_COMMANDS = []
  27 + """
27 28 "go_active",
28 29 "go_idle",
29 30 "go_active",
... ... @@ -33,6 +34,7 @@ class AgentB(Agent):
33 34 "go_idle",
34 35 "exit",
35 36 ]
  37 + """
36 38  
37 39  
38 40 """
... ...
src/common/models.py
... ... @@ -278,6 +278,12 @@ class Command(models.Model):
278 278  
279 279 # -------------- Command CLASS (static) METHODS --------------
280 280  
  281 + """
  282 + @classmethod
  283 + def send(cls, cmd:Command):
  284 + cls.objects.create(sender=cmd.name, receiver=cmd.receiver, name=cmd.name)
  285 + """
  286 +
281 287 @classmethod
282 288 def get_peremption_date_from_now(cls):
283 289 return datetime.utcnow().astimezone() - timedelta(hours = cls.COMMANDS_PEREMPTION_HOURS)
... ... @@ -374,6 +380,8 @@ class Command(models.Model):
374 380  
375 381 # --- BOOLEAN (test) functions ---
376 382  
  383 + def send(self): self.save()
  384 +
377 385 def is_generic(self):
378 386 """
379 387 Is this a generic command ?
... ... @@ -412,7 +420,14 @@ class Command(models.Model):
412 420 if elapsed_time > max_time:
413 421 """
414 422  
415   - # --- SETTERS functions ---
  423 + # --- GETTERS/SETTERS functions ---
  424 +
  425 + def get_result(self):
  426 + return self.result
  427 +
  428 + def set_result(self, result:str):
  429 + self.result = result
  430 + self.save()
416 431  
417 432 def set_read_time(self):
418 433 self.receiver_read_time = datetime.utcnow().astimezone()
... ... @@ -421,15 +436,20 @@ class Command(models.Model):
421 436  
422 437 def set_as_processed(self):
423 438 print(f"- Set command {self.name} as processed")
  439 + print(self)
424 440 self.receiver_status_code = self.CMD_STATUS_CODES.CMD_EXECUTED
425 441 self.receiver_processed_time = datetime.utcnow().astimezone()
426   - # Optimization: update the related fields
427   - self.save(update_fields=["receiver_status_code", "receiver_processed_time"])
  442 + self.save()
  443 + # Optimization: update the related fields, but does not work, why ?
  444 + ##self.save(update_fields=["receiver_status_code", "receiver_processed_time"])
428 445  
429 446 def set_as_outofdate(self):
430 447 print(f"- Set this command as expired (older than its validity duration of {self.validity_duration_sec}s): {self}")
431 448 self.set_status_to(self.CMD_STATUS_CODES.CMD_OUTOFDATE)
432 449  
  450 + def set_as_pending(self):
  451 + self.set_status_to(self.CMD_STATUS_CODES.CMD_PENDING)
  452 +
433 453 def set_as_skipped(self):
434 454 self.set_status_to(self.CMD_STATUS_CODES.CMD_SKIPPED)
435 455  
... ... @@ -448,7 +468,9 @@ class Command(models.Model):
448 468  
449 469 def set_status_to(self, status:str):
450 470 self.receiver_status_code = status
451   - self.save(update_fields=["receiver_status_code"])
  471 + self.save()
  472 + # Optimization, but does not work, why ?...
  473 + ##self.save(update_fields=["receiver_status_code"])
452 474  
453 475  
454 476 class AgentSurvey(models.Model):
... ...