Skip to content

Commit

Permalink
refactor(Backend): switch to Backend enums to avoid errors with wrong…
Browse files Browse the repository at this point in the history
… initialization
  • Loading branch information
BjoernLudwigPTB committed Feb 3, 2022
1 parent a1ec4ae commit 08f769c
Show file tree
Hide file tree
Showing 16 changed files with 279 additions and 320 deletions.
49 changes: 30 additions & 19 deletions agentMET4FOF/agents/base_agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,7 @@ def __init__(
backend=Backend.OSBRAIN,
mesa_model=None,
):
if backend not in Backend:
if backend.lower() == "osbrain":
self.backend = Backend.OSBRAIN
elif backend.lower() == "mesa":
self.backend = Backend.MESA
else:
raise NotImplementedError(
"Backend has not been implemented. Valid choices are"
f"{tuple(Backend)}."
)
warnings.warn(
f"The backend was specified using the string '{backend}'"
f"but should be specified as one of {tuple(Backend)}. These "
f"constants can and should be imported using "
f"'from agentMET4FOF.utils import Backend'. The "
f"string-based initialization might be removed any time. Please switch "
f"instantly.",
DeprecationWarning,
)
self.backend = self.validate_backend(backend)

if self.backend == Backend.OSBRAIN:
self._remove_methods(MesaAgent)
Expand All @@ -85,6 +67,35 @@ def __init__(
self.name = name
self.mesa_model = mesa_model

@staticmethod
def validate_backend(backend: Union[str, Backend]) -> Backend:
if isinstance(backend, str):
if backend.lower() == "osbrain":
actual_backend = Backend.OSBRAIN
elif backend.lower() == "mesa":
actual_backend = Backend.MESA
else:
raise AgentMET4FOF.raise_not_implemented_backend()
warnings.warn(
f"The backend was specified using the string '{backend}'"
f"but should be specified as one of {tuple(Backend)}. These "
f"constants can and should be imported using "
f"'from agentMET4FOF.utils import Backend'. The "
f"string-based initialization might be removed any time. Please switch "
f"instantly to {actual_backend}.",
DeprecationWarning,
)
return actual_backend
if backend not in Backend:
raise AgentMET4FOF.raise_not_implemented_backend()
return backend

@staticmethod
def raise_not_implemented_backend():
return NotImplementedError(
f"Backend has not been implemented. Valid choices are {tuple(Backend)}."
)

def init_mesa(self, name):
# MESA Specific parameters
self.mesa_message_queue = deque([])
Expand Down
63 changes: 32 additions & 31 deletions agentMET4FOF/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

__all__ = ["AgentNetwork"]

from .utils import Backend


class AgentNetwork:
"""Object for starting a new Agent Network or connect to an existing Agent Network
Expand All @@ -32,7 +34,7 @@ class _AgentController(AgentMET4FOF):
"""

def init_parameters(
self, ns=None, backend="osbrain", mesa_model=None, log_mode=True
self, ns=None, backend=Backend.OSBRAIN, mesa_model=None, log_mode=True
):
self.backend = backend
self.states = {0: "Idle", 1: "Running", 2: "Pause", 3: "Stop"}
Expand All @@ -43,7 +45,7 @@ def init_parameters(
self.coalitions = []
self.log_mode = log_mode

if backend == "mesa":
if backend == Backend.MESA:
self.mesa_model = mesa_model

def start_mesa_timer(self, mesa_update_interval):
Expand Down Expand Up @@ -85,7 +87,8 @@ def _transform_string_into_valid_name(self, name: str) -> str:
"""Ensure that name does not contain invalid characters
osBrain does not allow spaces in agents' names, so we replace them by
underscores.
underscores. Mesa does not allow a single space as name, so we replace
that as well by an underscore.
Parameters
----------
Expand All @@ -96,10 +99,10 @@ def _transform_string_into_valid_name(self, name: str) -> str:
Returns
-------
str
the cleaned version of the name, i.e. for ``backend == 'osbrain'``
without spaces
the cleaned version of the name, i.e. for ``backend == Backend.OSBRAIN``
without spaces and for ``backend == Backend.MESA`` not a single space
"""
if self.backend == "osbrain":
if self.backend == Backend.OSBRAIN or name == " ":
return name.replace(" ", "_")
return name

Expand All @@ -118,15 +121,15 @@ def get_agent(self, agent_name: str) -> Optional[Union[AgentMET4FOF, Proxy]]:
the provided name can be found
"""
name_to_search_for = self._transform_string_into_valid_name(agent_name)
if self.backend == "osbrain":
if self.backend == Backend.OSBRAIN:
try:
return self.ns.proxy(name_to_search_for)
except NamingError as e:
self.log_info(
f"{self.get_agent.__name__}(agent_name='{name_to_search_for}') "
f"failed: {e}"
)
elif self.backend == "mesa":
else: # self.backend == Backend.MESA:
return self.mesa_model.get_agent(name_to_search_for)

def get_agentType_count(self, agent_type: Type[AgentMET4FOF]) -> int:
Expand Down Expand Up @@ -187,7 +190,7 @@ def add_agent(
new_name = self.generate_module_name_byUnique(name)

# actual instantiation of agent, depending on backend
if self.backend == "osbrain":
if self.backend == Backend.OSBRAIN:
new_agent = self._add_osbrain_agent(
name=self._transform_string_into_valid_name(new_name),
agentType=agentType,
Expand All @@ -197,7 +200,7 @@ def add_agent(
loop_wait=loop_wait,
**kwargs,
)
elif self.backend == "mesa":
elif self.backend == Backend.MESA:
# handle osbrain and mesa here
new_agent = self._add_mesa_agent(
name=self._transform_string_into_valid_name(new_name),
Expand Down Expand Up @@ -294,7 +297,7 @@ def concatenate_exclude_names_with_anyway_invisibles():
if name not in concatenate_exclude_names_with_anyway_invisibles()
]

if self.backend == "osbrain":
if self.backend == Backend.OSBRAIN:
return return_osbrain_agents()

if exclude_names is None:
Expand Down Expand Up @@ -522,23 +525,23 @@ def __init__(
dashboard_update_interval=3,
dashboard_max_monitors=10,
dashboard_port=8050,
backend="osbrain",
backend=Backend.OSBRAIN,
mesa_update_interval=0.1,
network_stylesheet=default_agent_network_stylesheet,
**dashboard_kwargs,
):
"""
Parameters
----------
ip_addr: str
ip_addr : str
Ip address of server to connect/start
port: int
port : int
Port of server to connect/start
connect: bool
connect : bool
False sets Agent network to connect mode and will connect to specified
address, True (Default) sets Agent network to initially try to connect
and if it cant find one, it will start a new server at specified address
log_filename: str
log_filename : str
Name of log file, acceptable csv format. It will be saved locally,
in the same folder as the python script in which this AgentNetwork is
instantiated on.
Expand All @@ -555,13 +558,16 @@ def __init__(
Due to complexity in managing and instantiating dynamic figures,
a maximum number of monitors is specified first and only the each
Monitor Agent will occupy one of these figures.
dashboard_port: int
dashboard_port : int
Port of the dashboard to be hosted on. By default is port 8050.
backend : Backend
the backend to use for either simulating, debugging or local
high-performance execution with Mesa or osBrain. See tutorial 6 for details.
**dashboard_kwargs
Additional key words to be passed in initialising the dashboard
"""

self.backend = backend
self.backend = AgentMET4FOF.validate_backend(backend)
self.ip_addr = ip_addr
self.port = port
self._controller = None
Expand All @@ -580,20 +586,15 @@ def __init__(
self.save_logfile = False

# handle different choices of backends
if self.backend == "osbrain":
if self.backend == Backend.OSBRAIN:
if connect:
self.connect(ip_addr, port)
else:
self.connect(ip_addr, port)
if self.ns == 0:
self.start_server_osbrain(ip_addr, port)
elif self.backend == "mesa":
else: # self.backend == Backend.MESA
self.start_server_mesa()
else:
raise NotImplementedError(
"Backend has not been implemented. Valid choices are 'osbrain' and "
"'mesa'."
)

if isinstance(dashboard_extensions, list) == False:
dashboard_extensions = [dashboard_extensions]
Expand All @@ -618,11 +619,11 @@ def __init__(
dashboard_params.update(dashboard_kwargs)

# Initialize dashboard process/thread.
if self.backend == "osbrain":
if self.backend == Backend.OSBRAIN:
from .dashboard.Dashboard import AgentDashboardThread

self.dashboard_proc = AgentDashboardThread(**dashboard_params)
elif self.backend == "mesa":
else: # self.backend == Backend.MESA
from .dashboard.Dashboard import AgentDashboardThread

self.dashboard_proc = AgentDashboardThread(**dashboard_params)
Expand Down Expand Up @@ -924,7 +925,7 @@ def agents_by_type(
self._get_controller().get_agent(agent_name)
for agent_name in all_agent_names
]
if self.backend == "mesa":
if self.backend == Backend.MESA:
return {agent for agent in all_agents if isinstance(agent, only_type)}

return {
Expand Down Expand Up @@ -1033,9 +1034,9 @@ def shutdown(self):
# Shutdown the nameserver.
# This leaves some process clutter in the process list, but the actual
# processes are ended.
if self.backend == "osbrain":
if self.backend == Backend.OSBRAIN:
self._get_controller().get_attr("ns").shutdown()
elif self.backend == "mesa":
else: # self.backend == Backend.MESA
self._get_controller().stop_mesa_timer()
self.mesa_model.shutdown()

Expand All @@ -1047,7 +1048,7 @@ def shutdown(self):
# ensuring the proper termination of the dash.Dash app.
self.dashboard_proc.terminate()
# Then wait for the termination of the actual thread or at least finish the
# execution of the join method in case of the "Mesa" backend. See #163
# execution of the join method in case of the Mesa backend. See #163
# for the search for a proper solution to this issue.
self.dashboard_proc.join(timeout=10)
return 0
Expand Down
7 changes: 5 additions & 2 deletions agentMET4FOF_tutorials/agent_module/ui_main_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
# Note also, the exposed and tunable parameters in the `ParameterisedSineGeneratorAgent` are defined in the class

from agentMET4FOF.agents import AgentNetwork
from agentMET4FOF_tutorials.tutorial_1_generator_agent import SineGeneratorAgent
from agentMET4FOF.utils import Backend
from agentMET4FOF_tutorials.agent_module import ui_module_example
from agentMET4FOF_tutorials.tutorial_1_generator_agent import SineGeneratorAgent


def demonstrate_generator_agent_use():
# Start agent network server.
agent_network = AgentNetwork(dashboard_modules=[ui_module_example], backend="mesa")
agent_network = AgentNetwork(
dashboard_modules=[ui_module_example], backend=Backend.MESA
)
sine_agent = agent_network.add_agent(agentType=SineGeneratorAgent)

coalition = agent_network.add_coalition(agents=[])
Expand Down
7 changes: 5 additions & 2 deletions agentMET4FOF_tutorials/buffering/metrological_buffering.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
MetrologicalDataStreamMET4FOF,
MetrologicalSineGenerator,
)
from agentMET4FOF.utils import Backend


class MetrologicalSineGeneratorAgent(MetrologicalAgent):
Expand Down Expand Up @@ -81,7 +82,7 @@ def agent_loop(self):
def demonstrate_metrological_stream():

# start agent network server
agent_network = AgentNetwork(dashboard_modules=True, backend="mesa")
agent_network = AgentNetwork(dashboard_modules=True, backend=Backend.MESA)

# Initialize signal generating class outside of agent framework.
signal = MetrologicalSineGenerator()
Expand All @@ -95,7 +96,9 @@ def demonstrate_metrological_stream():

# Initialize metrologically enabled plotting agent.
monitor_agent = agent_network.add_agent(
"MonitorAgent", agentType=MetrologicalMonitorAgent, buffer_size=50,
"MonitorAgent",
agentType=MetrologicalMonitorAgent,
buffer_size=50,
)

# Bind agents.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from time import sleep

from agentMET4FOF.agents.base_agents import (
MonitorAgent,
)
from agentMET4FOF.agents.signal_agents import NoiseAgent, SineGeneratorAgent
from agentMET4FOF.network import AgentNetwork
from agentMET4FOF.utils import Backend


def demonstrate_noise_agent_use():
agent_network = AgentNetwork(backend="mesa")
agent_network = AgentNetwork(backend=Backend.MESA)

sine_agent = agent_network.add_agent(
name="Clean sine signal", agentType=SineGeneratorAgent
Expand All @@ -28,4 +31,6 @@ def demonstrate_noise_agent_use():


if __name__ == "__main__":
demonstrate_noise_agent_use()
noisy_network = demonstrate_noise_agent_use()
sleep(60)
noisy_network.shutdown()
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
StaticSineWithJitterGeneratorAgent,
)
from agentMET4FOF.network import AgentNetwork
from agentMET4FOF.utils import Backend


def demonstrate_sine_with_jitter_agent_use():
agent_network = AgentNetwork(backend="mesa")
agent_network = AgentNetwork(backend=Backend.MESA)

sine_agent = agent_network.add_agent(
name="Clean sine signal", agentType=SineGeneratorAgent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
SineWithJitterGeneratorAgent,
)
from agentMET4FOF.network import AgentNetwork
from agentMET4FOF.utils import Backend


def demonstrate_noise_jitter_removal_agent():
# start agent network server
agentNetwork = AgentNetwork(backend="mesa")
agentNetwork = AgentNetwork(backend=Backend.MESA)
# init agents

sine_with_jitter_agent = agentNetwork.add_agent(
Expand Down
Loading

0 comments on commit 08f769c

Please sign in to comment.