diff --git a/pyAmakCore/classes/agent.py b/pyAmakCore/classes/agent.py index 16606ab466f276cb4e3a15f816c40e92977f822d..f5a979cee0771987c76cec4463613dcfcb34b811 100644 --- a/pyAmakCore/classes/agent.py +++ b/pyAmakCore/classes/agent.py @@ -104,6 +104,12 @@ class Agent: """ self.__neighbours = [] + def get_criticality(self) -> float: + """ + return criticality + """ + return self.__criticality + def compute_criticality(self) -> float: """ compute_criticality @@ -168,7 +174,7 @@ class Agent: this is the first phase of a cycle """ self.on_perceive() - self.compute_criticality() + self.__criticality = self.compute_criticality() self._next_phase() def _phase2(self) -> None: @@ -177,7 +183,7 @@ class Agent: """ self.on_decide() self.on_act() - self.compute_criticality() + self.__criticality = self.compute_criticality() self._next_phase() def _next_phase(self): diff --git a/pyAmakCore/classes/amas.py b/pyAmakCore/classes/amas.py index b78109818ba2a54c771b8fe58aae8209bfc0e98f..d24b11510190a8bbb38eae15c38fba0080166e53 100644 --- a/pyAmakCore/classes/amas.py +++ b/pyAmakCore/classes/amas.py @@ -30,16 +30,24 @@ class Amas(Schedulable, Loggable): Loggable.__init__(self) self.__environment: Environment = environment self.__agents: List[Agent] = [] + self.__agent_to_add: List[Agent] = [] + self.__agent_to_remove: List[Agent] = [] + self.__nbrcycle: int = 0 - self.scheduler: Scheduler = Scheduler() - self.scheduler.add_schedulable(self) - self.__environment.add_scheduler(self.scheduler) + self.__scheduler: Scheduler = Scheduler() + self.__scheduler.add_schedulable(self) + self.__environment.add_scheduler(self.__scheduler) self.__execution_policy: ExecutionPolicy = ExecutionPolicy.ONE_PHASE self.on_initial_agents_creation() + for agent in self.__agent_to_add: + if agent not in self.__agents: + self.__agents.append(agent) + self.__agent_to_add = [] + for agent in self.__agents: agent.compute_criticality() @@ -52,9 +60,9 @@ class Amas(Schedulable, Loggable): """ add agent in the amas agents list without duplicate """ - if agent in self.__agents: + if agent in self.__agents or agent in self.__agent_to_add: return - self.__agents.append(agent) + self.__agent_to_add.append(agent) def add_agents(self, agents: List[Agent]) -> None: """ @@ -67,9 +75,7 @@ class Amas(Schedulable, Loggable): """ remove agent from amas """ - if agent not in self.__agents: - return - self.__agents.remove(agent) + self.__agent_to_remove.append(agent) def get_environment(self) -> Environment: """ @@ -112,7 +118,7 @@ class Amas(Schedulable, Loggable): Unlock Scheduler and wait for his response """ self.give_token_syncro() - self.scheduler.take_amas_token() + self.__scheduler.take_amas_token() def cycle(self) -> None: """ @@ -120,6 +126,11 @@ class Amas(Schedulable, Loggable): """ print("Cycle : ", self.__nbrcycle) + for agent in self.__agent_to_add: + if agent not in self.__agents: + self.__agents.append(agent) + self.__agent_to_add = [] + self.on_cycle_begin() self.synchronization() @@ -146,6 +157,12 @@ class Amas(Schedulable, Loggable): self.synchronization() self.on_cycle_end() + + for agent in self.__agent_to_remove: + if agent in self.__agents: + self.__agents.remove(agent) + self.__agent_to_remove = [] + self.to_csv(self.get_cycle(), self.get_agents()) self.__nbrcycle += 1 @@ -153,29 +170,29 @@ class Amas(Schedulable, Loggable): """ Tell scheduler to start """ - self.scheduler.give_semaphore_start_stop() + self.__scheduler.give_semaphore_start_stop() def take_token(self) -> None: """ Tell scheduler to stop """ - self.scheduler.take_semaphore_start_stop() + self.__scheduler.take_semaphore_start_stop() def exit_program(self) -> None: """ exit the program at the end of the cycle """ self.put_token() - self.scheduler.exit_bool = True + self.__scheduler.exit_bool = True def set_sleep(self, sleep_time) -> None: """ set sleep between 2 cycles """ - self.scheduler.sleep_time = sleep_time + self.__scheduler.sleep_time = sleep_time def start(self) -> None: """ launch the system """ - self.scheduler.run() + self.__scheduler.run() diff --git a/pyAmakCore/classes/communicating_agent.py b/pyAmakCore/classes/communicating_agent.py index be6d0f017e1311317746d4b70d0dae89dce28166..0dbbede2b325a6c723127be266980d7224fc0a42 100644 --- a/pyAmakCore/classes/communicating_agent.py +++ b/pyAmakCore/classes/communicating_agent.py @@ -17,10 +17,11 @@ class Mail: Class message """ - def __init__(self, id_sender: int, id_receiver: int, message: Any) -> None: + def __init__(self, id_sender: int, id_receiver: int, message: Any, sending_date: int) -> None: self.__id_sender = id_sender self.__id_receiver = id_receiver self.__message = message + self.__sending_date = sending_date def get_id_sender(self): """ @@ -40,6 +41,12 @@ class Mail: """ return self.__message + def get_sending_date(self): + """ + return sending_date + """ + return self.__sending_date + class Mailbox: """ @@ -72,7 +79,7 @@ class Mailbox: """ this method is called to send a message """ - mail = Mail(self.__owner_id, id_receiver, message) + mail = Mail(self.__owner_id, id_receiver, message, self.__amas.get_cycle()) for agent in self.__amas.get_agents(): if agent.get_id() == id_receiver: @@ -106,12 +113,12 @@ class CommunicatingAgent(Agent): Override of phase 1 agent so he read mail before he perceive this is the first phase of a cycle """ - self._read_mails() + self.read_mails() self.on_perceive() self.compute_criticality() self._next_phase() - def _read_mails(self) -> None: + def read_mails(self) -> None: """ method that open all mail in the mailbox """ diff --git a/pyAmakCore/classes/environment.py b/pyAmakCore/classes/environment.py index ce59b09f9ddefe7cd24c6e0505f253b5fd367904..52c7727c464fe8b370087be926fc7a4d30068956 100644 --- a/pyAmakCore/classes/environment.py +++ b/pyAmakCore/classes/environment.py @@ -16,7 +16,7 @@ class Environment(Schedulable): def __init__(self) -> None: super().__init__() - self.scheduler: Scheduler = None + self.__scheduler: Scheduler = None self.on_initialization() # tell scheduler that init is done self.give_token_syncro() @@ -26,15 +26,15 @@ class Environment(Schedulable): set scheduler pointer to scheduler add add self to schedulables of scheduler """ - self.scheduler = scheduler - self.scheduler.add_schedulable(self) + self.__scheduler = scheduler + self.__scheduler.add_schedulable(self) def synchronization(self) -> None: """ Unlock Scheduler and wait for his response """ self.give_token_syncro() - self.scheduler.take_environment_token() + self.__scheduler.take_environment_token() def cycle(self) -> None: """ diff --git a/pyAmakCore/classes/schedulable.py b/pyAmakCore/classes/schedulable.py index 45d906cfd98594f3447dec26f4d314c88aaaeecc..7cd78bd28568908727fb6e3b5c6b5d93502508b0 100644 --- a/pyAmakCore/classes/schedulable.py +++ b/pyAmakCore/classes/schedulable.py @@ -9,7 +9,7 @@ sys.path.insert(0, str(pathlib.Path(__file__).parent)) from pyAmakCore.exception.override import ToOverrideWarning -class Schedulable(): +class Schedulable: """ Class Schedulable """ diff --git a/pyAmakCore/classes/scheduler.py b/pyAmakCore/classes/scheduler.py index 9c20e643c2fbdcc801123126f183c78a54d5a78a..a7c2b593b4f8147a604246af37fbc3c41ee366f4 100644 --- a/pyAmakCore/classes/scheduler.py +++ b/pyAmakCore/classes/scheduler.py @@ -11,7 +11,7 @@ sys.path.insert(0, str(pathlib.Path(__file__).parent)) from pyAmakCore.classes.schedulable import Schedulable -class Scheduler(): +class Scheduler: """ Scheduler class, to make sure that environment and amas are always sync together """ diff --git a/pyAmakCore/classes/tools/amasIHM.py b/pyAmakCore/classes/tools/amasIHM.py new file mode 100644 index 0000000000000000000000000000000000000000..5c1a6889566a7cf70374a6cfc999547e0ffe48cf --- /dev/null +++ b/pyAmakCore/classes/tools/amasIHM.py @@ -0,0 +1,36 @@ +""" +Amas class that need to be used for pyAmakIhm +""" +from pyAmakCore.classes.amas import Amas +from pyAmakCore.classes.environment import Environment + + +class AmasIHM(Amas): + """ + Convenient class to override while using pyAmakIHM + """ + + def __init__(self, environment: Environment): + self.__observer = None + super().__init__(environment) + + def get_Agents_Sorted(self): + """ + sort agent by id + """ + agents = self.get_agents() + agents.sort(key=lambda x: x.get_id()) + return agents + + def cycle(self) -> None: + """ + override amas cycle, to update obsever after each cycle + """ + super().cycle() + self.__observer.updateCycle(self.get_environment(), self) + + def attach(self, observer: 'Controleur') -> None: + """ + set observer pointer to observer + """ + self.__observer = observer diff --git a/pyAmakCore/classes/tools/loggable.py b/pyAmakCore/classes/tools/loggable.py index 19eeb4d2c3e15453b8c4372d9d074b8103bc8acd..094e4f0ab9ce6789398d85a181a4872de0e8b58b 100644 --- a/pyAmakCore/classes/tools/loggable.py +++ b/pyAmakCore/classes/tools/loggable.py @@ -2,6 +2,7 @@ class allowing to save the state of the system at a given moment """ from os import path +from typing import List from pandas import DataFrame @@ -13,16 +14,16 @@ class Loggable: def __init__(self): self.__do_log = False self.__file_path = None + self.__ignore_attribute = ["_Agent__amas", "_Agent__environment"] - def to_csv(self, cycle, var_list): + def to_csv(self, cycle: int, var_list: List['Agent']) -> None: """ get cycle and agent list and print them """ if not self.__do_log: return - ignore_attribute = ["_Agent__amas", "_Agent__environment"] - table = [{**{e: x[e] for e in x if e not in ignore_attribute}, + table = [{**{e: x[e] for e in x if e not in self.__ignore_attribute}, **{'nombre_cycle': cycle}} for x in map(vars, var_list)] dataframe = DataFrame(table) @@ -34,8 +35,28 @@ class Loggable: else: dataframe.to_csv(path_or_buf=self.__file_path, index=False) - def set_do_log(self, boolean): + def set_do_log(self, boolean: bool) -> None: + """ + tell the amas if it should log or not + """ self.__do_log = boolean - def set_file_path(self, path_to_file): + def set_file_path(self, path_to_file: str) -> None: + """ + specify path to csv + """ self.__file_path = path_to_file + + def add_ignore_attribute(self, attribute: str) -> None: + """ + add attribute in ignored attribute + """ + self.__ignore_attribute.append(attribute) + + def remove_ignore_attribute(self, attribute: str) -> None: + """ + remove attribute in ignored attribute + """ + if attribute not in self.__ignore_attribute: + return + self.__ignore_attribute.remove(attribute) diff --git a/pyAmakCore/enumeration/agent_phase.py b/pyAmakCore/enumeration/agent_phase.py index 56464f8aa0030c6e3fca7cc0ba585b617bbbaa5a..259a21e87d72f749556e4580280c7dc51a30c6ef 100644 --- a/pyAmakCore/enumeration/agent_phase.py +++ b/pyAmakCore/enumeration/agent_phase.py @@ -1,3 +1,6 @@ +""" +Agent phases +""" from enum import Enum, auto diff --git a/pyAmakCore/enumeration/executionPolicy.py b/pyAmakCore/enumeration/executionPolicy.py index 78fc09cd586362c974118a760a6dc53c0fbf6fd2..7adbd01392977e72e0a719d28a1ca57a5283e441 100644 --- a/pyAmakCore/enumeration/executionPolicy.py +++ b/pyAmakCore/enumeration/executionPolicy.py @@ -1,3 +1,6 @@ +""" +Agent execution policy +""" from enum import Enum, auto diff --git a/pyAmakCore/tests/test_amas/test_agent.py b/pyAmakCore/tests/test_amas/test_agent.py index 5726bce9394dd991bd47160002d645f03c2b2c9b..e43150f67f56a23074c9a934c3f706401a997a83 100644 --- a/pyAmakCore/tests/test_amas/test_agent.py +++ b/pyAmakCore/tests/test_amas/test_agent.py @@ -8,6 +8,11 @@ from pyAmakCore.classes.amas import Amas from pyAmakCore.classes.environment import Environment from pyAmakCore.classes.agent import Agent +class SimplerAmas(Amas): + + def synchronization(self): + self._Amas__scheduler.give_amas_token() + super().synchronization() class TestAmasAgents(TestCase): """ @@ -33,30 +38,31 @@ class TestAmasAgents(TestCase): """ environment = Environment() - amas = Amas(environment) + amas = SimplerAmas(environment) agent1 = Agent(amas) agent2 = Agent(amas) agent3 = Agent(amas) - agent4 = Agent(amas) - agent5 = Agent(amas) - # add 1 agent amas.add_agent(agent1) + amas.cycle() self.assertEqual(amas.get_agents(), [agent1]) # don't remove previous agent amas.add_agent(agent2) + amas.cycle() self.assertEqual(amas.get_agents(), [agent1, agent2]) # add agent in good order amas.add_agent(agent3) + amas.cycle() self.assertEqual(amas.get_agents(), [agent1, agent2, agent3]) # don't add duplicate amas.add_agent(agent1) amas.add_agent(agent2) amas.add_agent(agent3) + amas.cycle() self.assertEqual(amas.get_agents(), [agent1, agent2, agent3]) def test_add_agents(self) -> None: @@ -65,7 +71,7 @@ class TestAmasAgents(TestCase): """ environment = Environment() - amas = Amas(environment) + amas = SimplerAmas(environment) agent1 = Agent(amas) agent2 = Agent(amas) @@ -74,36 +80,43 @@ class TestAmasAgents(TestCase): agent5 = Agent(amas) amas.add_agents([agent1, agent2, agent3, agent4, agent5]) + amas.cycle() self.assertEqual(amas.get_agents(), [agent1, agent2, agent3, agent4, agent5]) - amas = Amas(environment) + amas = SimplerAmas(environment) amas.add_agents([agent1, agent2]) + amas.cycle() self.assertEqual(amas.get_agents(), [agent1, agent2]) amas.add_agents([agent1, agent2, agent4, agent2, agent3]) + amas.cycle() self.assertEqual(amas.get_agents(), [agent1, agent2, agent4, agent3]) def test_remove_agent(self) -> None: environment = Environment() - amas = Amas(environment) + amas = SimplerAmas(environment) agent1 = Agent(amas) agent2 = Agent(amas) agent3 = Agent(amas) amas.remove_agent(agent2) + amas.cycle() self.assertEqual(amas.get_agents(), []) amas.add_agents([agent1, agent2, agent3]) amas.remove_agent(agent2) + amas.cycle() self.assertEqual(amas.get_agents(), [agent1, agent3]) amas.remove_agent(agent2) + amas.cycle() self.assertEqual(amas.get_agents(), [agent1, agent3]) amas.remove_agent(agent1) amas.remove_agent(agent3) + amas.cycle() self.assertEqual(amas.get_agents(), []) diff --git a/pyAmakCore/tests/test_amas/test_nbr_cycle.py b/pyAmakCore/tests/test_amas/test_nbr_cycle.py index 7bc3a8e2cb5e871c9b05c15fcf8898fe19eddb00..e4341b38daeb7eb8fa9f24a3bf6f2b0e86e3856e 100644 --- a/pyAmakCore/tests/test_amas/test_nbr_cycle.py +++ b/pyAmakCore/tests/test_amas/test_nbr_cycle.py @@ -12,7 +12,7 @@ from pyAmakCore.classes.environment import Environment class SimplerAmas(Amas): def synchronization(self): - self.scheduler.give_amas_token() + self._Amas__scheduler.give_amas_token() super().synchronization() diff --git a/pyAmakCore/tests/test_communicating_agent/test_mail.py b/pyAmakCore/tests/test_communicating_agent/test_mail.py index 9f875811be0ec0334166e2a871e3b480a7f1ffae..444505df65d027c74ab77e81a8d50fa82d365a2f 100644 --- a/pyAmakCore/tests/test_communicating_agent/test_mail.py +++ b/pyAmakCore/tests/test_communicating_agent/test_mail.py @@ -15,17 +15,18 @@ class TestMail(TestCase): """ Test mail init """ - mail = Mail(1, 5, None) + mail = Mail(1, 5, None, 0) self.assertEqual(mail.get_id_sender(), 1) self.assertEqual(mail.get_id_receiver(), 5) self.assertEqual(mail.get_message(), None) + self.assertEqual(mail.get_sending_date(), 0) - mail = Mail(255, 0, "test") + mail = Mail(255, 0, "test", 12) self.assertEqual(mail.get_id_sender(), 255) self.assertEqual(mail.get_id_receiver(), 0) self.assertEqual(mail.get_message(), "test") - + self.assertEqual(mail.get_sending_date(), 12) if __name__ == '__main__': main() diff --git a/release/changelog.txt b/release/changelog.txt new file mode 100644 index 0000000000000000000000000000000000000000..06af3f7857489efc206e726f505ec486d4a3e29d --- /dev/null +++ b/release/changelog.txt @@ -0,0 +1,12 @@ +v0.0.5: + * Fix rare bugs : an agent that would be removed in the cycle could be called by another agent + * CSV : Add method : add_ignore_attribute(self, attribute: str) -> None, remove_ignore_attribute(self, attribute: str) -> None + to manage ignored attribute + +v0.0.4: + * add AmasIHM (old examples will no longer work from now on if using AmasIHM ) + +v0.0.3 : + * Add communicating agent + +v0.0.2 \ No newline at end of file diff --git a/release/pyAmakCore-0.0.2-py3-none-any.whl b/release/pyAmakCore-0.0.2-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..6cdca8943d9a7dc1f3bb5b900aea91220a4a16e3 Binary files /dev/null and b/release/pyAmakCore-0.0.2-py3-none-any.whl differ diff --git a/release/pyAmakCore-0.0.3-py3-none-any.whl b/release/pyAmakCore-0.0.3-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..9a5070130b134c9adc743c60e39fb2ca73374c8e Binary files /dev/null and b/release/pyAmakCore-0.0.3-py3-none-any.whl differ diff --git a/release/pyAmakCore-0.0.4-py3-none-any.whl b/release/pyAmakCore-0.0.4-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..904c43441aa8ff18808817250b162d8ea6d94d88 Binary files /dev/null and b/release/pyAmakCore-0.0.4-py3-none-any.whl differ diff --git a/release/pyAmakCore-0.0.5-py3-none-any.whl b/release/pyAmakCore-0.0.5-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..1475c4083eac33230b399e4722b27ce678b43692 Binary files /dev/null and b/release/pyAmakCore-0.0.5-py3-none-any.whl differ diff --git a/setup.py b/setup.py index 58c025a96c70d9f344b9d08422c581a589eb9946..c80c5a1fd15a52b50f6131c38cdf309c329b3c96 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup, find_packages setup( name='pyAmakCore', packages=find_packages(), - version='0.0.3', + version='0.0.5', description='AmakFramework in python', author='BE', install_requires=[],