Skip to content

Commit

Permalink
cleaning everything for v3
Browse files Browse the repository at this point in the history
  • Loading branch information
romancardenas committed May 27, 2024
1 parent 1d5ec79 commit 5488450
Show file tree
Hide file tree
Showing 85 changed files with 1,607 additions and 2,421 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,4 @@ dmypy.json

# Log File
*.log
*.csv
12 changes: 12 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Module",
"type": "python",
"request": "launch",
"module": "tests.test_devstone",
// "env": {"PYTHONPATH": "${workspaceFolder}/libs/"}
}
]
}
10 changes: 10 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"python.autoComplete.extraPaths": ["${workspaceFolder}/xdevs"],
"python.testing.cwd": "${workspaceFolder/xdevs}",
"python.analysis.include": [
"${workspaceFolder}/xdevs"
],
"python.analysis.extraPaths": [
"${workspaceFolder}"
],
}
File renamed without changes.
67 changes: 67 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
[build-system]
requires = ["setuptools >= 61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "xdevs"
version = "3.0.0"
requires-python = ">=3.8"
authors = [
{name = "Román Cárdenas"},
{name = "Óscar Fernández Sebastián"},
{name = "Kevin Henares"},
{name = "José L. Risco-Martín"},
]
maintainers = [
{name = "Román Cárdenas", email = "[email protected]"},
]
description = "xDEVS M&S framework"
readme = "README.md"
license = {file = "LICENSE.txt"}
keywords = ["DEVS", "modeling", "simulation"]

[project.optional-dependencies]
sql = ["sqlalchemy"]
elasticsearch = ["elasticsearch"]
mqtt = ["paho-mqtt"]

[project.urls]
Homepage = "https://github.com/iscar-ucm/xdevs"
Documentation = "https://github.com/iscar-ucm/xdevs"
Repository = "https://github.com/iscar-ucm/xdevs.py.git"
"Bug Tracker" = "https://github.com/iscar-ucm/xdevs.py/issues"
Changelog = "https://github.com/iscar-ucm/xdevs.py/blob/main/CHANGELOG.md"

[project.entry-points."xdevs.transducers"]
csv = "xdevs.plugins.transducers.csv:CSVTransducer"
sql = "xdevs.plugins.transducers.sql:SQLTransducer"
elasticsearch = "xdevs.plugins.transducers.elasticsearch:ElasticsearchTransducer"

[project.entry-points."xdevs.input_handlers"]
function = "xdevs.plugins.input_handlers.function:CallableFunction"
csv = "xdevs.plugins.input_handlers.csv:CSVInputHandler"
tcp = "xdevs.plugins.input_handlers.tcp:TCPInputHandler"
mqtt = "xdevs.plugins.input_handlers.mqtt:MQTTInputHandler"

[project.entry-points."xdevs.output_handlers"]
csv = "xdevs.plugins.output_handlers.csv:CSVOutputHandler"
tcp = "xdevs.plugins.output_handlers.tcp:TCPOutputHandler"
mqtt = "xdevs.plugins.output_handlers.mqtt:MQTTOutputHandler"

[project.entry-points."xdevs.components"]
generator = "xdevs.examples.gpt.gpt:Generator"
transducer = "xdevs.examples.gpt.gpt:Transducer"
processor = "xdevs.examples.gpt.gpt:Processor"
gpt = "xdevs.examples.gpt.gpt:Gpt"
ef = "xdevs.examples.gpt.efp:Ef"
efp = "xdevs.examples.gpt.efp:Efp"

[project.entry-points."xdevs.wrappers"]
pypdevs = "xdevs.plugins.wrappers.pypdevs:PyPDEVSWrapper"

[tool.setuptools]
include-package-data = false

[tool.setuptools.packages.find]
include = ["xdevs*"]
exclude = ["xdevs.tests*"]
47 changes: 0 additions & 47 deletions setup.py

This file was deleted.

33 changes: 23 additions & 10 deletions xdevs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import math
import functools
import logging
import math
import sys
from typing import TypeVar
import warnings

T = TypeVar('T')

INFINITY = math.inf
PHASE_PASSIVE = "passive"
PHASE_ACTIVE = "active"
INFINITY: float = math.inf
PHASE_PASSIVE: str = "passive"
PHASE_ACTIVE: str = "active"

DEBUG_LEVEL = None
loggers = dict()
DEBUG_LEVEL: int | str | None = None
LOGGERS: dict[str, logging.Logger] = dict()


def get_logger(name, dl=None):
if name in loggers:
return loggers[name]
def get_logger(name: str, dl: int | str = None):
if name in LOGGERS:
return LOGGERS[name]
else:
logger = logging.getLogger(name)

Expand All @@ -24,5 +29,13 @@ def get_logger(name, dl=None):
else:
logger.disabled = True

loggers[name] = logger
LOGGERS[name] = logger
return logger


def deprecated(func):
@functools.wraps(func)
def new_func(*args, **kwargs):
warnings.warn(f"Call to deprecated function {func.__name__}.", category=DeprecationWarning, stacklevel=2)
return func(*args, **kwargs)
return new_func
2 changes: 2 additions & 0 deletions xdevs/abc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .handler import InputHandler, OutputHandler
from .transducer import Transducer
138 changes: 138 additions & 0 deletions xdevs/abc/handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import queue
import sys
from abc import ABC, abstractmethod
from typing import Callable, Any


class Connector:
def __init__(self, conections: dict[str, str]):
"""
Función para conectar de forma correcta los puertos (que usen protocolo MQTT)
:param conections: dict[key: str, value: str]. Donde la key es el puerto de al que me quiero conectar y el
value es el puerto de mi acoplado.
"""
self.connections: dict[str, str] = conections

def input_handler(self, port: str):
if self.connections is not None:
value = self.connections.get(port)
if value is not None:
return value
return port


class InputHandler(ABC):
def __init__(self, *args, **kwargs):
"""
Handler interface for injecting external events to the system.
:param queue: used to collect and inject all external events joining the system.
:param Callable[[Any], tuple[str, str]] event_parser: event parser function. It transforms incoming events
into tuples (port, message). Note that both are represented as strings. Messages need further parsing.
:param dict[str, Callable[[str], Any]] msg_parsers: message parsers. Keys are port names, and values are
functions that take a string and returns an object of the corresponding port type. If a parser is not
defined, the input handler assumes that the port type is str and forward the message as is. By default, all
the ports are assumed to accept str objects.
"""
self.queue = kwargs.get('queue')
if self.queue is None:
raise ValueError('queue is mandatory')
self.event_parser: Callable[[Any], tuple[str, str]] | None = kwargs.get('event_parser')
self.msg_parsers: dict[str, Callable[[str], Any]] = kwargs.get('msg_parsers', dict())

self.connections: dict[str, str] = kwargs.get('connections', dict())
self.connector = Connector(conections=self.connections)

def initialize(self):
"""Performs any task before calling the run method. It is implementation-specific. By default, it is empty."""
pass

def exit(self):
"""Performs any task after the run method. It is implementation-specific. By default, it is empty."""
pass

@abstractmethod
def run(self):
"""Execution of the input handler. It is implementation-specific"""
pass

def push_event(self, event: Any):
"""Parses event as tuple port-message and pushes it to the queue."""
try:
port, msg = self.event_parser(event)
# AQUI IRIA EL CONECTOR MQTT; para corregir el puerto en cuestion
port = self.connector.input_handler(port)
except Exception as e:
# if an exception is triggered while parsing the event, we ignore it
print(f'error parsing input event ("{event}"): {e}. Event will be ignored', file=sys.stderr)
return
self.push_msg(port, msg)

def push_msg(self, port: str, msg: str):
"""Parses the message as the proper object and pushes it to the queue."""
try:
# if parser is not defined, we forward the message as is (i.e., in string format)
msg = self.msg_parsers.get(port, lambda x: x)(msg)
except Exception as e:
# if an exception is triggered while parsing the message, we ignore it
print(f'error parsing input msg ("{msg}") in port {port}: {e}. Message will be ignored', file=sys.stderr)
return
self.queue.put((port, msg))


class OutputHandler(ABC):
def __init__(self, *args, **kwargs):
"""
Handler interface for ejecting internal events from the system.
:param queue.SimpleQueue() queue: is the queue where all the desired events to be ejected are put.
:param Callable[[str, str], Any] event_parser: event parser function. It transforms incoming tuples
(port, message) into events. Note that both are represented as strings.
:param dict[str, Callable[[Any], str]] msg_parser: message parsers. Keys are port names, and values are
functions that take a string and returns an object of the corresponding port type. If a parser is not
defined, the output handler assumes that the port type is str and forward the message as is. By default, all
the ports are assumed to accept str objects.
TODO documentation
"""
self.queue = queue.SimpleQueue()
self.event_parser: Callable[[str, str], Any] | None = kwargs.get('event_parser')
self.msg_parsers: dict[str, Callable[[Any], str]] = kwargs.get('msg_parsers', dict())

def initialize(self):
"""Performs any task before calling the run method. It is implementation-specific. By default, it is empty."""
pass

def exit(self):
"""Performs any task before calling the run method. It is implementation-specific. By default, it is empty."""
pass

@abstractmethod
def run(self):
"""Execution of the output handler. It is implementation-specific"""
pass

def pop_event(self) -> Any:
"""Waits until it receives an outgoing event and parses it with the desired format."""
while True:
port, msg = self.pop_msg()
# print(f'POP_EVENT: recibo port = {port} y msg = {msg}')
try:
event = self.event_parser(port, msg)
except Exception as e:
print(f'error parsing output event ("{port}","{msg}"): {e}. Event will be ignored', file=sys.stderr)
continue
return event

def pop_msg(self) -> tuple[str, str]:
"""Waits until it receives an outgoing message and returns the port and message in string format."""
while True:
port, msg = self.queue.get()
# print(f'POP_MSG: recibo port = {port} y msg = {msg}')
try:
msg = self.msg_parsers.get(port, lambda x: str(x))(msg)
except Exception as e:
print(f'error parsing output msg ("{msg}"): {e}. Message will be ignored', file=sys.stderr)
continue
return port, msg
Loading

0 comments on commit 5488450

Please sign in to comment.