diff --git a/note/cycle.txt b/note/cycle.txt deleted file mode 100644 index 47ad4b11a65ed7e9cc03153878a90ef53c03c950..0000000000000000000000000000000000000000 --- a/note/cycle.txt +++ /dev/null @@ -1,20 +0,0 @@ - - -init : - base - on_init - - syncro - - - -cycle : - - on cycle begin - syncro - - agent action - syncro - - on cycle end - syncro \ No newline at end of file diff --git a/note/how to use b/note/how to use deleted file mode 100644 index 94ec796203c44ddf49dde3ed98cd17efdeed89b5..0000000000000000000000000000000000000000 --- a/note/how to use +++ /dev/null @@ -1,13 +0,0 @@ -1 - create env -2 - create amas -3 - amas.start() - -to end : -amas.exit_program() - - -to start : -amas.put_token() - -to stop : -amas.take_token() \ No newline at end of file diff --git a/pyAmakCore/classes/agent.py b/pyAmakCore/classes/agent.py index f5a979cee0771987c76cec4463613dcfcb34b811..a94ae7537228f8cfdb18a9310e353e1598082239 100644 --- a/pyAmakCore/classes/agent.py +++ b/pyAmakCore/classes/agent.py @@ -7,12 +7,10 @@ from typing import List import sys import pathlib - sys.path.insert(0, str(pathlib.Path(__file__).parent)) from pyAmakCore.exception.override import ToOverrideWarning from pyAmakCore.enumeration.agent_phase import Phase -from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy class Agent: @@ -116,7 +114,13 @@ class Agent: """ return -inf - def _get_most_critical_neighbor(self, including_me: bool = False) -> 'Agent': + def set_criticality(self, criticality: float) -> None: + """ + set agent criticality to criticality + """ + self.__criticality = criticality + + def get_most_critical_neighbor(self, including_me: bool = False) -> 'Agent': """ Convenient method giving the most critical neighbor at a given moment """ @@ -135,10 +139,6 @@ class Agent: return criticalest[randint(0, len(criticalest) - 1)] - """ - Cycle - """ - def on_cycle_begin(self) -> None: """ this method is called by each agent at the start of their cycle @@ -169,24 +169,10 @@ class Agent: """ ToOverrideWarning("on_act") - def _phase1(self) -> None: - """ - this is the first phase of a cycle - """ - self.on_perceive() - self.__criticality = self.compute_criticality() - self._next_phase() - - def _phase2(self) -> None: + def next_phase(self) -> None: """ - this is the second phase of a cycle + set agent phase to the next phase """ - self.on_decide() - self.on_act() - self.__criticality = self.compute_criticality() - self._next_phase() - - def _next_phase(self): next_phase = { Phase.INITIALIZING: Phase.PERCEPTION, Phase.PERCEPTION: Phase.PERCEPTION_DONE, @@ -196,32 +182,6 @@ class Agent: } self.__phase = next_phase.get(self.__phase) - def run(self) -> None: - """ - Full cycle of an agent - """ - self._next_phase() - - execution_policy = self.__amas.get_execution_policy() - - if execution_policy == ExecutionPolicy.TWO_PHASES: - if self.__phase == Phase.PERCEPTION: - self.on_cycle_begin() - self._phase1() - return - - if self.__phase == Phase.DECISION_AND_ACTION: - self._phase2() - self.on_cycle_end() - return - - if execution_policy == ExecutionPolicy.ONE_PHASE: - self.on_cycle_begin() - self._phase1() - self._next_phase() - self._phase2() - self.on_cycle_end() - def __eq__(self, other: 'Agent') -> bool: # check class if other is None: diff --git a/pyAmakCore/classes/amas.py b/pyAmakCore/classes/amas.py index d24b11510190a8bbb38eae15c38fba0080166e53..7a460931b4116cf7aa36716b6aa64d51a5ac0c36 100644 --- a/pyAmakCore/classes/amas.py +++ b/pyAmakCore/classes/amas.py @@ -1,18 +1,15 @@ """ Amas Class """ -from threading import Thread from typing import List import sys import pathlib - sys.path.insert(0, str(pathlib.Path(__file__).parent)) from pyAmakCore.classes.tools.loggable import Loggable -from pyAmakCore.classes.schedulable import Schedulable -from pyAmakCore.classes.scheduler import Scheduler +from pyAmakCore.classes.tools.schedulable import Schedulable from pyAmakCore.classes.environment import Environment from pyAmakCore.classes.agent import Agent from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy @@ -24,43 +21,53 @@ class Amas(Schedulable, Loggable): Amas Class """ - def __init__(self, environment: Environment) -> None: + def __init__(self, + environment: Environment, + execution_policy: ExecutionPolicy = ExecutionPolicy.ONE_PHASE + ) -> None: Schedulable.__init__(self) 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.__execution_policy: ExecutionPolicy = ExecutionPolicy.ONE_PHASE + self.__execution_policy: ExecutionPolicy = execution_policy + self.on_initialization() self.on_initial_agents_creation() + def add_pending_agent(self) -> List[Agent]: + """ + add pending agent into agent and return added agents + """ 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() + self.__agents.append(agent) + tmp = self.__agent_to_add + self.__agent_to_add = [] + return tmp - self.on_initialization() + def remove_pending_agent(self): + """ + add pending agent into agent and return removed agents + """ + for agent in self.__agent_to_remove: + self.__agents.remove(agent) + tmp = self.__agent_to_remove + self.__agent_to_remove = [] - # tell scheduler that init is done - self.give_token_syncro() + return tmp def add_agent(self, agent: 'Agent') -> None: """ add agent in the amas agents list without duplicate """ - if agent in self.__agents or agent in self.__agent_to_add: + if agent in self.__agents: + return + if agent in self.__agent_to_add: return self.__agent_to_add.append(agent) @@ -75,6 +82,10 @@ class Amas(Schedulable, Loggable): """ remove agent from amas """ + if agent not in self.__agents: + return + if agent in self.__agent_to_remove: + return self.__agent_to_remove.append(agent) def get_environment(self) -> Environment: @@ -95,104 +106,9 @@ class Amas(Schedulable, Loggable): """ return self.__execution_policy - def set_execution_policy(self, execution_policy: ExecutionPolicy) -> None: - """ - set system execution_policy - """ - self.__execution_policy = execution_policy - - def get_cycle(self) -> int: - """ - return nbr of cycle - """ - return self.__nbrcycle - def on_initial_agents_creation(self) -> None: """ This method is called at the end of __init__() """ ToOverrideWarning("on_initial_agents_creation") - def synchronization(self) -> None: - """ - Unlock Scheduler and wait for his response - """ - self.give_token_syncro() - self.__scheduler.take_amas_token() - - def cycle(self) -> None: - """ - Main behavior of Amas - """ - 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() - - threads = [] - # suffle - for agent in self.__agents: - current_thread = Thread(target=agent.run) - threads.append(current_thread) - current_thread.start() - - for thread in threads: - thread.join() - - if self.__execution_policy == ExecutionPolicy.TWO_PHASES: - threads = [] - for agent in self.__agents: - current_thread = Thread(target=agent.run) - threads.append(current_thread) - current_thread.start() - - for thread in threads: - thread.join() - - 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 - - def put_token(self) -> None: - """ - Tell scheduler to start - """ - self.__scheduler.give_semaphore_start_stop() - - def take_token(self) -> None: - """ - Tell scheduler to 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 - - def set_sleep(self, sleep_time) -> None: - """ - set sleep between 2 cycles - """ - self.__scheduler.sleep_time = sleep_time - - def start(self) -> None: - """ - launch the system - """ - self.__scheduler.run() diff --git a/pyAmakCore/classes/communicating_agent.py b/pyAmakCore/classes/communicating_agent.py index 0dbbede2b325a6c723127be266980d7224fc0a42..e31b4ccce66e9a84a2856456e0f0e25c8558610e 100644 --- a/pyAmakCore/classes/communicating_agent.py +++ b/pyAmakCore/classes/communicating_agent.py @@ -5,10 +5,9 @@ from typing import Any, List import pathlib import sys -from pyAmakCore.exception.mailbox import ReceiverIsNotSelf, ReceiverDontExist - sys.path.insert(0, str(pathlib.Path(__file__).parent)) +from pyAmakCore.exception.mailbox import ReceiverIsNotSelf, ReceiverDontExist from pyAmakCore.classes.agent import Agent @@ -108,16 +107,6 @@ class CommunicatingAgent(Agent): """ self.__mailbox.receive_mail(mail) - def _phase1(self): - """ - Override of phase 1 agent so he read mail before he perceive - this is the first phase of a cycle - """ - self.read_mails() - self.on_perceive() - self.compute_criticality() - self._next_phase() - 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 52c7727c464fe8b370087be926fc7a4d30068956..ba45fa2fbd31d064e5fa9da62ff08b4020cfff64 100644 --- a/pyAmakCore/classes/environment.py +++ b/pyAmakCore/classes/environment.py @@ -3,10 +3,10 @@ Environment class """ import sys import pathlib + sys.path.insert(0, str(pathlib.Path(__file__).parent)) -from pyAmakCore.classes.schedulable import Schedulable -from pyAmakCore.classes.scheduler import Scheduler +from pyAmakCore.classes.tools.schedulable import Schedulable class Environment(Schedulable): @@ -16,34 +16,4 @@ class Environment(Schedulable): def __init__(self) -> None: super().__init__() - self.__scheduler: Scheduler = None self.on_initialization() - # tell scheduler that init is done - self.give_token_syncro() - - def add_scheduler(self, scheduler: Scheduler) -> None: - """ - set scheduler pointer to scheduler - add add self to schedulables of scheduler - """ - 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() - - def cycle(self) -> None: - """ - Main behavior of Environment - """ - self.on_cycle_begin() - self.synchronization() - - # mileu de cycle - self.synchronization() - - self.on_cycle_end() diff --git a/pyAmakCore/classes/scheduler.py b/pyAmakCore/classes/scheduler.py index a7c2b593b4f8147a604246af37fbc3c41ee366f4..b16b13ffedf7dd8bfbaedff0972635e1364138a6 100644 --- a/pyAmakCore/classes/scheduler.py +++ b/pyAmakCore/classes/scheduler.py @@ -1,14 +1,17 @@ """ Scheduler class """ -from threading import Semaphore, Thread -from time import sleep -from typing import List import sys import pathlib +import pickle +from threading import Semaphore, Thread +from time import sleep + sys.path.insert(0, str(pathlib.Path(__file__).parent)) -from pyAmakCore.classes.schedulable import Schedulable +from pyAmakCore.classes.amas import Amas +from pyAmakCore.classes.scheduler_tool.schedulable_thread import SchedulableThread +from pyAmakCore.classes.scheduler_tool.amas_thread import AmasThread class Scheduler: @@ -16,104 +19,124 @@ class Scheduler: Scheduler class, to make sure that environment and amas are always sync together """ - def __init__(self) -> None: + def __init__(self, amas: Amas) -> None: + self.amas: Amas = amas - # List of all schedulables - self.__schedulables: List[Schedulable] = [] - # Sleep timer between 2 cycle - self.sleep_time: float = 0 + self.exit_bool = False - # Semaphore for start/stop button - self.__semaphore_start_stop: Semaphore = Semaphore(0) - # Semaphore to tell amas to continue - self.__semaphore_amas: Semaphore = Semaphore(0) - # Semaphore to tell environment to continue - self.__semaphore_environment: Semaphore = Semaphore(0) + self.semaphore_start_stop = Semaphore(0) - self.exit_bool = False + self.schedulables = [] + self.schedulables_threads = [] - def add_schedulable(self, schedulable: Schedulable) -> None: - """ - add schedulable in schedulables list - """ - self.__schedulables.append(schedulable) + self.add_schedulables(amas, AmasThread) + self.add_schedulables(amas.get_environment(), SchedulableThread) - def take_amas_token(self) -> None: - """ - make amas wait here for a token - """ - self.__semaphore_amas.acquire() + self.sleep_time = 0 - def give_amas_token(self) -> None: - """ - unlock amas - """ - self.__semaphore_amas.release() + def get_amas(self): + return self.amas + + def add_schedulables(self, schedulable, cls): + schedulable_thread = cls(schedulable) + self.schedulables.append(schedulable_thread) + current_thread = Thread(target=schedulable_thread.run) + self.schedulables_threads.append(current_thread) + current_thread.start() - def take_environment_token(self) -> None: + def wait_schedulables(self) -> None: """ - make environment wait here for a token + wait for all schedulable to release a token """ - self.__semaphore_environment.acquire() + for schedulable in self.schedulables: + schedulable.action_done.acquire() - def give_environment_token(self) -> None: + def start_schedulables(self) -> None: """ - unlock environment + wait for all schedulable to release a token """ - self.__semaphore_environment.release() + for schedulable in self.schedulables: + schedulable.is_waiting.release() - def take_semaphore_start_stop(self) -> None: + def first_part(self) -> None: """ - Scheduler will wait for a token to start the cycle + first part of a cycle """ - self.__semaphore_start_stop.acquire() + self.start_schedulables() + # on cycle begin + self.wait_schedulables() - def give_semaphore_start_stop(self) -> None: + def main_part(self) -> None: """ - give a token to unlock Scheduler + main part of a cycle """ - self.__semaphore_start_stop.release() + self.start_schedulables() + # agent cycle + self.wait_schedulables() - def syncro_schedulable(self) -> None: + def last_part(self) -> None: """ - wait for all schedulable to release a token + last part of a cycle """ - for schedulable in self.__schedulables: - schedulable.take_token_syncro() + self.start_schedulables() + # on cycle end + self.wait_schedulables() def run(self) -> None: """ main part of amak core """ - # wait that all schedulable are ready - self.syncro_schedulable() - while not self.exit_bool: + print("Cycle : ", self.amas.get_cycle()) - self.__semaphore_start_stop.acquire() + self.semaphore_start_stop.acquire() if self.exit_bool: - return - self.__semaphore_start_stop.release() + break + self.semaphore_start_stop.release() + + self.first_part() + self.main_part() + self.last_part() + + sleep(self.sleep_time) + self.close_childs() - threads = [] - for schedulable in self.__schedulables: - current_thread = Thread(target=schedulable.run) - threads.append(current_thread) - current_thread.start() + def close_childs(self): + for schedulable in self.schedulables: + schedulable.exit_bool = True + schedulable.is_waiting.release() + for thread in self.schedulables_threads: + thread.join(0) - # on cycle begin - self.syncro_schedulable() - self.give_amas_token() - self.give_environment_token() + """ + program interface + """ - # main part of the cycle - self.syncro_schedulable() - self.give_amas_token() - self.give_environment_token() + def exit_program(self): + self.exit_bool = True + self.semaphore_start_stop.release() - # on cycle end + def start(self): + self.semaphore_start_stop.release() - for thread in threads: - thread.join() + def stop(self): + self.semaphore_start_stop.acquire() + + def set_sleep(self, sleep_time: int): + self.sleep_time = sleep_time + + """ + load & save program + """ + + def save(self): + with open('filename.pickle', 'wb') as handle: + pickle.dump(self.amas, handle, protocol=pickle.HIGHEST_PROTOCOL) + + @classmethod + def load(cls): + with open('filename.pickle', 'rb') as handle: + amas_object = pickle.load(handle) + + return cls(amas_object) - sleep(self.sleep_time) diff --git a/pyAmakCore/classes/scheduler_tool/__init__.py b/pyAmakCore/classes/scheduler_tool/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/pyAmakCore/classes/scheduler_tool/agent_thread.py b/pyAmakCore/classes/scheduler_tool/agent_thread.py new file mode 100644 index 0000000000000000000000000000000000000000..72098d2bb31d8433ac47ee3525e97e3e5b680b1d --- /dev/null +++ b/pyAmakCore/classes/scheduler_tool/agent_thread.py @@ -0,0 +1,74 @@ +""" +Class Agent thread +""" +from threading import Semaphore +import sys +import pathlib + + +sys.path.insert(0, str(pathlib.Path(__file__).parent.parent)) + +from pyAmakCore.classes.communicating_agent import CommunicatingAgent +from pyAmakCore.classes.agent import Agent +from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy + + +class AgentThread: + """ + thread class used to thread agent + """ + action_done = Semaphore(0) + execution_policy = ExecutionPolicy.ONE_PHASE + + def __init__(self, agent: Agent): + + self.agent: Agent = agent + self.is_waiting: Semaphore = Semaphore(0) + self.exit_bool: bool = False + + def phase1(self) -> None: + """ + this is the first phase of a cycle + """ + if isinstance(self.agent, CommunicatingAgent): + self.agent.read_mails() + + self.agent.on_perceive() + self.agent.set_criticality(self.agent.compute_criticality()) + self.agent.next_phase() + + def phase2(self) -> None: + """ + this is the second phase of a cycle + """ + self.agent.on_decide() + self.agent.on_act() + self.agent.set_criticality(self.agent.compute_criticality()) + self.agent.next_phase() + + def run(self) -> None: + """ + main part of an agent thread + """ + while not self.exit_bool: + + self.is_waiting.acquire() + if self.exit_bool: + return + + self.agent.next_phase() + self.agent.on_cycle_begin() + + self.phase1() + + if AgentThread.execution_policy == ExecutionPolicy.TWO_PHASES: + AgentThread.action_done.release() + self.is_waiting.acquire() + + self.agent.next_phase() + + self.phase2() + + self.agent.on_cycle_end() + + AgentThread.action_done.release() diff --git a/pyAmakCore/classes/scheduler_tool/amas_thread.py b/pyAmakCore/classes/scheduler_tool/amas_thread.py new file mode 100644 index 0000000000000000000000000000000000000000..1d70f237eca2a161bff77ff0eafde8878cb23d7c --- /dev/null +++ b/pyAmakCore/classes/scheduler_tool/amas_thread.py @@ -0,0 +1,122 @@ +""" +thread class for amas +""" +from threading import Thread +from typing import List + +import sys +import pathlib + +from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy + +sys.path.insert(0, str(pathlib.Path(__file__).parent.parent)) + +from pyAmakCore.classes.scheduler_tool.agent_thread import AgentThread +from pyAmakCore.classes.amas import Amas +from pyAmakCore.classes.agent import Agent +from pyAmakCore.classes.scheduler_tool.schedulable_thread import SchedulableThread + + +class AmasThread(SchedulableThread): + """ + thread class used to thread amas + """ + + def __init__(self, amas: Amas) -> None: + super().__init__(amas) + + self.agents: List[AgentThread] = [] + self.agents_thread: List[Thread] = [] + + AgentThread.execution_policy = self.schedulable.get_execution_policy() + + self.schedulable.add_pending_agent() + for agent in self.schedulable.get_agents(): + self.add_agent(agent) + + def add_agent(self, agent: Agent) -> None: + """ + make agent a thread and start the thread + """ + agent_thread = AgentThread(agent) + self.agents.append(agent_thread) + current_thread = Thread(target=agent_thread.run) + self.agents_thread.append(current_thread) + current_thread.start() + + def add_agents(self) -> None: + """ + if there are new agents, add them + """ + added_agent = self.schedulable.add_pending_agent() + for agent in added_agent: + self.add_agent(agent) + + def remove_agents(self) -> None: + """ + if some agents are removed, close their thread + """ + removed_agent = self.schedulable.remove_pending_agent() + for agent in removed_agent: + for i in range(len(self.agents)): + if agent == self.agents[i].agent: + self.agents[i].exit_bool = True + self.agents[i].is_waiting.release() + self.agents_thread[i].join() + + self.agents.remove(self.agents[i]) + self.agents_thread.remove(self.agents_thread[i]) + + def on_cycle_begin(self) -> None: + """ + start of cycle + """ + self.add_agents() + self.schedulable.on_cycle_begin() + + def main_cycle_part(self) -> None: + """ + main part of the cycle + """ + for agent in self.agents: + agent.is_waiting.release() + # agent cycle + + if self.schedulable.get_execution_policy() == ExecutionPolicy.TWO_PHASES: + # wait agents + for i in range(len(self.agents)): + AgentThread.action_done.acquire() + # start phase 2 for all agents + for agent in self.agents: + agent.is_waiting.release() + + for i in range(len(self.agents)): + AgentThread.action_done.acquire() + + def on_cycle_end(self) -> None: + """ + end of cycle + """ + self.schedulable.on_cycle_end() + + self.remove_agents() + self.schedulable.to_csv(self.schedulable.get_cycle(), self.schedulable.get_agents()) + + self.schedulable.cycle() + + def run(self) -> None: + super().run() + self.close_child() + + def close_child(self) -> None: + """ + tell all child threads to close + """ + for agent in self.agents: + agent.exit_bool = True + agent.is_waiting.release() + + for thread in self.agents_thread: + thread.join(0) + + diff --git a/pyAmakCore/classes/scheduler_tool/schedulable_thread.py b/pyAmakCore/classes/scheduler_tool/schedulable_thread.py new file mode 100644 index 0000000000000000000000000000000000000000..17f347ab275403fbe548ec6390baeab4b8e4525a --- /dev/null +++ b/pyAmakCore/classes/scheduler_tool/schedulable_thread.py @@ -0,0 +1,53 @@ +""" +thread class for Schedulable +""" +from threading import Semaphore + +import sys +import pathlib + +sys.path.insert(0, str(pathlib.Path(__file__).parent.parent)) + +from pyAmakCore.classes.tools.schedulable import Schedulable + + +class SchedulableThread: + """ + thread class used to thread schedulable + """ + + def __init__(self, schedulable: Schedulable): + + self.schedulable: Schedulable = schedulable + self.is_waiting: Semaphore = Semaphore(0) + self.exit_bool: bool = False + self.action_done: Semaphore = Semaphore(0) + + def on_cycle_begin(self): + self.schedulable.on_cycle_begin() + + def main_cycle_part(self): + pass + + def on_cycle_end(self): + self.schedulable.on_cycle_end() + self.schedulable.cycle() + + def run(self) -> None: + """ + main part of a schedulable thread + """ + while not self.exit_bool: + self.is_waiting.acquire() + if self.exit_bool: + break + self.on_cycle_begin() + self.action_done.release() + + self.is_waiting.acquire() + self.main_cycle_part() + self.action_done.release() + + self.is_waiting.acquire() + self.on_cycle_end() + self.action_done.release() diff --git a/pyAmakCore/classes/tools/amasIHM.py b/pyAmakCore/classes/tools/amasIHM.py deleted file mode 100644 index 5c1a6889566a7cf70374a6cfc999547e0ffe48cf..0000000000000000000000000000000000000000 --- a/pyAmakCore/classes/tools/amasIHM.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -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 094e4f0ab9ce6789398d85a181a4872de0e8b58b..5ac77e22b2350d9b629a4ecce8ca0ed964fc5ec0 100644 --- a/pyAmakCore/classes/tools/loggable.py +++ b/pyAmakCore/classes/tools/loggable.py @@ -3,7 +3,6 @@ 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 diff --git a/pyAmakCore/classes/schedulable.py b/pyAmakCore/classes/tools/schedulable.py similarity index 52% rename from pyAmakCore/classes/schedulable.py rename to pyAmakCore/classes/tools/schedulable.py index 7cd78bd28568908727fb6e3b5c6b5d93502508b0..c594ba8d90814ad5dbb767db5b7bfb9eb93749a4 100644 --- a/pyAmakCore/classes/schedulable.py +++ b/pyAmakCore/classes/tools/schedulable.py @@ -1,10 +1,9 @@ """ Schedulable interface """ -from threading import Semaphore import sys import pathlib -sys.path.insert(0, str(pathlib.Path(__file__).parent)) +sys.path.insert(0, str(pathlib.Path(__file__).parent.parent)) from pyAmakCore.exception.override import ToOverrideWarning @@ -13,7 +12,16 @@ class Schedulable: """ Class Schedulable """ - syncro_semaphore: Semaphore = Semaphore(0) + + def __init__(self): + + self.__nbr_cycle = 0 + + def get_cycle(self) -> int: + return self.__nbr_cycle + + def cycle(self) -> None: + self.__nbr_cycle += 1 def on_initialization(self) -> None: """ @@ -32,27 +40,3 @@ class Schedulable: This method will be executed at the end of each cycle """ ToOverrideWarning("on_cycle_end") - - def run(self) -> None: - """ - Method that will be called by the thread - """ - self.cycle() - - def cycle(self) -> None: - """ - Main behavior of the Schedulable - """ - ToOverrideWarning("cycle") - - def take_token_syncro(self) -> None: - """ - Scheduler will wait here that the schedulable release a token - """ - self.syncro_semaphore.acquire() - - def give_token_syncro(self) -> None: - """ - Unlock scheduler - """ - self.syncro_semaphore.release() diff --git a/pyAmakCore/classes/tools/schedulerIHM.py b/pyAmakCore/classes/tools/schedulerIHM.py new file mode 100644 index 0000000000000000000000000000000000000000..9390671fd74934ee13020da1a0b4c51099fe6a76 --- /dev/null +++ b/pyAmakCore/classes/tools/schedulerIHM.py @@ -0,0 +1,32 @@ +""" +Scheduler class that need to be used for pyAmakIhm +""" +import pathlib +from time import sleep + +import sys + +sys.path.insert(0, str(pathlib.Path(__file__).parent.parent)) + +from pyAmakCore.classes.amas import Amas +from pyAmakCore.classes.scheduler import Scheduler + + +class SchedulerIHM(Scheduler): + """ + Convenient class to override while using pyAmakIHM + """ + + def __init__(self, amas: Amas): + self.__observer = None + super().__init__(amas) + + def last_part(self) -> None: + super().last_part() + self.__observer.updateCycle() + + def attach(self, observer: 'Controleur') -> None: + """ + set observer pointer to observer + """ + self.__observer = observer diff --git a/pyAmakCore/tests/memory_leak/main.py b/pyAmakCore/tests/memory_leak/main.py index 189fad630707dcfbb9f06245fb3368bceadd0324..a1a804ff4b60c888679878d63c78422a05d2768f 100644 --- a/pyAmakCore/tests/memory_leak/main.py +++ b/pyAmakCore/tests/memory_leak/main.py @@ -1,38 +1,39 @@ +from pyAmakCore.exception.override import ToOverrideWarning +from pyAmakCore.classes.agent import Agent from pyAmakCore.classes.amas import Amas from pyAmakCore.classes.environment import Environment -from pyAmakCore.classes.agent import Agent +from pyAmakCore.classes.scheduler import Scheduler class SimpleAgent(Agent): - """ - test - """ + pass class SimpleAmas(Amas): - """ - test - """ + def on_initialization(self) -> None: + self.set_do_log(True) def on_initial_agents_creation(self) -> None: for i in range(10): - self.add_agent(Agent(self)) - + self.add_agent(SimpleAgent(self)) class SimpleEnv(Environment): - """ - test - """ + pass +import time +start_time = time.time() +ToOverrideWarning.enable_warning(False) env = SimpleEnv() amas = SimpleAmas(env) -amas.put_token() +scheduler = Scheduler(amas) -amas.start() +scheduler.start() +scheduler.run() +print("--- %s seconds ---" % (time.time() - start_time)) """ There are no visible memory leak in pyAmakCore -""" \ No newline at end of file +""" diff --git a/pyAmakCore/tests/test_agent/test_get_most_critical_neighbor.py b/pyAmakCore/tests/test_agent/test_get_most_critical_neighbor.py index 4dff5c44a84625b18c6826c1d0b88cd9a21b2a2c..c190a4a6be87f19a87bacd81998dcb1368adcd99 100644 --- a/pyAmakCore/tests/test_agent/test_get_most_critical_neighbor.py +++ b/pyAmakCore/tests/test_agent/test_get_most_critical_neighbor.py @@ -11,9 +11,6 @@ class SimpleAgent(Agent): def set_criticality(self, i): self._Agent__criticality = i - def get_most_critical(self, i): - return self._get_most_critical_neighbor(i) - class TestAgentGetMostCriticalNeighbor(TestCase): """ @@ -44,7 +41,7 @@ class TestAgentGetMostCriticalNeighbor(TestCase): n4.set_criticality(25) agent.add_neighbour(n4) - self.assertEqual(agent.get_most_critical(False), n4) + self.assertEqual(agent.get_most_critical_neighbor(False), n4) def test_get_most_critical_neighbor_with_self(self): Agent.reset_agent() @@ -70,7 +67,7 @@ class TestAgentGetMostCriticalNeighbor(TestCase): n4.set_criticality(25) agent.add_neighbour(n4) - self.assertEqual(agent.get_most_critical(True), agent) + self.assertEqual(agent.get_most_critical_neighbor(True), agent) if __name__ == '__main__': diff --git a/pyAmakCore/tests/test_agent/test_run.py b/pyAmakCore/tests/test_agent/test_run.py deleted file mode 100644 index 991cb562b9fa394cda7aff988ffaa8171da595e0..0000000000000000000000000000000000000000 --- a/pyAmakCore/tests/test_agent/test_run.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -test that run of Agent work as intended -""" -from unittest import TestCase, main - -from pyAmakCore.classes.agent import Agent -from pyAmakCore.classes.amas import Amas -from pyAmakCore.classes.environment import Environment -from pyAmakCore.enumeration.agent_phase import Phase -from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy - - -class TestAgentRun(TestCase): - """ - Test class for Agent run - """ - - def test_run_one_phase(self): - """ - test that run() work and return correct phase with 1 phase policy - """ - Agent.reset_agent() - environment = Environment() - amas = Amas(environment) - amas.set_execution_policy(ExecutionPolicy.ONE_PHASE) - - agent = Agent(amas) - self.assertEqual(agent.get_phase(), Phase.INITIALIZING) - agent.run() - self.assertEqual(agent.get_phase(), Phase.DECISION_AND_ACTION_DONE) - agent.run() - self.assertEqual(agent.get_phase(), Phase.DECISION_AND_ACTION_DONE) - - def test_run_two_phase(self): - """ - test that run() work and return correct phase with 2 phase policy - """ - Agent.reset_agent() - environment = Environment() - amas = Amas(environment) - amas.set_execution_policy(ExecutionPolicy.TWO_PHASES) - - agent = Agent(amas) - self.assertEqual(agent.get_phase(), Phase.INITIALIZING) - agent.run() - self.assertEqual(agent.get_phase(), Phase.PERCEPTION_DONE) - agent.run() - self.assertEqual(agent.get_phase(), Phase.DECISION_AND_ACTION_DONE) - - -if __name__ == '__main__': - main() diff --git a/pyAmakCore/tests/test_amas/test_agent.py b/pyAmakCore/tests/test_amas/test_agent.py index e43150f67f56a23074c9a934c3f706401a997a83..b5a9b187db038f3165b0ee0a2baaac9a722c03a5 100644 --- a/pyAmakCore/tests/test_amas/test_agent.py +++ b/pyAmakCore/tests/test_amas/test_agent.py @@ -45,24 +45,24 @@ class TestAmasAgents(TestCase): agent3 = Agent(amas) # add 1 agent amas.add_agent(agent1) - amas.cycle() + amas.add_pending_agent() self.assertEqual(amas.get_agents(), [agent1]) # don't remove previous agent amas.add_agent(agent2) - amas.cycle() + amas.add_pending_agent() self.assertEqual(amas.get_agents(), [agent1, agent2]) # add agent in good order amas.add_agent(agent3) - amas.cycle() + amas.add_pending_agent() 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() + amas.add_pending_agent() self.assertEqual(amas.get_agents(), [agent1, agent2, agent3]) def test_add_agents(self) -> None: @@ -80,16 +80,16 @@ class TestAmasAgents(TestCase): agent5 = Agent(amas) amas.add_agents([agent1, agent2, agent3, agent4, agent5]) - amas.cycle() + amas.add_pending_agent() self.assertEqual(amas.get_agents(), [agent1, agent2, agent3, agent4, agent5]) amas = SimplerAmas(environment) amas.add_agents([agent1, agent2]) - amas.cycle() + amas.add_pending_agent() self.assertEqual(amas.get_agents(), [agent1, agent2]) amas.add_agents([agent1, agent2, agent4, agent2, agent3]) - amas.cycle() + amas.add_pending_agent() self.assertEqual(amas.get_agents(), [agent1, agent2, agent4, agent3]) def test_remove_agent(self) -> None: @@ -101,22 +101,23 @@ class TestAmasAgents(TestCase): agent3 = Agent(amas) amas.remove_agent(agent2) - amas.cycle() + amas.remove_pending_agent() self.assertEqual(amas.get_agents(), []) amas.add_agents([agent1, agent2, agent3]) + amas.add_pending_agent() amas.remove_agent(agent2) - amas.cycle() + amas.remove_pending_agent() self.assertEqual(amas.get_agents(), [agent1, agent3]) amas.remove_agent(agent2) - amas.cycle() + amas.remove_pending_agent() self.assertEqual(amas.get_agents(), [agent1, agent3]) amas.remove_agent(agent1) amas.remove_agent(agent3) - amas.cycle() + amas.remove_pending_agent() self.assertEqual(amas.get_agents(), []) diff --git a/release/changelog.txt b/release/changelog.txt index 06af3f7857489efc206e726f505ec486d4a3e29d..89ba2421f6a4473f248b68aaece5b509675c4337 100644 --- a/release/changelog.txt +++ b/release/changelog.txt @@ -1,3 +1,7 @@ +v0.1.0: + WARNING : all previous example will no longer work in this version, and all v0.1.0+ example won't work in previous version + * Way better thread management + 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 diff --git a/setup.py b/setup.py index c80c5a1fd15a52b50f6131c38cdf309c329b3c96..3d73d5dccd4434e871c2cf07eb5550230a49ab19 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.5', + version='0.1.0', description='AmakFramework in python', author='BE', install_requires=[],