From 56ca6c381256988e980a60e0d1ea99db8f9ee6fe Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Mon, 19 Dec 2022 13:17:21 +0100 Subject: [PATCH 01/60] Real time simulation simulate_rt was created --- xdevs/examples/basic/basic.py | 316 +++++++++++++++++----------------- xdevs/sim.py | 26 ++- 2 files changed, 182 insertions(+), 160 deletions(-) diff --git a/xdevs/examples/basic/basic.py b/xdevs/examples/basic/basic.py index 62fa003..85febd9 100644 --- a/xdevs/examples/basic/basic.py +++ b/xdevs/examples/basic/basic.py @@ -10,180 +10,182 @@ class Job: - def __init__(self, name): - self.name = name - self.time = 0 - - + def __init__(self, name): + self.name = name + self.time = 0 + + class Generator(Atomic): - def __init__(self, name, period): - super().__init__(name) - self.i_start = Port(Job, "i_start") - self.i_stop = Port(Job, "i_stop") - self.o_out = Port(Job, "o_out") - - self.add_in_port(self.i_start) - self.add_in_port(self.i_stop) - self.add_out_port(self.o_out) - - self.period = period - self.job_counter = 1 - - def initialize(self): - self.hold_in(PHASE_ACTIVE, self.period) - - def exit(self): - pass - - def deltint(self): - self.job_counter += 1 - self.hold_in(PHASE_ACTIVE, self.period) - - def deltext(self, e): - self.passivate() - - def lambdaf(self): - self.o_out.add(Job(str(self.job_counter))) - - + def __init__(self, name, period): + super().__init__(name) + self.i_start = Port(Job, "i_start") + self.i_stop = Port(Job, "i_stop") + self.o_out = Port(Job, "o_out") + + self.add_in_port(self.i_start) + self.add_in_port(self.i_stop) + self.add_out_port(self.o_out) + + self.period = period + self.job_counter = 1 + + def initialize(self): + self.hold_in(PHASE_ACTIVE, self.period) + + def exit(self): + pass + + def deltint(self): + self.job_counter += 1 + self.hold_in(PHASE_ACTIVE, self.period) + + def deltext(self, e): + self.passivate() + + def lambdaf(self): + self.o_out.add(Job(str(self.job_counter))) + + class Processor(Atomic): - def __init__(self, name, proc_time): - super().__init__(name) - - self.i_in = Port(Job, "i_in") - self.o_out = Port(Job, "o_out") - - self.add_in_port(self.i_in) - self.add_out_port(self.o_out) - - self.current_job = None - self.proc_time = proc_time - - def initialize(self): - self.passivate() - - def exit(self): - pass - - def deltint(self): - self.passivate() - - def deltext(self, e): - if self.phase == PHASE_PASSIVE: - self.current_job = self.i_in.get() - self.hold_in(PHASE_ACTIVE, self.proc_time) - self.continuef(e) - - def lambdaf(self): - self.o_out.add(self.current_job) - - + def __init__(self, name, proc_time): + super().__init__(name) + + self.i_in = Port(Job, "i_in") + self.o_out = Port(Job, "o_out") + + self.add_in_port(self.i_in) + self.add_out_port(self.o_out) + + self.current_job = None + self.proc_time = proc_time + + def initialize(self): + self.passivate() + + def exit(self): + pass + + def deltint(self): + self.passivate() + + def deltext(self, e): + if self.phase == PHASE_PASSIVE: + self.current_job = self.i_in.get() + self.hold_in(PHASE_ACTIVE, self.proc_time) + self.continuef(e) + + def lambdaf(self): + self.o_out.add(self.current_job) + + class Transducer(Atomic): - def __init__(self, name, obs_time): - super().__init__(name) - - self.i_arrived = Port(Job, "i_arrived") - self.i_solved = Port(Job, "i_solved") - self.o_out = Port(Job, "o_out") - - self.add_in_port(self.i_arrived) - self.add_in_port(self.i_solved) - self.add_out_port(self.o_out) - - self.jobs_arrived = [] - self.jobs_solved = [] - - self.total_ta = 0 - self.clock = 0 - self.obs_time = obs_time - - def initialize(self): - self.hold_in(PHASE_ACTIVE, self.obs_time) - - def exit(self): - pass - - def deltint(self): - self.clock += self.sigma - - if self.phase == PHASE_ACTIVE: - if self.jobs_solved: - avg_ta = self.total_ta / len(self.jobs_solved) - throughput = len(self.jobs_solved) / self.clock if self.clock > 0 else 0 - else: - avg_ta = 0 - throughput = 0 - - logger.info("End time: %f" % self.clock) - logger.info("Jobs arrived: %d" % len(self.jobs_arrived)) - logger.info("Jobs solved: %d" % len(self.jobs_solved)) - logger.info("Average TA: %f" % avg_ta) - logger.info("Throughput: %f\n" % throughput) - - self.hold_in(PHASE_DONE, 0) - else: - self.passivate() - - def deltext(self, e): - self.clock += e - - if self.phase == PHASE_ACTIVE: - if self.i_arrived: - job = self.i_arrived.get() - logger.info("Starting job %s @ t = %d" % (job.name, self.clock)) - job.time = self.clock - self.jobs_arrived.append(job) - - if self.i_solved: - job = self.i_solved.get() - logger.info("Job %s finished @ t = %d" % (job.name, self.clock)) - self.total_ta += self.clock - job.time - self.jobs_solved.append(job) - - self.continuef(e) - - def lambdaf(self): - if self.phase == PHASE_DONE: - self.o_out.add(Job("null")) - + def __init__(self, name, obs_time): + super().__init__(name) + + self.i_arrived = Port(Job, "i_arrived") + self.i_solved = Port(Job, "i_solved") + self.o_out = Port(Job, "o_out") + + self.add_in_port(self.i_arrived) + self.add_in_port(self.i_solved) + self.add_out_port(self.o_out) + + self.jobs_arrived = [] + self.jobs_solved = [] + + self.total_ta = 0 + self.clock = 0 + self.obs_time = obs_time + + def initialize(self): + self.hold_in(PHASE_ACTIVE, self.obs_time) + + def exit(self): + pass + + def deltint(self): + self.clock += self.sigma + + if self.phase == PHASE_ACTIVE: + if self.jobs_solved: + avg_ta = self.total_ta / len(self.jobs_solved) + throughput = len(self.jobs_solved) / self.clock if self.clock > 0 else 0 + else: + avg_ta = 0 + throughput = 0 + + logger.info("End time: %f" % self.clock) + logger.info("Jobs arrived: %d" % len(self.jobs_arrived)) + logger.info("Jobs solved: %d" % len(self.jobs_solved)) + logger.info("Average TA: %f" % avg_ta) + logger.info("Throughput: %f\n" % throughput) + + self.hold_in(PHASE_DONE, 0) + else: + self.passivate() + + def deltext(self, e): + self.clock += e + + if self.phase == PHASE_ACTIVE: + if self.i_arrived: + job = self.i_arrived.get() + logger.info("Starting job %s @ t = %d" % (job.name, self.clock)) + job.time = self.clock + self.jobs_arrived.append(job) + + if self.i_solved: + job = self.i_solved.get() + logger.info("Job %s finished @ t = %d" % (job.name, self.clock)) + self.total_ta += self.clock - job.time + self.jobs_solved.append(job) + + self.continuef(e) + + def lambdaf(self): + if self.phase == PHASE_DONE: + self.o_out.add(Job("null")) + class Gpt(Coupled): - def __init__(self, name, period, obs_time): - super().__init__(name) + def __init__(self, name, period, obs_time): + super().__init__(name) - if period < 1: - raise ValueError("period has to be greater than 0") + if period < 1: + raise ValueError("period has to be greater than 0") - if obs_time < 0: - raise ValueError("obs_time has to be greater or equal than 0") + if obs_time < 0: + raise ValueError("obs_time has to be greater or equal than 0") - gen = Generator("generator", period) - proc = Processor("processor", 3*period) - trans = Transducer("transducer", obs_time) + gen = Generator("generator", period) + proc = Processor("processor", 3 * period) + trans = Transducer("transducer", obs_time) - self.add_component(gen) - self.add_component(proc) - self.add_component(trans) + self.add_component(gen) + self.add_component(proc) + self.add_component(trans) - self.add_coupling(gen.o_out, proc.i_in) - self.add_coupling(gen.o_out, trans.i_arrived) - self.add_coupling(proc.o_out, trans.i_solved) - self.add_coupling(trans.o_out, gen.i_stop) + self.add_coupling(gen.o_out, proc.i_in) + self.add_coupling(gen.o_out, trans.i_arrived) + self.add_coupling(proc.o_out, trans.i_solved) + self.add_coupling(trans.o_out, gen.i_stop) class Wrap(Coupled): - def __init__(self, name, period, obs_time): - super().__init__("wrap") + def __init__(self, name, period, obs_time): + super().__init__("wrap") - gpt = Gpt(name, period, obs_time) + gpt = Gpt(name, period, obs_time) - self.add_component(gpt) + self.add_component(gpt) if __name__ == '__main__': - gpt = Gpt("gpt", 1, 100) - coord = Coordinator(gpt) - coord.initialize() - coord.simulate() + gpt = Gpt("gpt", 2, 100) + coord = Coordinator(gpt) + coord.initialize() + # coord.simulate() + coord.simulate_rt() + # coord.simulate_rt(50) diff --git a/xdevs/sim.py b/xdevs/sim.py index af47bef..0aeed20 100644 --- a/xdevs/sim.py +++ b/xdevs/sim.py @@ -3,6 +3,7 @@ import _thread import itertools import pickle +import time from abc import ABC, abstractmethod from collections import defaultdict @@ -75,7 +76,6 @@ def clear(self): class Simulator(AbstractSimulator): - model: Atomic def __init__(self, model: Atomic, clock: SimulationClock, @@ -130,7 +130,6 @@ def clear(self): class Coordinator(AbstractSimulator): - model: Coupled def __init__(self, model: Coupled, clock: SimulationClock = None, flatten: bool = False, @@ -330,6 +329,28 @@ def simulate_time(self, time_interv: float = 10000): self.clear() self.clock.time = self.time_next + def simulate_rt(self, time_interv: float = 10000): + """ + Simulates the behavior of a DEVS model in real time over a specified time interval. + + :param time_interv: The time interval to simulate, in seconds. Default is 10000. + :type time_interv: float + """ + self.clock.time = self.time_next + tf = self.clock.time + time_interv + + while self.clock.time < tf: + self.lambdaf() + self.deltfcn() + self._execute_transducers() + self.clear() + if self.time_next < float("inf"): + # sleep_time = self.time_next-self.clock.time + # print(">>> {}".format(sleep_time)) + # time.sleep(sleep_time) + time.sleep(self.time_next - self.clock.time) + self.clock.time = self.time_next + def simulate_inf(self): while True: self.lambdaf() @@ -390,7 +411,6 @@ def add_task_to_pool(self, task): class ParallelProcessCoordinator(Coordinator): - coordinators: list[ParallelProcessCoordinator] def __init__(self, model: Coupled, clock: SimulationClock = None, From 31be89b9896315eb2ccd129471767ecc618c0453 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Tue, 20 Dec 2022 13:48:17 +0100 Subject: [PATCH 02/60] Real time simulation V2 In sim.py the simulate_rt has been modified. In order to take into account, the internal time it takes to execute lambdas and deltas. --- xdevs/sim.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/xdevs/sim.py b/xdevs/sim.py index 0aeed20..9c40f83 100644 --- a/xdevs/sim.py +++ b/xdevs/sim.py @@ -340,15 +340,21 @@ def simulate_rt(self, time_interv: float = 10000): tf = self.clock.time + time_interv while self.clock.time < tf: + t_b = self.clock.time # time before executing lambdas and deltas self.lambdaf() self.deltfcn() self._execute_transducers() self.clear() - if self.time_next < float("inf"): + t_a = self.clock.time # time after executing lambdas and deltas + if self.time_next < float("inf") and (self.time_next - self.clock.time - t_a + t_b > 0): + # Infinite time is not allowed. + # Negative time is not allowed. If lambdas and deltas' time is large. + # sleep_time = self.time_next-self.clock.time # print(">>> {}".format(sleep_time)) # time.sleep(sleep_time) - time.sleep(self.time_next - self.clock.time) + time.sleep(self.time_next - self.clock.time - t_a + t_b) + # - (t_a-t_b) = - t_a + t_b .In order to subtract the time it took to execute lambdas and deltas self.clock.time = self.time_next def simulate_inf(self): From 080b1bae4563f6263285a85e3723b14f4672c8d2 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Tue, 20 Dec 2022 13:48:17 +0100 Subject: [PATCH 03/60] Real time simulation V2 In sim.py the simulate_rt function has been modified. Now, it takes into account the internal time it takes to execute lambdas and deltas. --- xdevs/sim.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/xdevs/sim.py b/xdevs/sim.py index 0aeed20..9c40f83 100644 --- a/xdevs/sim.py +++ b/xdevs/sim.py @@ -340,15 +340,21 @@ def simulate_rt(self, time_interv: float = 10000): tf = self.clock.time + time_interv while self.clock.time < tf: + t_b = self.clock.time # time before executing lambdas and deltas self.lambdaf() self.deltfcn() self._execute_transducers() self.clear() - if self.time_next < float("inf"): + t_a = self.clock.time # time after executing lambdas and deltas + if self.time_next < float("inf") and (self.time_next - self.clock.time - t_a + t_b > 0): + # Infinite time is not allowed. + # Negative time is not allowed. If lambdas and deltas' time is large. + # sleep_time = self.time_next-self.clock.time # print(">>> {}".format(sleep_time)) # time.sleep(sleep_time) - time.sleep(self.time_next - self.clock.time) + time.sleep(self.time_next - self.clock.time - t_a + t_b) + # - (t_a-t_b) = - t_a + t_b .In order to subtract the time it took to execute lambdas and deltas self.clock.time = self.time_next def simulate_inf(self): From b85d16aae813d980defcee69a87e56057e0e3f9d Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Sat, 31 Dec 2022 18:36:23 +0100 Subject: [PATCH 04/60] Real time simulation V3 In sim.py the simulate_rt function has been modified. Now, there is a maximun time the simulation can be delayed. --- .gitignore | 3 ++ xdevs/examples/basic/basic.py | 1 + xdevs/sim.py | 55 ++++++++++++++++++++++++++++------- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 8ff9d15..6d7bf86 100644 --- a/.gitignore +++ b/.gitignore @@ -133,3 +133,6 @@ dmypy.json #MacOS stuff .DS_Store + +# Log File +xdevs/examples/basic/SimulationLogs \ No newline at end of file diff --git a/xdevs/examples/basic/basic.py b/xdevs/examples/basic/basic.py index 85febd9..2a9d65f 100644 --- a/xdevs/examples/basic/basic.py +++ b/xdevs/examples/basic/basic.py @@ -1,4 +1,5 @@ import logging +import time from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger from xdevs.models import Atomic, Coupled, Port diff --git a/xdevs/sim.py b/xdevs/sim.py index 9c40f83..72abd70 100644 --- a/xdevs/sim.py +++ b/xdevs/sim.py @@ -4,6 +4,7 @@ import itertools import pickle import time +import logging from abc import ABC, abstractmethod from collections import defaultdict @@ -153,6 +154,21 @@ def __init__(self, model: Coupled, clock: SimulationClock = None, flatten: bool self.event_transducers_mapping = event_transducers_mapping self.state_transducers_mapping = state_transducers_mapping + # A log named logger is created. Logs will be sent to the file SimulationsLogs.log. The file can be find in + # xdevs/examples/basic/ + + self.logger = logging.getLogger("Simulation_Coordinator") + + fh = logging.FileHandler("SimulationLogs.log", mode='w') # for overwrite the file add mode='w' + + self.logger.setLevel(logging.DEBUG) + + formatter = logging.Formatter('%(asctime)s %(name)s - %(levelname)s - %(message)s') + + fh.setFormatter(formatter) + + self.logger.addHandler(fh) + @property def root_coordinator(self) -> bool: return self.model.parent is None @@ -329,32 +345,51 @@ def simulate_time(self, time_interv: float = 10000): self.clear() self.clock.time = self.time_next - def simulate_rt(self, time_interv: float = 10000): + def simulate_rt(self, time_interv: float = 10000, max_delay: float = 10): """ Simulates the behavior of a DEVS model in real time over a specified time interval. :param time_interv: The time interval to simulate, in seconds. Default is 10000. :type time_interv: float + + :param max_delay : Maximun time the system can be delayed. Default is 10 s + :type max_delay: float + """ + self.logger.debug('Starting simulation_rt') + self.clock.time = self.time_next tf = self.clock.time + time_interv + total_delayed_t = 0.0 + t_b = time.time() # initial time before executing lambdas and deltas while self.clock.time < tf: - t_b = self.clock.time # time before executing lambdas and deltas self.lambdaf() self.deltfcn() self._execute_transducers() self.clear() - t_a = self.clock.time # time after executing lambdas and deltas - if self.time_next < float("inf") and (self.time_next - self.clock.time - t_a + t_b > 0): + t_a = time.time() # time after executing lambdas and deltas + t_elapsed = t_a - t_b # time elapsed between the deltas and lambdas execution + + sleep = self.time_next - self.clock.time + if self.time_next < float("inf"): # Infinite time is not allowed. - # Negative time is not allowed. If lambdas and deltas' time is large. + if sleep < t_elapsed: + delayed_t = t_elapsed - sleep + total_delayed_t += delayed_t + if total_delayed_t > max_delay: + raise RuntimeError('ERROR: to much delayed time ') + + sleep = 0 + else: + sleep -= t_elapsed + if total_delayed_t > 0: + sleep = max(0, sleep - total_delayed_t) + total_delayed_t = max(0, total_delayed_t-sleep) + + time.sleep(sleep) + t_b = time.time() - # sleep_time = self.time_next-self.clock.time - # print(">>> {}".format(sleep_time)) - # time.sleep(sleep_time) - time.sleep(self.time_next - self.clock.time - t_a + t_b) - # - (t_a-t_b) = - t_a + t_b .In order to subtract the time it took to execute lambdas and deltas self.clock.time = self.time_next def simulate_inf(self): From 55426773f287680fa4e14d669cbdff2b3311e80b Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Mon, 23 Jan 2023 10:02:54 +0100 Subject: [PATCH 05/60] Real time simulation V4 In sim.py the simulate_rt function has been modified. Now, it implemets time_scale for increasing or decreasing the simulated time. Although the functionality is the same, the structure of the code has been uptaded. --- .gitignore | 2 +- xdevs/examples/basic/basic.py | 2 +- xdevs/sim.py | 39 +++++++++++++++++++---------------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 6d7bf86..433aa04 100644 --- a/.gitignore +++ b/.gitignore @@ -135,4 +135,4 @@ dmypy.json .DS_Store # Log File -xdevs/examples/basic/SimulationLogs \ No newline at end of file +.log \ No newline at end of file diff --git a/xdevs/examples/basic/basic.py b/xdevs/examples/basic/basic.py index 2a9d65f..3a31813 100644 --- a/xdevs/examples/basic/basic.py +++ b/xdevs/examples/basic/basic.py @@ -1,5 +1,5 @@ import logging -import time + from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger from xdevs.models import Atomic, Coupled, Port diff --git a/xdevs/sim.py b/xdevs/sim.py index 72abd70..877f64b 100644 --- a/xdevs/sim.py +++ b/xdevs/sim.py @@ -345,22 +345,24 @@ def simulate_time(self, time_interv: float = 10000): self.clear() self.clock.time = self.time_next - def simulate_rt(self, time_interv: float = 10000, max_delay: float = 10): + def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, time_scale: float = 1/10): """ Simulates the behavior of a DEVS model in real time over a specified time interval. :param time_interv: The time interval to simulate, in seconds. Default is 10000. :type time_interv: float - :param max_delay : Maximun time the system can be delayed. Default is 10 s + :param max_delay : Maximum time the system can be delayed. Default is 10 ms :type max_delay: float - """ - self.logger.debug('Starting simulation_rt') + :param time_scale: It defines an escale for increasing or decreasing the simulated time. Default is 1 ms + :type time_scale: float + """ self.clock.time = self.time_next tf = self.clock.time + time_interv total_delayed_t = 0.0 + sleep = 0.0 t_b = time.time() # initial time before executing lambdas and deltas while self.clock.time < tf: @@ -368,29 +370,30 @@ def simulate_rt(self, time_interv: float = 10000, max_delay: float = 10): self.deltfcn() self._execute_transducers() self.clear() - t_a = time.time() # time after executing lambdas and deltas - t_elapsed = t_a - t_b # time elapsed between the deltas and lambdas execution - sleep = self.time_next - self.clock.time if self.time_next < float("inf"): # Infinite time is not allowed. - if sleep < t_elapsed: - delayed_t = t_elapsed - sleep - total_delayed_t += delayed_t + + t_a = time.time() # time after executing lambdas and deltas + t_elapsed = t_a - t_b # time elapsed between the deltas and lambdas execution + sleep = (self.time_next - self.clock.time) * time_scale - t_elapsed - total_delayed_t + + if sleep < 0: + # A negative value of sleep implies to much t_elapsed + total_delayed_t = -sleep + # The delayed_time is updated to -sleep. (- sleep is the exceed time) + # The max_delay criteria is checked if total_delayed_t > max_delay: raise RuntimeError('ERROR: to much delayed time ') - + # Having delayed_time implies that sleep must be 0 sleep = 0 else: - sleep -= t_elapsed - if total_delayed_t > 0: - sleep = max(0, sleep - total_delayed_t) - total_delayed_t = max(0, total_delayed_t-sleep) - - time.sleep(sleep) - t_b = time.time() + # Sleep is positive. t_elapsed and delayed_time are small. Everything go well + total_delayed_t = 0 + time.sleep(sleep) self.clock.time = self.time_next + t_b = time.time() def simulate_inf(self): while True: From 74e07319f59d26d004dc6009f2c08ce49e660050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Mon, 23 Jan 2023 10:06:49 +0100 Subject: [PATCH 06/60] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 433aa04..3f8454a 100644 --- a/.gitignore +++ b/.gitignore @@ -135,4 +135,4 @@ dmypy.json .DS_Store # Log File -.log \ No newline at end of file +*.log From 8e2590da4ea314293d488ba2807504d99a4a73b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Mon, 23 Jan 2023 10:09:31 +0100 Subject: [PATCH 07/60] Update sim.py --- xdevs/sim.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/xdevs/sim.py b/xdevs/sim.py index 877f64b..317fbfd 100644 --- a/xdevs/sim.py +++ b/xdevs/sim.py @@ -345,7 +345,7 @@ def simulate_time(self, time_interv: float = 10000): self.clear() self.clock.time = self.time_next - def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, time_scale: float = 1/10): + def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, time_scale: float = 1): """ Simulates the behavior of a DEVS model in real time over a specified time interval. @@ -355,7 +355,7 @@ def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, time_ :param max_delay : Maximum time the system can be delayed. Default is 10 ms :type max_delay: float - :param time_scale: It defines an escale for increasing or decreasing the simulated time. Default is 1 ms + :param time_scale: Scale for increasing or decreasing the simulated time. Default is 1 s (i.e. no scale) :type time_scale: float """ @@ -385,13 +385,11 @@ def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, time_ # The max_delay criteria is checked if total_delayed_t > max_delay: raise RuntimeError('ERROR: to much delayed time ') - # Having delayed_time implies that sleep must be 0 - sleep = 0 else: # Sleep is positive. t_elapsed and delayed_time are small. Everything go well total_delayed_t = 0 - - time.sleep(sleep) + time.sleep(sleep) + self.clock.time = self.time_next t_b = time.time() From 391df81744d654521a2f177b183879546a47cf3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Fri, 3 Feb 2023 08:47:35 +0100 Subject: [PATCH 08/60] Experimental external event injection --- xdevs/examples/async_rt/basic.py | 207 +++++++++++++++++++++++++++++++ xdevs/sim.py | 81 ++++++------ 2 files changed, 253 insertions(+), 35 deletions(-) create mode 100644 xdevs/examples/async_rt/basic.py diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py new file mode 100644 index 0000000..0cc1ee4 --- /dev/null +++ b/xdevs/examples/async_rt/basic.py @@ -0,0 +1,207 @@ +import logging +import queue +import time + +from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger +from xdevs.models import Atomic, Coupled, Port +from xdevs.sim import Coordinator + +logger = get_logger(__name__, logging.DEBUG) + +PHASE_DONE = "done" + + +class Job: + def __init__(self, name): + self.name = name + self.time = 0 + + +class Generator(Atomic): + + def __init__(self, name, period): + super().__init__(name) + self.i_start = Port(Job, "i_start") + self.i_extern = Port(Job, "i_extern") # receives additional jobs from outside + self.i_stop = Port(Job, "i_stop") + self.o_out = Port(Job, "o_out") + + self.add_in_port(self.i_start) + self.add_in_port(self.i_stop) + self.add_in_port(self.i_extern) + self.add_out_port(self.o_out) + + self.period = period + self.job_counter = 1 + self.extern_jobs = list() # stores external jobs + + def initialize(self): + self.hold_in(PHASE_ACTIVE, self.period) + + def exit(self): + pass + + def deltint(self): + self.job_counter += 1 + self.extern_jobs.clear() + self.hold_in(PHASE_ACTIVE, self.period) + + def deltext(self, e): + self.sigma -= e + for msg in self.i_extern.values: + logger.info("Generator received external job. It will forward it in the next lambda") + self.extern_jobs.append(msg) + if not self.i_stop.empty(): + self.passivate() + + def lambdaf(self): + self.o_out.add(Job(str(self.job_counter))) + for msg in self.extern_jobs: # we also forward external messages + self.o_out.add(msg) + + +class Processor(Atomic): + def __init__(self, name, proc_time): + super().__init__(name) + + self.i_in = Port(Job, "i_in") + self.o_out = Port(Job, "o_out") + + self.add_in_port(self.i_in) + self.add_out_port(self.o_out) + + self.current_job = None + self.proc_time = proc_time + + def initialize(self): + self.passivate() + + def exit(self): + pass + + def deltint(self): + self.passivate() + + def deltext(self, e): + if self.phase == PHASE_PASSIVE: + self.current_job = self.i_in.get() + self.hold_in(PHASE_ACTIVE, self.proc_time) + self.continuef(e) + + def lambdaf(self): + self.o_out.add(self.current_job) + + +class Transducer(Atomic): + + def __init__(self, name, obs_time): + super().__init__(name) + + self.i_arrived = Port(Job, "i_arrived") + self.i_solved = Port(Job, "i_solved") + self.o_out = Port(Job, "o_out") + + self.add_in_port(self.i_arrived) + self.add_in_port(self.i_solved) + self.add_out_port(self.o_out) + + self.jobs_arrived = [] + self.jobs_solved = [] + + self.total_ta = 0 + self.clock = 0 + self.obs_time = obs_time + + def initialize(self): + self.hold_in(PHASE_ACTIVE, self.obs_time) + + def exit(self): + pass + + def deltint(self): + self.clock += self.sigma + + if self.phase == PHASE_ACTIVE: + if self.jobs_solved: + avg_ta = self.total_ta / len(self.jobs_solved) + throughput = len(self.jobs_solved) / self.clock if self.clock > 0 else 0 + else: + avg_ta = 0 + throughput = 0 + + logger.info("End time: %f" % self.clock) + logger.info("Jobs arrived: %d" % len(self.jobs_arrived)) + logger.info("Jobs solved: %d" % len(self.jobs_solved)) + logger.info("Average TA: %f" % avg_ta) + logger.info("Throughput: %f\n" % throughput) + + self.hold_in(PHASE_DONE, 0) + else: + self.passivate() + + def deltext(self, e): + self.clock += e + + if self.phase == PHASE_ACTIVE: + for job in self.i_arrived.values: + logger.info("Starting job %s @ t = %d" % (job.name, self.clock)) + job.time = self.clock + self.jobs_arrived.append(job) + + if self.i_solved: + job = self.i_solved.get() + logger.info("Job %s finished @ t = %d" % (job.name, self.clock)) + self.total_ta += self.clock - job.time + self.jobs_solved.append(job) + + self.continuef(e) + + def lambdaf(self): + if self.phase == PHASE_DONE: + self.o_out.add(Job("null")) + + +class RTGpt(Coupled): + def __init__(self, name, period, obs_time): + super().__init__(name) + + if period < 1: + raise ValueError("period has to be greater than 0") + + if obs_time < 0: + raise ValueError("obs_time has to be greater or equal than 0") + + gen = Generator("generator", period) + proc = Processor("processor", 3 * period) + trans = Transducer("transducer", obs_time) + + self.add_component(gen) + self.add_component(proc) + self.add_component(trans) + + # new input port for receiving input events + self.i_extern = Port(Job, "i_extern") + self.add_in_port(self.i_extern) + # new coupling for forwarding messages to generator + self.add_coupling(self.i_extern, gen.i_extern) + + self.add_coupling(gen.o_out, proc.i_in) + self.add_coupling(gen.o_out, trans.i_arrived) + self.add_coupling(proc.o_out, trans.i_solved) + self.add_coupling(trans.o_out, gen.i_stop) + + +def inject_messages(q: queue.SimpleQueue): + i = -1 + while True: + time.sleep(5) # duermo 5 segundos + # la cola espera tuplas (port_name, msg) + q.put(("i_extern", Job(i))) + i -= 1 + + +if __name__ == '__main__': + gpt = RTGpt("gpt", 2, 100) + coord = Coordinator(gpt) + coord.initialize() + coord.simulate_rt(time_interv=20, event_handler=inject_messages) diff --git a/xdevs/sim.py b/xdevs/sim.py index 317fbfd..0fb47cc 100644 --- a/xdevs/sim.py +++ b/xdevs/sim.py @@ -3,13 +3,15 @@ import _thread import itertools import pickle -import time import logging +import threading +import queue +import time as rt_time from abc import ABC, abstractmethod from collections import defaultdict from concurrent import futures -from typing import Generator +from typing import Callable, Generator from xmlrpc.server import SimpleXMLRPCServer from xdevs import INFINITY @@ -345,53 +347,62 @@ def simulate_time(self, time_interv: float = 10000): self.clear() self.clock.time = self.time_next - def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, time_scale: float = 1): + def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, + time_scale: float = 1, event_handler: Callable[[queue.SimpleQueue], None] = None): """ Simulates the behavior of a DEVS model in real time over a specified time interval. :param time_interv: The time interval to simulate, in seconds. Default is 10000. - :type time_interv: float - :param max_delay : Maximum time the system can be delayed. Default is 10 ms - :type max_delay: float - :param time_scale: Scale for increasing or decreasing the simulated time. Default is 1 s (i.e. no scale) - :type time_scale: float - + :param event_handler: external event handler function. + If set, a thread will execute the function to inject external messages """ - self.clock.time = self.time_next + self.clock.time = 0 tf = self.clock.time + time_interv - total_delayed_t = 0.0 - sleep = 0.0 - t_b = time.time() # initial time before executing lambdas and deltas + q = queue.SimpleQueue() + if event_handler is not None: + t = threading.Thread(target=event_handler, daemon=True, args=[q]) + t.start() + + sleep = self.time_next * time_scale + + t_before = rt_time.time() # real time before executing lambdas and deltas + t_after = t_before # real time after executing lambdas and deltas + total_delayed_t = 0.0 # delay compensation buffer while self.clock.time < tf: - self.lambdaf() + # FIRST WE COMPUTE SLEEP TIME + v_sleep = (self.time_next - self.clock.time) # virtual sleep time + r_sleep = v_sleep * time_scale - (t_after - t_before) - total_delayed_t # real sleep time + # THEN WE CHECK THAT DELAY IS NOT TOO BAD + if r_sleep < 0: + total_delayed_t = -sleep + if total_delayed_t > max_delay: # too much delay -> stop execution + raise RuntimeError('ERROR: to much delayed time ') + else: # Sleep is positive. time elapsed and delayed_time are small. Everything went well + total_delayed_t = 0 + # TIME TO SLEEP/WAIT FOR EXTERNAL MESSAGES + try: + port_name, msg = q.get(timeout=r_sleep) # get message with timeout + self.model.get_in_port(port_name).add(msg) + # re-compute virtual sleep time + v_sleep = min(v_sleep, (rt_time.time() - t_after) / time_scale) + except queue.Empty: + pass + # UPDATE SIMULATION CLOCK AND STORE TIME BEFORE NEXT CYCLE + self.clock.time += v_sleep + t_before = rt_time.time() + # EXECUTE NEXT CYCLE + if self.clock.time == self.time_next: # now lambdas are optional + self.lambdaf() self.deltfcn() self._execute_transducers() self.clear() + # STORE TIME AFTER THE CYCLE + t_after = rt_time.time() # time after executing lambdas and deltas + print("done") - if self.time_next < float("inf"): - # Infinite time is not allowed. - - t_a = time.time() # time after executing lambdas and deltas - t_elapsed = t_a - t_b # time elapsed between the deltas and lambdas execution - sleep = (self.time_next - self.clock.time) * time_scale - t_elapsed - total_delayed_t - - if sleep < 0: - # A negative value of sleep implies to much t_elapsed - total_delayed_t = -sleep - # The delayed_time is updated to -sleep. (- sleep is the exceed time) - # The max_delay criteria is checked - if total_delayed_t > max_delay: - raise RuntimeError('ERROR: to much delayed time ') - else: - # Sleep is positive. t_elapsed and delayed_time are small. Everything go well - total_delayed_t = 0 - time.sleep(sleep) - - self.clock.time = self.time_next - t_b = time.time() def simulate_inf(self): while True: From de04ff0b0f1ef2eb077648f1c03d9f60971caf90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Thu, 9 Feb 2023 13:00:30 +0100 Subject: [PATCH 09/60] Update sim.py --- xdevs/sim.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/xdevs/sim.py b/xdevs/sim.py index 0fb47cc..ebfe4cc 100644 --- a/xdevs/sim.py +++ b/xdevs/sim.py @@ -366,18 +366,19 @@ def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, t = threading.Thread(target=event_handler, daemon=True, args=[q]) t.start() - sleep = self.time_next * time_scale - t_before = rt_time.time() # real time before executing lambdas and deltas t_after = t_before # real time after executing lambdas and deltas total_delayed_t = 0.0 # delay compensation buffer while self.clock.time < tf: + if event_handler is None and self.time_next == float("inf"): + print("infinity reached and no event handler configured") + break; # FIRST WE COMPUTE SLEEP TIME v_sleep = (self.time_next - self.clock.time) # virtual sleep time r_sleep = v_sleep * time_scale - (t_after - t_before) - total_delayed_t # real sleep time # THEN WE CHECK THAT DELAY IS NOT TOO BAD if r_sleep < 0: - total_delayed_t = -sleep + total_delayed_t = -r_sleep if total_delayed_t > max_delay: # too much delay -> stop execution raise RuntimeError('ERROR: to much delayed time ') else: # Sleep is positive. time elapsed and delayed_time are small. Everything went well @@ -385,14 +386,14 @@ def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, # TIME TO SLEEP/WAIT FOR EXTERNAL MESSAGES try: port_name, msg = q.get(timeout=r_sleep) # get message with timeout + t_before = rt_time.time() self.model.get_in_port(port_name).add(msg) # re-compute virtual sleep time - v_sleep = min(v_sleep, (rt_time.time() - t_after) / time_scale) + v_sleep = min(v_sleep, (t_before - t_after) / time_scale) except queue.Empty: - pass + t_before = rt_time.time() # UPDATE SIMULATION CLOCK AND STORE TIME BEFORE NEXT CYCLE self.clock.time += v_sleep - t_before = rt_time.time() # EXECUTE NEXT CYCLE if self.clock.time == self.time_next: # now lambdas are optional self.lambdaf() @@ -403,7 +404,6 @@ def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, t_after = rt_time.time() # time after executing lambdas and deltas print("done") - def simulate_inf(self): while True: self.lambdaf() From beae260b07f35f8170a74dc2f6f199ff9b83994c Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Fri, 3 Mar 2023 10:02:34 +0100 Subject: [PATCH 10/60] Manager_rt First approach to manager_rt implementation. --- xdevs/examples/async_rt/basic.py | 30 +++++-- xdevs/examples/basic/basic.py | 10 ++- xdevs/plugins/managers/__init__.py | 0 xdevs/plugins/managers/rt_manager.py | 59 ++++++++++++++ xdevs/plugins/managers/v_manager.py | 18 +++++ xdevs/sim.py | 13 +++- xdevs/simRt/CoordRt.py | 112 +++++++++++++++++++++++++++ xdevs/simRt/ManagerRt.py | 45 +++++++++++ xdevs/simRt/__init__.py | 0 xdevs/sim_rt.py | 72 +++++++++++++++++ 10 files changed, 348 insertions(+), 11 deletions(-) create mode 100644 xdevs/plugins/managers/__init__.py create mode 100644 xdevs/plugins/managers/rt_manager.py create mode 100644 xdevs/plugins/managers/v_manager.py create mode 100644 xdevs/simRt/CoordRt.py create mode 100644 xdevs/simRt/ManagerRt.py create mode 100644 xdevs/simRt/__init__.py create mode 100644 xdevs/sim_rt.py diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py index 0cc1ee4..8139340 100644 --- a/xdevs/examples/async_rt/basic.py +++ b/xdevs/examples/async_rt/basic.py @@ -1,12 +1,14 @@ import logging import queue -import time +import time as rt_time from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger from xdevs.models import Atomic, Coupled, Port +from xdevs.plugins.managers.rt_manager import RtManager +from xdevs.simRt.CoordRt import CoordinatorRt from xdevs.sim import Coordinator -logger = get_logger(__name__, logging.DEBUG) +logger = get_logger(__name__, logging.WARNING) PHASE_DONE = "done" @@ -144,13 +146,13 @@ def deltext(self, e): if self.phase == PHASE_ACTIVE: for job in self.i_arrived.values: - logger.info("Starting job %s @ t = %d" % (job.name, self.clock)) + logger.info("Starting job %s @ t = %d @ t_r = %f" % (job.name, self.clock, rt_time.time())) job.time = self.clock self.jobs_arrived.append(job) if self.i_solved: job = self.i_solved.get() - logger.info("Job %s finished @ t = %d" % (job.name, self.clock)) + logger.info("Job %s finished @ t = %d @ t_r = %f" % (job.name, self.clock, rt_time.time())) self.total_ta += self.clock - job.time self.jobs_solved.append(job) @@ -194,7 +196,7 @@ def __init__(self, name, period, obs_time): def inject_messages(q: queue.SimpleQueue): i = -1 while True: - time.sleep(5) # duermo 5 segundos + rt_time.sleep(3) # duermo x segundos # la cola espera tuplas (port_name, msg) q.put(("i_extern", Job(i))) i -= 1 @@ -202,6 +204,24 @@ def inject_messages(q: queue.SimpleQueue): if __name__ == '__main__': gpt = RTGpt("gpt", 2, 100) + + manager = RtManager(time_scale=1, max_delay=0.01) + manager.add_event_handler(inject_messages) + + c = CoordinatorRt(gpt, manager) + c.initialize() + t_ini = rt_time.time() + print(f' >>> COMENZAMOS : {t_ini}') + c.executeTEMP(time_interv=20) + print(f' >>> FIN : {rt_time.time()}') + print(f' Tiempo ejecutado = {rt_time.time()-t_ini}') + +""" coord = Coordinator(gpt) coord.initialize() + t_ini = rt_time.time() + print(f' >>> COMENZAMOS : {t_ini}') coord.simulate_rt(time_interv=20, event_handler=inject_messages) + print(f' >>> FIN : {rt_time.time()}') + print(f' Tiempo ejecutado = {rt_time.time()-t_ini}') +""" \ No newline at end of file diff --git a/xdevs/examples/basic/basic.py b/xdevs/examples/basic/basic.py index 3a31813..81ab638 100644 --- a/xdevs/examples/basic/basic.py +++ b/xdevs/examples/basic/basic.py @@ -4,6 +4,8 @@ from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger from xdevs.models import Atomic, Coupled, Port from xdevs.sim import Coordinator +from xdevs.sim_rt import CoordinatorRt +import time logger = get_logger(__name__, logging.DEBUG) @@ -133,13 +135,13 @@ def deltext(self, e): if self.phase == PHASE_ACTIVE: if self.i_arrived: job = self.i_arrived.get() - logger.info("Starting job %s @ t = %d" % (job.name, self.clock)) + logger.info("Starting job %s @ t = %d @ t = %d" % (job.name, self.clock, time.time_ns())) job.time = self.clock self.jobs_arrived.append(job) if self.i_solved: job = self.i_solved.get() - logger.info("Job %s finished @ t = %d" % (job.name, self.clock)) + logger.info("Job %s finished @ t = %d @ t = %d" % (job.name, self.clock, time.time())) self.total_ta += self.clock - job.time self.jobs_solved.append(job) @@ -185,8 +187,8 @@ def __init__(self, name, period, obs_time): if __name__ == '__main__': gpt = Gpt("gpt", 2, 100) - coord = Coordinator(gpt) + coord = CoordinatorRt(gpt, 1, 0.10) coord.initialize() # coord.simulate() - coord.simulate_rt() + coord.simulate() # coord.simulate_rt(50) diff --git a/xdevs/plugins/managers/__init__.py b/xdevs/plugins/managers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xdevs/plugins/managers/rt_manager.py b/xdevs/plugins/managers/rt_manager.py new file mode 100644 index 0000000..9418c24 --- /dev/null +++ b/xdevs/plugins/managers/rt_manager.py @@ -0,0 +1,59 @@ +import queue +import time as rt_time +from typing import Any, Callable + +from xdevs.simRt.ManagerRt import ManagerRt + + +class RtManager(ManagerRt): + + def __init__(self, **kwargs): + self.t_a = 0 + self.total_delayed_t: float = 0 + self.lock = True + self.error = 0 + super().__init__(**kwargs) + + def first_or_n_iteration(self, t_sleep): + self.lock = False + return t_sleep * self.time_scale + + def sleep(self, t_sleep: float) -> tuple[float, list[tuple[str, Any]]]: + + #print(f' >>> t_a = {self.t_a}') + + if self.lock: + r_sleep = self.first_or_n_iteration(t_sleep) + else: + r_sleep = t_sleep * self.time_scale - (rt_time.time() - self.t_a) - self.total_delayed_t + #print(f' >>> t_e = {rt_time.time() - self.t_a}') + + #print(f' >>> r_sleep = {r_sleep}') + + if r_sleep < 0: + self.total_delayed_t = -r_sleep + r_sleep = 0 + if self.total_delayed_t > self.max_delay: # too much delay -> stop execution + raise RuntimeError('ERROR: to much delayed time ') + else: # Sleep is positive. time elapsed and delayed_time are small. Everything went well + self.total_delayed_t = 0 + + #print(f' >>> total_delayed_t = {self.total_delayed_t}') + + t_busqueda = rt_time.time() + try: + port_name, msg = self.queue.get(timeout=r_sleep) + slept = (rt_time.time() - t_busqueda) + #print(f'>> slept = {slept} = {r_sleep} - ({rt_time.time()}-{t_busqueda})') + #print('<<< MSG >>>') + self.t_a = rt_time.time() + return slept, [(port_name, msg)] + except queue.Empty: + slept = rt_time.time() - t_busqueda + #print(f' No hay mensajes: r_sleep = {r_sleep} y el tiempo que ha tardado .time() - t_busqueda = {rt_time.time() - t_busqueda}') + self.error += (slept - r_sleep) + #print(f' Error = {slept - r_sleep} Error acumulado = {self.error}') + #print(f'>> slept = {slept}') + #print('<<< NO MSG >>>') + self.t_a = rt_time.time() + return slept, [] diff --git a/xdevs/plugins/managers/v_manager.py b/xdevs/plugins/managers/v_manager.py new file mode 100644 index 0000000..f3657f7 --- /dev/null +++ b/xdevs/plugins/managers/v_manager.py @@ -0,0 +1,18 @@ +import queue +from typing import Callable + +from xdevs.simRt.ManagerRt import ManagerRt + +class Vmanager(ManagerRt): + + def __int__(self, **kwargs): + super().__init__(**kwargs) + + def sleep(self, t_sleep) -> tuple[float, list[tuple[str, str]]]: + return t_sleep, [] + + def initialize(self): + pass + + def add_event_handler(self, handler: Callable[[queue.SimpleQueue], None]): + print('This manager does not accept any msgs') diff --git a/xdevs/sim.py b/xdevs/sim.py index ebfe4cc..b9d67fa 100644 --- a/xdevs/sim.py +++ b/xdevs/sim.py @@ -360,7 +360,6 @@ def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, """ self.clock.time = 0 tf = self.clock.time + time_interv - q = queue.SimpleQueue() if event_handler is not None: t = threading.Thread(target=event_handler, daemon=True, args=[q]) @@ -372,13 +371,14 @@ def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, while self.clock.time < tf: if event_handler is None and self.time_next == float("inf"): print("infinity reached and no event handler configured") - break; + break # FIRST WE COMPUTE SLEEP TIME v_sleep = (self.time_next - self.clock.time) # virtual sleep time r_sleep = v_sleep * time_scale - (t_after - t_before) - total_delayed_t # real sleep time # THEN WE CHECK THAT DELAY IS NOT TOO BAD if r_sleep < 0: total_delayed_t = -r_sleep + r_sleep = 0 if total_delayed_t > max_delay: # too much delay -> stop execution raise RuntimeError('ERROR: to much delayed time ') else: # Sleep is positive. time elapsed and delayed_time are small. Everything went well @@ -387,13 +387,22 @@ def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, try: port_name, msg = q.get(timeout=r_sleep) # get message with timeout t_before = rt_time.time() + print('####') + print(port_name) + print(self.model.get_in_port(port_name)) + print(msg) self.model.get_in_port(port_name).add(msg) # re-compute virtual sleep time + print(f' <<< v_sleep = {v_sleep}') + print(f' <<< (t_b - t_a) / t_scale = {(t_before - t_after) / time_scale}') v_sleep = min(v_sleep, (t_before - t_after) / time_scale) + except queue.Empty: t_before = rt_time.time() # UPDATE SIMULATION CLOCK AND STORE TIME BEFORE NEXT CYCLE + print(f' <<< clock.time = {self.clock.time}') self.clock.time += v_sleep + print(f' <<< clock.time = {self.clock.time}') # EXECUTE NEXT CYCLE if self.clock.time == self.time_next: # now lambdas are optional self.lambdaf() diff --git a/xdevs/simRt/CoordRt.py b/xdevs/simRt/CoordRt.py new file mode 100644 index 0000000..33be6c2 --- /dev/null +++ b/xdevs/simRt/CoordRt.py @@ -0,0 +1,112 @@ +import queue +import time as rt_time +from xdevs.models import Coupled +from xdevs.sim import Coordinator +from xdevs.simRt.ManagerRt import ManagerRt + + +class CoordinatorRt(Coordinator): + + def __init__(self, model: Coupled, manager: ManagerRt): + super().__init__(model) + + self.manager = manager + self.time_scale = manager.time_scale + self.max_delay = manager.max_delay + + def initialize(self): + super().initialize() + self.manager.initialize() + + def simulate(self, time_interv: float = 10000): + """ + Simulates the behavior of a DEVS model in real time over a specified time interval. + + :param time_interv: The time interval to simulate, in seconds. Default is 10000. + """ + self.clock.time = 0 + tf = self.clock.time + time_interv + + t_before = rt_time.time() # real time before executing lambdas and deltas + t_after = t_before # real time after executing lambdas and deltas + total_delayed_t = 0.0 # delay compensation buffer + + while self.clock.time < tf: + if self.time_next == float("inf"): + print("infinity reached and no event handler configured") + break + # FIRST WE COMPUTE SLEEP TIME + v_sleep = (self.time_next - self.clock.time) # virtual sleep time + r_sleep = v_sleep * self.time_scale - (t_after - t_before) - total_delayed_t # real sleep time + # THEN WE CHECK THAT DELAY IS NOT TOO BAD + if r_sleep < 0: + total_delayed_t = -r_sleep + if total_delayed_t > self.max_delay: # too much delay -> stop execution + raise RuntimeError('ERROR: to much delayed time ') + else: # Sleep is positive. time elapsed and delayed_time are small. Everything went well + total_delayed_t = 0 + rt_time.sleep(r_sleep) + t_before = rt_time.time() + v_sleep = min(v_sleep, (t_before - t_after) / self.time_scale) + # TIME TO SLEEP + # UPDATE SIMULATION CLOCK AND STORE TIME BEFORE NEXT CYCLE + self.clock.time += v_sleep + # EXECUTE NEXT CYCLE + if self.clock.time == self.time_next: # now lambdas are optional + self.lambdaf() + self.deltfcn() + self._execute_transducers() + self.clear() + # STORE TIME AFTER THE CYCLE + t_after = rt_time.time() # time after executing lambdas and deltas + print("done") + + def executeTEMP(self, time_interv: float = 10000): + t_a_sleep = 0 + self.clock.time = 0 + + while self.clock.time < time_interv: + if self.time_next == float("inf"): # comprobacion del evento_handler + print("infinity reached and no event handler configured") + break + + t_sleep = self.time_next - self.clock.time + #print(f' <<< t_sleep = {t_sleep}') + slept, msgs = self.manager.sleep(t_sleep) + t_b_sleep = rt_time.time() + #print(f' <->-<-> Tiempo b m & deltas = {t_b_sleep}') + for m in msgs: + port = self.model.get_in_port(m[0]) + if port is not None: + try: + port.add(m[1]) + except TypeError as e: + print(f'{e}') + else: + print(f'{m[0]} does not exit') + #print('## ACTUALIZAR V_TIME msgs ##') + #print(f't_sleep = {t_sleep} , slept/t_scale = {slept / self.time_scale}') + t_sleep = min(t_sleep, slept / self.time_scale) + + + + # Actualizar tiempo + + #print('## ACTUALIZAR V_TIME ##') + #print(f't_sleep = {t_sleep} , slept/t_scale = {slept/self.time_scale}') + #print(f' <<< t_sleep actualizado? = {t_sleep}') + # UPDATE SIMULATION CLOCK + #print(f' <<< clock.time = {self.clock.time}') + self.clock.time += t_sleep + # OJO -> ESTO SOLO SERA VALIDO SI EL MANAGER ES V_MANAGER. + #print(f' <<< clock.time = {self.clock.time}') + + # EXECUTE NEXT CYCLE + if self.clock.time == self.time_next: # now lambdas are optional + self.lambdaf() + self.deltfcn() + self._execute_transducers() + self.clear() + + t_a_sleep = rt_time.time() + #print(f' <->-<-> Tiempo a m & deltas = {t_a_sleep}') diff --git a/xdevs/simRt/ManagerRt.py b/xdevs/simRt/ManagerRt.py new file mode 100644 index 0000000..26030bb --- /dev/null +++ b/xdevs/simRt/ManagerRt.py @@ -0,0 +1,45 @@ +import queue +import threading +from abc import ABC, abstractmethod +from typing import Callable, Any + + +class ManagerRt(ABC): + def __init__(self, **kwargs): + self.last_v_time: float = 0 + self.time_scale: float = kwargs.get('time_scale') # TODO + self.max_delay: float = kwargs.get('max_delay') # TODO + if self.max_delay < 0: + raise Exception('Negative delay is not valid.') + self.event_handlers = list() + self.threads = list() + # self.event_handler: Callable[[queue.SimpleQueue], None] = kwargs.get('event_handler') + + self.queue = queue.SimpleQueue() + + # todo hacer clase event_handler + def add_event_handler(self, handler: Callable[[queue.SimpleQueue], None]): + self.event_handlers.append(handler) + + @abstractmethod + def sleep(self, t_until: float) -> tuple[float, list[tuple[Any, Any]]]: + """ + Simulates the time of the simulation of the DEVS model. + time will be the instant the manager must sleep at most. + It returns a float that will be the time until it actually slept. + It returns a [], that contains the port_name and the msg. + """ + pass + + def initialize(self): + """ Executes any required action before starting simulation. """ + if self.event_handlers is None: # TODO + raise Exception('No handlers available.') + for handler in self.event_handlers: + t = threading.Thread(daemon=True, target=handler, args=[self.queue]) + t.start() + self.threads.append(t) + + def exit(self, final_t: float): + """ Executes any required action after complete simulation. """ + self.last_v_time = final_t diff --git a/xdevs/simRt/__init__.py b/xdevs/simRt/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xdevs/sim_rt.py b/xdevs/sim_rt.py new file mode 100644 index 0000000..18e47af --- /dev/null +++ b/xdevs/sim_rt.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +import _thread +import itertools +import pickle +import logging +import threading +import queue +import time as rt_time + +from abc import ABC, abstractmethod +from collections import defaultdict +from concurrent import futures +from typing import Callable, Generator +from xmlrpc.server import SimpleXMLRPCServer + +from xdevs import INFINITY +from xdevs.models import Atomic, Coupled, Component, Port, T +from xdevs.sim import AbstractSimulator, Coordinator +from xdevs.transducers import Transducer + + +class CoordinatorRt(Coordinator): + + def __init__(self, model: Coupled, time_scale: float, max_delay: float): + super().__init__(model) + + self.time_scale = time_scale + self.max_delay = max_delay + + def simulate(self, time_interv : float = 10000): + """ + Simulates the behavior of a DEVS model in real time over a specified time interval. + + :param time_interv: The time interval to simulate, in seconds. Default is 10000. + """ + self.clock.time = 0 + tf = self.clock.time + time_interv + + t_before = rt_time.time() # real time before executing lambdas and deltas + t_after = t_before # real time after executing lambdas and deltas + total_delayed_t = 0.0 # delay compensation buffer + + while self.clock.time < tf: + if self.time_next == float("inf"): + print("infinity reached and no event handler configured") + break + # FIRST WE COMPUTE SLEEP TIME + v_sleep = (self.time_next - self.clock.time) # virtual sleep time + r_sleep = v_sleep * self.time_scale - (t_after - t_before) - total_delayed_t # real sleep time + # THEN WE CHECK THAT DELAY IS NOT TOO BAD + if r_sleep < 0: + total_delayed_t = -r_sleep + if total_delayed_t > self.max_delay: # too much delay -> stop execution + raise RuntimeError('ERROR: to much delayed time ') + else: # Sleep is positive. time elapsed and delayed_time are small. Everything went well + total_delayed_t = 0 + rt_time.sleep(r_sleep) + t_before = rt_time.time() + v_sleep = min(v_sleep, (t_before - t_after) / self.time_scale) + # TIME TO SLEEP + # UPDATE SIMULATION CLOCK AND STORE TIME BEFORE NEXT CYCLE + self.clock.time += v_sleep + # EXECUTE NEXT CYCLE + if self.clock.time == self.time_next: # now lambdas are optional + self.lambdaf() + self.deltfcn() + self._execute_transducers() + self.clear() + # STORE TIME AFTER THE CYCLE + t_after = rt_time.time() # time after executing lambdas and deltas + print("done") From 7127c408c6afde44442e242de50e4beefae66668 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Sun, 5 Mar 2023 19:58:25 +0100 Subject: [PATCH 11/60] Manager_rt V2 Second approach to manager_rt implementation. The execution time has been optimized. --- xdevs/plugins/managers/rt_manager.py | 59 ------------------- xdevs/plugins/managers/v_manager.py | 18 ------ xdevs/simRt/__init__.py | 0 .../{plugins/managers => sim_rt}/__init__.py | 0 .../{simRt/CoordRt.py => sim_rt/coord_rt.py} | 35 +++-------- .../{simRt/ManagerRt.py => sim_rt/manager.py} | 20 +++---- xdevs/sim_rt/manager_rt.py | 54 +++++++++++++++++ 7 files changed, 71 insertions(+), 115 deletions(-) delete mode 100644 xdevs/plugins/managers/rt_manager.py delete mode 100644 xdevs/plugins/managers/v_manager.py delete mode 100644 xdevs/simRt/__init__.py rename xdevs/{plugins/managers => sim_rt}/__init__.py (100%) rename xdevs/{simRt/CoordRt.py => sim_rt/coord_rt.py} (74%) rename xdevs/{simRt/ManagerRt.py => sim_rt/manager.py} (74%) create mode 100644 xdevs/sim_rt/manager_rt.py diff --git a/xdevs/plugins/managers/rt_manager.py b/xdevs/plugins/managers/rt_manager.py deleted file mode 100644 index 9418c24..0000000 --- a/xdevs/plugins/managers/rt_manager.py +++ /dev/null @@ -1,59 +0,0 @@ -import queue -import time as rt_time -from typing import Any, Callable - -from xdevs.simRt.ManagerRt import ManagerRt - - -class RtManager(ManagerRt): - - def __init__(self, **kwargs): - self.t_a = 0 - self.total_delayed_t: float = 0 - self.lock = True - self.error = 0 - super().__init__(**kwargs) - - def first_or_n_iteration(self, t_sleep): - self.lock = False - return t_sleep * self.time_scale - - def sleep(self, t_sleep: float) -> tuple[float, list[tuple[str, Any]]]: - - #print(f' >>> t_a = {self.t_a}') - - if self.lock: - r_sleep = self.first_or_n_iteration(t_sleep) - else: - r_sleep = t_sleep * self.time_scale - (rt_time.time() - self.t_a) - self.total_delayed_t - #print(f' >>> t_e = {rt_time.time() - self.t_a}') - - #print(f' >>> r_sleep = {r_sleep}') - - if r_sleep < 0: - self.total_delayed_t = -r_sleep - r_sleep = 0 - if self.total_delayed_t > self.max_delay: # too much delay -> stop execution - raise RuntimeError('ERROR: to much delayed time ') - else: # Sleep is positive. time elapsed and delayed_time are small. Everything went well - self.total_delayed_t = 0 - - #print(f' >>> total_delayed_t = {self.total_delayed_t}') - - t_busqueda = rt_time.time() - try: - port_name, msg = self.queue.get(timeout=r_sleep) - slept = (rt_time.time() - t_busqueda) - #print(f'>> slept = {slept} = {r_sleep} - ({rt_time.time()}-{t_busqueda})') - #print('<<< MSG >>>') - self.t_a = rt_time.time() - return slept, [(port_name, msg)] - except queue.Empty: - slept = rt_time.time() - t_busqueda - #print(f' No hay mensajes: r_sleep = {r_sleep} y el tiempo que ha tardado .time() - t_busqueda = {rt_time.time() - t_busqueda}') - self.error += (slept - r_sleep) - #print(f' Error = {slept - r_sleep} Error acumulado = {self.error}') - #print(f'>> slept = {slept}') - #print('<<< NO MSG >>>') - self.t_a = rt_time.time() - return slept, [] diff --git a/xdevs/plugins/managers/v_manager.py b/xdevs/plugins/managers/v_manager.py deleted file mode 100644 index f3657f7..0000000 --- a/xdevs/plugins/managers/v_manager.py +++ /dev/null @@ -1,18 +0,0 @@ -import queue -from typing import Callable - -from xdevs.simRt.ManagerRt import ManagerRt - -class Vmanager(ManagerRt): - - def __int__(self, **kwargs): - super().__init__(**kwargs) - - def sleep(self, t_sleep) -> tuple[float, list[tuple[str, str]]]: - return t_sleep, [] - - def initialize(self): - pass - - def add_event_handler(self, handler: Callable[[queue.SimpleQueue], None]): - print('This manager does not accept any msgs') diff --git a/xdevs/simRt/__init__.py b/xdevs/simRt/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/xdevs/plugins/managers/__init__.py b/xdevs/sim_rt/__init__.py similarity index 100% rename from xdevs/plugins/managers/__init__.py rename to xdevs/sim_rt/__init__.py diff --git a/xdevs/simRt/CoordRt.py b/xdevs/sim_rt/coord_rt.py similarity index 74% rename from xdevs/simRt/CoordRt.py rename to xdevs/sim_rt/coord_rt.py index 33be6c2..f928177 100644 --- a/xdevs/simRt/CoordRt.py +++ b/xdevs/sim_rt/coord_rt.py @@ -1,13 +1,14 @@ import queue import time as rt_time from xdevs.models import Coupled +from xdevs.plugins.managers.rt_manager import RtManager from xdevs.sim import Coordinator -from xdevs.simRt.ManagerRt import ManagerRt +from xdevs.sim_rt.ManagerRt import AbstractRTManager class CoordinatorRt(Coordinator): - def __init__(self, model: Coupled, manager: ManagerRt): + def __init__(self, model: Coupled, manager: RtManager): super().__init__(model) self.manager = manager @@ -16,7 +17,7 @@ def __init__(self, model: Coupled, manager: ManagerRt): def initialize(self): super().initialize() - self.manager.initialize() + self.manager.initialize(self.clock.time) def simulate(self, time_interv: float = 10000): """ @@ -62,19 +63,15 @@ def simulate(self, time_interv: float = 10000): print("done") def executeTEMP(self, time_interv: float = 10000): - t_a_sleep = 0 self.clock.time = 0 while self.clock.time < time_interv: if self.time_next == float("inf"): # comprobacion del evento_handler + print(self.time_next) print("infinity reached and no event handler configured") break - t_sleep = self.time_next - self.clock.time - #print(f' <<< t_sleep = {t_sleep}') - slept, msgs = self.manager.sleep(t_sleep) - t_b_sleep = rt_time.time() - #print(f' <->-<-> Tiempo b m & deltas = {t_b_sleep}') + slept, msgs = self.manager.new_sleep(self.time_next) for m in msgs: port = self.model.get_in_port(m[0]) if port is not None: @@ -84,23 +81,10 @@ def executeTEMP(self, time_interv: float = 10000): print(f'{e}') else: print(f'{m[0]} does not exit') - #print('## ACTUALIZAR V_TIME msgs ##') - #print(f't_sleep = {t_sleep} , slept/t_scale = {slept / self.time_scale}') - t_sleep = min(t_sleep, slept / self.time_scale) - - + slept = min(t_sleep, slept) - # Actualizar tiempo - - #print('## ACTUALIZAR V_TIME ##') - #print(f't_sleep = {t_sleep} , slept/t_scale = {slept/self.time_scale}') - #print(f' <<< t_sleep actualizado? = {t_sleep}') # UPDATE SIMULATION CLOCK - #print(f' <<< clock.time = {self.clock.time}') - self.clock.time += t_sleep - # OJO -> ESTO SOLO SERA VALIDO SI EL MANAGER ES V_MANAGER. - #print(f' <<< clock.time = {self.clock.time}') - + self.clock.time = slept # EXECUTE NEXT CYCLE if self.clock.time == self.time_next: # now lambdas are optional self.lambdaf() @@ -108,5 +92,4 @@ def executeTEMP(self, time_interv: float = 10000): self._execute_transducers() self.clear() - t_a_sleep = rt_time.time() - #print(f' <->-<-> Tiempo a m & deltas = {t_a_sleep}') + self.manager.exit(self.clock.time) diff --git a/xdevs/simRt/ManagerRt.py b/xdevs/sim_rt/manager.py similarity index 74% rename from xdevs/simRt/ManagerRt.py rename to xdevs/sim_rt/manager.py index 26030bb..306fd45 100644 --- a/xdevs/simRt/ManagerRt.py +++ b/xdevs/sim_rt/manager.py @@ -1,27 +1,20 @@ import queue import threading -from abc import ABC, abstractmethod from typing import Callable, Any -class ManagerRt(ABC): +class AbstractRTManager: def __init__(self, **kwargs): self.last_v_time: float = 0 - self.time_scale: float = kwargs.get('time_scale') # TODO - self.max_delay: float = kwargs.get('max_delay') # TODO - if self.max_delay < 0: - raise Exception('Negative delay is not valid.') self.event_handlers = list() self.threads = list() # self.event_handler: Callable[[queue.SimpleQueue], None] = kwargs.get('event_handler') - self.queue = queue.SimpleQueue() # todo hacer clase event_handler def add_event_handler(self, handler: Callable[[queue.SimpleQueue], None]): self.event_handlers.append(handler) - @abstractmethod def sleep(self, t_until: float) -> tuple[float, list[tuple[Any, Any]]]: """ Simulates the time of the simulation of the DEVS model. @@ -29,12 +22,15 @@ def sleep(self, t_until: float) -> tuple[float, list[tuple[Any, Any]]]: It returns a float that will be the time until it actually slept. It returns a [], that contains the port_name and the msg. """ - pass + self.last_v_time = t_until + return self.last_v_time, [] - def initialize(self): + def initialize(self, initial_t: float): """ Executes any required action before starting simulation. """ - if self.event_handlers is None: # TODO - raise Exception('No handlers available.') + # TODO + # if self.event_handlers is None: + # raise Exception('No handlers available.') + self.last_v_time = initial_t for handler in self.event_handlers: t = threading.Thread(daemon=True, target=handler, args=[self.queue]) t.start() diff --git a/xdevs/sim_rt/manager_rt.py b/xdevs/sim_rt/manager_rt.py new file mode 100644 index 0000000..d4b0557 --- /dev/null +++ b/xdevs/sim_rt/manager_rt.py @@ -0,0 +1,54 @@ +import queue +import time as rt_time +from typing import Any, Callable + +from xdevs.sim_rt.manager import AbstractRTManager + + +class RtManager(AbstractRTManager): + + def __init__(self, **kwargs): + self.time_scale: float = kwargs.get('time_scale') + self.max_delay: float = kwargs.get('max_delay') + if self.max_delay < 0: + raise Exception('Negative delay is not valid.') + self.baseline_r_time = 0 + self.total_delayed_t: float = 0 + self.error = 0 + self.last_r_time = 0 + super().__init__(**kwargs) + + def initialize(self, initial_t): + super().initialize(initial_t) + print(f' <<< last_v_time = {self.last_v_time}') + self.baseline_r_time = rt_time.time() + self.last_r_time = self.baseline_r_time + + def sleep(self, next_v_time) -> tuple[float, list[tuple[str, Any]]]: + next_r_time = self.last_r_time + (next_v_time - self.last_v_time) * self.time_scale + # print(f' + next_r_time = {next_r_time}') + try: + if next_r_time - rt_time.time() > 0: + port_name, msg = self.queue.get(timeout=next_r_time - rt_time.time()) + r_time = min(next_r_time, rt_time.time()) + v_time = (r_time - self.baseline_r_time) / self.time_scale + self.last_v_time = v_time + self.last_r_time = r_time + return v_time, [(port_name, msg)] + return next_v_time, [] + except queue.Empty: + self.last_v_time = next_v_time + self.last_r_time = next_r_time + return next_v_time, [] + + def exit(self, final_t: float): + + print(f' ¿tiempo ejecutado? = {rt_time.time() - self.baseline_r_time}') + print(f' final_t? = {final_t * self.time_scale}') + print(f' max_delay = {self.max_delay*self.time_scale}') + print(f' error acumulado = {self.error}') + + if rt_time.time() - self.baseline_r_time - (final_t * self.time_scale) > (self.max_delay*self.time_scale): + self.error += rt_time.time() - self.baseline_r_time - (final_t * self.time_scale) + raise Exception('Too much delay.') + pass From 51b3886ec0d875f9c369cfcdf60b3e0d1469bf4f Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Mon, 6 Mar 2023 13:32:10 +0100 Subject: [PATCH 12/60] rt_manager Updated max_delay implementation. Merged manager and manager_rt into rt_manager. --- xdevs/examples/async_rt/basic.py | 26 +++++--- xdevs/examples/basic/basic.py | 2 +- xdevs/{sim_rt => rt_sim}/__init__.py | 0 xdevs/rt_sim/rt_coord.py | 48 ++++++++++++++ xdevs/rt_sim/rt_manager.py | 79 +++++++++++++++++++++++ xdevs/sim_rt/coord_rt.py | 95 ---------------------------- xdevs/sim_rt/manager.py | 41 ------------ xdevs/sim_rt/manager_rt.py | 54 ---------------- 8 files changed, 145 insertions(+), 200 deletions(-) rename xdevs/{sim_rt => rt_sim}/__init__.py (100%) create mode 100644 xdevs/rt_sim/rt_coord.py create mode 100644 xdevs/rt_sim/rt_manager.py delete mode 100644 xdevs/sim_rt/coord_rt.py delete mode 100644 xdevs/sim_rt/manager.py delete mode 100644 xdevs/sim_rt/manager_rt.py diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py index 8139340..4102faf 100644 --- a/xdevs/examples/async_rt/basic.py +++ b/xdevs/examples/async_rt/basic.py @@ -1,12 +1,13 @@ import logging import queue +import random import time as rt_time from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger from xdevs.models import Atomic, Coupled, Port -from xdevs.plugins.managers.rt_manager import RtManager -from xdevs.simRt.CoordRt import CoordinatorRt -from xdevs.sim import Coordinator +from xdevs.rt_sim.rt_manager import RtManager +from xdevs.rt_sim.rt_coord import CoordinatorRt + logger = get_logger(__name__, logging.WARNING) @@ -196,25 +197,32 @@ def __init__(self, name, period, obs_time): def inject_messages(q: queue.SimpleQueue): i = -1 while True: - rt_time.sleep(3) # duermo x segundos + f = round(random.gauss(3, 0.6), 2) + rt_time.sleep(f * time_scale) # duermo f segundos # la cola espera tuplas (port_name, msg) q.put(("i_extern", Job(i))) i -= 1 if __name__ == '__main__': - gpt = RTGpt("gpt", 2, 100) + execution_time = 30 + time_scale = 1 + max_delay = 0.02 + + gpt = RTGpt("gpt", 2, 3600) - manager = RtManager(time_scale=1, max_delay=0.01) + manager = RtManager(time_scale=time_scale, max_delay=max_delay) manager.add_event_handler(inject_messages) c = CoordinatorRt(gpt, manager) c.initialize() t_ini = rt_time.time() print(f' >>> COMENZAMOS : {t_ini}') - c.executeTEMP(time_interv=20) + c.simulate(time_interv=execution_time) print(f' >>> FIN : {rt_time.time()}') - print(f' Tiempo ejecutado = {rt_time.time()-t_ini}') + print(f' Tiempo ejecutado = {(rt_time.time() - t_ini)}') + print(f' error execution_time = ' + f'{((rt_time.time() - t_ini - (execution_time * time_scale)) / (execution_time * time_scale)) * 100}') """ coord = Coordinator(gpt) @@ -224,4 +232,4 @@ def inject_messages(q: queue.SimpleQueue): coord.simulate_rt(time_interv=20, event_handler=inject_messages) print(f' >>> FIN : {rt_time.time()}') print(f' Tiempo ejecutado = {rt_time.time()-t_ini}') -""" \ No newline at end of file +""" diff --git a/xdevs/examples/basic/basic.py b/xdevs/examples/basic/basic.py index 81ab638..11653fb 100644 --- a/xdevs/examples/basic/basic.py +++ b/xdevs/examples/basic/basic.py @@ -4,7 +4,7 @@ from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger from xdevs.models import Atomic, Coupled, Port from xdevs.sim import Coordinator -from xdevs.sim_rt import CoordinatorRt +from xdevs.rt_sim import CoordinatorRt import time logger = get_logger(__name__, logging.DEBUG) diff --git a/xdevs/sim_rt/__init__.py b/xdevs/rt_sim/__init__.py similarity index 100% rename from xdevs/sim_rt/__init__.py rename to xdevs/rt_sim/__init__.py diff --git a/xdevs/rt_sim/rt_coord.py b/xdevs/rt_sim/rt_coord.py new file mode 100644 index 0000000..5185565 --- /dev/null +++ b/xdevs/rt_sim/rt_coord.py @@ -0,0 +1,48 @@ +from xdevs.models import Coupled +from xdevs.rt_sim.rt_manager import RtManager +from xdevs.sim import Coordinator + + +class CoordinatorRt(Coordinator): + + def __init__(self, model: Coupled, manager: RtManager): + super().__init__(model) + + self.manager = manager + self.time_scale = manager.time_scale + self.max_delay = manager.max_delay + + def initialize(self): + super().initialize() + self.manager.initialize(self.clock.time) + + def simulate(self, time_interv: float = 10000): + self.clock.time = 0 + + while self.clock.time < time_interv: + if self.time_next == float("inf"): # comprobacion del evento_handler + print("infinity reached") + break + t_sleep = self.time_next - self.clock.time + slept, msgs = self.manager.sleep(self.time_next) + for m in msgs: + port = self.model.get_in_port(m[0]) + if port is not None: + try: + port.add(m[1]) + except TypeError as e: + print(f'{e}') + else: + print(f'{m[0]} does not exit') + slept = min(t_sleep, slept) + + # UPDATE SIMULATION CLOCK + self.clock.time = slept + # EXECUTE NEXT CYCLE + if self.clock.time == self.time_next: # now lambdas are optional + self.lambdaf() + self.deltfcn() + self._execute_transducers() + self.clear() + + self.manager.exit(self.clock.time) diff --git a/xdevs/rt_sim/rt_manager.py b/xdevs/rt_sim/rt_manager.py new file mode 100644 index 0000000..057b171 --- /dev/null +++ b/xdevs/rt_sim/rt_manager.py @@ -0,0 +1,79 @@ +import queue +import threading +from typing import Callable, Any +import time as rt_time + + +class RtManager: + def __init__(self, **kwargs): + self.time_scale: float = kwargs.get('time_scale') + if self.time_scale <= 0: + raise Exception('Negative or zero time_scale is not valid.') + self.max_delay: float = kwargs.get('max_delay') + if self.max_delay < 0: + raise Exception('Negative delay is not valid.') + + self.baseline_r_time = 0 + self.error = 0 + self.last_r_time = 0 + self.last_v_time: float = 0 + + self.event_handlers = list() + self.threads = list() + # self.event_handler: Callable[[queue.SimpleQueue], None] = kwargs.get('event_handler') + self.queue = queue.SimpleQueue() + + # todo hacer clase event_handler + def add_event_handler(self, handler: Callable[[queue.SimpleQueue], None]): + self.event_handlers.append(handler) + + def sleep(self, next_v_time: float) -> tuple[float, list[tuple[Any, Any]]]: + """ + Simulates the time of the simulation of the DEVS model. + time will be the instant the manager must sleep at most. + It returns a float that will be the time until it actually slept. + It returns a [], that contains the port_name and the msg. + """ + next_r_time = self.last_r_time + (next_v_time - self.last_v_time) * self.time_scale + + try: + if next_r_time - rt_time.time() > 0: + port_name, msg = self.queue.get(timeout=next_r_time - rt_time.time()) + r_time = min(next_r_time, rt_time.time()) + v_time = (r_time - self.baseline_r_time) / self.time_scale + self.last_v_time = v_time + self.last_r_time = r_time + return v_time, [(port_name, msg)] + print(f'TIMEOUT ERROR, resta-> {next_r_time - rt_time.time()}') + print('-> r_time', next_r_time) + print('-> .time', rt_time.time()) + return next_v_time, [] # en vez hacer, raise exception por .time > next_r_time ? + + except queue.Empty: + self.last_v_time = next_v_time + self.last_r_time = next_r_time + + if rt_time.time() - self.baseline_r_time - (next_v_time * self.time_scale) > ( + self.max_delay * self.time_scale): + raise Exception('Too much delay.') + + return next_v_time, [] + + def initialize(self, initial_t: float): + for handler in self.event_handlers: + t = threading.Thread(daemon=True, target=handler, args=[self.queue]) + t.start() + self.threads.append(t) + self.last_v_time = initial_t + self.baseline_r_time = rt_time.time() + self.last_r_time = self.baseline_r_time + + def exit(self, final_t: float): + self.last_v_time = final_t + """ + print(f' ¿tiempo ejecutado? = {rt_time.time() - self.baseline_r_time}') + print(f' final_t? = {final_t * self.time_scale}') + print(f' max_delay = {self.max_delay*self.time_scale}') + """ + if rt_time.time() - self.baseline_r_time - (final_t * self.time_scale) > (self.max_delay * self.time_scale): + raise Exception('Too much delay.') diff --git a/xdevs/sim_rt/coord_rt.py b/xdevs/sim_rt/coord_rt.py deleted file mode 100644 index f928177..0000000 --- a/xdevs/sim_rt/coord_rt.py +++ /dev/null @@ -1,95 +0,0 @@ -import queue -import time as rt_time -from xdevs.models import Coupled -from xdevs.plugins.managers.rt_manager import RtManager -from xdevs.sim import Coordinator -from xdevs.sim_rt.ManagerRt import AbstractRTManager - - -class CoordinatorRt(Coordinator): - - def __init__(self, model: Coupled, manager: RtManager): - super().__init__(model) - - self.manager = manager - self.time_scale = manager.time_scale - self.max_delay = manager.max_delay - - def initialize(self): - super().initialize() - self.manager.initialize(self.clock.time) - - def simulate(self, time_interv: float = 10000): - """ - Simulates the behavior of a DEVS model in real time over a specified time interval. - - :param time_interv: The time interval to simulate, in seconds. Default is 10000. - """ - self.clock.time = 0 - tf = self.clock.time + time_interv - - t_before = rt_time.time() # real time before executing lambdas and deltas - t_after = t_before # real time after executing lambdas and deltas - total_delayed_t = 0.0 # delay compensation buffer - - while self.clock.time < tf: - if self.time_next == float("inf"): - print("infinity reached and no event handler configured") - break - # FIRST WE COMPUTE SLEEP TIME - v_sleep = (self.time_next - self.clock.time) # virtual sleep time - r_sleep = v_sleep * self.time_scale - (t_after - t_before) - total_delayed_t # real sleep time - # THEN WE CHECK THAT DELAY IS NOT TOO BAD - if r_sleep < 0: - total_delayed_t = -r_sleep - if total_delayed_t > self.max_delay: # too much delay -> stop execution - raise RuntimeError('ERROR: to much delayed time ') - else: # Sleep is positive. time elapsed and delayed_time are small. Everything went well - total_delayed_t = 0 - rt_time.sleep(r_sleep) - t_before = rt_time.time() - v_sleep = min(v_sleep, (t_before - t_after) / self.time_scale) - # TIME TO SLEEP - # UPDATE SIMULATION CLOCK AND STORE TIME BEFORE NEXT CYCLE - self.clock.time += v_sleep - # EXECUTE NEXT CYCLE - if self.clock.time == self.time_next: # now lambdas are optional - self.lambdaf() - self.deltfcn() - self._execute_transducers() - self.clear() - # STORE TIME AFTER THE CYCLE - t_after = rt_time.time() # time after executing lambdas and deltas - print("done") - - def executeTEMP(self, time_interv: float = 10000): - self.clock.time = 0 - - while self.clock.time < time_interv: - if self.time_next == float("inf"): # comprobacion del evento_handler - print(self.time_next) - print("infinity reached and no event handler configured") - break - t_sleep = self.time_next - self.clock.time - slept, msgs = self.manager.new_sleep(self.time_next) - for m in msgs: - port = self.model.get_in_port(m[0]) - if port is not None: - try: - port.add(m[1]) - except TypeError as e: - print(f'{e}') - else: - print(f'{m[0]} does not exit') - slept = min(t_sleep, slept) - - # UPDATE SIMULATION CLOCK - self.clock.time = slept - # EXECUTE NEXT CYCLE - if self.clock.time == self.time_next: # now lambdas are optional - self.lambdaf() - self.deltfcn() - self._execute_transducers() - self.clear() - - self.manager.exit(self.clock.time) diff --git a/xdevs/sim_rt/manager.py b/xdevs/sim_rt/manager.py deleted file mode 100644 index 306fd45..0000000 --- a/xdevs/sim_rt/manager.py +++ /dev/null @@ -1,41 +0,0 @@ -import queue -import threading -from typing import Callable, Any - - -class AbstractRTManager: - def __init__(self, **kwargs): - self.last_v_time: float = 0 - self.event_handlers = list() - self.threads = list() - # self.event_handler: Callable[[queue.SimpleQueue], None] = kwargs.get('event_handler') - self.queue = queue.SimpleQueue() - - # todo hacer clase event_handler - def add_event_handler(self, handler: Callable[[queue.SimpleQueue], None]): - self.event_handlers.append(handler) - - def sleep(self, t_until: float) -> tuple[float, list[tuple[Any, Any]]]: - """ - Simulates the time of the simulation of the DEVS model. - time will be the instant the manager must sleep at most. - It returns a float that will be the time until it actually slept. - It returns a [], that contains the port_name and the msg. - """ - self.last_v_time = t_until - return self.last_v_time, [] - - def initialize(self, initial_t: float): - """ Executes any required action before starting simulation. """ - # TODO - # if self.event_handlers is None: - # raise Exception('No handlers available.') - self.last_v_time = initial_t - for handler in self.event_handlers: - t = threading.Thread(daemon=True, target=handler, args=[self.queue]) - t.start() - self.threads.append(t) - - def exit(self, final_t: float): - """ Executes any required action after complete simulation. """ - self.last_v_time = final_t diff --git a/xdevs/sim_rt/manager_rt.py b/xdevs/sim_rt/manager_rt.py deleted file mode 100644 index d4b0557..0000000 --- a/xdevs/sim_rt/manager_rt.py +++ /dev/null @@ -1,54 +0,0 @@ -import queue -import time as rt_time -from typing import Any, Callable - -from xdevs.sim_rt.manager import AbstractRTManager - - -class RtManager(AbstractRTManager): - - def __init__(self, **kwargs): - self.time_scale: float = kwargs.get('time_scale') - self.max_delay: float = kwargs.get('max_delay') - if self.max_delay < 0: - raise Exception('Negative delay is not valid.') - self.baseline_r_time = 0 - self.total_delayed_t: float = 0 - self.error = 0 - self.last_r_time = 0 - super().__init__(**kwargs) - - def initialize(self, initial_t): - super().initialize(initial_t) - print(f' <<< last_v_time = {self.last_v_time}') - self.baseline_r_time = rt_time.time() - self.last_r_time = self.baseline_r_time - - def sleep(self, next_v_time) -> tuple[float, list[tuple[str, Any]]]: - next_r_time = self.last_r_time + (next_v_time - self.last_v_time) * self.time_scale - # print(f' + next_r_time = {next_r_time}') - try: - if next_r_time - rt_time.time() > 0: - port_name, msg = self.queue.get(timeout=next_r_time - rt_time.time()) - r_time = min(next_r_time, rt_time.time()) - v_time = (r_time - self.baseline_r_time) / self.time_scale - self.last_v_time = v_time - self.last_r_time = r_time - return v_time, [(port_name, msg)] - return next_v_time, [] - except queue.Empty: - self.last_v_time = next_v_time - self.last_r_time = next_r_time - return next_v_time, [] - - def exit(self, final_t: float): - - print(f' ¿tiempo ejecutado? = {rt_time.time() - self.baseline_r_time}') - print(f' final_t? = {final_t * self.time_scale}') - print(f' max_delay = {self.max_delay*self.time_scale}') - print(f' error acumulado = {self.error}') - - if rt_time.time() - self.baseline_r_time - (final_t * self.time_scale) > (self.max_delay*self.time_scale): - self.error += rt_time.time() - self.baseline_r_time - (final_t * self.time_scale) - raise Exception('Too much delay.') - pass From dc9f7d1e96a096ec30398e398949ffe4d9a2dd8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Mon, 6 Mar 2023 16:56:13 +0100 Subject: [PATCH 13/60] Review --- xdevs/examples/async_rt/basic.py | 44 ++++++--------- xdevs/rt_sim/__init__.py | 2 + xdevs/rt_sim/rt_coord.py | 44 +++++++-------- xdevs/rt_sim/rt_manager.py | 95 +++++++++++++++----------------- xdevs/sim.py | 71 +----------------------- xdevs/sim_rt.py | 72 ------------------------ 6 files changed, 83 insertions(+), 245 deletions(-) delete mode 100644 xdevs/sim_rt.py diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py index 4102faf..06068c2 100644 --- a/xdevs/examples/async_rt/basic.py +++ b/xdevs/examples/async_rt/basic.py @@ -1,15 +1,14 @@ import logging import queue import random -import time as rt_time +import time from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger from xdevs.models import Atomic, Coupled, Port -from xdevs.rt_sim.rt_manager import RtManager -from xdevs.rt_sim.rt_coord import CoordinatorRt +from xdevs.rt_sim import RealTimeCoordinator, RealTimeManager -logger = get_logger(__name__, logging.WARNING) +logger = get_logger(__name__, logging.INFO) PHASE_DONE = "done" @@ -147,13 +146,13 @@ def deltext(self, e): if self.phase == PHASE_ACTIVE: for job in self.i_arrived.values: - logger.info("Starting job %s @ t = %d @ t_r = %f" % (job.name, self.clock, rt_time.time())) + logger.info("Starting job %s @ t = %d @ t_r = %f" % (job.name, self.clock, time.time())) job.time = self.clock self.jobs_arrived.append(job) if self.i_solved: job = self.i_solved.get() - logger.info("Job %s finished @ t = %d @ t_r = %f" % (job.name, self.clock, rt_time.time())) + logger.info("Job %s finished @ t = %d @ t_r = %f" % (job.name, self.clock, time.time())) self.total_ta += self.clock - job.time self.jobs_solved.append(job) @@ -198,38 +197,29 @@ def inject_messages(q: queue.SimpleQueue): i = -1 while True: f = round(random.gauss(3, 0.6), 2) - rt_time.sleep(f * time_scale) # duermo f segundos + time.sleep(f) # duermo f segundos # la cola espera tuplas (port_name, msg) q.put(("i_extern", Job(i))) i -= 1 if __name__ == '__main__': - execution_time = 30 + execution_time = 60 time_scale = 1 - max_delay = 0.02 + max_jitter = 0.2 gpt = RTGpt("gpt", 2, 3600) - manager = RtManager(time_scale=time_scale, max_delay=max_delay) + manager = RealTimeManager(max_jitter=max_jitter, time_scale=time_scale) manager.add_event_handler(inject_messages) - c = CoordinatorRt(gpt, manager) - c.initialize() - t_ini = rt_time.time() + c = RealTimeCoordinator(gpt, manager) + #c.initialize() # ahora lo hago como parte de simulate + t_ini = time.time() print(f' >>> COMENZAMOS : {t_ini}') c.simulate(time_interv=execution_time) - print(f' >>> FIN : {rt_time.time()}') - print(f' Tiempo ejecutado = {(rt_time.time() - t_ini)}') - print(f' error execution_time = ' - f'{((rt_time.time() - t_ini - (execution_time * time_scale)) / (execution_time * time_scale)) * 100}') - -""" - coord = Coordinator(gpt) - coord.initialize() - t_ini = rt_time.time() - print(f' >>> COMENZAMOS : {t_ini}') - coord.simulate_rt(time_interv=20, event_handler=inject_messages) - print(f' >>> FIN : {rt_time.time()}') - print(f' Tiempo ejecutado = {rt_time.time()-t_ini}') -""" + print(f' >>> FIN : {time.time()}') + print(f' Tiempo a ejecutar (s) = {execution_time * time_scale}') + print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') + print(f' Error (%) = ' + f'{((time.time() - t_ini - (execution_time * time_scale)) / (execution_time * time_scale)) * 100}') diff --git a/xdevs/rt_sim/__init__.py b/xdevs/rt_sim/__init__.py index e69de29..4e4f02e 100644 --- a/xdevs/rt_sim/__init__.py +++ b/xdevs/rt_sim/__init__.py @@ -0,0 +1,2 @@ +from .rt_coord import RealTimeCoordinator +from .rt_manager import RealTimeManager diff --git a/xdevs/rt_sim/rt_coord.py b/xdevs/rt_sim/rt_coord.py index 5185565..9d24164 100644 --- a/xdevs/rt_sim/rt_coord.py +++ b/xdevs/rt_sim/rt_coord.py @@ -1,48 +1,44 @@ from xdevs.models import Coupled -from xdevs.rt_sim.rt_manager import RtManager +from xdevs.rt_sim.rt_manager import RealTimeManager from xdevs.sim import Coordinator -class CoordinatorRt(Coordinator): - - def __init__(self, model: Coupled, manager: RtManager): +class RealTimeCoordinator(Coordinator): + def __init__(self, model: Coupled, manager: RealTimeManager): super().__init__(model) - - self.manager = manager - self.time_scale = manager.time_scale - self.max_delay = manager.max_delay + self.manager: RealTimeManager = manager def initialize(self): super().initialize() self.manager.initialize(self.clock.time) - def simulate(self, time_interv: float = 10000): - self.clock.time = 0 + def exit(self): + self.manager.exit(self.clock.time) + super().exit() + def simulate(self, time_interv: float = 10000): + self.initialize() while self.clock.time < time_interv: - if self.time_next == float("inf"): # comprobacion del evento_handler - print("infinity reached") + if self.time_next == float("inf") and not self.manager.event_handlers: # solo paramos si no hay handlers! + print('infinity reached') break - t_sleep = self.time_next - self.clock.time - slept, msgs = self.manager.sleep(self.time_next) - for m in msgs: - port = self.model.get_in_port(m[0]) + t, msgs = self.manager.sleep(self.time_next) + # INJECT EXTERNAL EVENTS + for port_id, msg in msgs: + port = self.model.get_in_port(port_id) if port is not None: try: - port.add(m[1]) + port.add(msg) except TypeError as e: - print(f'{e}') + print(f'invalid message type: {e}') else: - print(f'{m[0]} does not exit') - slept = min(t_sleep, slept) - + print(f'input port {port_id} does not exit') # UPDATE SIMULATION CLOCK - self.clock.time = slept + self.clock.time = t # EXECUTE NEXT CYCLE if self.clock.time == self.time_next: # now lambdas are optional self.lambdaf() self.deltfcn() self._execute_transducers() self.clear() - - self.manager.exit(self.clock.time) + self.exit() diff --git a/xdevs/rt_sim/rt_manager.py b/xdevs/rt_sim/rt_manager.py index 057b171..28c8147 100644 --- a/xdevs/rt_sim/rt_manager.py +++ b/xdevs/rt_sim/rt_manager.py @@ -1,32 +1,50 @@ +from __future__ import annotations + import queue import threading +import time from typing import Callable, Any -import time as rt_time -class RtManager: - def __init__(self, **kwargs): - self.time_scale: float = kwargs.get('time_scale') - if self.time_scale <= 0: - raise Exception('Negative or zero time_scale is not valid.') - self.max_delay: float = kwargs.get('max_delay') - if self.max_delay < 0: - raise Exception('Negative delay is not valid.') +class RealTimeManager: + def __init__(self, max_jitter: float = None, time_scale: float = 1): + """ + TODO documentation + :param max_jitter: + :param time_scale: + """ + if max_jitter is not None and max_jitter < 0: + raise ValueError('negative maximum jitter is not valid.') + self.max_jitter: float | None = max_jitter + if time_scale <= 0: + raise ValueError('negative or zero time_scale is not valid.') + self.time_scale: float = time_scale - self.baseline_r_time = 0 - self.error = 0 - self.last_r_time = 0 + self.initial_r_time: float = 0 + self.last_r_time: float = 0 self.last_v_time: float = 0 self.event_handlers = list() self.threads = list() - # self.event_handler: Callable[[queue.SimpleQueue], None] = kwargs.get('event_handler') self.queue = queue.SimpleQueue() - # todo hacer clase event_handler + # TODO hacer clase event_handler def add_event_handler(self, handler: Callable[[queue.SimpleQueue], None]): self.event_handlers.append(handler) + def initialize(self, initial_t: float): + for handler in self.event_handlers: + t = threading.Thread(daemon=True, target=handler, args=[self.queue]) + t.start() + self.threads.append(t) + self.last_v_time = initial_t + self.initial_r_time = time.time() + self.last_r_time = self.initial_r_time + + def exit(self, final_t: float): + self.last_v_time = final_t + # TODO llamar al método exit de los handlers + def sleep(self, next_v_time: float) -> tuple[float, list[tuple[Any, Any]]]: """ Simulates the time of the simulation of the DEVS model. @@ -35,45 +53,18 @@ def sleep(self, next_v_time: float) -> tuple[float, list[tuple[Any, Any]]]: It returns a [], that contains the port_name and the msg. """ next_r_time = self.last_r_time + (next_v_time - self.last_v_time) * self.time_scale - + events: list[tuple[str, Any]] = list() try: - if next_r_time - rt_time.time() > 0: - port_name, msg = self.queue.get(timeout=next_r_time - rt_time.time()) - r_time = min(next_r_time, rt_time.time()) - v_time = (r_time - self.baseline_r_time) / self.time_scale - self.last_v_time = v_time - self.last_r_time = r_time - return v_time, [(port_name, msg)] - print(f'TIMEOUT ERROR, resta-> {next_r_time - rt_time.time()}') - print('-> r_time', next_r_time) - print('-> .time', rt_time.time()) - return next_v_time, [] # en vez hacer, raise exception por .time > next_r_time ? - + events.append(self.queue.get(timeout=max(next_r_time - time.time(), 0))) # first event has timeout + #while not self.queue.empty(): # if we reach this point, we make sure that we leave the queue empty + # events.append(self.queue.get()) + r_time = min(next_r_time, time.time()) + v_time = (r_time - self.initial_r_time) / self.time_scale + self.last_v_time = v_time + self.last_r_time = r_time except queue.Empty: self.last_v_time = next_v_time self.last_r_time = next_r_time - - if rt_time.time() - self.baseline_r_time - (next_v_time * self.time_scale) > ( - self.max_delay * self.time_scale): - raise Exception('Too much delay.') - - return next_v_time, [] - - def initialize(self, initial_t: float): - for handler in self.event_handlers: - t = threading.Thread(daemon=True, target=handler, args=[self.queue]) - t.start() - self.threads.append(t) - self.last_v_time = initial_t - self.baseline_r_time = rt_time.time() - self.last_r_time = self.baseline_r_time - - def exit(self, final_t: float): - self.last_v_time = final_t - """ - print(f' ¿tiempo ejecutado? = {rt_time.time() - self.baseline_r_time}') - print(f' final_t? = {final_t * self.time_scale}') - print(f' max_delay = {self.max_delay*self.time_scale}') - """ - if rt_time.time() - self.baseline_r_time - (final_t * self.time_scale) > (self.max_delay * self.time_scale): - raise Exception('Too much delay.') + if self.max_jitter is not None and abs(time.time() - self.last_r_time) > self.max_jitter: + raise RuntimeError('maximum jitter exceeded.') + return self.last_v_time, events diff --git a/xdevs/sim.py b/xdevs/sim.py index b9d67fa..b236d64 100644 --- a/xdevs/sim.py +++ b/xdevs/sim.py @@ -4,14 +4,11 @@ import itertools import pickle import logging -import threading -import queue -import time as rt_time from abc import ABC, abstractmethod from collections import defaultdict from concurrent import futures -from typing import Callable, Generator +from typing import Generator from xmlrpc.server import SimpleXMLRPCServer from xdevs import INFINITY @@ -347,72 +344,6 @@ def simulate_time(self, time_interv: float = 10000): self.clear() self.clock.time = self.time_next - def simulate_rt(self, time_interv: float = 10000, max_delay: float = 0.01, - time_scale: float = 1, event_handler: Callable[[queue.SimpleQueue], None] = None): - """ - Simulates the behavior of a DEVS model in real time over a specified time interval. - - :param time_interv: The time interval to simulate, in seconds. Default is 10000. - :param max_delay : Maximum time the system can be delayed. Default is 10 ms - :param time_scale: Scale for increasing or decreasing the simulated time. Default is 1 s (i.e. no scale) - :param event_handler: external event handler function. - If set, a thread will execute the function to inject external messages - """ - self.clock.time = 0 - tf = self.clock.time + time_interv - q = queue.SimpleQueue() - if event_handler is not None: - t = threading.Thread(target=event_handler, daemon=True, args=[q]) - t.start() - - t_before = rt_time.time() # real time before executing lambdas and deltas - t_after = t_before # real time after executing lambdas and deltas - total_delayed_t = 0.0 # delay compensation buffer - while self.clock.time < tf: - if event_handler is None and self.time_next == float("inf"): - print("infinity reached and no event handler configured") - break - # FIRST WE COMPUTE SLEEP TIME - v_sleep = (self.time_next - self.clock.time) # virtual sleep time - r_sleep = v_sleep * time_scale - (t_after - t_before) - total_delayed_t # real sleep time - # THEN WE CHECK THAT DELAY IS NOT TOO BAD - if r_sleep < 0: - total_delayed_t = -r_sleep - r_sleep = 0 - if total_delayed_t > max_delay: # too much delay -> stop execution - raise RuntimeError('ERROR: to much delayed time ') - else: # Sleep is positive. time elapsed and delayed_time are small. Everything went well - total_delayed_t = 0 - # TIME TO SLEEP/WAIT FOR EXTERNAL MESSAGES - try: - port_name, msg = q.get(timeout=r_sleep) # get message with timeout - t_before = rt_time.time() - print('####') - print(port_name) - print(self.model.get_in_port(port_name)) - print(msg) - self.model.get_in_port(port_name).add(msg) - # re-compute virtual sleep time - print(f' <<< v_sleep = {v_sleep}') - print(f' <<< (t_b - t_a) / t_scale = {(t_before - t_after) / time_scale}') - v_sleep = min(v_sleep, (t_before - t_after) / time_scale) - - except queue.Empty: - t_before = rt_time.time() - # UPDATE SIMULATION CLOCK AND STORE TIME BEFORE NEXT CYCLE - print(f' <<< clock.time = {self.clock.time}') - self.clock.time += v_sleep - print(f' <<< clock.time = {self.clock.time}') - # EXECUTE NEXT CYCLE - if self.clock.time == self.time_next: # now lambdas are optional - self.lambdaf() - self.deltfcn() - self._execute_transducers() - self.clear() - # STORE TIME AFTER THE CYCLE - t_after = rt_time.time() # time after executing lambdas and deltas - print("done") - def simulate_inf(self): while True: self.lambdaf() diff --git a/xdevs/sim_rt.py b/xdevs/sim_rt.py deleted file mode 100644 index 18e47af..0000000 --- a/xdevs/sim_rt.py +++ /dev/null @@ -1,72 +0,0 @@ -from __future__ import annotations - -import _thread -import itertools -import pickle -import logging -import threading -import queue -import time as rt_time - -from abc import ABC, abstractmethod -from collections import defaultdict -from concurrent import futures -from typing import Callable, Generator -from xmlrpc.server import SimpleXMLRPCServer - -from xdevs import INFINITY -from xdevs.models import Atomic, Coupled, Component, Port, T -from xdevs.sim import AbstractSimulator, Coordinator -from xdevs.transducers import Transducer - - -class CoordinatorRt(Coordinator): - - def __init__(self, model: Coupled, time_scale: float, max_delay: float): - super().__init__(model) - - self.time_scale = time_scale - self.max_delay = max_delay - - def simulate(self, time_interv : float = 10000): - """ - Simulates the behavior of a DEVS model in real time over a specified time interval. - - :param time_interv: The time interval to simulate, in seconds. Default is 10000. - """ - self.clock.time = 0 - tf = self.clock.time + time_interv - - t_before = rt_time.time() # real time before executing lambdas and deltas - t_after = t_before # real time after executing lambdas and deltas - total_delayed_t = 0.0 # delay compensation buffer - - while self.clock.time < tf: - if self.time_next == float("inf"): - print("infinity reached and no event handler configured") - break - # FIRST WE COMPUTE SLEEP TIME - v_sleep = (self.time_next - self.clock.time) # virtual sleep time - r_sleep = v_sleep * self.time_scale - (t_after - t_before) - total_delayed_t # real sleep time - # THEN WE CHECK THAT DELAY IS NOT TOO BAD - if r_sleep < 0: - total_delayed_t = -r_sleep - if total_delayed_t > self.max_delay: # too much delay -> stop execution - raise RuntimeError('ERROR: to much delayed time ') - else: # Sleep is positive. time elapsed and delayed_time are small. Everything went well - total_delayed_t = 0 - rt_time.sleep(r_sleep) - t_before = rt_time.time() - v_sleep = min(v_sleep, (t_before - t_after) / self.time_scale) - # TIME TO SLEEP - # UPDATE SIMULATION CLOCK AND STORE TIME BEFORE NEXT CYCLE - self.clock.time += v_sleep - # EXECUTE NEXT CYCLE - if self.clock.time == self.time_next: # now lambdas are optional - self.lambdaf() - self.deltfcn() - self._execute_transducers() - self.clear() - # STORE TIME AFTER THE CYCLE - t_after = rt_time.time() # time after executing lambdas and deltas - print("done") From 76b939633f976a7240c15691e4ffab521af02a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Mon, 6 Mar 2023 16:59:01 +0100 Subject: [PATCH 14/60] Minor changes --- xdevs/rt_sim/rt_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xdevs/rt_sim/rt_manager.py b/xdevs/rt_sim/rt_manager.py index 28c8147..5385f34 100644 --- a/xdevs/rt_sim/rt_manager.py +++ b/xdevs/rt_sim/rt_manager.py @@ -56,8 +56,8 @@ def sleep(self, next_v_time: float) -> tuple[float, list[tuple[Any, Any]]]: events: list[tuple[str, Any]] = list() try: events.append(self.queue.get(timeout=max(next_r_time - time.time(), 0))) # first event has timeout - #while not self.queue.empty(): # if we reach this point, we make sure that we leave the queue empty - # events.append(self.queue.get()) + while not self.queue.empty(): # if we reach this point, we make sure that we leave the queue empty + events.append(self.queue.get()) r_time = min(next_r_time, time.time()) v_time = (r_time - self.initial_r_time) / self.time_scale self.last_v_time = v_time From 370f300d3523f13691634724d11892b0a15c26ac Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Wed, 8 Mar 2023 00:25:21 +0100 Subject: [PATCH 15/60] Window msg update First approach to window msg update. --- xdevs/examples/async_rt/basic.py | 3 ++- xdevs/rt_sim/rt_manager.py | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py index 06068c2..ac857f9 100644 --- a/xdevs/examples/async_rt/basic.py +++ b/xdevs/examples/async_rt/basic.py @@ -197,6 +197,7 @@ def inject_messages(q: queue.SimpleQueue): i = -1 while True: f = round(random.gauss(3, 0.6), 2) + f = 3 time.sleep(f) # duermo f segundos # la cola espera tuplas (port_name, msg) q.put(("i_extern", Job(i))) @@ -204,7 +205,7 @@ def inject_messages(q: queue.SimpleQueue): if __name__ == '__main__': - execution_time = 60 + execution_time = 30 time_scale = 1 max_jitter = 0.2 diff --git a/xdevs/rt_sim/rt_manager.py b/xdevs/rt_sim/rt_manager.py index 5385f34..621e9ef 100644 --- a/xdevs/rt_sim/rt_manager.py +++ b/xdevs/rt_sim/rt_manager.py @@ -12,6 +12,7 @@ def __init__(self, max_jitter: float = None, time_scale: float = 1): TODO documentation :param max_jitter: :param time_scale: + :param msg_window: """ if max_jitter is not None and max_jitter < 0: raise ValueError('negative maximum jitter is not valid.') @@ -19,6 +20,9 @@ def __init__(self, max_jitter: float = None, time_scale: float = 1): if time_scale <= 0: raise ValueError('negative or zero time_scale is not valid.') self.time_scale: float = time_scale + #if msg_window is not None and msg_window < 0: + # raise ValueError('negative msg window is not valid.') + self.msg_window: float = 0.3 #msg_window self.initial_r_time: float = 0 self.last_r_time: float = 0 @@ -56,8 +60,19 @@ def sleep(self, next_v_time: float) -> tuple[float, list[tuple[Any, Any]]]: events: list[tuple[str, Any]] = list() try: events.append(self.queue.get(timeout=max(next_r_time - time.time(), 0))) # first event has timeout - while not self.queue.empty(): # if we reach this point, we make sure that we leave the queue empty - events.append(self.queue.get()) + #while not self.queue.empty(): # if we reach this point, we make sure that we leave the queue empty + # events.append(self.queue.get()) + + window = min(self.msg_window, max(next_r_time - time.time(), 0)) + while window > 0: + t_ini = time.time() + try: + events.append(self.queue.get(timeout=window)) + window = max(0, min(window - (time.time() - t_ini), next_r_time - time.time())) + except queue.Empty: + # A msg was not found during window time -> window = 0 + window = 0 + r_time = min(next_r_time, time.time()) v_time = (r_time - self.initial_r_time) / self.time_scale self.last_v_time = v_time From fc5c25276f5611e1b1981eccbe07bb79dc151b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Wed, 8 Mar 2023 09:17:00 +0100 Subject: [PATCH 16/60] Review --- xdevs/rt_sim/rt_manager.py | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/xdevs/rt_sim/rt_manager.py b/xdevs/rt_sim/rt_manager.py index 621e9ef..d8a34f5 100644 --- a/xdevs/rt_sim/rt_manager.py +++ b/xdevs/rt_sim/rt_manager.py @@ -7,22 +7,22 @@ class RealTimeManager: - def __init__(self, max_jitter: float = None, time_scale: float = 1): + def __init__(self, max_jitter: float = None, time_scale: float = 1, event_window: float = 0): """ TODO documentation :param max_jitter: :param time_scale: - :param msg_window: + :param event_window: """ if max_jitter is not None and max_jitter < 0: - raise ValueError('negative maximum jitter is not valid.') + raise ValueError('negative max_jitter is not valid.') self.max_jitter: float | None = max_jitter if time_scale <= 0: raise ValueError('negative or zero time_scale is not valid.') self.time_scale: float = time_scale - #if msg_window is not None and msg_window < 0: - # raise ValueError('negative msg window is not valid.') - self.msg_window: float = 0.3 #msg_window + if event_window < 0: + raise ValueError('negative event_window is not valid.') + self.event_window: float = event_window self.initial_r_time: float = 0 self.last_r_time: float = 0 @@ -32,7 +32,7 @@ def __init__(self, max_jitter: float = None, time_scale: float = 1): self.threads = list() self.queue = queue.SimpleQueue() - # TODO hacer clase event_handler + # TODO hacer clases InputHandler y OutputHandler def add_event_handler(self, handler: Callable[[queue.SimpleQueue], None]): self.event_handlers.append(handler) @@ -54,32 +54,30 @@ def sleep(self, next_v_time: float) -> tuple[float, list[tuple[Any, Any]]]: Simulates the time of the simulation of the DEVS model. time will be the instant the manager must sleep at most. It returns a float that will be the time until it actually slept. - It returns a [], that contains the port_name and the msg. + It returns a list that contains tuples with destination port name and the received msg. """ next_r_time = self.last_r_time + (next_v_time - self.last_v_time) * self.time_scale events: list[tuple[str, Any]] = list() try: - events.append(self.queue.get(timeout=max(next_r_time - time.time(), 0))) # first event has timeout - #while not self.queue.empty(): # if we reach this point, we make sure that we leave the queue empty - # events.append(self.queue.get()) - - window = min(self.msg_window, max(next_r_time - time.time(), 0)) - while window > 0: - t_ini = time.time() + # First, we wait for a single message + events.append(self.queue.get(timeout=max(next_r_time - time.time(), 0))) + # Only if we receive one message will we wait for an additional event time window + t_window = min(time.time() + self.event_window, next_r_time) + while True: try: - events.append(self.queue.get(timeout=window)) - window = max(0, min(window - (time.time() - t_ini), next_r_time - time.time())) + events.append(self.queue.get(timeout=max(t_window - time.time(), 0))) except queue.Empty: - # A msg was not found during window time -> window = 0 - window = 0 - + break # event window timeout, we are done with messages + # Finally, we compute the current time. Must be between last_r_time and next_r_time r_time = min(next_r_time, time.time()) v_time = (r_time - self.initial_r_time) / self.time_scale self.last_v_time = v_time self.last_r_time = r_time except queue.Empty: + # we did not receive any message, just update the time self.last_v_time = next_v_time self.last_r_time = next_r_time + # If needed, we check that the jitter is not too big if self.max_jitter is not None and abs(time.time() - self.last_r_time) > self.max_jitter: raise RuntimeError('maximum jitter exceeded.') return self.last_v_time, events From f61be28ca71688225ac0770ef8bf869066afe52e Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Fri, 10 Mar 2023 17:52:51 +0100 Subject: [PATCH 17/60] InputHandler (wip) --- setup.py | 7 ++++ xdevs/examples/async_rt/basic.py | 7 +++- xdevs/plugins/input_handlers/__init__.py | 0 .../input_handlers/callable_function.py | 20 +++++++++ xdevs/rt_sim/input_handler.py | 42 +++++++++++++++++++ xdevs/rt_sim/rt_coord.py | 2 +- xdevs/rt_sim/rt_manager.py | 35 ++++++++++------ 7 files changed, 98 insertions(+), 15 deletions(-) create mode 100644 xdevs/plugins/input_handlers/__init__.py create mode 100644 xdevs/plugins/input_handlers/callable_function.py create mode 100644 xdevs/rt_sim/input_handler.py diff --git a/setup.py b/setup.py index e83e07a..ee17bcc 100644 --- a/setup.py +++ b/setup.py @@ -13,6 +13,13 @@ 'sql = xdevs.plugins.transducers.sql_transducer:SQLTransducer', 'elasticsearch = xdevs.plugins.transducers.elasticsearch_transducer:ElasticsearchTransducer', ], + + 'xdevs.plugins.input_handlers': [ + + 'function = xdevs.plugins.input_handlers.callable_function:CallableFunction', + + ], + 'xdevs.plugins.wrappers': [ 'pypdevs = xdevs.plugins.wrappers.pypdevs:PyPDEVSWrapper' ]}, diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py index ac857f9..30ecac6 100644 --- a/xdevs/examples/async_rt/basic.py +++ b/xdevs/examples/async_rt/basic.py @@ -202,6 +202,9 @@ def inject_messages(q: queue.SimpleQueue): # la cola espera tuplas (port_name, msg) q.put(("i_extern", Job(i))) i -= 1 + time.sleep(0.3) + q.put(("i_extern", Job(i))) + i -= 1 if __name__ == '__main__': @@ -211,8 +214,8 @@ def inject_messages(q: queue.SimpleQueue): gpt = RTGpt("gpt", 2, 3600) - manager = RealTimeManager(max_jitter=max_jitter, time_scale=time_scale) - manager.add_event_handler(inject_messages) + manager = RealTimeManager(max_jitter=max_jitter, time_scale=time_scale, event_window=0.5) + manager.add_input_handler('function', function=inject_messages) c = RealTimeCoordinator(gpt, manager) #c.initialize() # ahora lo hago como parte de simulate diff --git a/xdevs/plugins/input_handlers/__init__.py b/xdevs/plugins/input_handlers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xdevs/plugins/input_handlers/callable_function.py b/xdevs/plugins/input_handlers/callable_function.py new file mode 100644 index 0000000..ff0c927 --- /dev/null +++ b/xdevs/plugins/input_handlers/callable_function.py @@ -0,0 +1,20 @@ +from xdevs.rt_sim.input_handler import InputHandler + +class CallableFunction(InputHandler): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self.function = kwargs.get('function') + if self.function is None: + raise ValueError('function is mandatory') + self.args = kwargs.get('f_args', list()) + self.kwargs = kwargs.get('f_kwargs', dict()) + + def initialize(self): + pass + + def exit(self): + pass + + def run(self): + self.function(self.queue, *self.args, **self.kwargs) diff --git a/xdevs/rt_sim/input_handler.py b/xdevs/rt_sim/input_handler.py new file mode 100644 index 0000000..39cb00d --- /dev/null +++ b/xdevs/rt_sim/input_handler.py @@ -0,0 +1,42 @@ +from abc import ABC, abstractmethod +from typing import ClassVar, Type + +import pkg_resources + + +class InputHandler(ABC): + def __init__(self, **kwargs): + self.queue = kwargs.get('queue') + if self.queue is None: + raise ValueError('queue is mandatory') + + @abstractmethod + def initialize(self): + pass + + @abstractmethod + def exit(self): + pass + + @abstractmethod + def run(self): + "Execution of the InputHandler" + pass + + +class InputHandlers: + _plugins: ClassVar[dict[str, Type[InputHandler]]] = { + ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs.plugins.input_handlers') + } + + @staticmethod + def add_plugin(name: str, plugin: Type[InputHandler]): + if name in InputHandlers._plugins: + raise ValueError('xDEVS input_handler plugin with name "{}" already exists'.format(name)) + InputHandlers._plugins[name] = plugin + + @staticmethod + def create_input_handler(name: str, **kwargs) -> InputHandler: + if name not in InputHandlers._plugins: + raise ValueError('xDEVS input_handler plugin with name "{}" not found'.format(name)) + return InputHandlers._plugins[name](**kwargs) \ No newline at end of file diff --git a/xdevs/rt_sim/rt_coord.py b/xdevs/rt_sim/rt_coord.py index 9d24164..d3c900d 100644 --- a/xdevs/rt_sim/rt_coord.py +++ b/xdevs/rt_sim/rt_coord.py @@ -19,7 +19,7 @@ def exit(self): def simulate(self, time_interv: float = 10000): self.initialize() while self.clock.time < time_interv: - if self.time_next == float("inf") and not self.manager.event_handlers: # solo paramos si no hay handlers! + if self.time_next == float("inf") and not self.manager.input_handlers: # solo paramos si no hay handlers! print('infinity reached') break t, msgs = self.manager.sleep(self.time_next) diff --git a/xdevs/rt_sim/rt_manager.py b/xdevs/rt_sim/rt_manager.py index d8a34f5..a54171c 100644 --- a/xdevs/rt_sim/rt_manager.py +++ b/xdevs/rt_sim/rt_manager.py @@ -1,18 +1,29 @@ from __future__ import annotations import queue +import sys import threading import time from typing import Callable, Any +from xdevs.rt_sim.input_handler import InputHandler, InputHandlers + +def run_handler(i_handler: InputHandler): + i_handler.initialize() + try: + i_handler.run() + except Exception: + i_handler.exit() + sys.exit() class RealTimeManager: def __init__(self, max_jitter: float = None, time_scale: float = 1, event_window: float = 0): """ - TODO documentation - :param max_jitter: - :param time_scale: - :param event_window: + The RealTimeManager is responsible for collecting external events and implement real time in the simulation. + + :param max_jitter: Maximum delay time the system can absorb. Default is None (i.e., no jitter check) + :param time_scale: Scale for increasing or decreasing the simulated time. Default is 1 s (i.e., no scale) + :param event_window: Additional time is added to check for others events. Default is 0 (i.e., no window) """ if max_jitter is not None and max_jitter < 0: raise ValueError('negative max_jitter is not valid.') @@ -28,17 +39,20 @@ def __init__(self, max_jitter: float = None, time_scale: float = 1, event_window self.last_r_time: float = 0 self.last_v_time: float = 0 - self.event_handlers = list() + self.input_handlers: list[InputHandler] = list() self.threads = list() self.queue = queue.SimpleQueue() # TODO hacer clases InputHandler y OutputHandler - def add_event_handler(self, handler: Callable[[queue.SimpleQueue], None]): - self.event_handlers.append(handler) + # handler será del tipo InputHandler + def add_input_handler(self, handler_id: str, **kwargs): + i_handler = InputHandlers.create_input_handler(handler_id, **kwargs, queue=self.queue) + + self.input_handlers.append(i_handler) def initialize(self, initial_t: float): - for handler in self.event_handlers: - t = threading.Thread(daemon=True, target=handler, args=[self.queue]) + for handler in self.input_handlers: + t = threading.Thread(daemon=True, target=run_handler, args=[handler]) t.start() self.threads.append(t) self.last_v_time = initial_t @@ -47,13 +61,10 @@ def initialize(self, initial_t: float): def exit(self, final_t: float): self.last_v_time = final_t - # TODO llamar al método exit de los handlers def sleep(self, next_v_time: float) -> tuple[float, list[tuple[Any, Any]]]: """ Simulates the time of the simulation of the DEVS model. - time will be the instant the manager must sleep at most. - It returns a float that will be the time until it actually slept. It returns a list that contains tuples with destination port name and the received msg. """ next_r_time = self.last_r_time + (next_v_time - self.last_v_time) * self.time_scale From 35080934fa3aed023b3cf929a4afe99d02f5a590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Sun, 12 Mar 2023 09:16:20 +0100 Subject: [PATCH 18/60] Review --- xdevs/rt_sim/input_handler.py | 27 ++++++++++++++++++++++--- xdevs/rt_sim/rt_coord.py | 4 ++-- xdevs/rt_sim/rt_manager.py | 37 +++++++++++++++++++++++------------ 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/xdevs/rt_sim/input_handler.py b/xdevs/rt_sim/input_handler.py index 39cb00d..df520ab 100644 --- a/xdevs/rt_sim/input_handler.py +++ b/xdevs/rt_sim/input_handler.py @@ -1,26 +1,33 @@ from abc import ABC, abstractmethod from typing import ClassVar, Type - import pkg_resources class InputHandler(ABC): def __init__(self, **kwargs): + """ + TODO documentation + + :param queue: Queue used to + :param kwargs: + """ self.queue = kwargs.get('queue') if self.queue is None: raise ValueError('queue is mandatory') @abstractmethod def initialize(self): + """Performs any task before calling the run method. It is implementation-specific""" pass @abstractmethod def exit(self): + """Performs any task after the run method. It is implementation-specific""" pass @abstractmethod def run(self): - "Execution of the InputHandler" + """Execution of the input handler. It is implementation-specific""" pass @@ -31,12 +38,26 @@ class InputHandlers: @staticmethod def add_plugin(name: str, plugin: Type[InputHandler]): + """ + Registers a custom input handler to the plugin system. + + :param name: name used to identify the custom input handler. It must be unique. + :param plugin: custom input handler type. Note that it must not be an object, just the class. + """ if name in InputHandlers._plugins: raise ValueError('xDEVS input_handler plugin with name "{}" already exists'.format(name)) InputHandlers._plugins[name] = plugin @staticmethod def create_input_handler(name: str, **kwargs) -> InputHandler: + """ + Creates a new input handler. Note that this is done by the real-time manager. + Users do not directly create input handlers using this method. + + :param name: unique ID of the input handler to be created. + :param kwargs: any additional configuration parameter needed for creating the input handler. + :return: an instance of the InputHandler class. + """ if name not in InputHandlers._plugins: raise ValueError('xDEVS input_handler plugin with name "{}" not found'.format(name)) - return InputHandlers._plugins[name](**kwargs) \ No newline at end of file + return InputHandlers._plugins[name](**kwargs) diff --git a/xdevs/rt_sim/rt_coord.py b/xdevs/rt_sim/rt_coord.py index d3c900d..6a4fc72 100644 --- a/xdevs/rt_sim/rt_coord.py +++ b/xdevs/rt_sim/rt_coord.py @@ -19,7 +19,7 @@ def exit(self): def simulate(self, time_interv: float = 10000): self.initialize() while self.clock.time < time_interv: - if self.time_next == float("inf") and not self.manager.input_handlers: # solo paramos si no hay handlers! + if self.time_next == float("inf") and not self.manager.input_handlers: print('infinity reached') break t, msgs = self.manager.sleep(self.time_next) @@ -36,7 +36,7 @@ def simulate(self, time_interv: float = 10000): # UPDATE SIMULATION CLOCK self.clock.time = t # EXECUTE NEXT CYCLE - if self.clock.time == self.time_next: # now lambdas are optional + if self.clock.time == self.time_next: self.lambdaf() self.deltfcn() self._execute_transducers() diff --git a/xdevs/rt_sim/rt_manager.py b/xdevs/rt_sim/rt_manager.py index a54171c..14ebff4 100644 --- a/xdevs/rt_sim/rt_manager.py +++ b/xdevs/rt_sim/rt_manager.py @@ -1,21 +1,22 @@ from __future__ import annotations - import queue import sys import threading import time -from typing import Callable, Any - +from typing import Any from xdevs.rt_sim.input_handler import InputHandler, InputHandlers + def run_handler(i_handler: InputHandler): + """""" i_handler.initialize() try: i_handler.run() - except Exception: + except Exception: # TODO try to narrow this catch i_handler.exit() sys.exit() + class RealTimeManager: def __init__(self, max_jitter: float = None, time_scale: float = 1, event_window: float = 0): """ @@ -41,16 +42,26 @@ def __init__(self, max_jitter: float = None, time_scale: float = 1, event_window self.input_handlers: list[InputHandler] = list() self.threads = list() - self.queue = queue.SimpleQueue() + self.input_queue = queue.SimpleQueue() - # TODO hacer clases InputHandler y OutputHandler - # handler será del tipo InputHandler def add_input_handler(self, handler_id: str, **kwargs): - i_handler = InputHandlers.create_input_handler(handler_id, **kwargs, queue=self.queue) + """ + TODO documentation + :param handler_id: + :param kwargs: + :return: + """ + i_handler = InputHandlers.create_input_handler(handler_id, **kwargs, queue=self.input_queue) self.input_handlers.append(i_handler) def initialize(self, initial_t: float): + """ + TODO documentation + + :param initial_t: + :return: + """ for handler in self.input_handlers: t = threading.Thread(daemon=True, target=run_handler, args=[handler]) t.start() @@ -64,19 +75,21 @@ def exit(self, final_t: float): def sleep(self, next_v_time: float) -> tuple[float, list[tuple[Any, Any]]]: """ - Simulates the time of the simulation of the DEVS model. - It returns a list that contains tuples with destination port name and the received msg. + TODO documentation + + :param next_v_time: + :return: """ next_r_time = self.last_r_time + (next_v_time - self.last_v_time) * self.time_scale events: list[tuple[str, Any]] = list() try: # First, we wait for a single message - events.append(self.queue.get(timeout=max(next_r_time - time.time(), 0))) + events.append(self.input_queue.get(timeout=max(next_r_time - time.time(), 0))) # Only if we receive one message will we wait for an additional event time window t_window = min(time.time() + self.event_window, next_r_time) while True: try: - events.append(self.queue.get(timeout=max(t_window - time.time(), 0))) + events.append(self.input_queue.get(timeout=max(t_window - time.time(), 0))) except queue.Empty: break # event window timeout, we are done with messages # Finally, we compute the current time. Must be between last_r_time and next_r_time From 6a9c4c5497f8e60b33056d35710d8ec3b7c78b58 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Sun, 12 Mar 2023 23:01:14 +0100 Subject: [PATCH 19/60] csv_InputHandler (wip) --- setup.py | 1 + xdevs/examples/async_rt/basic.py | 18 ++++++---- .../input_handlers/csv_input_handler.py | 34 +++++++++++++++++++ xdevs/plugins/input_handlers/prueba.csv | 11 ++++++ xdevs/rt_sim/rt_coord.py | 1 + 5 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 xdevs/plugins/input_handlers/csv_input_handler.py create mode 100644 xdevs/plugins/input_handlers/prueba.csv diff --git a/setup.py b/setup.py index ee17bcc..1d05b00 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,7 @@ 'xdevs.plugins.input_handlers': [ 'function = xdevs.plugins.input_handlers.callable_function:CallableFunction', + 'csv_handler = xdevs.plugins.input_handlers.csv_input_handler:CSVInputHandler', ], diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py index 30ecac6..6f7d28a 100644 --- a/xdevs/examples/async_rt/basic.py +++ b/xdevs/examples/async_rt/basic.py @@ -7,7 +7,6 @@ from xdevs.models import Atomic, Coupled, Port from xdevs.rt_sim import RealTimeCoordinator, RealTimeManager - logger = get_logger(__name__, logging.INFO) PHASE_DONE = "done" @@ -197,28 +196,33 @@ def inject_messages(q: queue.SimpleQueue): i = -1 while True: f = round(random.gauss(3, 0.6), 2) - f = 3 + # f = 3 time.sleep(f) # duermo f segundos # la cola espera tuplas (port_name, msg) q.put(("i_extern", Job(i))) i -= 1 - time.sleep(0.3) - q.put(("i_extern", Job(i))) - i -= 1 + # test ventana manager + # time.sleep(0.3) + # q.put(("i_extern", Job(i))) + # i -= 1 if __name__ == '__main__': execution_time = 30 time_scale = 1 max_jitter = 0.2 + event_window = 0.5 gpt = RTGpt("gpt", 2, 3600) - manager = RealTimeManager(max_jitter=max_jitter, time_scale=time_scale, event_window=0.5) + manager = RealTimeManager(max_jitter=max_jitter, time_scale=time_scale, event_window=event_window) + + manager.add_input_handler('csv_handler', file="../../plugins/input_handlers/prueba.csv") + manager.add_input_handler('function', function=inject_messages) c = RealTimeCoordinator(gpt, manager) - #c.initialize() # ahora lo hago como parte de simulate + # c.initialize() # ahora lo hago como parte de simulate t_ini = time.time() print(f' >>> COMENZAMOS : {t_ini}') c.simulate(time_interv=execution_time) diff --git a/xdevs/plugins/input_handlers/csv_input_handler.py b/xdevs/plugins/input_handlers/csv_input_handler.py new file mode 100644 index 0000000..07f2c48 --- /dev/null +++ b/xdevs/plugins/input_handlers/csv_input_handler.py @@ -0,0 +1,34 @@ +import csv +import time + +from xdevs.rt_sim.input_handler import InputHandler + + +class CSVInputHandler(InputHandler): + def __init__(self, file, **kwargs): + super().__init__(**kwargs) + self.file = file + if self.file is None: + raise ValueError('file is mandatory') + self.data = list() + self.reader = None + self.csv_file = None + + def initialize(self): + self.csv_file = open(self.file, 'r') + self.reader = csv.DictReader(self.csv_file) + i = 0 + for row in self.reader: + self.data.append(list()) + for field in self.reader.fieldnames: + self.data[i].append(row[field]) + i = i + 1 + + def exit(self): + self.csv_file.close() + + def run(self): + + for row in self.data: + time.sleep(int(row[0])) + self.queue.put((row[1], row[2])) diff --git a/xdevs/plugins/input_handlers/prueba.csv b/xdevs/plugins/input_handlers/prueba.csv new file mode 100644 index 0000000..af921f4 --- /dev/null +++ b/xdevs/plugins/input_handlers/prueba.csv @@ -0,0 +1,11 @@ +t_wait,port,job_id +3,i_extern,-1 +6,i_extern,-2 +9,i_extern,-3 +12,i_extern,-4 +15,i_extern,-5 +18,i_extern,-6 +21,i_extern,-7 +24,i_extern,-8 +27,i_extern,-9 + diff --git a/xdevs/rt_sim/rt_coord.py b/xdevs/rt_sim/rt_coord.py index 6a4fc72..60abffb 100644 --- a/xdevs/rt_sim/rt_coord.py +++ b/xdevs/rt_sim/rt_coord.py @@ -23,6 +23,7 @@ def simulate(self, time_interv: float = 10000): print('infinity reached') break t, msgs = self.manager.sleep(self.time_next) + print(f'¿msgs? = {msgs}') # INJECT EXTERNAL EVENTS for port_id, msg in msgs: port = self.model.get_in_port(port_id) From 156a834bcf19c73f089d4f05f220efa57ac1034f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Mon, 13 Mar 2023 11:44:21 +0100 Subject: [PATCH 20/60] CSV handler review --- xdevs/examples/async_rt/basic.py | 6 ++- .../async_rt}/prueba.csv | 1 - .../input_handlers/callable_function.py | 8 +--- .../input_handlers/csv_input_handler.py | 48 ++++++++++--------- xdevs/rt_sim/input_handler.py | 6 +-- 5 files changed, 32 insertions(+), 37 deletions(-) rename xdevs/{plugins/input_handlers => examples/async_rt}/prueba.csv (99%) diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py index 6f7d28a..dd57e78 100644 --- a/xdevs/examples/async_rt/basic.py +++ b/xdevs/examples/async_rt/basic.py @@ -217,12 +217,14 @@ def inject_messages(q: queue.SimpleQueue): manager = RealTimeManager(max_jitter=max_jitter, time_scale=time_scale, event_window=event_window) - manager.add_input_handler('csv_handler', file="../../plugins/input_handlers/prueba.csv") + parsers = { + 'i_extern': lambda x: Job(int(x)) # le digo al input handler como convertir el string a Job con una función + } + manager.add_input_handler('csv_handler', file="prueba.csv", parsers=parsers) manager.add_input_handler('function', function=inject_messages) c = RealTimeCoordinator(gpt, manager) - # c.initialize() # ahora lo hago como parte de simulate t_ini = time.time() print(f' >>> COMENZAMOS : {t_ini}') c.simulate(time_interv=execution_time) diff --git a/xdevs/plugins/input_handlers/prueba.csv b/xdevs/examples/async_rt/prueba.csv similarity index 99% rename from xdevs/plugins/input_handlers/prueba.csv rename to xdevs/examples/async_rt/prueba.csv index af921f4..f8b7784 100644 --- a/xdevs/plugins/input_handlers/prueba.csv +++ b/xdevs/examples/async_rt/prueba.csv @@ -8,4 +8,3 @@ t_wait,port,job_id 21,i_extern,-7 24,i_extern,-8 27,i_extern,-9 - diff --git a/xdevs/plugins/input_handlers/callable_function.py b/xdevs/plugins/input_handlers/callable_function.py index ff0c927..ac8d671 100644 --- a/xdevs/plugins/input_handlers/callable_function.py +++ b/xdevs/plugins/input_handlers/callable_function.py @@ -1,20 +1,14 @@ from xdevs.rt_sim.input_handler import InputHandler + class CallableFunction(InputHandler): def __init__(self, **kwargs): super().__init__(**kwargs) - self.function = kwargs.get('function') if self.function is None: raise ValueError('function is mandatory') self.args = kwargs.get('f_args', list()) self.kwargs = kwargs.get('f_kwargs', dict()) - def initialize(self): - pass - - def exit(self): - pass - def run(self): self.function(self.queue, *self.args, **self.kwargs) diff --git a/xdevs/plugins/input_handlers/csv_input_handler.py b/xdevs/plugins/input_handlers/csv_input_handler.py index 07f2c48..7ab074e 100644 --- a/xdevs/plugins/input_handlers/csv_input_handler.py +++ b/xdevs/plugins/input_handlers/csv_input_handler.py @@ -1,34 +1,36 @@ import csv import time - +from typing import Callable, Any from xdevs.rt_sim.input_handler import InputHandler class CSVInputHandler(InputHandler): - def __init__(self, file, **kwargs): + def __init__(self, **kwargs): + """ + TODO documentation + + :param str file: CSV file path. + :param str delimiter: column delimiter in CSV file. By default, it is set to ','. + :param dict[str, Callable[[str], Any]] parsers: message parsers. Keys are port names, and values are functions + that take an string and returns an object of the corresponding port type. If a parser is not defined, this + 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. + :param kwargs: see the InputHandler base class for more details. + """ super().__init__(**kwargs) - self.file = file + self.file: str = kwargs.get('file') if self.file is None: raise ValueError('file is mandatory') - self.data = list() - self.reader = None - self.csv_file = None - - def initialize(self): - self.csv_file = open(self.file, 'r') - self.reader = csv.DictReader(self.csv_file) - i = 0 - for row in self.reader: - self.data.append(list()) - for field in self.reader.fieldnames: - self.data[i].append(row[field]) - i = i + 1 - - def exit(self): - self.csv_file.close() + self.delimiter: str = kwargs.get('delimiter', ',') + self.parsers: dict[str, Callable[[str], Any]] = kwargs.get('parsers', dict()) def run(self): - - for row in self.data: - time.sleep(int(row[0])) - self.queue.put((row[1], row[2])) + with open(self.file, newline='') as csv_file: + csv_reader = csv.reader(csv_file, delimiter=self.delimiter) + for t, port, msg in csv_reader: + try: + time.sleep(float(t)) + except ValueError: # unable to cast to float -> probably header, ignore it + continue + # if parser is not defined, we forward the message as is (i.e., in string format) + self.queue.put((port, self.parsers.get(port, lambda x: x))) diff --git a/xdevs/rt_sim/input_handler.py b/xdevs/rt_sim/input_handler.py index df520ab..848ae4b 100644 --- a/xdevs/rt_sim/input_handler.py +++ b/xdevs/rt_sim/input_handler.py @@ -15,14 +15,12 @@ def __init__(self, **kwargs): if self.queue is None: raise ValueError('queue is mandatory') - @abstractmethod def initialize(self): - """Performs any task before calling the run method. It is implementation-specific""" + """Performs any task before calling the run method. It is implementation-specific. By default, it is empty.""" pass - @abstractmethod def exit(self): - """Performs any task after the run method. It is implementation-specific""" + """Performs any task after the run method. It is implementation-specific. By default, it is empty.""" pass @abstractmethod From 9d37a5bd8441e1c68f1fd05e0ec89a72d097d554 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Tue, 14 Mar 2023 00:25:12 +0100 Subject: [PATCH 21/60] CSV documentation & format file check --- xdevs/examples/async_rt/basic.py | 2 +- xdevs/examples/async_rt/prueba.csv | 20 ++++++------- .../input_handlers/csv_input_handler.py | 30 +++++++++++++++---- xdevs/rt_sim/rt_coord.py | 1 - 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py index dd57e78..fde12eb 100644 --- a/xdevs/examples/async_rt/basic.py +++ b/xdevs/examples/async_rt/basic.py @@ -218,7 +218,7 @@ def inject_messages(q: queue.SimpleQueue): manager = RealTimeManager(max_jitter=max_jitter, time_scale=time_scale, event_window=event_window) parsers = { - 'i_extern': lambda x: Job(int(x)) # le digo al input handler como convertir el string a Job con una función + 'i_extern': lambda x: Job(x) # le digo al input handler como convertir el string a Job con una función } manager.add_input_handler('csv_handler', file="prueba.csv", parsers=parsers) diff --git a/xdevs/examples/async_rt/prueba.csv b/xdevs/examples/async_rt/prueba.csv index f8b7784..3e56b74 100644 --- a/xdevs/examples/async_rt/prueba.csv +++ b/xdevs/examples/async_rt/prueba.csv @@ -1,10 +1,10 @@ -t_wait,port,job_id -3,i_extern,-1 -6,i_extern,-2 -9,i_extern,-3 -12,i_extern,-4 -15,i_extern,-5 -18,i_extern,-6 -21,i_extern,-7 -24,i_extern,-8 -27,i_extern,-9 +t,port,msg +0,i_extern,-1_csv +3,-2_csv +3,i_extern,-3_csv +3,i_extern, +3,i_extern,-5_csv +,i_extern,-6_csv +3,i_extern,-7_csv +3,,-8_csv +3,i_extern,-9_csv \ No newline at end of file diff --git a/xdevs/plugins/input_handlers/csv_input_handler.py b/xdevs/plugins/input_handlers/csv_input_handler.py index 7ab074e..4271c6a 100644 --- a/xdevs/plugins/input_handlers/csv_input_handler.py +++ b/xdevs/plugins/input_handlers/csv_input_handler.py @@ -7,7 +7,15 @@ class CSVInputHandler(InputHandler): def __init__(self, **kwargs): """ - TODO documentation + CSVInputHandler reads a file and insert the messages in the corresponding port of the system. + + File must contain 3 columns: + + 1st -> t, is for the time between the messages are inserted in the system. t = 0 or '' , no time is waited. + + 2nd -> port, is for specify the port name. Port = '' ,the row will be omitted. + + 3rd -> msg, is for inserting the message which will be transmitted. :param str file: CSV file path. :param str delimiter: column delimiter in CSV file. By default, it is set to ','. @@ -27,10 +35,20 @@ def __init__(self, **kwargs): def run(self): with open(self.file, newline='') as csv_file: csv_reader = csv.reader(csv_file, delimiter=self.delimiter) - for t, port, msg in csv_reader: + for row in csv_reader: + # t = row[0], port = row[1], msg = row[2] try: - time.sleep(float(t)) - except ValueError: # unable to cast to float -> probably header, ignore it + if len(row) < 3 or row[1] == '': + # if row[2] = msg = '', '' will be inserted. No ValueError for msg = '' + raise ValueError('Format of file is wrong. ' + 'Each row must have at least 3 values: t, port, and msg.') + else: + try: + time.sleep(float(row[0])) + except ValueError: # unable to cast to float -> probably header, ignore it + continue + # if parser is not defined, we forward the message as is (i.e., in string format) + self.queue.put((row[1], row[2] if self.parsers is None else self.parsers.get(row[1])(row[2]))) + except ValueError as e: + print(f'Error in {self.file}: {e} Skipping row {row}') continue - # if parser is not defined, we forward the message as is (i.e., in string format) - self.queue.put((port, self.parsers.get(port, lambda x: x))) diff --git a/xdevs/rt_sim/rt_coord.py b/xdevs/rt_sim/rt_coord.py index 60abffb..6a4fc72 100644 --- a/xdevs/rt_sim/rt_coord.py +++ b/xdevs/rt_sim/rt_coord.py @@ -23,7 +23,6 @@ def simulate(self, time_interv: float = 10000): print('infinity reached') break t, msgs = self.manager.sleep(self.time_next) - print(f'¿msgs? = {msgs}') # INJECT EXTERNAL EVENTS for port_id, msg in msgs: port = self.model.get_in_port(port_id) From cc019f8302bdb14d03196a82dc40d94769c2af8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Tue, 14 Mar 2023 08:51:26 +0100 Subject: [PATCH 22/60] pretty-print errors --- .../input_handlers/csv_input_handler.py | 42 ++++++++++++------- xdevs/rt_sim/rt_coord.py | 5 ++- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/xdevs/plugins/input_handlers/csv_input_handler.py b/xdevs/plugins/input_handlers/csv_input_handler.py index 4271c6a..34d0311 100644 --- a/xdevs/plugins/input_handlers/csv_input_handler.py +++ b/xdevs/plugins/input_handlers/csv_input_handler.py @@ -1,4 +1,5 @@ import csv +import sys import time from typing import Callable, Any from xdevs.rt_sim.input_handler import InputHandler @@ -35,20 +36,31 @@ def __init__(self, **kwargs): def run(self): with open(self.file, newline='') as csv_file: csv_reader = csv.reader(csv_file, delimiter=self.delimiter) - for row in csv_reader: - # t = row[0], port = row[1], msg = row[2] + for i, row in enumerate(csv_reader): + # 1. unwrap row try: - if len(row) < 3 or row[1] == '': - # if row[2] = msg = '', '' will be inserted. No ValueError for msg = '' - raise ValueError('Format of file is wrong. ' - 'Each row must have at least 3 values: t, port, and msg.') - else: - try: - time.sleep(float(row[0])) - except ValueError: # unable to cast to float -> probably header, ignore it - continue - # if parser is not defined, we forward the message as is (i.e., in string format) - self.queue.put((row[1], row[2] if self.parsers is None else self.parsers.get(row[1])(row[2]))) - except ValueError as e: - print(f'Error in {self.file}: {e} Skipping row {row}') + t, port, msg, *_others = row + except ValueError: + print(f'LINE {i + 1}: invalid row ({row}). Rows must have 3 columns:' + ' t, port, and msg. Row will be ignored', file=sys.stderr) continue + # 2. sleep + try: + time.sleep(float(t)) + except ValueError: + if i != 0: # To avoid logging an error while parsing the header + print(f'LINE {i + 1}: error parsing t ("{t}"). Row will be ignored', file=sys.stderr) + continue + # 3. make sure that port is not empty + if not port: + print(f'LINE {i + 1}: port ID is empty. Row will be ignored', file=sys.stderr) + continue + # 4. parse message + try: + # if parser is not defined, we forward the message as is (i.e., in string format) + msg = self.parsers.get(port, lambda x: x)(msg) + except Exception: + print(f'LINE {i + 1}: error parsing msg ("{msg}"). Row will be ignored', file=sys.stderr) + continue + # 5. inject event to queue + self.queue.put((port, msg)) diff --git a/xdevs/rt_sim/rt_coord.py b/xdevs/rt_sim/rt_coord.py index 6a4fc72..6555733 100644 --- a/xdevs/rt_sim/rt_coord.py +++ b/xdevs/rt_sim/rt_coord.py @@ -1,3 +1,4 @@ +import sys from xdevs.models import Coupled from xdevs.rt_sim.rt_manager import RealTimeManager from xdevs.sim import Coordinator @@ -30,9 +31,9 @@ def simulate(self, time_interv: float = 10000): try: port.add(msg) except TypeError as e: - print(f'invalid message type: {e}') + print(f'invalid message type: {e}', file=sys.stderr) else: - print(f'input port {port_id} does not exit') + print(f'input port "{port_id}" does not exit', file=sys.stderr) # UPDATE SIMULATION CLOCK self.clock.time = t # EXECUTE NEXT CYCLE From 8ab2807bfa12b0989040e3e30257f066dd0dcdb6 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Mon, 10 Apr 2023 10:49:16 +0200 Subject: [PATCH 23/60] OutputHandler and some documentation OutputHandler interface + CSVOutputHandler --- setup.py | 4 + xdevs/examples/async_rt/basic.py | 37 ++++++++-- xdevs/examples/async_rt/prueba.csv | 10 +-- .../input_handlers/csv_input_handler.py | 5 +- xdevs/plugins/output_handlers/__init__.py | 0 .../output_handlers/csv_output_handler.py | 38 ++++++++++ xdevs/rt_sim/input_handler.py | 8 +- xdevs/rt_sim/output_handler.py | 61 ++++++++++++++++ xdevs/rt_sim/rt_coord.py | 16 +++- xdevs/rt_sim/rt_manager.py | 73 ++++++++++++++----- 10 files changed, 216 insertions(+), 36 deletions(-) create mode 100644 xdevs/plugins/output_handlers/__init__.py create mode 100644 xdevs/plugins/output_handlers/csv_output_handler.py create mode 100644 xdevs/rt_sim/output_handler.py diff --git a/setup.py b/setup.py index 1d05b00..6640ca9 100644 --- a/setup.py +++ b/setup.py @@ -21,6 +21,10 @@ ], + 'xdevs.plugins.output_handlers': [ + 'csv_out_handler = xdevs.plugins.output_handlers.csv_output_handler:CSVOutputHandler', + ], + 'xdevs.plugins.wrappers': [ 'pypdevs = xdevs.plugins.wrappers.pypdevs:PyPDEVSWrapper' ]}, diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py index fde12eb..ce959a1 100644 --- a/xdevs/examples/async_rt/basic.py +++ b/xdevs/examples/async_rt/basic.py @@ -17,6 +17,9 @@ def __init__(self, name): self.name = name self.time = 0 + def __str__(self): + return f'Job:: {self.name}' + class Generator(Atomic): @@ -36,6 +39,8 @@ def __init__(self, name, period): self.job_counter = 1 self.extern_jobs = list() # stores external jobs + self.generate = True + def initialize(self): self.hold_in(PHASE_ACTIVE, self.period) @@ -50,15 +55,18 @@ def deltint(self): def deltext(self, e): self.sigma -= e for msg in self.i_extern.values: - logger.info("Generator received external job. It will forward it in the next lambda") + #logger.info("Generator received external job. It will forward it in the next lambda") self.extern_jobs.append(msg) if not self.i_stop.empty(): - self.passivate() - + self.generate = False def lambdaf(self): - self.o_out.add(Job(str(self.job_counter))) + if self.generate: + job = Job(str(self.job_counter)) + self.o_out.add(job) + # logger.info("Starting job %s @ t_r = %f" % (job.name, time.time())) for msg in self.extern_jobs: # we also forward external messages self.o_out.add(msg) + # logger.info("Starting job %s @ t_r = %f" % (msg.name, time.time())) class Processor(Atomic): @@ -91,6 +99,7 @@ def deltext(self, e): def lambdaf(self): self.o_out.add(self.current_job) + # logger.info("Job %s finished @ t_r = %f" % (self.current_job.name, time.time())) class Transducer(Atomic): @@ -145,13 +154,13 @@ def deltext(self, e): if self.phase == PHASE_ACTIVE: for job in self.i_arrived.values: - logger.info("Starting job %s @ t = %d @ t_r = %f" % (job.name, self.clock, time.time())) + #logger.info("Starting job %s @ t = %d @ t_r = %f" % (job.name, self.clock, time.time())) job.time = self.clock self.jobs_arrived.append(job) if self.i_solved: job = self.i_solved.get() - logger.info("Job %s finished @ t = %d @ t_r = %f" % (job.name, self.clock, time.time())) + #logger.info("Job %s finished @ t = %d @ t_r = %f" % (job.name, self.clock, time.time())) self.total_ta += self.clock - job.time self.jobs_solved.append(job) @@ -186,6 +195,18 @@ def __init__(self, name, period, obs_time): # new coupling for forwarding messages to generator self.add_coupling(self.i_extern, gen.i_extern) + # new output port for sending solved jobs + self.o_extern_proc = Port(Job, 'o_extern_proc') + self.o_extern_gen = Port(Job, 'o_extern_gen') + self.o_extern_trans = Port(Job, 'o_extern_trans') + self.add_out_port(self.o_extern_proc) + self.add_out_port(self.o_extern_gen) + self.add_out_port(self.o_extern_trans) + # new coupling + self.add_coupling(proc.o_out, self.o_extern_proc) + self.add_coupling(gen.o_out, self.o_extern_gen) + self.add_coupling(trans.o_out, self.o_extern_trans) + self.add_coupling(gen.o_out, proc.i_in) self.add_coupling(gen.o_out, trans.i_arrived) self.add_coupling(proc.o_out, trans.i_solved) @@ -208,7 +229,7 @@ def inject_messages(q: queue.SimpleQueue): if __name__ == '__main__': - execution_time = 30 + execution_time = 31 time_scale = 1 max_jitter = 0.2 event_window = 0.5 @@ -224,6 +245,8 @@ def inject_messages(q: queue.SimpleQueue): manager.add_input_handler('function', function=inject_messages) + manager.add_output_handler('csv_out_handler', output_file='csv_output_v3.csv') + c = RealTimeCoordinator(gpt, manager) t_ini = time.time() print(f' >>> COMENZAMOS : {t_ini}') diff --git a/xdevs/examples/async_rt/prueba.csv b/xdevs/examples/async_rt/prueba.csv index 3e56b74..454c49a 100644 --- a/xdevs/examples/async_rt/prueba.csv +++ b/xdevs/examples/async_rt/prueba.csv @@ -1,10 +1,10 @@ t,port,msg -0,i_extern,-1_csv -3,-2_csv +3,i_extern,-1_csv +3,i_extern,-2_csv 3,i_extern,-3_csv -3,i_extern, +3,i_extern,-4_csv 3,i_extern,-5_csv -,i_extern,-6_csv +3,i_extern,-6_csv 3,i_extern,-7_csv -3,,-8_csv +3,i_extern,-8_csv 3,i_extern,-9_csv \ No newline at end of file diff --git a/xdevs/plugins/input_handlers/csv_input_handler.py b/xdevs/plugins/input_handlers/csv_input_handler.py index 34d0311..74a03a7 100644 --- a/xdevs/plugins/input_handlers/csv_input_handler.py +++ b/xdevs/plugins/input_handlers/csv_input_handler.py @@ -21,10 +21,9 @@ def __init__(self, **kwargs): :param str file: CSV file path. :param str delimiter: column delimiter in CSV file. By default, it is set to ','. :param dict[str, Callable[[str], Any]] parsers: message parsers. Keys are port names, and values are functions - that take an string and returns an object of the corresponding port type. If a parser is not defined, this + that take a string and returns an object of the corresponding port type. If a parser is not defined, this 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. - :param kwargs: see the InputHandler base class for more details. """ super().__init__(**kwargs) self.file: str = kwargs.get('file') @@ -63,4 +62,4 @@ def run(self): print(f'LINE {i + 1}: error parsing msg ("{msg}"). Row will be ignored', file=sys.stderr) continue # 5. inject event to queue - self.queue.put((port, msg)) + self.push_to_queue(port, msg) diff --git a/xdevs/plugins/output_handlers/__init__.py b/xdevs/plugins/output_handlers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xdevs/plugins/output_handlers/csv_output_handler.py b/xdevs/plugins/output_handlers/csv_output_handler.py new file mode 100644 index 0000000..393901c --- /dev/null +++ b/xdevs/plugins/output_handlers/csv_output_handler.py @@ -0,0 +1,38 @@ +import csv +import queue +import time +import datetime + +from xdevs.rt_sim.output_handler import OutputHandler + + +class CSVOutputHandler(OutputHandler): + def __init__(self, **kwargs): + """ + CSVOutputHandler store in a file the outgoing events in the form : Port, p_type. + If not output_file is given, a default file is created by the name csv_output_date_time.csv. + + :param output_file: path or name of the file in which the data is going to be store. + """ + super().__init__() + self.csv_queue = self.queue + + self.output_file = kwargs.get('output_file', None) + if self.output_file is None: + self.output_file = 'csv_output_'+datetime.datetime.now().strftime('%d%m%Y_%H%M%S')+'.csv' + + def run(self): + while True: + # First we check for outgoing events + try: + row = self.csv_queue.get() + except queue.Empty: + continue + # If an outgoing event occurs we append it to the last line of the file. + if row is not None: + with open(self.output_file, 'a+', newline='') as csv_file: + writer = csv.writer(csv_file) + writer.writerow(row) + + csv_file.close() + row = None diff --git a/xdevs/rt_sim/input_handler.py b/xdevs/rt_sim/input_handler.py index 848ae4b..41c9ab9 100644 --- a/xdevs/rt_sim/input_handler.py +++ b/xdevs/rt_sim/input_handler.py @@ -6,10 +6,9 @@ class InputHandler(ABC): def __init__(self, **kwargs): """ - TODO documentation + Handler interface for injecting external events to the system. - :param queue: Queue used to - :param kwargs: + :param queue: used to collect and inject all external events joining the system. """ self.queue = kwargs.get('queue') if self.queue is None: @@ -28,6 +27,9 @@ def run(self): """Execution of the input handler. It is implementation-specific""" pass + def push_to_queue(self, port, msg): + """Adding the port and message to the queue.""" + self.queue.put((port, msg)) class InputHandlers: _plugins: ClassVar[dict[str, Type[InputHandler]]] = { diff --git a/xdevs/rt_sim/output_handler.py b/xdevs/rt_sim/output_handler.py new file mode 100644 index 0000000..b57ed1d --- /dev/null +++ b/xdevs/rt_sim/output_handler.py @@ -0,0 +1,61 @@ +import queue +from abc import ABC, abstractmethod +from typing import ClassVar, Type + +import pkg_resources + + +class OutputHandler(ABC): + def __init__(self, **kwargs): + """ + Handler interface for ejecting internal events from the system. + + """ + self.queue = queue.SimpleQueue() + + 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 + + +class OutputHandlers: + _plugins: ClassVar[dict[str, Type[OutputHandler]]] = { + ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs.plugins.output_handlers') + } + + @staticmethod + def add_plugin(name: str, plugin: Type[OutputHandler]): + """ + Registers a custom output handler to the plugin system. + + :param name: name used to identify the custom input handler. It must be unique. + :param plugin: custom input handler type. Note that it must not be an object, just the class. + """ + if name in OutputHandlers._plugins: + raise ValueError('xDEVS output_handler plugin with name "{}" already exists'.format(name)) + OutputHandlers._plugins[name] = plugin + + @staticmethod + def create_output_handler(name: str, **kwargs) -> OutputHandler: + """ + + Creates a new output handler. Note that this is done by the real-time manager. + Users do not directly create output handlers using this method. + + :param name: unique ID of the output handler to be created. + :param kwargs: any additional configuration parameter needed for creating the output handler. + :return: an instance of the OutputHandler class. + """ + if name not in OutputHandlers._plugins: + raise ValueError('xDEVS output_handler plugin with name "{}" not found'.format(name)) + return OutputHandlers._plugins[name](**kwargs) + diff --git a/xdevs/rt_sim/rt_coord.py b/xdevs/rt_sim/rt_coord.py index 6555733..0f37d5c 100644 --- a/xdevs/rt_sim/rt_coord.py +++ b/xdevs/rt_sim/rt_coord.py @@ -1,4 +1,7 @@ import sys +import threading +import time + from xdevs.models import Coupled from xdevs.rt_sim.rt_manager import RealTimeManager from xdevs.sim import Coordinator @@ -23,7 +26,10 @@ def simulate(self, time_interv: float = 10000): if self.time_next == float("inf") and not self.manager.input_handlers: print('infinity reached') break - t, msgs = self.manager.sleep(self.time_next) + t, msgs = self.manager.sleep(min(time_interv, self.time_next)) + # Sleeping time must be the min value between time_next and time_interv. Time_next can not be greater + # than time_interv because if it is so, you will be sleeping over the time_interv. + # INJECT EXTERNAL EVENTS for port_id, msg in msgs: port = self.model.get_in_port(port_id) @@ -40,6 +46,14 @@ def simulate(self, time_interv: float = 10000): if self.clock.time == self.time_next: self.lambdaf() self.deltfcn() + # EJECT INTERNAL EVENTS + for port in self.model.out_ports: + try: + msg = port.get() + self.manager.inject_to_output_handler(port.name, str(msg)) + except StopIteration: + msg = None + continue self._execute_transducers() self.clear() self.exit() diff --git a/xdevs/rt_sim/rt_manager.py b/xdevs/rt_sim/rt_manager.py index 14ebff4..acd4a6f 100644 --- a/xdevs/rt_sim/rt_manager.py +++ b/xdevs/rt_sim/rt_manager.py @@ -5,10 +5,10 @@ import time from typing import Any from xdevs.rt_sim.input_handler import InputHandler, InputHandlers +from xdevs.rt_sim.output_handler import OutputHandler, OutputHandlers -def run_handler(i_handler: InputHandler): - """""" +def run_input_handler(i_handler: InputHandler): i_handler.initialize() try: i_handler.run() @@ -17,6 +17,15 @@ def run_handler(i_handler: InputHandler): sys.exit() +def run_output_handler(o_handler: OutputHandler): + o_handler.initialize() + try: + o_handler.run() + except Exception: # TODO try to narrow this catch + o_handler.exit() + sys.exit() + + class RealTimeManager: def __init__(self, max_jitter: float = None, time_scale: float = 1, event_window: float = 0): """ @@ -40,32 +49,62 @@ def __init__(self, max_jitter: float = None, time_scale: float = 1, event_window self.last_r_time: float = 0 self.last_v_time: float = 0 - self.input_handlers: list[InputHandler] = list() self.threads = list() + # Queue for processing the external events that are being injecting in the system self.input_queue = queue.SimpleQueue() + # Lists for storing any handler + self.input_handlers: list[InputHandler] = list() + self.output_handlers: list[OutputHandler] = list() def add_input_handler(self, handler_id: str, **kwargs): """ - TODO documentation + Add a new InputHandler to the system. - :param handler_id: - :param kwargs: - :return: + :param handler_id: unique ID of the input handler to be created. + :param kwargs: any additional configuration parameter needed for creating the input handler. """ i_handler = InputHandlers.create_input_handler(handler_id, **kwargs, queue=self.input_queue) self.input_handlers.append(i_handler) + def add_output_handler(self, handler_id: str, **kwargs): + """ + Add a new OutputHandler to the system. + + :param handler_id: unique ID of the output handler to be created. + :param kwargs: any additional configuration parameter needed for creating the output handler. + """ + o_handler = OutputHandlers.create_output_handler(handler_id, **kwargs) + self.output_handlers.append(o_handler) + + def inject_to_output_handler(self, port: str, msg): + """ + An outgoing event is inserted in the queues of all OutputHandlers. + + :param port: Name of the port which has an outgoing event. + :param msg: Info of the outgoing event + """ + for o_handler in self.output_handlers: + o_handler.queue.put((port, msg)) + def initialize(self, initial_t: float): """ - TODO documentation + Initialize function of the real time manager. + It is responsible for creating and starting any handler in the handler's list. - :param initial_t: - :return: + :param initial_t: initial time of the simulation. """ - for handler in self.input_handlers: - t = threading.Thread(daemon=True, target=run_handler, args=[handler]) - t.start() - self.threads.append(t) + + if self.input_handlers is not None: + for input_handler in self.input_handlers: + t_in = threading.Thread(daemon=True, target=run_input_handler, args=[input_handler]) + t_in.start() + self.threads.append(t_in) + if self.output_handlers is not None: + for output_handler in self.output_handlers: + t_out = threading.Thread(daemon=True, target=run_output_handler, args=[output_handler]) + t_out.start() + self.threads.append(t_out) + self.last_v_time = initial_t self.initial_r_time = time.time() self.last_r_time = self.initial_r_time @@ -75,10 +114,10 @@ def exit(self, final_t: float): def sleep(self, next_v_time: float) -> tuple[float, list[tuple[Any, Any]]]: """ - TODO documentation + Function that implements the real time specification by waiting for ingoing events to the system. - :param next_v_time: - :return: + :param next_v_time: simulation time until next cycle. + :return: a tuple of: actual simulation time, a list of ingoing events. """ next_r_time = self.last_r_time + (next_v_time - self.last_v_time) * self.time_scale events: list[tuple[str, Any]] = list() From 65ebf4c0a074e38c57729674a8980e2df15d273e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Tue, 11 Apr 2023 18:01:44 +0200 Subject: [PATCH 24/60] Review --- xdevs/examples/async_rt/basic.py | 2 +- .../input_handlers/csv_input_handler.py | 2 +- .../output_handlers/csv_output_handler.py | 41 +++++-------- xdevs/rt_sim/output_handler.py | 6 +- xdevs/rt_sim/rt_coord.py | 22 +++---- xdevs/rt_sim/rt_manager.py | 59 +++++++------------ 6 files changed, 48 insertions(+), 84 deletions(-) diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py index ce959a1..c016eba 100644 --- a/xdevs/examples/async_rt/basic.py +++ b/xdevs/examples/async_rt/basic.py @@ -245,7 +245,7 @@ def inject_messages(q: queue.SimpleQueue): manager.add_input_handler('function', function=inject_messages) - manager.add_output_handler('csv_out_handler', output_file='csv_output_v3.csv') + manager.add_output_handler('csv_out_handler', file='csv_output_v3.csv') c = RealTimeCoordinator(gpt, manager) t_ini = time.time() diff --git a/xdevs/plugins/input_handlers/csv_input_handler.py b/xdevs/plugins/input_handlers/csv_input_handler.py index 74a03a7..790c769 100644 --- a/xdevs/plugins/input_handlers/csv_input_handler.py +++ b/xdevs/plugins/input_handlers/csv_input_handler.py @@ -14,7 +14,7 @@ def __init__(self, **kwargs): 1st -> t, is for the time between the messages are inserted in the system. t = 0 or '' , no time is waited. - 2nd -> port, is for specify the port name. Port = '' ,the row will be omitted. + 2nd -> port, is for specifying the port name. Port = '' ,the row will be omitted. 3rd -> msg, is for inserting the message which will be transmitted. diff --git a/xdevs/plugins/output_handlers/csv_output_handler.py b/xdevs/plugins/output_handlers/csv_output_handler.py index 393901c..cb2cc6c 100644 --- a/xdevs/plugins/output_handlers/csv_output_handler.py +++ b/xdevs/plugins/output_handlers/csv_output_handler.py @@ -1,38 +1,29 @@ import csv -import queue import time -import datetime - from xdevs.rt_sim.output_handler import OutputHandler class CSVOutputHandler(OutputHandler): def __init__(self, **kwargs): """ - CSVOutputHandler store in a file the outgoing events in the form : Port, p_type. - If not output_file is given, a default file is created by the name csv_output_date_time.csv. + CSVOutputHandler store in a file the outgoing events in the form : time, port, msg. + If not file is given, a default file is created by the name csv_output_date_time.csv. - :param output_file: path or name of the file in which the data is going to be store. + :param output_file: path or name of the output file. By default, it is set to 'output.csv' + :param str delimiter: column delimiter in CSV file. By default, it is set to ','. """ super().__init__() - self.csv_queue = self.queue - - self.output_file = kwargs.get('output_file', None) - if self.output_file is None: - self.output_file = 'csv_output_'+datetime.datetime.now().strftime('%d%m%Y_%H%M%S')+'.csv' + self.file = kwargs.get('file', 'output.csv') # better to have a fixed name for automation + self.delimiter: str = kwargs.get('delimiter', ',') def run(self): - while True: - # First we check for outgoing events - try: - row = self.csv_queue.get() - except queue.Empty: - continue - # If an outgoing event occurs we append it to the last line of the file. - if row is not None: - with open(self.output_file, 'a+', newline='') as csv_file: - writer = csv.writer(csv_file) - writer.writerow(row) - - csv_file.close() - row = None + initial_time = time.time() + # in general, it is not a good idea to append to an existing file when logging a simulation + with open(self.file, 'w', newline='') as file: + writer = csv.writer(file, delimiter=self.delimiter) + writer.writerow(("t", "port", "msg")) + while True: + port, msg = self.queue.get() # blocks indefinitely until it receives a message + writer.writerow((time.time() - initial_time, port, msg)) + file.flush() + # no need to close the file, the with open block does the trick diff --git a/xdevs/rt_sim/output_handler.py b/xdevs/rt_sim/output_handler.py index b57ed1d..a9ec08f 100644 --- a/xdevs/rt_sim/output_handler.py +++ b/xdevs/rt_sim/output_handler.py @@ -7,10 +7,7 @@ class OutputHandler(ABC): def __init__(self, **kwargs): - """ - Handler interface for ejecting internal events from the system. - - """ + """Handler interface for ejecting internal events from the system.""" self.queue = queue.SimpleQueue() def initialize(self): @@ -58,4 +55,3 @@ def create_output_handler(name: str, **kwargs) -> OutputHandler: if name not in OutputHandlers._plugins: raise ValueError('xDEVS output_handler plugin with name "{}" not found'.format(name)) return OutputHandlers._plugins[name](**kwargs) - diff --git a/xdevs/rt_sim/rt_coord.py b/xdevs/rt_sim/rt_coord.py index 0f37d5c..fb55401 100644 --- a/xdevs/rt_sim/rt_coord.py +++ b/xdevs/rt_sim/rt_coord.py @@ -1,7 +1,4 @@ import sys -import threading -import time - from xdevs.models import Coupled from xdevs.rt_sim.rt_manager import RealTimeManager from xdevs.sim import Coordinator @@ -26,11 +23,9 @@ def simulate(self, time_interv: float = 10000): if self.time_next == float("inf") and not self.manager.input_handlers: print('infinity reached') break + # SLEEP UNTIL NEXT STATE TRANSITION t, msgs = self.manager.sleep(min(time_interv, self.time_next)) - # Sleeping time must be the min value between time_next and time_interv. Time_next can not be greater - # than time_interv because if it is so, you will be sleeping over the time_interv. - - # INJECT EXTERNAL EVENTS + # INJECT EXTERNAL EVENTS (if any) for port_id, msg in msgs: port = self.model.get_in_port(port_id) if port is not None: @@ -42,18 +37,15 @@ def simulate(self, time_interv: float = 10000): print(f'input port "{port_id}" does not exit', file=sys.stderr) # UPDATE SIMULATION CLOCK self.clock.time = t - # EXECUTE NEXT CYCLE + # EXECUTE NEXT CYCLE (if applies) if self.clock.time == self.time_next: self.lambdaf() self.deltfcn() - # EJECT INTERNAL EVENTS + # EJECT NEW OUTPUT EVENTS for port in self.model.out_ports: - try: - msg = port.get() - self.manager.inject_to_output_handler(port.name, str(msg)) - except StopIteration: - msg = None - continue + self.manager.output_messages(port) + # EXECUTE TRANSDUCERS (if any) self._execute_transducers() + # CLEAR THE PORTS OF THE MODEL self.clear() self.exit() diff --git a/xdevs/rt_sim/rt_manager.py b/xdevs/rt_sim/rt_manager.py index acd4a6f..10b8638 100644 --- a/xdevs/rt_sim/rt_manager.py +++ b/xdevs/rt_sim/rt_manager.py @@ -4,25 +4,17 @@ import threading import time from typing import Any +from xdevs.models import Port from xdevs.rt_sim.input_handler import InputHandler, InputHandlers from xdevs.rt_sim.output_handler import OutputHandler, OutputHandlers -def run_input_handler(i_handler: InputHandler): - i_handler.initialize() +def run_handler(handler: InputHandler | OutputHandler): + handler.initialize() try: - i_handler.run() + handler.run() except Exception: # TODO try to narrow this catch - i_handler.exit() - sys.exit() - - -def run_output_handler(o_handler: OutputHandler): - o_handler.initialize() - try: - o_handler.run() - except Exception: # TODO try to narrow this catch - o_handler.exit() + handler.exit() sys.exit() @@ -76,16 +68,6 @@ def add_output_handler(self, handler_id: str, **kwargs): o_handler = OutputHandlers.create_output_handler(handler_id, **kwargs) self.output_handlers.append(o_handler) - def inject_to_output_handler(self, port: str, msg): - """ - An outgoing event is inserted in the queues of all OutputHandlers. - - :param port: Name of the port which has an outgoing event. - :param msg: Info of the outgoing event - """ - for o_handler in self.output_handlers: - o_handler.queue.put((port, msg)) - def initialize(self, initial_t: float): """ Initialize function of the real time manager. @@ -93,18 +75,11 @@ def initialize(self, initial_t: float): :param initial_t: initial time of the simulation. """ - - if self.input_handlers is not None: - for input_handler in self.input_handlers: - t_in = threading.Thread(daemon=True, target=run_input_handler, args=[input_handler]) - t_in.start() - self.threads.append(t_in) - if self.output_handlers is not None: - for output_handler in self.output_handlers: - t_out = threading.Thread(daemon=True, target=run_output_handler, args=[output_handler]) - t_out.start() - self.threads.append(t_out) - + for handlers in self.input_handlers, self.output_handlers: + for handler in handlers: + thread = threading.Thread(daemon=True, target=run_handler, args=[handler]) + thread.start() + self.threads.append(thread) self.last_v_time = initial_t self.initial_r_time = time.time() self.last_r_time = self.initial_r_time @@ -116,8 +91,8 @@ def sleep(self, next_v_time: float) -> tuple[float, list[tuple[Any, Any]]]: """ Function that implements the real time specification by waiting for ingoing events to the system. - :param next_v_time: simulation time until next cycle. - :return: a tuple of: actual simulation time, a list of ingoing events. + :param next_v_time: simulation time of the next cycle. + :return: a tuple of: actual simulation time when function returned and list of ingoing events. """ next_r_time = self.last_r_time + (next_v_time - self.last_v_time) * self.time_scale events: list[tuple[str, Any]] = list() @@ -144,3 +119,13 @@ def sleep(self, next_v_time: float) -> tuple[float, list[tuple[Any, Any]]]: if self.max_jitter is not None and abs(time.time() - self.last_r_time) > self.max_jitter: raise RuntimeError('maximum jitter exceeded.') return self.last_v_time, events + + def output_messages(self, port: Port): + """ + An outgoing event is inserted in the queues of all OutputHandlers. + + :param port: output port of the topmost DEVS model under simulation. + """ + for o_handler in self.output_handlers: + for msg in port.values: + o_handler.queue.put((port.name, msg)) From 65006b4149ca9a5f43147adc7439daf49146c3b0 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Tue, 25 Apr 2023 17:37:17 +0200 Subject: [PATCH 25/60] TCP handlers --- setup.py | 2 + .../input_handlers/csv_input_handler.py | 7 +- .../input_handlers/tcp_input_handler.py | 100 +++++++++++++++ .../output_handlers/csv_output_handler.py | 4 +- .../output_handlers/tcp_output_handler.py | 117 ++++++++++++++++++ xdevs/rt_sim/input_handler.py | 16 ++- 6 files changed, 237 insertions(+), 9 deletions(-) create mode 100644 xdevs/plugins/input_handlers/tcp_input_handler.py create mode 100644 xdevs/plugins/output_handlers/tcp_output_handler.py diff --git a/setup.py b/setup.py index 6640ca9..986f35e 100644 --- a/setup.py +++ b/setup.py @@ -18,11 +18,13 @@ 'function = xdevs.plugins.input_handlers.callable_function:CallableFunction', 'csv_handler = xdevs.plugins.input_handlers.csv_input_handler:CSVInputHandler', + 'tcp_handler = xdevs.plugins.input_handlers.tcp_input_handler:TCPInputHandler', ], 'xdevs.plugins.output_handlers': [ 'csv_out_handler = xdevs.plugins.output_handlers.csv_output_handler:CSVOutputHandler', + 'tcp_out_handler = xdevs.plugins.output_handlers.tcp_output_handler:TCPOutputHandler', ], 'xdevs.plugins.wrappers': [ diff --git a/xdevs/plugins/input_handlers/csv_input_handler.py b/xdevs/plugins/input_handlers/csv_input_handler.py index 790c769..ebaa4b7 100644 --- a/xdevs/plugins/input_handlers/csv_input_handler.py +++ b/xdevs/plugins/input_handlers/csv_input_handler.py @@ -20,17 +20,12 @@ def __init__(self, **kwargs): :param str file: CSV file path. :param str delimiter: column delimiter in CSV file. By default, it is set to ','. - :param dict[str, Callable[[str], Any]] 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, this - 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. """ super().__init__(**kwargs) self.file: str = kwargs.get('file') if self.file is None: raise ValueError('file is mandatory') self.delimiter: str = kwargs.get('delimiter', ',') - self.parsers: dict[str, Callable[[str], Any]] = kwargs.get('parsers', dict()) def run(self): with open(self.file, newline='') as csv_file: @@ -57,7 +52,7 @@ def run(self): # 4. parse message try: # if parser is not defined, we forward the message as is (i.e., in string format) - msg = self.parsers.get(port, lambda x: x)(msg) + msg = self.msg_parsers.get(port, lambda x: x)(msg) except Exception: print(f'LINE {i + 1}: error parsing msg ("{msg}"). Row will be ignored', file=sys.stderr) continue diff --git a/xdevs/plugins/input_handlers/tcp_input_handler.py b/xdevs/plugins/input_handlers/tcp_input_handler.py new file mode 100644 index 0000000..8011215 --- /dev/null +++ b/xdevs/plugins/input_handlers/tcp_input_handler.py @@ -0,0 +1,100 @@ +import queue +import socket +import threading + +from xdevs.rt_sim.input_handler import InputHandler + +# Server in which the ingoing event will be joining the system + +def tcp_format(msg): + """Default function for converting incoming msg to events. Incoming msg must be Port,msg.""" + return msg.decode().split(',') + + +def client_handler(client_socket, addr, parser_f, q): + """Function to handle each client connection.""" + + print(f'Connected to: {addr}') + while True: + data = client_socket.recv(1024) + # No existe valor por defecto. Es obligatorio pasarle un valor. + # 1024 por ser potencia de 2 y tener espacio de sobra. + if not data: + print(f'Connection closed with {addr}') + break + data = parser_f(data) + # print(f'data to inyect in q is : {data}') + q.put(data) + # q.put((parser_f(data))) + +class TCPInputHandler(InputHandler): + def __init__(self, **kwargs): + """ + TCPInputHandler is a socket server. The server receives the clients messages and inject them to the system as + ingoing events. + + Default format for client messages must be: Port,msg. Be aware that all the clients must use the same format. If + a different format is chosen a new function to parser them must be given (event_parser). + + It is recommended that to implement multiple clients with different message formats, create as many + TCPInputHandlers as formats. + + :param str host: is the IP of the network interface on which the server is listening for incoming connections. + Interesting values are '127.0.0.1' for the loopback interface (LocalHost) or '0.0.0.0' for listening to all + interfaces. Default is '0.0.0.0' + :param int port: is the port in which the server is listening + :param Callable[any, [str,str]] event_parser: A function that converts the messages of each client (any) to the + correct ingoing event format required by the system (str, str). First str must be the port name for the + ingoing event and the second one what is going to be injected in that port. + """ + super().__init__(**kwargs) + + self.host = kwargs.get('host', '0.0.0.0') # 0.0.0.0 -> listening to all interfaces. If the server is in the + # same device use LocalHost + + self.port = kwargs.get('port') + if self.port is None: + raise ValueError('PORT is mandatory') + + if self.event_parser is None: + self.event_parser = tcp_format + + + self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.clients_connected = list() + + + # A list to handle all the clients msgs + self.msg_q = queue.SimpleQueue() + + def initialize(self): + self.server_socket.bind((self.host, self.port)) + self.server_socket.listen() + print('Server listening...') + t = threading.Thread(target=self.queue_handler, daemon=True) + t.start() + + def run(self): + while True: + client_socket, address = self.server_socket.accept() + self.clients_connected.append(threading.Thread(target=client_handler, daemon=True, + args=(client_socket, address, self.event_parser, self.msg_q))) + self.clients_connected[len(self.clients_connected)-1].start() + + def queue_handler(self): + """Messages from each client are pushed to the queue.""" + while True: + port, msg = self.msg_q.get() + # msg is parser into the port type. + msg = self.msg_parsers.get(port, lambda x: x)(msg) + # print(f'MENSAJE RECIBIDO E INYECTADO: {port, msg}') + self.push_to_queue(port, msg) + + +if __name__ == '__main__': + + input_queue = queue.SimpleQueue() + + TCP = TCPInputHandler(port=4321, queue=input_queue) + TCP.initialize() + TCP.run() diff --git a/xdevs/plugins/output_handlers/csv_output_handler.py b/xdevs/plugins/output_handlers/csv_output_handler.py index cb2cc6c..c779b85 100644 --- a/xdevs/plugins/output_handlers/csv_output_handler.py +++ b/xdevs/plugins/output_handlers/csv_output_handler.py @@ -7,9 +7,9 @@ class CSVOutputHandler(OutputHandler): def __init__(self, **kwargs): """ CSVOutputHandler store in a file the outgoing events in the form : time, port, msg. - If not file is given, a default file is created by the name csv_output_date_time.csv. + If not file is given, a default file is created by the name output.csv. - :param output_file: path or name of the output file. By default, it is set to 'output.csv' + :param file: path or name of the output file. By default, it is set to 'output.csv' :param str delimiter: column delimiter in CSV file. By default, it is set to ','. """ super().__init__() diff --git a/xdevs/plugins/output_handlers/tcp_output_handler.py b/xdevs/plugins/output_handlers/tcp_output_handler.py new file mode 100644 index 0000000..193148a --- /dev/null +++ b/xdevs/plugins/output_handlers/tcp_output_handler.py @@ -0,0 +1,117 @@ +import socket +import time +import threading + +from xdevs.rt_sim.output_handler import OutputHandler + +# This will be a client that subscribe to a server to send the outgoing event of the system. + +def clear_queue(q): + """Function that removes all elements in a queue.""" + try: + while not q.empty(): + q.get() + except TypeError as e: + print(f'Argument in clear_queue must be a queue type: {e}') + +def tcp_default_format(port, msg): + """Default format for outgoing events.""" + return f'{port},{msg}' + + +class TCPOutputHandler(OutputHandler): + def __init__(self, **kwargs): + """ + TCPOutHandler is a socket client that sends to a server (described as host, port) the outgoing events of the + system. By default, the events are in the form: port, msg. + + TODO: si el handler se ha conectado mas tarde al servidor enviara toda la cola de golpe. + posibles soluciones: 1) poner una espera para que se envie uno por linea + 2) borrar toda la cola anterior. <- OFS apoya esta + + :param str host: is the IP of the device where the server is hosted. Default is 'LocalHost'. + :param int port: is the port in which the host is listening. + :param float t_wait: is the time (in s) for trying to reconnect to the server if a ConnectionRefusedError + exception occurs. Default is 10 s. + :param Callable[[Any, Any], str] event_parser: A function that determines the format of outgoing events. By + default, the format is 'port,msg', where 'port' is the name of the port in which an event occurred, and + 'msg' is the message given by the port. + + """ + super().__init__() + + self.host = kwargs.get('host', 'LocalHost') + + self.port = kwargs.get('port') + if self.port is None: + raise ValueError('port is mandatory') + + self.t_wait = kwargs.get('t_wait', 10) + + self.event_parser = kwargs.get('event_parser', tcp_default_format) + + self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + self.lock = None + + def exit(self): + print(f'CLosing client to server {self.host} in port {self.port}...') + self.lock = False + self.client_socket.close() + + def run(self): + self.lock = True + while True: + # First we connect to the server. + while self.lock: + try: + self.client_socket.connect((self.host, self.port)) + print('Connected to server...') + clear_queue(self.queue) # TODO solucion 2 + self.lock = False + except ConnectionRefusedError: + # If the connection is refused, wait for a time t_wait and try again. + # This exception can be raised when: the port is blocked or closed by a firewall, host is not + # available or close, among others. + print(f'Connection refused, trying again in {self.t_wait} s. ') + time.sleep(self.t_wait) + except OSError as e: + # If a system error occurred when connecting, we assume that the server has been shut down. + print(f'Error while connecting to server: {e}') + self.lock = False + + # If the connection was a success. + + + # First we check for outgoing events + port, msg = self.queue.get() + + # If an outgoing event occurs it is sent to the server ,but first it is formatted. + + data = self.event_parser(port, msg) + + if self.client_socket.fileno() > 0: + # We can only send data if the client_socket is not close. Client_socket is closed when .fileno() return + # 0 + self.client_socket.sendall(data.encode()) + # time.sleep(0.1) TODO solucion 1 + + print(f'msg sent: {data}') + + +if __name__ == '__main__': + + def inject_msg(): + print(f'Thread active') + for i in range(20): + TCP.queue.put(('Port', f' msg: {i} ')) + print(f'Msg in q and i = {i}') + time.sleep(2.5) + TCP.exit() + print('Closing...') + + + TCP = TCPOutputHandler(host='LocalHost', port=4321) + t = threading.Thread(target=inject_msg, daemon=True) + t.start() + TCP.run() diff --git a/xdevs/rt_sim/input_handler.py b/xdevs/rt_sim/input_handler.py index 41c9ab9..f841dd1 100644 --- a/xdevs/rt_sim/input_handler.py +++ b/xdevs/rt_sim/input_handler.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import ClassVar, Type +from typing import ClassVar, Type, Callable, Any import pkg_resources @@ -9,11 +9,25 @@ def __init__(self, **kwargs): Handler interface for injecting external events to the system. :param queue: used to collect and inject all external events joining the system. + :param event_parser: # TODO + :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') + # event_parser que va a ser para el tcp-syst una unica funcion. + + # Ver si lo pongo en la padre o especifico para cada implementación. Puede necesitarsse una función por + # implementacion, pero se llamarán igual "event_parser". + self.event_parser = kwargs.get('event_parser', None) + + self.msg_parsers: dict[str, Callable[[str], Any]] = 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 From 0d7d6293aee0504986b33093112334fe347149b0 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Tue, 25 Apr 2023 17:53:04 +0200 Subject: [PATCH 26/60] TCPOutputHandler Exception OSError corrected. --- .../output_handlers/tcp_output_handler.py | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/xdevs/plugins/output_handlers/tcp_output_handler.py b/xdevs/plugins/output_handlers/tcp_output_handler.py index 193148a..647a6ea 100644 --- a/xdevs/plugins/output_handlers/tcp_output_handler.py +++ b/xdevs/plugins/output_handlers/tcp_output_handler.py @@ -63,40 +63,40 @@ def run(self): self.lock = True while True: # First we connect to the server. - while self.lock: - try: - self.client_socket.connect((self.host, self.port)) - print('Connected to server...') - clear_queue(self.queue) # TODO solucion 2 - self.lock = False - except ConnectionRefusedError: - # If the connection is refused, wait for a time t_wait and try again. - # This exception can be raised when: the port is blocked or closed by a firewall, host is not - # available or close, among others. - print(f'Connection refused, trying again in {self.t_wait} s. ') - time.sleep(self.t_wait) - except OSError as e: - # If a system error occurred when connecting, we assume that the server has been shut down. - print(f'Error while connecting to server: {e}') - self.lock = False - - # If the connection was a success. - - - # First we check for outgoing events - port, msg = self.queue.get() - - # If an outgoing event occurs it is sent to the server ,but first it is formatted. - - data = self.event_parser(port, msg) - - if self.client_socket.fileno() > 0: - # We can only send data if the client_socket is not close. Client_socket is closed when .fileno() return - # 0 - self.client_socket.sendall(data.encode()) - # time.sleep(0.1) TODO solucion 1 - - print(f'msg sent: {data}') + try: + while self.lock: + try: + self.client_socket.connect((self.host, self.port)) + print('Connected to server...') + clear_queue(self.queue) # TODO solucion 2 + self.lock = False + except ConnectionRefusedError: + # If the connection is refused, wait for a time t_wait and try again. + # This exception can be raised when: the port is blocked or closed by a firewall, host is not + # available or close, among others. + print(f'Connection refused, trying again in {self.t_wait} s. ') + time.sleep(self.t_wait) + + # If the connection was a success. + # First we check for outgoing events + port, msg = self.queue.get() + + # If an outgoing event occurs it is sent to the server ,but first it is formatted. + + data = self.event_parser(port, msg) + + if self.client_socket.fileno() > 0: + # We can only send data if the client_socket is not close. Client_socket is closed when .fileno() return + # 0 + self.client_socket.sendall(data.encode()) + # time.sleep(0.1) TODO solucion 1 + + print(f'msg sent: {data}') + + except OSError as e: + # If a system error occurred when connecting, we assume that the server has been shut down. + print(f'Error while connecting to server: {e}') + break if __name__ == '__main__': From e4b0f44c621dfc5d7541df4e9f4bf4dabad7be97 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Wed, 26 Apr 2023 22:50:55 +0200 Subject: [PATCH 27/60] TCPOutputHandler update. --- .../output_handlers/tcp_output_handler.py | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/xdevs/plugins/output_handlers/tcp_output_handler.py b/xdevs/plugins/output_handlers/tcp_output_handler.py index 647a6ea..05e2955 100644 --- a/xdevs/plugins/output_handlers/tcp_output_handler.py +++ b/xdevs/plugins/output_handlers/tcp_output_handler.py @@ -4,6 +4,7 @@ from xdevs.rt_sim.output_handler import OutputHandler + # This will be a client that subscribe to a server to send the outgoing event of the system. def clear_queue(q): @@ -14,6 +15,7 @@ def clear_queue(q): except TypeError as e: print(f'Argument in clear_queue must be a queue type: {e}') + def tcp_default_format(port, msg): """Default format for outgoing events.""" return f'{port},{msg}' @@ -25,10 +27,6 @@ def __init__(self, **kwargs): TCPOutHandler is a socket client that sends to a server (described as host, port) the outgoing events of the system. By default, the events are in the form: port, msg. - TODO: si el handler se ha conectado mas tarde al servidor enviara toda la cola de golpe. - posibles soluciones: 1) poner una espera para que se envie uno por linea - 2) borrar toda la cola anterior. <- OFS apoya esta - :param str host: is the IP of the device where the server is hosted. Default is 'LocalHost'. :param int port: is the port in which the host is listening. :param float t_wait: is the time (in s) for trying to reconnect to the server if a ConnectionRefusedError @@ -52,24 +50,35 @@ def __init__(self, **kwargs): self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.lock = None + self.is_connected = False def exit(self): print(f'CLosing client to server {self.host} in port {self.port}...') - self.lock = False self.client_socket.close() + self.is_connected = False def run(self): - self.lock = True while True: - # First we connect to the server. + # First we check for outgoing events + port, msg = self.queue.get() try: - while self.lock: + if self.is_connected: + + # If an outgoing event occurs it is sent to the server ,but first it is formatted. + data = self.event_parser(port, msg) + + if self.client_socket.fileno() > 0: + # We can only send data if the client_socket is not close. Client_socket is closed when + # .fileno() return 0 + self.client_socket.sendall(data.encode()) + else: try: + self.client_socket.connect((self.host, self.port)) print('Connected to server...') - clear_queue(self.queue) # TODO solucion 2 - self.lock = False + + self.is_connected = True + except ConnectionRefusedError: # If the connection is refused, wait for a time t_wait and try again. # This exception can be raised when: the port is blocked or closed by a firewall, host is not @@ -77,22 +86,6 @@ def run(self): print(f'Connection refused, trying again in {self.t_wait} s. ') time.sleep(self.t_wait) - # If the connection was a success. - # First we check for outgoing events - port, msg = self.queue.get() - - # If an outgoing event occurs it is sent to the server ,but first it is formatted. - - data = self.event_parser(port, msg) - - if self.client_socket.fileno() > 0: - # We can only send data if the client_socket is not close. Client_socket is closed when .fileno() return - # 0 - self.client_socket.sendall(data.encode()) - # time.sleep(0.1) TODO solucion 1 - - print(f'msg sent: {data}') - except OSError as e: # If a system error occurred when connecting, we assume that the server has been shut down. print(f'Error while connecting to server: {e}') From 7dd3ca5cfdc22c3cbf48b9dd08e970438f977061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Thu, 27 Apr 2023 11:10:52 +0200 Subject: [PATCH 28/60] Review (WIP) --- .../input_handlers/csv_input_handler.py | 12 +---- .../input_handlers/tcp_input_handler.py | 47 ++++++++----------- .../output_handlers/tcp_output_handler.py | 35 ++++++-------- xdevs/rt_sim/input_handler.py | 34 ++++++++++---- xdevs/rt_sim/output_handler.py | 31 +++++++++++- 5 files changed, 90 insertions(+), 69 deletions(-) diff --git a/xdevs/plugins/input_handlers/csv_input_handler.py b/xdevs/plugins/input_handlers/csv_input_handler.py index ebaa4b7..2d8b5b9 100644 --- a/xdevs/plugins/input_handlers/csv_input_handler.py +++ b/xdevs/plugins/input_handlers/csv_input_handler.py @@ -1,7 +1,6 @@ import csv import sys import time -from typing import Callable, Any from xdevs.rt_sim.input_handler import InputHandler @@ -49,12 +48,5 @@ def run(self): if not port: print(f'LINE {i + 1}: port ID is empty. Row will be ignored', file=sys.stderr) continue - # 4. parse message - 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: - print(f'LINE {i + 1}: error parsing msg ("{msg}"). Row will be ignored', file=sys.stderr) - continue - # 5. inject event to queue - self.push_to_queue(port, msg) + # 4. inject event to queue + self.push_msg(port, msg) diff --git a/xdevs/plugins/input_handlers/tcp_input_handler.py b/xdevs/plugins/input_handlers/tcp_input_handler.py index 8011215..27aaed2 100644 --- a/xdevs/plugins/input_handlers/tcp_input_handler.py +++ b/xdevs/plugins/input_handlers/tcp_input_handler.py @@ -1,32 +1,26 @@ +from __future__ import annotations import queue import socket import threading - from xdevs.rt_sim.input_handler import InputHandler -# Server in which the ingoing event will be joining the system - -def tcp_format(msg): - """Default function for converting incoming msg to events. Incoming msg must be Port,msg.""" - return msg.decode().split(',') - -def client_handler(client_socket, addr, parser_f, q): +def client_handler(client_socket, addr, q): # TODO no parsea eventos, eso lo hace el input manager """Function to handle each client connection.""" - print(f'Connected to: {addr}') + print(f'Connected to client {addr}') while True: data = client_socket.recv(1024) # No existe valor por defecto. Es obligatorio pasarle un valor. # 1024 por ser potencia de 2 y tener espacio de sobra. if not data: - print(f'Connection closed with {addr}') + print(f'Connection with client {addr} closed') break - data = parser_f(data) # print(f'data to inyect in q is : {data}') q.put(data) # q.put((parser_f(data))) + class TCPInputHandler(InputHandler): def __init__(self, **kwargs): """ @@ -48,26 +42,26 @@ def __init__(self, **kwargs): ingoing event and the second one what is going to be injected in that port. """ super().__init__(**kwargs) + if self.event_parser is None: + self.event_parser = lambda x: x.decode().split(',') - self.host = kwargs.get('host', '0.0.0.0') # 0.0.0.0 -> listening to all interfaces. If the server is in the - # same device use LocalHost + self.host: str = kwargs.get('host', '0.0.0.0') # 0.0.0.0 -> listening to all interfaces. If the server is in the + # same device use LocalHost TODO yo lo dejaría en localhost por defecto (igual que el output handler) - self.port = kwargs.get('port') + self.port: int = kwargs.get('port') if self.port is None: - raise ValueError('PORT is mandatory') - - if self.event_parser is None: - self.event_parser = tcp_format - + raise ValueError('TCP port is mandatory') self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.clients_connected = list() - + self.clients_connected: list[threading.Thread] = list() # A list to handle all the clients msgs self.msg_q = queue.SimpleQueue() def initialize(self): + # TODO hay que revisar este handler. La idea es un poco lo contrario a lo que haces + # TODO en run, debemos encargarnos de meter mensajes de una cola a otra + # TODO Las demás hebras auxiliares harán el truco de las conexiones TCP. self.server_socket.bind((self.host, self.port)) self.server_socket.listen() print('Server listening...') @@ -75,20 +69,19 @@ def initialize(self): t.start() def run(self): + # TODO aqui debería ejecutarse el código de queue handler (ese método no sería necesario). + # TODO Esto de las conexiones sería lo de initialize while True: client_socket, address = self.server_socket.accept() self.clients_connected.append(threading.Thread(target=client_handler, daemon=True, - args=(client_socket, address, self.event_parser, self.msg_q))) + args=(client_socket, address, self.msg_q))) self.clients_connected[len(self.clients_connected)-1].start() def queue_handler(self): """Messages from each client are pushed to the queue.""" while True: - port, msg = self.msg_q.get() - # msg is parser into the port type. - msg = self.msg_parsers.get(port, lambda x: x)(msg) - # print(f'MENSAJE RECIBIDO E INYECTADO: {port, msg}') - self.push_to_queue(port, msg) + event = self.msg_q.get() + self.push_event(event) if __name__ == '__main__': diff --git a/xdevs/plugins/output_handlers/tcp_output_handler.py b/xdevs/plugins/output_handlers/tcp_output_handler.py index 05e2955..eee627c 100644 --- a/xdevs/plugins/output_handlers/tcp_output_handler.py +++ b/xdevs/plugins/output_handlers/tcp_output_handler.py @@ -1,6 +1,7 @@ import socket import time import threading +from typing import Any, Callable from xdevs.rt_sim.output_handler import OutputHandler @@ -31,46 +32,39 @@ def __init__(self, **kwargs): :param int port: is the port in which the host is listening. :param float t_wait: is the time (in s) for trying to reconnect to the server if a ConnectionRefusedError exception occurs. Default is 10 s. - :param Callable[[Any, Any], str] event_parser: A function that determines the format of outgoing events. By + :param Callable[[str, Any], str] event_parser: A function that determines the format of outgoing events. By default, the format is 'port,msg', where 'port' is the name of the port in which an event occurred, and 'msg' is the message given by the port. """ - super().__init__() + super().__init__(**kwargs) - self.host = kwargs.get('host', 'LocalHost') - - self.port = kwargs.get('port') + self.host: str = kwargs.get('host', 'LocalHost') + self.port: int = kwargs.get('port') if self.port is None: - raise ValueError('port is mandatory') - - self.t_wait = kwargs.get('t_wait', 10) + raise ValueError('TCP port is mandatory') + self.t_wait: float = kwargs.get('t_wait', 10) - self.event_parser = kwargs.get('event_parser', tcp_default_format) + self.event_parser: Callable[[str, Any], str] = kwargs.get('event_parser', lambda port, msg: f'{port},{msg}') self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - - self.is_connected = False + self.is_connected: bool = False def exit(self): - print(f'CLosing client to server {self.host} in port {self.port}...') + print(f'Closing client to server {self.host} in port {self.port}...') self.client_socket.close() self.is_connected = False def run(self): while True: - # First we check for outgoing events - port, msg = self.queue.get() + # Wait for an outgoing event + event = self.pop_event() try: if self.is_connected: - - # If an outgoing event occurs it is sent to the server ,but first it is formatted. - data = self.event_parser(port, msg) - if self.client_socket.fileno() > 0: # We can only send data if the client_socket is not close. Client_socket is closed when # .fileno() return 0 - self.client_socket.sendall(data.encode()) + self.client_socket.sendall(event.encode()) else: try: @@ -84,7 +78,8 @@ def run(self): # This exception can be raised when: the port is blocked or closed by a firewall, host is not # available or close, among others. print(f'Connection refused, trying again in {self.t_wait} s. ') - time.sleep(self.t_wait) + time.sleep(self.t_wait) # TODO no sleep, guarda en una variable el tiempo de cooldown + # TODO Si no, se nos acumulan mensajes en la cola! except OSError as e: # If a system error occurred when connecting, we assume that the server has been shut down. diff --git a/xdevs/rt_sim/input_handler.py b/xdevs/rt_sim/input_handler.py index f841dd1..b49efe3 100644 --- a/xdevs/rt_sim/input_handler.py +++ b/xdevs/rt_sim/input_handler.py @@ -1,5 +1,7 @@ +from __future__ import annotations from abc import ABC, abstractmethod from typing import ClassVar, Type, Callable, Any +import sys import pkg_resources @@ -9,23 +11,17 @@ def __init__(self, **kwargs): Handler interface for injecting external events to the system. :param queue: used to collect and inject all external events joining the system. - :param event_parser: # TODO + :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') - - # event_parser que va a ser para el tcp-syst una unica funcion. - - # Ver si lo pongo en la padre o especifico para cada implementación. Puede necesitarsse una función por - # implementacion, pero se llamarán igual "event_parser". - self.event_parser = kwargs.get('event_parser', None) - + 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()) def initialize(self): @@ -41,10 +37,28 @@ def run(self): """Execution of the input handler. It is implementation-specific""" pass - def push_to_queue(self, port, msg): + def push_event(self, event: Any): + """Parses event as tuple port message and pushes msg to the queue.""" + try: + port, msg = self.event_parser(event) + except Exception: + # if an exception is triggered while parsing the event, we ignore it + print(f'error parsing input event ("{event}"). Event will be ignored', file=sys.stderr) + return + self.push_msg(port, msg) + + def push_msg(self, port: str, msg: str): """Adding the port and message 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: + # if an exception is triggered while parsing the message, we ignore it + print(f'error parsing input msg ("{msg}"). Message will be ignored', file=sys.stderr) + return self.queue.put((port, msg)) + class InputHandlers: _plugins: ClassVar[dict[str, Type[InputHandler]]] = { ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs.plugins.input_handlers') diff --git a/xdevs/rt_sim/output_handler.py b/xdevs/rt_sim/output_handler.py index a9ec08f..0ace2d5 100644 --- a/xdevs/rt_sim/output_handler.py +++ b/xdevs/rt_sim/output_handler.py @@ -1,14 +1,22 @@ +from __future__ import annotations import queue from abc import ABC, abstractmethod -from typing import ClassVar, Type +import sys +from typing import Any, Callable, ClassVar, Type import pkg_resources class OutputHandler(ABC): def __init__(self, **kwargs): - """Handler interface for ejecting internal events from the system.""" + """ + Handler interface for ejecting internal events from the system. + + 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.""" @@ -23,6 +31,25 @@ def run(self): """Execution of the output handler. It is implementation-specific""" pass + def pop_event(self) -> Any: + while True: + port, msg = self.pop_msg() + try: + event = self.event_parser(port, msg) + except Exception: + print(f'error parsing output event ("{port}","{msg}"). Event will be ignored', file=sys.stderr) + continue + return event + + def pop_msg(self) -> tuple[str, str]: + while True: + port, msg = self.queue.get() + try: + msg = self.msg_parsers.get(port, lambda x: str(x))(msg) + except Exception: + print(f'error parsing output msg ("{msg}"). Message will be ignored', file=sys.stderr) + continue + return port, msg class OutputHandlers: _plugins: ClassVar[dict[str, Type[OutputHandler]]] = { From a078b1a76b4abd52b8dc4a49a22c7bc8d28c00c9 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Sat, 29 Apr 2023 13:30:22 +0200 Subject: [PATCH 29/60] TCP reviewed --- .../input_handlers/tcp_input_handler.py | 82 +++++++++++-------- .../output_handlers/tcp_output_handler.py | 24 ++---- xdevs/rt_sim/output_handler.py | 2 + 3 files changed, 57 insertions(+), 51 deletions(-) diff --git a/xdevs/plugins/input_handlers/tcp_input_handler.py b/xdevs/plugins/input_handlers/tcp_input_handler.py index 27aaed2..6ed09d5 100644 --- a/xdevs/plugins/input_handlers/tcp_input_handler.py +++ b/xdevs/plugins/input_handlers/tcp_input_handler.py @@ -5,7 +5,36 @@ from xdevs.rt_sim.input_handler import InputHandler -def client_handler(client_socket, addr, q): # TODO no parsea eventos, eso lo hace el input manager +class TCPServer: + """ + TODO + """ + def __init__(self, host, port, q): + """ + TODO + :param host: + :param port: + :param q: + """ + self.host = host + self.port = port + + self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.clients_connected: list[threading.Thread] = list() + + self.events_queue = q + + def start(self): + self.server_socket.bind((self.host, self.port)) + self.server_socket.listen() + print('Server listening...') + while True: + client_socket, address = self.server_socket.accept() + self.clients_connected.append(threading.Thread(target=client_handler, daemon=True, + args=(client_socket, address, self.events_queue))) + self.clients_connected[len(self.clients_connected) - 1].start() + +def client_handler(client_socket, addr, q): """Function to handle each client connection.""" print(f'Connected to client {addr}') @@ -16,9 +45,7 @@ def client_handler(client_socket, addr, q): # TODO no parsea eventos, eso lo ha if not data: print(f'Connection with client {addr} closed') break - # print(f'data to inyect in q is : {data}') q.put(data) - # q.put((parser_f(data))) class TCPInputHandler(InputHandler): @@ -27,65 +54,48 @@ def __init__(self, **kwargs): TCPInputHandler is a socket server. The server receives the clients messages and inject them to the system as ingoing events. - Default format for client messages must be: Port,msg. Be aware that all the clients must use the same format. If - a different format is chosen a new function to parser them must be given (event_parser). + Default format for client messages must be: Port,msg. If a different format is chosen a new function to parser + them must be given (event_parser). - It is recommended that to implement multiple clients with different message formats, create as many - TCPInputHandlers as formats. + Be aware that all the clients must use the same format. It is recommended that to implement multiple clients + with different message formats, create as many TCPInputHandlers as formats. :param str host: is the IP of the network interface on which the server is listening for incoming connections. Interesting values are '127.0.0.1' for the loopback interface (LocalHost) or '0.0.0.0' for listening to all - interfaces. Default is '0.0.0.0' + interfaces. Default is 'LocalHost' :param int port: is the port in which the server is listening :param Callable[any, [str,str]] event_parser: A function that converts the messages of each client (any) to the correct ingoing event format required by the system (str, str). First str must be the port name for the ingoing event and the second one what is going to be injected in that port. """ + + kwargs['event_parser'] = kwargs.get('event_parser', lambda x: x.decode().split(',')) + super().__init__(**kwargs) - if self.event_parser is None: - self.event_parser = lambda x: x.decode().split(',') - self.host: str = kwargs.get('host', '0.0.0.0') # 0.0.0.0 -> listening to all interfaces. If the server is in the - # same device use LocalHost TODO yo lo dejaría en localhost por defecto (igual que el output handler) + self.host: str = kwargs.get('host', 'LocalHost') self.port: int = kwargs.get('port') if self.port is None: raise ValueError('TCP port is mandatory') - self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.clients_connected: list[threading.Thread] = list() - # A list to handle all the clients msgs - self.msg_q = queue.SimpleQueue() + self.event_queue = queue.SimpleQueue() + + # TCP server to handle the communications + self.server = TCPServer(self.host, self.port, self.event_queue) + self.server_thread: threading.Thread = threading.Thread(target=self.server.start, daemon=True) def initialize(self): - # TODO hay que revisar este handler. La idea es un poco lo contrario a lo que haces - # TODO en run, debemos encargarnos de meter mensajes de una cola a otra - # TODO Las demás hebras auxiliares harán el truco de las conexiones TCP. - self.server_socket.bind((self.host, self.port)) - self.server_socket.listen() - print('Server listening...') - t = threading.Thread(target=self.queue_handler, daemon=True) - t.start() + self.server_thread.start() def run(self): - # TODO aqui debería ejecutarse el código de queue handler (ese método no sería necesario). - # TODO Esto de las conexiones sería lo de initialize while True: - client_socket, address = self.server_socket.accept() - self.clients_connected.append(threading.Thread(target=client_handler, daemon=True, - args=(client_socket, address, self.msg_q))) - self.clients_connected[len(self.clients_connected)-1].start() - - def queue_handler(self): - """Messages from each client are pushed to the queue.""" - while True: - event = self.msg_q.get() + event = self.event_queue.get() self.push_event(event) if __name__ == '__main__': - input_queue = queue.SimpleQueue() TCP = TCPInputHandler(port=4321, queue=input_queue) diff --git a/xdevs/plugins/output_handlers/tcp_output_handler.py b/xdevs/plugins/output_handlers/tcp_output_handler.py index eee627c..cdf6dcd 100644 --- a/xdevs/plugins/output_handlers/tcp_output_handler.py +++ b/xdevs/plugins/output_handlers/tcp_output_handler.py @@ -5,18 +5,8 @@ from xdevs.rt_sim.output_handler import OutputHandler - # This will be a client that subscribe to a server to send the outgoing event of the system. -def clear_queue(q): - """Function that removes all elements in a queue.""" - try: - while not q.empty(): - q.get() - except TypeError as e: - print(f'Argument in clear_queue must be a queue type: {e}') - - def tcp_default_format(port, msg): """Default format for outgoing events.""" return f'{port},{msg}' @@ -56,6 +46,7 @@ def exit(self): self.is_connected = False def run(self): + timeout = 0 while True: # Wait for an outgoing event event = self.pop_event() @@ -65,7 +56,7 @@ def run(self): # We can only send data if the client_socket is not close. Client_socket is closed when # .fileno() return 0 self.client_socket.sendall(event.encode()) - else: + elif time.time() > timeout: try: self.client_socket.connect((self.host, self.port)) @@ -77,9 +68,9 @@ def run(self): # If the connection is refused, wait for a time t_wait and try again. # This exception can be raised when: the port is blocked or closed by a firewall, host is not # available or close, among others. - print(f'Connection refused, trying again in {self.t_wait} s. ') - time.sleep(self.t_wait) # TODO no sleep, guarda en una variable el tiempo de cooldown - # TODO Si no, se nos acumulan mensajes en la cola! + print(f'Connection refused, trying again in {self.t_wait} s.') + # Si un outgoing event tardase mas de self.t_wait, se conectaría cuando llegase dicho event. + timeout = time.time() + self.t_wait except OSError as e: # If a system error occurred when connecting, we assume that the server has been shut down. @@ -94,7 +85,10 @@ def inject_msg(): for i in range(20): TCP.queue.put(('Port', f' msg: {i} ')) print(f'Msg in q and i = {i}') - time.sleep(2.5) + if i == 3: + time.sleep(15) + else: + time.sleep(2.5) TCP.exit() print('Closing...') diff --git a/xdevs/rt_sim/output_handler.py b/xdevs/rt_sim/output_handler.py index 0ace2d5..19e0f27 100644 --- a/xdevs/rt_sim/output_handler.py +++ b/xdevs/rt_sim/output_handler.py @@ -32,6 +32,7 @@ def run(self): pass def pop_event(self) -> Any: + """Parsers the outgoing event to the desire implementation.""" while True: port, msg = self.pop_msg() try: @@ -42,6 +43,7 @@ def pop_event(self) -> Any: return event def pop_msg(self) -> tuple[str, str]: + """Looks in the queue for outgoing events and parser the output msg.""" while True: port, msg = self.queue.get() try: From 570768376e0dc020541f0609e2aac2a938704d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Wed, 3 May 2023 16:27:24 +0200 Subject: [PATCH 30/60] Better logging and docs --- xdevs/rt_sim/input_handler.py | 12 ++++++------ xdevs/rt_sim/output_handler.py | 13 +++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/xdevs/rt_sim/input_handler.py b/xdevs/rt_sim/input_handler.py index b49efe3..aae7fc6 100644 --- a/xdevs/rt_sim/input_handler.py +++ b/xdevs/rt_sim/input_handler.py @@ -38,23 +38,23 @@ def run(self): pass def push_event(self, event: Any): - """Parses event as tuple port message and pushes msg to the queue.""" + """Parses event as tuple port-message and pushes it to the queue.""" try: port, msg = self.event_parser(event) - except Exception: + except Exception as e: # if an exception is triggered while parsing the event, we ignore it - print(f'error parsing input event ("{event}"). Event will be ignored', file=sys.stderr) + 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): - """Adding the port and message to the queue.""" + """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: + except Exception as e: # if an exception is triggered while parsing the message, we ignore it - print(f'error parsing input msg ("{msg}"). Message will be ignored', file=sys.stderr) + print(f'error parsing input msg ("{msg}"): {e}. Message will be ignored', file=sys.stderr) return self.queue.put((port, msg)) diff --git a/xdevs/rt_sim/output_handler.py b/xdevs/rt_sim/output_handler.py index 19e0f27..40d7d23 100644 --- a/xdevs/rt_sim/output_handler.py +++ b/xdevs/rt_sim/output_handler.py @@ -32,27 +32,28 @@ def run(self): pass def pop_event(self) -> Any: - """Parsers the outgoing event to the desire implementation.""" + """Waits until it recevies an outgoing event and parses it with the desired format.""" while True: port, msg = self.pop_msg() try: event = self.event_parser(port, msg) - except Exception: - print(f'error parsing output event ("{port}","{msg}"). Event will be ignored', file=sys.stderr) + 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]: - """Looks in the queue for outgoing events and parser the output msg.""" + """Waits until it receives an outgoing message and returns the port and message in string format.""" while True: port, msg = self.queue.get() try: msg = self.msg_parsers.get(port, lambda x: str(x))(msg) - except Exception: - print(f'error parsing output msg ("{msg}"). Message will be ignored', file=sys.stderr) + except Exception as e: + print(f'error parsing output msg ("{msg}"): {e}. Message will be ignored', file=sys.stderr) continue return port, msg + class OutputHandlers: _plugins: ClassVar[dict[str, Type[OutputHandler]]] = { ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs.plugins.output_handlers') From 87eca7eeb22cc567a1f5bdafc451d85bb12bf588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Wed, 3 May 2023 16:27:48 +0200 Subject: [PATCH 31/60] PoC: socket server instead of TCP server --- .../input_handlers/tcp_input_handler.py | 76 ++++------------- .../output_handlers/tcp_output_handler.py | 11 +-- xdevs/plugins/util/__init__.py | 0 xdevs/plugins/util/socket_server.py | 82 +++++++++++++++++++ 4 files changed, 103 insertions(+), 66 deletions(-) create mode 100644 xdevs/plugins/util/__init__.py create mode 100644 xdevs/plugins/util/socket_server.py diff --git a/xdevs/plugins/input_handlers/tcp_input_handler.py b/xdevs/plugins/input_handlers/tcp_input_handler.py index 6ed09d5..43fc0ca 100644 --- a/xdevs/plugins/input_handlers/tcp_input_handler.py +++ b/xdevs/plugins/input_handlers/tcp_input_handler.py @@ -1,54 +1,12 @@ from __future__ import annotations import queue -import socket import threading +from typing import Any +from xdevs.plugins.util.socket_server import SocketServer from xdevs.rt_sim.input_handler import InputHandler -class TCPServer: - """ - TODO - """ - def __init__(self, host, port, q): - """ - TODO - :param host: - :param port: - :param q: - """ - self.host = host - self.port = port - - self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.clients_connected: list[threading.Thread] = list() - - self.events_queue = q - - def start(self): - self.server_socket.bind((self.host, self.port)) - self.server_socket.listen() - print('Server listening...') - while True: - client_socket, address = self.server_socket.accept() - self.clients_connected.append(threading.Thread(target=client_handler, daemon=True, - args=(client_socket, address, self.events_queue))) - self.clients_connected[len(self.clients_connected) - 1].start() - -def client_handler(client_socket, addr, q): - """Function to handle each client connection.""" - - print(f'Connected to client {addr}') - while True: - data = client_socket.recv(1024) - # No existe valor por defecto. Es obligatorio pasarle un valor. - # 1024 por ser potencia de 2 y tener espacio de sobra. - if not data: - print(f'Connection with client {addr} closed') - break - q.put(data) - - -class TCPInputHandler(InputHandler): +class TCPInputHandler(InputHandler): # TODO cambiar a SocketServerInputHandler (más generico que TCP, abre la puerta a SocketClientInputHandler) def __init__(self, **kwargs): """ TCPInputHandler is a socket server. The server receives the clients messages and inject them to the system as @@ -70,28 +28,30 @@ def __init__(self, **kwargs): """ kwargs['event_parser'] = kwargs.get('event_parser', lambda x: x.decode().split(',')) - super().__init__(**kwargs) - self.host: str = kwargs.get('host', 'LocalHost') - - self.port: int = kwargs.get('port') - if self.port is None: - raise ValueError('TCP port is mandatory') - - # A list to handle all the clients msgs - self.event_queue = queue.SimpleQueue() - - # TCP server to handle the communications - self.server = TCPServer(self.host, self.port, self.event_queue) + # process socket server configuration + self.server_address: tuple[Any, ...] = kwargs.get('address') + if self.server_address is None: + host: str = kwargs.get('host', 'LocalHost') + port: int = kwargs.get('port') + if port is None: + raise ValueError('TCP port is mandatory') + self.server_address = (host, port) + self.server_socket = kwargs.get('socket') + self.max_clients: int | None = kwargs.get('max_clients') + + # create socket server to handle the communications + self.server = SocketServer(self.server_address, self.server_socket, self.max_clients) self.server_thread: threading.Thread = threading.Thread(target=self.server.start, daemon=True) def initialize(self): self.server_thread.start() def run(self): + """It just forwards messages from the server queue to the RT manager's queue.""" while True: - event = self.event_queue.get() + event = self.server.input_queue.get() self.push_event(event) diff --git a/xdevs/plugins/output_handlers/tcp_output_handler.py b/xdevs/plugins/output_handlers/tcp_output_handler.py index cdf6dcd..26aeb93 100644 --- a/xdevs/plugins/output_handlers/tcp_output_handler.py +++ b/xdevs/plugins/output_handlers/tcp_output_handler.py @@ -5,14 +5,8 @@ from xdevs.rt_sim.output_handler import OutputHandler -# This will be a client that subscribe to a server to send the outgoing event of the system. -def tcp_default_format(port, msg): - """Default format for outgoing events.""" - return f'{port},{msg}' - - -class TCPOutputHandler(OutputHandler): +class TCPOutputHandler(OutputHandler): # TODO cambiar a SocketClientOutputHandler (más generico que TCP, abre la puerta a SocketServerOutputHandler) def __init__(self, **kwargs): """ TCPOutHandler is a socket client that sends to a server (described as host, port) the outgoing events of the @@ -52,7 +46,7 @@ def run(self): event = self.pop_event() try: if self.is_connected: - if self.client_socket.fileno() > 0: + if self.client_socket.fileno() > 0: # TODO no podemos usar esto en lugar de self.is_connected? # We can only send data if the client_socket is not close. Client_socket is closed when # .fileno() return 0 self.client_socket.sendall(event.encode()) @@ -63,6 +57,7 @@ def run(self): print('Connected to server...') self.is_connected = True + # TODO el mensaje habría que inyectarlo!! except ConnectionRefusedError: # If the connection is refused, wait for a time t_wait and try again. diff --git a/xdevs/plugins/util/__init__.py b/xdevs/plugins/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xdevs/plugins/util/socket_server.py b/xdevs/plugins/util/socket_server.py new file mode 100644 index 0000000..9ae0ecd --- /dev/null +++ b/xdevs/plugins/util/socket_server.py @@ -0,0 +1,82 @@ +from __future__ import annotations +import queue +import socket +import threading +from typing import Any + + +def input_client_handler(client_socket: socket.socket, address: tuple[Any, ...], + input_queue: queue.SimpleQueue, max_size: int = 1024): + """ + Function to handle a socket client that inputs external events to the simulation events. + + :param client_socket: socket assigned to the running client. + :param address: socket address of the event source endpoint. + :param input_queue: queue from the outside to the simulation. Messages are injected as raw byte arrays. + :param max_size: maximum size of incoming messages (in bytes). + """ + # TODO probablemente esto no tenga mucho sentido aquí y la lógica es mejor que la hagan los handlers + print(f'socket input client connected to {address}') + try: + while True: + data = client_socket.recv(max_size) + if not data: # connection closed + break + input_queue.put(data) + finally: + print(f'socket input client disconnected from {address}') + client_socket.close() + + +def output_client_handler(client_socket: socket.socket, address: tuple[Any, ...], output_queue: queue.SimpleQueue): + """ + Function to handle a TCP socket client that outputs simulation events to the outside. + + :param client_socket: socket assigned to the running client. + :param address: socket address of the event destination endpoint. + :param output_queue: queue from simulation to outside. Messages are already parsed as strings + """ + # TODO Podemos tener sockets tanto servidor como clientes para inputs y outputs + # TODO probablemente esto no tenga mucho sentido aquí y la lógica es mejor que la hagan los handlers + print(f'socket output client connected to {address}') + try: + while True: + event = output_queue.get() + client_socket.sendall(event) + finally: + print(f'socket output client disconnected from {address}') + client_socket.close() + + +class SocketServer: + def __init__(self, server_address: tuple[Any, ...], server_socket: socket.socket = None, max_clients: int = None): + """ + TCP server that manages the connectivity with TCP clients for inputting events. + + :param server_address: server address used when binding the server socket. + Usually, it is a tuple (IP address, socket number). However, this depends on the socket type used. + :param server_socket: server socket. By default, it uses the IPv4 family and socket stream type. + :param max_clients: maximum number of clients allowed concurrently. By default, it is None (i.e., no limit). + """ + # TODO idea loca: el servidor añade a una cola de clientes los nuevos clientes y se olvida. + # TODO es la responsabilidad del handler de turno hacer lo que sea que tiene que hacer con los sockets + self.server_address: tuple[Any, ...] = server_address + if server_socket is None: + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.server_socket: socket.socket = server_socket + self.max_clients: int | None = max_clients + self.clients: list[threading.Thread] = list() # TODO yo creo que nos lo podemos ahorrar + + self.input_queue: queue.SimpleQueue = queue.SimpleQueue() + + def start(self): + self.server_socket.bind(self.server_address) + self.server_socket.listen(self.max_clients) + print(f'socket server with address {self.server_address} is listening...') + while True: + client_socket, address = self.server_socket.accept() + # TODO en vez de esto, añadimos el resultado de eaccept a la cola + # TODO la hebra etc. la abre el handler de turno + self.clients.append(threading.Thread(target=input_client_handler, daemon=True, + args=(client_socket, address, self.input_queue))) + self.clients[-1].start() From 7b51314f63a5dce2398a8df6f90768613c3b9a02 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Sun, 7 May 2023 23:00:57 +0200 Subject: [PATCH 32/60] MQTT approach --- setup.py | 2 + .../input_handlers/mqtt_input_handler.py | 91 +++++++++++++++++++ .../output_handlers/mqtt_output_handler.py | 51 +++++++++++ 3 files changed, 144 insertions(+) create mode 100644 xdevs/plugins/input_handlers/mqtt_input_handler.py create mode 100644 xdevs/plugins/output_handlers/mqtt_output_handler.py diff --git a/setup.py b/setup.py index 986f35e..86390dc 100644 --- a/setup.py +++ b/setup.py @@ -19,12 +19,14 @@ 'function = xdevs.plugins.input_handlers.callable_function:CallableFunction', 'csv_handler = xdevs.plugins.input_handlers.csv_input_handler:CSVInputHandler', 'tcp_handler = xdevs.plugins.input_handlers.tcp_input_handler:TCPInputHandler', + 'mqtt_handler = xdevs.plugins.input_handlers.mqtt_input_handler:MQTTInputHandler', ], 'xdevs.plugins.output_handlers': [ 'csv_out_handler = xdevs.plugins.output_handlers.csv_output_handler:CSVOutputHandler', 'tcp_out_handler = xdevs.plugins.output_handlers.tcp_output_handler:TCPOutputHandler', + 'mqtt_handler = xdevs.plugins.output_handlers.mqtt_output_handler:MQTTOutputHandler', ], 'xdevs.plugins.wrappers': [ diff --git a/xdevs/plugins/input_handlers/mqtt_input_handler.py b/xdevs/plugins/input_handlers/mqtt_input_handler.py new file mode 100644 index 0000000..7c88cc5 --- /dev/null +++ b/xdevs/plugins/input_handlers/mqtt_input_handler.py @@ -0,0 +1,91 @@ +import queue +import threading + +from paho.mqtt.client import Client +from xdevs.rt_sim.input_handler import InputHandler + +# Desde este input handler me subscribo a topics para ver los mensajes que entran +# ruta: RTsys/coupled_name/input/port_name y to_do lo que llegue a ese puerto se inyecta. + +# Si y no, al final solo nos importa el nombre del puerto. El sistema es capaz de diferenciar entre puertos, por lo que +# si envio al final una tupla port,msg si dicho port no coincide con un nombre de un puerto de entrada del sistema +# deberia dar error. msg es el payload asoicado en el pacquete mqtt. Si port no coincide con ninguno no se inyecta nada. +# ¿No? +# Al final estaremos conectando un puerto de salida con uno de entrada. El puerto de salida debera publicar en +# RTsys/coupledAlqueInyecto/input/port_name y asi el input handler podra subscribirse a RTsys/coupledAlqueInyecto(que es +# el suyo)/input/#. + +######################################################################### +######################################################################### +######################################################################### +def on_connect(client, userdata, flags, rc): + print(f'Connected with mqtt: {rc}') # rc valor de exito o fracaso en la conexion + +def on_message(client, userdata, msg): + print(f'New msg arrived in {msg.topic} : {msg.payload.decode()} ') + client.event_queue.put(msg) + +class MQTTClient(Client): + def __init__(self, event_queue: queue = None, **kwargs): + super().__init__(**kwargs) + + self.on_message = kwargs.get('on_message', on_message) + self.on_connect = kwargs.get('on_connect', on_connect) + + self.event_queue = event_queue + + +######################################################################### +######################################################################### +######################################################################### + +def mqtt_parser(mqtt_msg): + topic = [item for item in mqtt_msg.topic.split('/')] + port = topic[-1] + + msg = mqtt_msg.payload.decode() + return port, msg + +class MQTTInputHandler(InputHandler): + def __init__(self, subscriptions: dict[str, int] = None, **kwargs): + + kwargs['event_parser'] = kwargs.get('event_parser', mqtt_parser) + + super().__init__(**kwargs) + + self.subscriptions = subscriptions + self.host = kwargs.get('host', 'test.mosquitto.org') + self.port = kwargs.get('port', 1883) + self.keepalive = kwargs.get('keepalive', 60) + + self.event_queue: queue.SimpleQueue = queue.SimpleQueue() + self.client = MQTTClient(event_queue=self.event_queue) + + self.client_thread: threading.Thread = threading.Thread(target=self.client.loop_forever, daemon=True) + + def initialize(self): + self.client.connect(self.host, self.port, self.keepalive) + for topic, qos in self.subscriptions.items(): + self.client.subscribe(topic, qos) + + self.client_thread.start() + + def run(self): + while True: + event = self.event_queue.get() + print(f'MQTT: Event pushed: {event}') + self.push_event(event) + +if __name__ == '__main__': + input_queue = queue.SimpleQueue() + event_Q = queue.SimpleQueue() + + sub: dict = { + 'ALSW/#': 0, + 'ALSW/TEP': 0, + 'RTsys/#': 0, + } + # C = MQTTClient(event_queue=event_Q) + IN = MQTTInputHandler(queue=input_queue, subscriptions=sub) + IN.initialize() + IN.run() diff --git a/xdevs/plugins/output_handlers/mqtt_output_handler.py b/xdevs/plugins/output_handlers/mqtt_output_handler.py new file mode 100644 index 0000000..6c2227e --- /dev/null +++ b/xdevs/plugins/output_handlers/mqtt_output_handler.py @@ -0,0 +1,51 @@ +import threading +import time +from typing import Callable, Any + +from xdevs.plugins.input_handlers.mqtt_input_handler import MQTTClient +from xdevs.rt_sim.output_handler import OutputHandler + + +class MQTTOutputHandler(OutputHandler): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self.host = kwargs.get('host', 'test.mosquitto.org') + self.port = kwargs.get('port', 1883) + self.keepalive = kwargs.get('keepalive', 60) + + self.client = MQTTClient() + + self.topic: str = kwargs.get('topic', 'RTsys/Output') + + self.event_parser: Callable[[str, Any], str] = kwargs.get('event_parser', + lambda port, msg: (f'{self.topic}/{port}', msg)) + + def initialize(self): + self.client.connect(self.host, self.port, self.keepalive) + + def run(self): + while True: + topic, payload = self.pop_event() + self.client.publish(topic, payload) + print(f'MQTT sends : {topic} : {payload}') + + +if __name__ == '__main__': + def inject_msg(): + print(f'Thread active') + for i in range(20): + OUT.queue.put(('Port', f' msg: {i} ')) + print(f'Msg in q and i = {i}') + if i == 3: + time.sleep(15) + else: + time.sleep(2.5) + print('Closing...') + + + OUT = MQTTOutputHandler() + OUT.initialize() + t = threading.Thread(target=inject_msg, daemon=True) + t.start() + OUT.run() From 3aceed848215d6716cb66f8d5eac271e0e20fb66 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Wed, 10 May 2023 23:05:16 +0200 Subject: [PATCH 33/60] AproachToDemoSystem --- .../store_cashier/client_generator.py | 2 +- xdevs/examples/store_cashier/employee.py | 2 +- xdevs/examples/store_cashier/employeesSys.py | 76 ++++++++++ xdevs/examples/store_cashier/gen.py | 48 +++++++ xdevs/examples/store_cashier/msg.py | 7 + xdevs/examples/store_cashier/output.csv | 131 ++++++++++++++++++ xdevs/examples/store_cashier/queueSys.py | 68 +++++++++ xdevs/examples/store_cashier/store_cashier.py | 90 +++++++++++- xdevs/examples/store_cashier/store_queue.py | 7 +- 9 files changed, 421 insertions(+), 10 deletions(-) create mode 100644 xdevs/examples/store_cashier/employeesSys.py create mode 100644 xdevs/examples/store_cashier/gen.py create mode 100644 xdevs/examples/store_cashier/output.csv create mode 100644 xdevs/examples/store_cashier/queueSys.py diff --git a/xdevs/examples/store_cashier/client_generator.py b/xdevs/examples/store_cashier/client_generator.py index ba86491..59523df 100644 --- a/xdevs/examples/store_cashier/client_generator.py +++ b/xdevs/examples/store_cashier/client_generator.py @@ -30,7 +30,7 @@ def deltint(self): self.clock += self.sigma self.state.next_client_id += 1 self.state.time_to_next = max(gauss(self.mean, self.stddev), 0) - logging.debug('({}) [{}]-> {}'.format(self.clock, self.name, str(self.state))) + # print('({}) [{}]-> {}'.format(self.clock, self.name, str(self.state))) self.hold_in(self.phase, self.state.time_to_next) def deltext(self, e): diff --git a/xdevs/examples/store_cashier/employee.py b/xdevs/examples/store_cashier/employee.py index 9750f68..78526a6 100644 --- a/xdevs/examples/store_cashier/employee.py +++ b/xdevs/examples/store_cashier/employee.py @@ -50,7 +50,7 @@ def deltext(self, e): self.state.clients_so_far += 1 self.state.client = pairing.client self.state.time_remaining = max(gauss(self.mean, self.stddev), 0) - logging.debug('({}) [{}]-> {}'.format(self.clock, self.name, str(self.state))) + print('({}) [{}]-> {}'.format(self.clock, self.name, str(self.state))) self.hold_in(self.phase, self.state.time_remaining) def lambdaf(self): diff --git a/xdevs/examples/store_cashier/employeesSys.py b/xdevs/examples/store_cashier/employeesSys.py new file mode 100644 index 0000000..470edca --- /dev/null +++ b/xdevs/examples/store_cashier/employeesSys.py @@ -0,0 +1,76 @@ +import time + +from xdevs.examples.store_cashier.employee import Employee +from xdevs.examples.store_cashier.msg import LeavingClient, ClientToEmployee, NewClient +from xdevs.models import Coupled, Port +from xdevs.rt_sim import RealTimeManager, RealTimeCoordinator + + +class EmployeesSys(Coupled): + def __init__(self, n_employees: int = 100, mean_employees: float = 5, + stddev_employees: float = 0, name=None): + super().__init__(name) + + # A single Employee has: + # self.input_client = Port(ClientToEmployee) + # self.output_ready = Port(int) + # self.output_client = Port(LeavingClient) + + self.input_client = Port(ClientToEmployee, 'InputClient') + self.output_ready = Port(int, 'OutputReady') + self.output_client = Port(LeavingClient, 'LeavingClient') + + self.add_in_port(self.input_client) + self.add_out_port(self.output_client) + self.add_out_port(self.output_ready) + + for i in range(n_employees): + employee = Employee(i, mean_employees, stddev_employees) + self.add_component(employee) + self.add_coupling(self.input_client, employee.input_client) + self.add_coupling(employee.output_ready, self.output_ready) + self.add_coupling(employee.output_client, self.output_client) + + # class ClientToEmployee: + # def __init__(self, new_client, employee_id): + # self.client = new_client + # self.employee_id = employee_id + + # Estoy pasando : MqttClient?i?t + +def input_client_parser(msg: str): + client, e_id = msg.split('?') + c = ClientToEmployee(NewClient(client, time.time()-t_ini), int(e_id)) + return c + + +if __name__ == '__main__': + sim_time = 50 + + E = EmployeesSys() + + e_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) + + msg_parser = { + 'InputClient': input_client_parser, + } + + sub = { + 'RTsys/InputClient': 0, + 'RTsys/AvailableEmployee': 0 + } + + e_manager.add_input_handler('mqtt_handler', subscriptions=sub, msg_parsers=msg_parser) + + e_manager.add_output_handler('csv_out_handler') + + e_coord = RealTimeCoordinator(E, e_manager) + + t_ini = time.time() + print(f' >>> COMENZAMOS : {t_ini}') + e_coord.simulate(time_interv=sim_time) + print(f' >>> FIN : {time.time()}') + print(f' Tiempo a ejecutar (s) = {sim_time}') + print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') + print(f' Error (%) = ' + f'{((time.time() - t_ini - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store_cashier/gen.py b/xdevs/examples/store_cashier/gen.py new file mode 100644 index 0000000..e2aeeda --- /dev/null +++ b/xdevs/examples/store_cashier/gen.py @@ -0,0 +1,48 @@ +import sys +import threading + +from xdevs.examples.store_cashier.msg import NewClient, ClientToEmployee +from xdevs.models import Coupled, Port +from xdevs.sim import Coordinator +from xdevs.rt_sim.rt_coord import RealTimeCoordinator +from xdevs.rt_sim.rt_manager import RealTimeManager +import time + +from client_generator import ClientGenerator +from store_queue import StoreQueue +from employee import Employee + +class GenSys(Coupled): + def __init__(self, mean_clients: float = 1, stddev_clients: float =0, name=None): + super().__init__(name) + generator = ClientGenerator(mean_clients, stddev_clients) + + self.out_gen_port = Port(NewClient) + self.add_out_port(self.out_gen_port) + + self.add_component(generator) + + self.add_coupling(generator.output_new_client, self.out_gen_port) + + +if __name__ == '__main__': + sim_time = 30 + mean_clients = 3 + stddev_clients = 0 + + gen = GenSys(mean_clients=mean_clients, stddev_clients=stddev_clients) + + gen_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) + gen_manager.add_output_handler('tcp_out_handler', PORT=5055) + + gen_coord = RealTimeCoordinator(gen, gen_manager) + + + t_ini = time.time() + print(f' >>> COMENZAMOS : {t_ini}') + gen_coord.simulate(time_interv=sim_time) + print(f' >>> FIN : {time.time()}') + print(f' Tiempo a ejecutar (s) = {sim_time }') + print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') + print(f' Error (%) = ' + f'{((time.time() - t_ini - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store_cashier/msg.py b/xdevs/examples/store_cashier/msg.py index 0c2aa84..ec2b58f 100644 --- a/xdevs/examples/store_cashier/msg.py +++ b/xdevs/examples/store_cashier/msg.py @@ -3,15 +3,22 @@ def __init__(self, client_id, t_entered): self.client_id = client_id self.t_entered = t_entered + def __str__(self): + return f'id::{self.client_id}; t_entered::{self.t_entered}' class ClientToEmployee: def __init__(self, new_client, employee_id): self.client = new_client self.employee_id = employee_id + def __str__(self): + return f'Client::{self.client} to Employee::{self.employee_id}' class LeavingClient: def __init__(self, client_id, t_entered, t_exited): self.client_id = client_id self.t_entered = t_entered self.t_exited = t_exited + + def __str__(self): + return f'Client::{self.client_id}; t_entered::{self.t_entered}; t_exited::{self.t_exited}' \ No newline at end of file diff --git a/xdevs/examples/store_cashier/output.csv b/xdevs/examples/store_cashier/output.csv new file mode 100644 index 0000000..2e3a196 --- /dev/null +++ b/xdevs/examples/store_cashier/output.csv @@ -0,0 +1,131 @@ +t,port,msg +0.002017974853515625,OutputReady,0 +0.002017974853515625,OutputReady,1 +0.002017974853515625,OutputReady,2 +0.002017974853515625,OutputReady,3 +0.002017974853515625,OutputReady,4 +0.002017974853515625,OutputReady,5 +0.002017974853515625,OutputReady,6 +0.002017974853515625,OutputReady,7 +0.002017974853515625,OutputReady,8 +0.002017974853515625,OutputReady,9 +0.002017974853515625,OutputReady,10 +0.002017974853515625,OutputReady,11 +0.002017974853515625,OutputReady,12 +0.002017974853515625,OutputReady,13 +0.002017974853515625,OutputReady,14 +0.002017974853515625,OutputReady,15 +0.002017974853515625,OutputReady,16 +0.002017974853515625,OutputReady,17 +0.002017974853515625,OutputReady,18 +0.002017974853515625,OutputReady,19 +0.002017974853515625,OutputReady,20 +0.002017974853515625,OutputReady,21 +0.002017974853515625,OutputReady,22 +0.002017974853515625,OutputReady,23 +0.002017974853515625,OutputReady,24 +0.002017974853515625,OutputReady,25 +0.002017974853515625,OutputReady,26 +0.002017974853515625,OutputReady,27 +0.002017974853515625,OutputReady,28 +0.002017974853515625,OutputReady,29 +0.002017974853515625,OutputReady,30 +0.002017974853515625,OutputReady,31 +0.002017974853515625,OutputReady,32 +0.002017974853515625,OutputReady,33 +0.002017974853515625,OutputReady,34 +0.002017974853515625,OutputReady,35 +0.002017974853515625,OutputReady,36 +0.002017974853515625,OutputReady,37 +0.002017974853515625,OutputReady,38 +0.002017974853515625,OutputReady,39 +0.002017974853515625,OutputReady,40 +0.002017974853515625,OutputReady,41 +0.002017974853515625,OutputReady,42 +0.002017974853515625,OutputReady,43 +0.002017974853515625,OutputReady,44 +0.002017974853515625,OutputReady,45 +0.002017974853515625,OutputReady,46 +0.002017974853515625,OutputReady,47 +0.002017974853515625,OutputReady,48 +0.002017974853515625,OutputReady,49 +0.0029985904693603516,OutputReady,50 +0.0029985904693603516,OutputReady,51 +0.0029985904693603516,OutputReady,52 +0.0029985904693603516,OutputReady,53 +0.0029985904693603516,OutputReady,54 +0.0029985904693603516,OutputReady,55 +0.0029985904693603516,OutputReady,56 +0.0029985904693603516,OutputReady,57 +0.0029985904693603516,OutputReady,58 +0.0029985904693603516,OutputReady,59 +0.0029985904693603516,OutputReady,60 +0.0029985904693603516,OutputReady,61 +0.0029985904693603516,OutputReady,62 +0.0029985904693603516,OutputReady,63 +0.0029985904693603516,OutputReady,64 +0.0029985904693603516,OutputReady,65 +0.0029985904693603516,OutputReady,66 +0.0029985904693603516,OutputReady,67 +0.003999471664428711,OutputReady,68 +0.003999471664428711,OutputReady,69 +0.003999471664428711,OutputReady,70 +0.003999471664428711,OutputReady,71 +0.003999471664428711,OutputReady,72 +0.003999471664428711,OutputReady,73 +0.003999471664428711,OutputReady,74 +0.003999471664428711,OutputReady,75 +0.003999471664428711,OutputReady,76 +0.003999471664428711,OutputReady,77 +0.003999471664428711,OutputReady,78 +0.003999471664428711,OutputReady,79 +0.003999471664428711,OutputReady,80 +0.003999471664428711,OutputReady,81 +0.003999471664428711,OutputReady,82 +0.003999471664428711,OutputReady,83 +0.003999471664428711,OutputReady,84 +0.003999471664428711,OutputReady,85 +0.003999471664428711,OutputReady,86 +0.003999471664428711,OutputReady,87 +0.003999471664428711,OutputReady,88 +0.003999471664428711,OutputReady,89 +0.003999471664428711,OutputReady,90 +0.003999471664428711,OutputReady,91 +0.003999471664428711,OutputReady,92 +0.003999471664428711,OutputReady,93 +0.003999471664428711,OutputReady,94 +0.003999471664428711,OutputReady,95 +0.003999471664428711,OutputReady,96 +0.003999471664428711,OutputReady,97 +0.003999471664428711,OutputReady,98 +0.003999471664428711,OutputReady,99 +9.00593113899231,LeavingClient,Client::MqttClient; t_entered::3.494825839996338; t_exited::9.001503705978394 +9.00593113899231,OutputReady,0 +10.413140535354614,LeavingClient,Client::MqttClient; t_entered::4.8867011070251465; t_exited::10.396871566772461 +10.413140535354614,OutputReady,1 +11.885594129562378,LeavingClient,Client::MqttClient; t_entered::6.368574857711792; t_exited::11.87810730934143 +11.885594129562378,OutputReady,2 +13.418968677520752,LeavingClient,Client::MqttClient; t_entered::7.899630784988403; t_exited::13.412913084030151 +13.418968677520752,OutputReady,3 +14.908637523651123,LeavingClient,Client::MqttClient; t_entered::9.39112639427185; t_exited::14.9002525806427 +14.908637523651123,OutputReady,4 +16.406394243240356,LeavingClient,Client::MqttClient; t_entered::10.901682138442993; t_exited::16.40144443511963 +16.406394243240356,OutputReady,5 +17.940479516983032,LeavingClient,Client::MqttClient; t_entered::12.43023681640625; t_exited::17.9303982257843 +17.940479516983032,OutputReady,6 +19.47294020652771,LeavingClient,Client::MqttClient; t_entered::13.948810577392578; t_exited::19.458441972732544 +19.47294020652771,OutputReady,7 +20.965007066726685,LeavingClient,Client::MqttClient; t_entered::15.448399305343628; t_exited::20.95260453224182 +20.965007066726685,OutputReady,8 +22.458984375,LeavingClient,Client::MqttClient; t_entered::16.943137407302856; t_exited::22.453478813171387 +22.458984375,OutputReady,9 +23.981883764266968,LeavingClient,Client::MqttClient; t_entered::18.476768732070923; t_exited::23.977070093154907 +23.981883764266968,OutputReady,10 +25.474586963653564,LeavingClient,Client::MqttClient; t_entered::19.955839157104492; t_exited::25.467308044433594 +25.474586963653564,OutputReady,11 +26.989243030548096,LeavingClient,Client::MqttClient; t_entered::21.467000722885132; t_exited::26.975001573562622 +26.989243030548096,OutputReady,12 +28.49605417251587,LeavingClient,Client::MqttClient; t_entered::22.974798679351807; t_exited::28.486523628234863 +28.49605417251587,OutputReady,13 +29.999546766281128,LeavingClient,Client::MqttClient; t_entered::24.485150575637817; t_exited::29.991554975509644 +29.999546766281128,OutputReady,14 diff --git a/xdevs/examples/store_cashier/queueSys.py b/xdevs/examples/store_cashier/queueSys.py new file mode 100644 index 0000000..cef19eb --- /dev/null +++ b/xdevs/examples/store_cashier/queueSys.py @@ -0,0 +1,68 @@ +import time + +from xdevs.examples.store_cashier.msg import ClientToEmployee, NewClient +from xdevs.examples.store_cashier.store_queue import StoreQueue +from xdevs.models import Coupled, Port +from xdevs.rt_sim import RealTimeManager, RealTimeCoordinator + + +class QueueSys(Coupled): + def __init__(self, name=None): + super().__init__(name) + + q_atomic = StoreQueue() + self.add_component(q_atomic) + # Available ports of queue component: + # self.input_new_client = port(newclient) + # self.input_available_employee = port(int) + # self.output_client_to_employee = port(clienttoemployee) + + self.input_new_client = Port(NewClient, 'NewClient') + self.input_available_employee = Port(int, 'AvailableEmployee') + self.output_client_to_employee = Port(ClientToEmployee, 'Client2Employee') + + self.add_in_port(self.input_available_employee) + self.add_in_port(self.input_new_client) + self.add_out_port(self.output_client_to_employee) + + self.add_coupling(self.input_new_client, q_atomic.input_new_client) + self.add_coupling(self.input_available_employee, q_atomic.input_available_employee) + self.add_coupling(q_atomic.output_client_to_employee, self.output_client_to_employee) + + +def parser_new_client(msg: str): + client_id, t_entered = msg.split('?') + return NewClient(client_id, t_entered) + + +if __name__ == '__main__': + sim_time = 60 + q = QueueSys() + + q_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) + + msg_parser = { + 'NewClient': parser_new_client, + 'AvailableEmployee': lambda x: int(x) + } + + q_manager.add_input_handler('tcp_handler', port=5055, max_clients=5, msg_parsers=msg_parser) + + subs = { + 'AvailableEmployee': 0, + } + + q_manager.add_output_handler('mqtt_handler', ) + + q_manager.add_output_handler('mqtt_handler') + + q_coord = RealTimeCoordinator(q, q_manager) + + t_ini = time.time() + print(f' >>> COMENZAMOS : {t_ini}') + q_coord.simulate(time_interv=sim_time) + print(f' >>> FIN : {time.time()}') + print(f' Tiempo a ejecutar (s) = {sim_time}') + print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') + print(f' Error (%) = ' + f'{((time.time() - t_ini - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store_cashier/store_cashier.py b/xdevs/examples/store_cashier/store_cashier.py index 4eeff9b..9b6a502 100644 --- a/xdevs/examples/store_cashier/store_cashier.py +++ b/xdevs/examples/store_cashier/store_cashier.py @@ -1,6 +1,11 @@ import sys -from xdevs.models import Coupled +import threading + +from xdevs.examples.store_cashier.msg import NewClient, ClientToEmployee +from xdevs.models import Coupled, Port from xdevs.sim import Coordinator +from xdevs.rt_sim.rt_coord import RealTimeCoordinator +from xdevs.rt_sim.rt_manager import RealTimeManager import time from client_generator import ClientGenerator @@ -17,9 +22,16 @@ def __init__(self, n_employees:int = 10000, mean_employees: float = 30, mean_cli generator = ClientGenerator(mean_clients, stddev_clients) queue = StoreQueue() + self.o_p_queue = Port(ClientToEmployee) + + self.add_out_port(self.o_p_queue) + self.add_component(generator) self.add_component(queue) self.add_coupling(generator.output_new_client, queue.input_new_client) + + self.add_coupling(queue.output_client_to_employee, self.o_p_queue) + for i in range(n_employees): employee = Employee(i, mean_employees, stddev_employees) self.add_component(employee) @@ -27,16 +39,54 @@ def __init__(self, n_employees:int = 10000, mean_employees: float = 30, mean_cli self.add_coupling(employee.output_ready, queue.input_available_employee) +class GenSys(Coupled): + def __init__(self, mean_clients: float = 1, stddev_clients: float =0, name=None): + super().__init__(name) + generator = ClientGenerator(mean_clients, stddev_clients) + + self.out_gen_port = Port(NewClient) + self.add_out_port(self.out_gen_port) + + self.add_component(generator) + + self.add_coupling(generator.output_new_client, self.out_gen_port) + + +class StoreWithoutGen(Coupled): + def __init__(self, n_employees:int = 10000, mean_employees: float = 30, stddev_employees: float =0, + name=None): + super().__init__(name) + + queue = StoreQueue() + + self.o_p_queue = Port(ClientToEmployee) + self.add_out_port(self.o_p_queue) + + self.i_port_gen = Port(NewClient) + self.add_in_port(self.i_port_gen) + + self.add_component(queue) + + self.add_coupling(self.i_port_gen, queue.input_new_client) + + self.add_coupling(queue.output_client_to_employee, self.o_p_queue) + + for i in range(n_employees): + employee = Employee(i, mean_employees, stddev_employees) + self.add_component(employee) + self.add_coupling(queue.output_client_to_employee, employee.input_client) + self.add_coupling(employee.output_ready, queue.input_available_employee) + def get_sec(time_str): h, m, s = time_str.split(':') return int(h) * 3600 + int(m) * 60 + int(s) if __name__ == '__main__': - sim_time = 30 * 60 + sim_time: float = 35 n_employees = 3 - mean_employees = 30 - mean_generator = 10 + mean_employees = 3 + mean_generator = 1 stddev_employees = 0 stddev_clients = 0 @@ -63,7 +113,7 @@ def get_sec(time_str): print("\tNumber of Employees: {}".format(n_employees)) print("\tMean time required by employee to dispatch clients: {} seconds (standard deviation of {})".format(mean_employees, stddev_employees)) print("\tMean time between new clients: {} seconds (standard deviation of {})".format(mean_generator, stddev_employees)) - + """ start = time.time() store = StoreCashier(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) middle = time.time() @@ -75,3 +125,33 @@ def get_sec(time_str): coord.simulate_time(sim_time) end = time.time() print("Simulation took: {} sec".format(end - start)) + """ + + # Real Time simulation: + + st = StoreWithoutGen(n_employees, mean_employees, stddev_employees) + + st_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) + st_manager.add_input_handler('tcp_handler', PORT=5055) + st_coord = RealTimeCoordinator(st, st_manager) + + + + """ + store = StoreCashier(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) + rt_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) + rt_manager.add_output_handler('csv_out_handler') + c = RealTimeCoordinator(store, rt_manager) + """ + + t_ini = time.time() + print(f' >>> COMENZAMOS : {t_ini}') + st_coord.simulate(time_interv=sim_time) + print(f' >>> FIN : {time.time()}') + print(f' Tiempo a ejecutar (s) = {sim_time }') + print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') + print(f' Error (%) = ' + f'{((time.time() - t_ini - sim_time) / sim_time) * 100}') + + + diff --git a/xdevs/examples/store_cashier/store_queue.py b/xdevs/examples/store_cashier/store_queue.py index af260f1..cb8f081 100644 --- a/xdevs/examples/store_cashier/store_queue.py +++ b/xdevs/examples/store_cashier/store_queue.py @@ -12,7 +12,8 @@ def __init__(self): self.pairings = deque() def __str__(self): - return ''.format(len(self.clients), len(self.employees)) + return ''.format(len(self.clients), len(self.employees), + len(self.pairings)) class StoreQueue(Atomic): @@ -27,7 +28,7 @@ def __init__(self, name: str = None): self.add_in_port(self.input_new_client) self.add_in_port(self.input_available_employee) - self.output_client_to_employee = Port(ClientToEmployee) + self.output_client_to_employee = Port(ClientToEmployee, 'OUTqueue') self.add_out_port(self.output_client_to_employee) def deltint(self): @@ -47,7 +48,7 @@ def deltext(self, e): self.state.pairings.appendleft(ClientToEmployee(new_client, self.state.employees.pop())) except IndexError: self.state.clients.appendleft(new_client) - logging.debug('({}) [{}]-> {}'.format(self.clock, self.name, str(self.state))) + print('({}) [{}]-> {}'.format(self.clock, self.name, str(self.state))) timeout = 0 if self.state.pairings else INFINITY self.hold_in(self.phase, timeout) From b99032e185e06bd3328d04a5bf9922853db8b333 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Wed, 10 May 2023 23:06:59 +0200 Subject: [PATCH 34/60] BackUpALL --- xdevs/examples/async_rt/basic.py | 33 ++++++++++----- xdevs/examples/async_rt/csv_output_v3.csv | 40 +++++++++++++++++++ .../input_handlers/mqtt_input_handler.py | 12 +++++- .../input_handlers/tcp_input_handler.py | 11 +++-- .../output_handlers/mqtt_output_handler.py | 6 ++- .../output_handlers/tcp_output_handler.py | 7 +++- xdevs/rt_sim/output_handler.py | 2 + 7 files changed, 91 insertions(+), 20 deletions(-) create mode 100644 xdevs/examples/async_rt/csv_output_v3.csv diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py index c016eba..2d6987a 100644 --- a/xdevs/examples/async_rt/basic.py +++ b/xdevs/examples/async_rt/basic.py @@ -55,18 +55,19 @@ def deltint(self): def deltext(self, e): self.sigma -= e for msg in self.i_extern.values: - #logger.info("Generator received external job. It will forward it in the next lambda") + logger.info("Generator received external job. It will forward it in the next lambda") self.extern_jobs.append(msg) if not self.i_stop.empty(): self.generate = False + def lambdaf(self): if self.generate: job = Job(str(self.job_counter)) self.o_out.add(job) - # logger.info("Starting job %s @ t_r = %f" % (job.name, time.time())) + logger.info("Starting job %s @ t_r = %f" % (job.name, time.time())) for msg in self.extern_jobs: # we also forward external messages self.o_out.add(msg) - # logger.info("Starting job %s @ t_r = %f" % (msg.name, time.time())) + logger.info("Starting job %s @ t_r = %f" % (msg.name, time.time())) class Processor(Atomic): @@ -99,7 +100,7 @@ def deltext(self, e): def lambdaf(self): self.o_out.add(self.current_job) - # logger.info("Job %s finished @ t_r = %f" % (self.current_job.name, time.time())) + logger.info("Job %s finished @ t_r = %f" % (self.current_job.name, time.time())) class Transducer(Atomic): @@ -154,13 +155,13 @@ def deltext(self, e): if self.phase == PHASE_ACTIVE: for job in self.i_arrived.values: - #logger.info("Starting job %s @ t = %d @ t_r = %f" % (job.name, self.clock, time.time())) + # logger.info("Starting job %s @ t = %d @ t_r = %f" % (job.name, self.clock, time.time())) job.time = self.clock self.jobs_arrived.append(job) if self.i_solved: job = self.i_solved.get() - #logger.info("Job %s finished @ t = %d @ t_r = %f" % (job.name, self.clock, time.time())) + # logger.info("Job %s finished @ t = %d @ t_r = %f" % (job.name, self.clock, time.time())) self.total_ta += self.clock - job.time self.jobs_solved.append(job) @@ -239,13 +240,25 @@ def inject_messages(q: queue.SimpleQueue): manager = RealTimeManager(max_jitter=max_jitter, time_scale=time_scale, event_window=event_window) parsers = { - 'i_extern': lambda x: Job(x) # le digo al input handler como convertir el string a Job con una función + 'i_extern': lambda x: Job(x), # le digo al input handler como convertir el string a Job con una función + 'tcp': lambda x: x.decode().split('.'), } - manager.add_input_handler('csv_handler', file="prueba.csv", parsers=parsers) + # manager.add_input_handler('csv_handler', file="prueba.csv", msg_parsers=parsers) + + # manager.add_input_handler('function', function=inject_messages) + # Si no quiero ir repitiendo parsers, se lo tendria que meter al manager + # manager.add_input_handler('tcp_handler', HOST='LocalHost', PORT=5055, parsers=parsers) + + # manager.add_output_handler('csv_out_handler', file='csv_output_v3.csv') - manager.add_input_handler('function', function=inject_messages) + # manager.add_output_handler('tcp_out_handler', PORT=1234) + + sub: dict = { + 'RTsys/i_extern': 0, + } + manager.add_input_handler('mqtt_handler', subscriptions=sub, msg_parsers=parsers) - manager.add_output_handler('csv_out_handler', file='csv_output_v3.csv') + manager.add_output_handler('mqtt_handler') c = RealTimeCoordinator(gpt, manager) t_ini = time.time() diff --git a/xdevs/examples/async_rt/csv_output_v3.csv b/xdevs/examples/async_rt/csv_output_v3.csv new file mode 100644 index 0000000..1276501 --- /dev/null +++ b/xdevs/examples/async_rt/csv_output_v3.csv @@ -0,0 +1,40 @@ +t,port,msg +2.011455774307251,o_extern_gen,Job:: 1 +4.015483379364014,o_extern_gen,Job:: 2 +4.015483379364014,o_extern_gen,Job:: -1_csv +4.015483379364014,o_extern_gen,Job:: -1 +6.0036680698394775,o_extern_proc,Job:: 1 +6.004634857177734,o_extern_gen,Job:: 3 +6.004634857177734,o_extern_gen,Job:: -2 +8.01294207572937,o_extern_gen,Job:: 4 +8.01294207572937,o_extern_gen,Job:: -2_csv +10.01301908493042,o_extern_gen,Job:: 5 +10.01401972770691,o_extern_gen,Job:: -3_csv +10.01401972770691,o_extern_gen,Job:: -3 +12.008435010910034,o_extern_proc,Job:: 3 +12.008435010910034,o_extern_gen,Job:: 6 +14.014714479446411,o_extern_gen,Job:: 7 +14.014714479446411,o_extern_gen,Job:: -4_csv +14.014714479446411,o_extern_gen,Job:: -4 +16.0102641582489,o_extern_gen,Job:: 8 +16.011170864105225,o_extern_gen,Job:: -5_csv +16.011170864105225,o_extern_gen,Job:: -5 +18.002914667129517,o_extern_proc,Job:: 6 +18.002914667129517,o_extern_gen,Job:: 9 +20.006149291992188,o_extern_gen,Job:: 10 +20.006149291992188,o_extern_gen,Job:: -6 +20.006149291992188,o_extern_gen,Job:: -6_csv +22.01274347305298,o_extern_gen,Job:: 11 +22.01274347305298,o_extern_gen,Job:: -7 +22.01274347305298,o_extern_gen,Job:: -7_csv +24.00597643852234,o_extern_proc,Job:: 9 +24.00597643852234,o_extern_gen,Job:: 12 +24.00597643852234,o_extern_gen,Job:: -8 +26.00346088409424,o_extern_gen,Job:: 13 +26.00346088409424,o_extern_gen,Job:: -8_csv +28.013100385665894,o_extern_gen,Job:: 14 +28.013100385665894,o_extern_gen,Job:: -9 +28.013100385665894,o_extern_gen,Job:: -9_csv +30.007229804992676,o_extern_proc,Job:: 12 +30.007229804992676,o_extern_gen,Job:: 15 +30.007229804992676,o_extern_gen,Job:: -10 diff --git a/xdevs/plugins/input_handlers/mqtt_input_handler.py b/xdevs/plugins/input_handlers/mqtt_input_handler.py index 7c88cc5..e6be409 100644 --- a/xdevs/plugins/input_handlers/mqtt_input_handler.py +++ b/xdevs/plugins/input_handlers/mqtt_input_handler.py @@ -4,6 +4,7 @@ from paho.mqtt.client import Client from xdevs.rt_sim.input_handler import InputHandler + # Desde este input handler me subscribo a topics para ver los mensajes que entran # ruta: RTsys/coupled_name/input/port_name y to_do lo que llegue a ese puerto se inyecta. @@ -19,12 +20,14 @@ ######################################################################### ######################################################################### def on_connect(client, userdata, flags, rc): - print(f'Connected with mqtt: {rc}') # rc valor de exito o fracaso en la conexion + print(f'MQTT client connected with mqtt: {rc}') # rc valor de exito o fracaso en la conexion + return rc def on_message(client, userdata, msg): - print(f'New msg arrived in {msg.topic} : {msg.payload.decode()} ') + # print(f'New msg arrived in {msg.topic} : {msg.payload.decode()} ') client.event_queue.put(msg) + class MQTTClient(Client): def __init__(self, event_queue: queue = None, **kwargs): super().__init__(**kwargs) @@ -48,6 +51,11 @@ def mqtt_parser(mqtt_msg): class MQTTInputHandler(InputHandler): def __init__(self, subscriptions: dict[str, int] = None, **kwargs): + """ + + :param subscriptions: diccionario con los topics y su qos + :param kwargs: + """ kwargs['event_parser'] = kwargs.get('event_parser', mqtt_parser) diff --git a/xdevs/plugins/input_handlers/tcp_input_handler.py b/xdevs/plugins/input_handlers/tcp_input_handler.py index 43fc0ca..67fe57b 100644 --- a/xdevs/plugins/input_handlers/tcp_input_handler.py +++ b/xdevs/plugins/input_handlers/tcp_input_handler.py @@ -1,10 +1,11 @@ from __future__ import annotations import queue +import socket import threading from typing import Any from xdevs.plugins.util.socket_server import SocketServer from xdevs.rt_sim.input_handler import InputHandler - +import socket class TCPInputHandler(InputHandler): # TODO cambiar a SocketServerInputHandler (más generico que TCP, abre la puerta a SocketClientInputHandler) def __init__(self, **kwargs): @@ -32,14 +33,14 @@ def __init__(self, **kwargs): # process socket server configuration self.server_address: tuple[Any, ...] = kwargs.get('address') - if self.server_address is None: + if self.server_address is None: # Este default es solo para ipv4. host: str = kwargs.get('host', 'LocalHost') port: int = kwargs.get('port') if port is None: raise ValueError('TCP port is mandatory') self.server_address = (host, port) self.server_socket = kwargs.get('socket') - self.max_clients: int | None = kwargs.get('max_clients') + self.max_clients: int | None = kwargs.get('max_clients', 5) # Si no le paso nada da error en socket_server # create socket server to handle the communications self.server = SocketServer(self.server_address, self.server_socket, self.max_clients) @@ -52,12 +53,14 @@ def run(self): """It just forwards messages from the server queue to the RT manager's queue.""" while True: event = self.server.input_queue.get() + print(f'Event pushed: [{event.decode()}]') # Porque no .decode() self.push_event(event) if __name__ == '__main__': input_queue = queue.SimpleQueue() + server_socket = socket.socket() - TCP = TCPInputHandler(port=4321, queue=input_queue) + TCP = TCPInputHandler(port=4321, queue=input_queue, max_clients=10) TCP.initialize() TCP.run() diff --git a/xdevs/plugins/output_handlers/mqtt_output_handler.py b/xdevs/plugins/output_handlers/mqtt_output_handler.py index 6c2227e..26a152b 100644 --- a/xdevs/plugins/output_handlers/mqtt_output_handler.py +++ b/xdevs/plugins/output_handlers/mqtt_output_handler.py @@ -16,15 +16,17 @@ def __init__(self, **kwargs): self.client = MQTTClient() - self.topic: str = kwargs.get('topic', 'RTsys/Output') + self.topic: str = kwargs.get('topic', 'RTsys') self.event_parser: Callable[[str, Any], str] = kwargs.get('event_parser', - lambda port, msg: (f'{self.topic}/{port}', msg)) + lambda port, msg: (f'{self.topic}/Output/{port}', msg)) def initialize(self): self.client.connect(self.host, self.port, self.keepalive) + print('MQTT connected') def run(self): + print('MQTT running...') while True: topic, payload = self.pop_event() self.client.publish(topic, payload) diff --git a/xdevs/plugins/output_handlers/tcp_output_handler.py b/xdevs/plugins/output_handlers/tcp_output_handler.py index 26aeb93..a442925 100644 --- a/xdevs/plugins/output_handlers/tcp_output_handler.py +++ b/xdevs/plugins/output_handlers/tcp_output_handler.py @@ -46,10 +46,10 @@ def run(self): event = self.pop_event() try: if self.is_connected: - if self.client_socket.fileno() > 0: # TODO no podemos usar esto en lugar de self.is_connected? + #if self.client_socket.fileno() > 0: # TODO no podemos usar esto en lugar de self.is_connected? # We can only send data if the client_socket is not close. Client_socket is closed when # .fileno() return 0 - self.client_socket.sendall(event.encode()) + self.client_socket.sendall(event.encode()) elif time.time() > timeout: try: @@ -58,6 +58,9 @@ def run(self): self.is_connected = True # TODO el mensaje habría que inyectarlo!! + # LLega un msg -> probamos a conectarnos -> Exito! pero... el msg se envio antes de conectarse + # se puede tratar como instante inicial I guess. + self.client_socket.sendall(event.encode()) except ConnectionRefusedError: # If the connection is refused, wait for a time t_wait and try again. diff --git a/xdevs/rt_sim/output_handler.py b/xdevs/rt_sim/output_handler.py index 40d7d23..b23db52 100644 --- a/xdevs/rt_sim/output_handler.py +++ b/xdevs/rt_sim/output_handler.py @@ -35,6 +35,7 @@ def pop_event(self) -> Any: """Waits until it recevies 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: @@ -46,6 +47,7 @@ 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: From 962d2911ded18ca5168bc35bfc7ea7c35128f40f Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Wed, 28 Jun 2023 23:59:05 +0200 Subject: [PATCH 35/60] BackUpALL_2 BackUp de todo lo hecho antes de entregar TFG --- xdevs/examples/async_rt/basic.py | 4 +- .../store_cashier/client_generator.py | 6 +- xdevs/examples/store_cashier/employee.py | 11 +- xdevs/examples/store_cashier/employeesSys.py | 36 ++- xdevs/examples/store_cashier/output.csv | 290 ++++++++++-------- xdevs/examples/store_cashier/queueSys.py | 23 +- xdevs/examples/store_cashier/store_cashier.py | 67 ++-- xdevs/examples/store_cashier/store_queue.py | 8 +- .../store_cashier/trial_CSV_store_cashier.py | 105 +++++++ .../store_cashier/trial_TCP_store_cashier.py | 120 ++++++++ .../trial_two_models_MQTT_gen.py | 81 +++++ .../trial_two_models_MQTT_store.py | 110 +++++++ .../input_handlers/mqtt_input_handler.py | 17 +- .../input_handlers/tcp_input_handler.py | 4 +- .../output_handlers/csv_output_handler.py | 4 +- .../output_handlers/mqtt_output_handler.py | 5 +- .../output_handlers/tcp_output_handler.py | 48 +-- xdevs/plugins/util/socket_server.py | 20 +- xdevs/rt_sim/input_handler.py | 17 +- xdevs/rt_sim/mqtt_connector.py | 23 ++ xdevs/rt_sim/output_handler.py | 14 +- xdevs/rt_sim/rt_manager.py | 2 +- 22 files changed, 792 insertions(+), 223 deletions(-) create mode 100644 xdevs/examples/store_cashier/trial_CSV_store_cashier.py create mode 100644 xdevs/examples/store_cashier/trial_TCP_store_cashier.py create mode 100644 xdevs/examples/store_cashier/trial_two_models_MQTT_gen.py create mode 100644 xdevs/examples/store_cashier/trial_two_models_MQTT_store.py create mode 100644 xdevs/rt_sim/mqtt_connector.py diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py index 2d6987a..2e83b16 100644 --- a/xdevs/examples/async_rt/basic.py +++ b/xdevs/examples/async_rt/basic.py @@ -256,9 +256,9 @@ def inject_messages(q: queue.SimpleQueue): sub: dict = { 'RTsys/i_extern': 0, } - manager.add_input_handler('mqtt_handler', subscriptions=sub, msg_parsers=parsers) + #manager.add_input_handler('mqtt_handler', subscriptions=sub, msg_parsers=parsers) - manager.add_output_handler('mqtt_handler') + #manager.add_output_handler('mqtt_handler') c = RealTimeCoordinator(gpt, manager) t_ini = time.time() diff --git a/xdevs/examples/store_cashier/client_generator.py b/xdevs/examples/store_cashier/client_generator.py index 59523df..3a94333 100644 --- a/xdevs/examples/store_cashier/client_generator.py +++ b/xdevs/examples/store_cashier/client_generator.py @@ -1,7 +1,7 @@ import logging from random import gauss from xdevs.models import Atomic, Port - +import time from msg import NewClient @@ -25,12 +25,16 @@ def __init__(self, mean: float = 10, stddev: float = 0, name: str = None): self.output_new_client = Port(NewClient) self.add_out_port(self.output_new_client) + self.time_started = time.time() def deltint(self): self.clock += self.sigma self.state.next_client_id += 1 self.state.time_to_next = max(gauss(self.mean, self.stddev), 0) + # Para simulacion # print('({}) [{}]-> {}'.format(self.clock, self.name, str(self.state))) + # Para RT + print('({:.4f}) [{}]-> {}'.format(time.time()-self.time_started, self.name, str(self.state))) self.hold_in(self.phase, self.state.time_to_next) def deltext(self, e): diff --git a/xdevs/examples/store_cashier/employee.py b/xdevs/examples/store_cashier/employee.py index 78526a6..4a2da21 100644 --- a/xdevs/examples/store_cashier/employee.py +++ b/xdevs/examples/store_cashier/employee.py @@ -1,4 +1,6 @@ +import datetime import logging +import time from random import gauss from xdevs.models import Atomic, Port, INFINITY @@ -34,6 +36,8 @@ def __init__(self, employee_id: int, mean: float = 30, stddev: float = 0, name: self.add_out_port(self.output_ready) self.add_out_port(self.output_client) + self.time_started = time.time() + def deltint(self): self.clock += self.sigma self.state.client = None @@ -50,11 +54,16 @@ def deltext(self, e): self.state.clients_so_far += 1 self.state.client = pairing.client self.state.time_remaining = max(gauss(self.mean, self.stddev), 0) - print('({}) [{}]-> {}'.format(self.clock, self.name, str(self.state))) + # Para simulacion + # print('({}) [{}]-> {}'.format(self.clock, self.name, str(self.state))) + # Para RT + print('({:.4f}) [{}]-> {}'.format(time.time() - self.time_started, self.name, str(self.state))) + #print(f'EMPAREJADO C-E EN:{datetime.datetime.now()}') self.hold_in(self.phase, self.state.time_remaining) def lambdaf(self): clock = self.clock + self.state.time_remaining + #print(f'METO LEAVING CLIENT EN :{datetime.datetime.now()}') if self.state.client is not None: self.output_client.add(LeavingClient(self.state.client.client_id, self.state.client.t_entered, clock)) self.output_ready.add(self.employee_id) diff --git a/xdevs/examples/store_cashier/employeesSys.py b/xdevs/examples/store_cashier/employeesSys.py index 470edca..310bdb1 100644 --- a/xdevs/examples/store_cashier/employeesSys.py +++ b/xdevs/examples/store_cashier/employeesSys.py @@ -1,3 +1,4 @@ +import datetime import time from xdevs.examples.store_cashier.employee import Employee @@ -7,8 +8,8 @@ class EmployeesSys(Coupled): - def __init__(self, n_employees: int = 100, mean_employees: float = 5, - stddev_employees: float = 0, name=None): + def __init__(self, n_employees: int = 3, mean_employees: float = 5, + stddev_employees: float = 0.8, name=None): super().__init__(name) # A single Employee has: @@ -39,9 +40,14 @@ def __init__(self, n_employees: int = 100, mean_employees: float = 5, # Estoy pasando : MqttClient?i?t def input_client_parser(msg: str): - client, e_id = msg.split('?') - c = ClientToEmployee(NewClient(client, time.time()-t_ini), int(e_id)) - return c + + # ("Client::id::3; t_entered::time.time to Employee::3") Formato de entrada + client = msg.split("::id::")[1].split(";")[0] + #t = time.time() - float(msg.split("t_entered::")[1].split(" t")[0]) + t = time.time() - t_ini + e_id = msg.split("Employee::")[1] + + return ClientToEmployee(NewClient(client, t), int(e_id)) if __name__ == '__main__': @@ -55,19 +61,29 @@ def input_client_parser(msg: str): 'InputClient': input_client_parser, } - sub = { - 'RTsys/InputClient': 0, + sub_input = { + 'RTsys/Output/Client2Employee': 0, + } + + sub_output = { # QUITAR 'RTsys/AvailableEmployee': 0 } - e_manager.add_input_handler('mqtt_handler', subscriptions=sub, msg_parsers=msg_parser) + connections = { + 'Client2Employee': 'InputClient' + } + + e_manager.add_input_handler('mqtt_handler', subscriptions=sub_input, msg_parsers=msg_parser, connections=connections) + e_manager.add_output_handler('mqtt_handler', subscriptions=sub_output) + + file = 'C:/Users/Usuario/Desktop/00 UNI/01 CUARTO/00 TFG/05 Resultados simulaciones/05 Simulacion final/csv_fin.csv' - e_manager.add_output_handler('csv_out_handler') + e_manager.add_output_handler('csv_out_handler', file=file) e_coord = RealTimeCoordinator(E, e_manager) t_ini = time.time() - print(f' >>> COMENZAMOS : {t_ini}') + print(f' >>> COMENZAMOS : {t_ini} : {datetime.datetime.now()}') e_coord.simulate(time_interv=sim_time) print(f' >>> FIN : {time.time()}') print(f' Tiempo a ejecutar (s) = {sim_time}') diff --git a/xdevs/examples/store_cashier/output.csv b/xdevs/examples/store_cashier/output.csv index 2e3a196..6e626e8 100644 --- a/xdevs/examples/store_cashier/output.csv +++ b/xdevs/examples/store_cashier/output.csv @@ -1,131 +1,161 @@ t,port,msg -0.002017974853515625,OutputReady,0 -0.002017974853515625,OutputReady,1 -0.002017974853515625,OutputReady,2 -0.002017974853515625,OutputReady,3 -0.002017974853515625,OutputReady,4 -0.002017974853515625,OutputReady,5 -0.002017974853515625,OutputReady,6 -0.002017974853515625,OutputReady,7 -0.002017974853515625,OutputReady,8 -0.002017974853515625,OutputReady,9 -0.002017974853515625,OutputReady,10 -0.002017974853515625,OutputReady,11 -0.002017974853515625,OutputReady,12 -0.002017974853515625,OutputReady,13 -0.002017974853515625,OutputReady,14 -0.002017974853515625,OutputReady,15 -0.002017974853515625,OutputReady,16 -0.002017974853515625,OutputReady,17 -0.002017974853515625,OutputReady,18 -0.002017974853515625,OutputReady,19 -0.002017974853515625,OutputReady,20 -0.002017974853515625,OutputReady,21 -0.002017974853515625,OutputReady,22 -0.002017974853515625,OutputReady,23 -0.002017974853515625,OutputReady,24 -0.002017974853515625,OutputReady,25 -0.002017974853515625,OutputReady,26 -0.002017974853515625,OutputReady,27 -0.002017974853515625,OutputReady,28 -0.002017974853515625,OutputReady,29 -0.002017974853515625,OutputReady,30 -0.002017974853515625,OutputReady,31 -0.002017974853515625,OutputReady,32 -0.002017974853515625,OutputReady,33 -0.002017974853515625,OutputReady,34 -0.002017974853515625,OutputReady,35 -0.002017974853515625,OutputReady,36 -0.002017974853515625,OutputReady,37 -0.002017974853515625,OutputReady,38 -0.002017974853515625,OutputReady,39 -0.002017974853515625,OutputReady,40 -0.002017974853515625,OutputReady,41 -0.002017974853515625,OutputReady,42 -0.002017974853515625,OutputReady,43 -0.002017974853515625,OutputReady,44 -0.002017974853515625,OutputReady,45 -0.002017974853515625,OutputReady,46 -0.002017974853515625,OutputReady,47 -0.002017974853515625,OutputReady,48 -0.002017974853515625,OutputReady,49 -0.0029985904693603516,OutputReady,50 -0.0029985904693603516,OutputReady,51 -0.0029985904693603516,OutputReady,52 -0.0029985904693603516,OutputReady,53 -0.0029985904693603516,OutputReady,54 -0.0029985904693603516,OutputReady,55 -0.0029985904693603516,OutputReady,56 -0.0029985904693603516,OutputReady,57 -0.0029985904693603516,OutputReady,58 -0.0029985904693603516,OutputReady,59 -0.0029985904693603516,OutputReady,60 -0.0029985904693603516,OutputReady,61 -0.0029985904693603516,OutputReady,62 -0.0029985904693603516,OutputReady,63 -0.0029985904693603516,OutputReady,64 -0.0029985904693603516,OutputReady,65 -0.0029985904693603516,OutputReady,66 -0.0029985904693603516,OutputReady,67 -0.003999471664428711,OutputReady,68 -0.003999471664428711,OutputReady,69 -0.003999471664428711,OutputReady,70 -0.003999471664428711,OutputReady,71 -0.003999471664428711,OutputReady,72 -0.003999471664428711,OutputReady,73 -0.003999471664428711,OutputReady,74 -0.003999471664428711,OutputReady,75 -0.003999471664428711,OutputReady,76 -0.003999471664428711,OutputReady,77 -0.003999471664428711,OutputReady,78 -0.003999471664428711,OutputReady,79 -0.003999471664428711,OutputReady,80 -0.003999471664428711,OutputReady,81 -0.003999471664428711,OutputReady,82 -0.003999471664428711,OutputReady,83 -0.003999471664428711,OutputReady,84 -0.003999471664428711,OutputReady,85 -0.003999471664428711,OutputReady,86 -0.003999471664428711,OutputReady,87 -0.003999471664428711,OutputReady,88 -0.003999471664428711,OutputReady,89 -0.003999471664428711,OutputReady,90 -0.003999471664428711,OutputReady,91 -0.003999471664428711,OutputReady,92 -0.003999471664428711,OutputReady,93 -0.003999471664428711,OutputReady,94 -0.003999471664428711,OutputReady,95 -0.003999471664428711,OutputReady,96 -0.003999471664428711,OutputReady,97 -0.003999471664428711,OutputReady,98 -0.003999471664428711,OutputReady,99 -9.00593113899231,LeavingClient,Client::MqttClient; t_entered::3.494825839996338; t_exited::9.001503705978394 -9.00593113899231,OutputReady,0 -10.413140535354614,LeavingClient,Client::MqttClient; t_entered::4.8867011070251465; t_exited::10.396871566772461 -10.413140535354614,OutputReady,1 -11.885594129562378,LeavingClient,Client::MqttClient; t_entered::6.368574857711792; t_exited::11.87810730934143 -11.885594129562378,OutputReady,2 -13.418968677520752,LeavingClient,Client::MqttClient; t_entered::7.899630784988403; t_exited::13.412913084030151 -13.418968677520752,OutputReady,3 -14.908637523651123,LeavingClient,Client::MqttClient; t_entered::9.39112639427185; t_exited::14.9002525806427 -14.908637523651123,OutputReady,4 -16.406394243240356,LeavingClient,Client::MqttClient; t_entered::10.901682138442993; t_exited::16.40144443511963 -16.406394243240356,OutputReady,5 -17.940479516983032,LeavingClient,Client::MqttClient; t_entered::12.43023681640625; t_exited::17.9303982257843 -17.940479516983032,OutputReady,6 -19.47294020652771,LeavingClient,Client::MqttClient; t_entered::13.948810577392578; t_exited::19.458441972732544 -19.47294020652771,OutputReady,7 -20.965007066726685,LeavingClient,Client::MqttClient; t_entered::15.448399305343628; t_exited::20.95260453224182 -20.965007066726685,OutputReady,8 -22.458984375,LeavingClient,Client::MqttClient; t_entered::16.943137407302856; t_exited::22.453478813171387 -22.458984375,OutputReady,9 -23.981883764266968,LeavingClient,Client::MqttClient; t_entered::18.476768732070923; t_exited::23.977070093154907 -23.981883764266968,OutputReady,10 -25.474586963653564,LeavingClient,Client::MqttClient; t_entered::19.955839157104492; t_exited::25.467308044433594 -25.474586963653564,OutputReady,11 -26.989243030548096,LeavingClient,Client::MqttClient; t_entered::21.467000722885132; t_exited::26.975001573562622 -26.989243030548096,OutputReady,12 -28.49605417251587,LeavingClient,Client::MqttClient; t_entered::22.974798679351807; t_exited::28.486523628234863 -28.49605417251587,OutputReady,13 -29.999546766281128,LeavingClient,Client::MqttClient; t_entered::24.485150575637817; t_exited::29.991554975509644 -29.999546766281128,OutputReady,14 +0.0030002593994140625,OutputReady,0 +0.0030002593994140625,OutputReady,1 +0.0030002593994140625,OutputReady,2 +0.0030002593994140625,OutputReady,3 +0.0030002593994140625,OutputReady,4 +0.0030002593994140625,OutputReady,5 +0.0030002593994140625,OutputReady,6 +0.003999233245849609,OutputReady,7 +0.003999233245849609,OutputReady,8 +0.003999233245849609,OutputReady,9 +0.003999233245849609,OutputReady,10 +0.003999233245849609,OutputReady,11 +0.003999233245849609,OutputReady,12 +0.003999233245849609,OutputReady,13 +0.003999233245849609,OutputReady,14 +0.003999233245849609,OutputReady,15 +0.003999233245849609,OutputReady,16 +0.003999233245849609,OutputReady,17 +0.003999233245849609,OutputReady,18 +0.003999233245849609,OutputReady,19 +0.003999233245849609,OutputReady,20 +0.003999233245849609,OutputReady,21 +0.003999233245849609,OutputReady,22 +0.003999233245849609,OutputReady,23 +0.003999233245849609,OutputReady,24 +0.003999233245849609,OutputReady,25 +0.003999233245849609,OutputReady,26 +0.003999233245849609,OutputReady,27 +0.003999233245849609,OutputReady,28 +0.003999233245849609,OutputReady,29 +0.003999233245849609,OutputReady,30 +0.003999233245849609,OutputReady,31 +0.003999233245849609,OutputReady,32 +0.003999233245849609,OutputReady,33 +0.003999233245849609,OutputReady,34 +0.003999233245849609,OutputReady,35 +0.003999233245849609,OutputReady,36 +0.003999233245849609,OutputReady,37 +0.003999233245849609,OutputReady,38 +0.003999233245849609,OutputReady,39 +0.003999233245849609,OutputReady,40 +0.004997968673706055,OutputReady,41 +0.004997968673706055,OutputReady,42 +0.004997968673706055,OutputReady,43 +0.004997968673706055,OutputReady,44 +0.004997968673706055,OutputReady,45 +0.004997968673706055,OutputReady,46 +0.004997968673706055,OutputReady,47 +0.004997968673706055,OutputReady,48 +0.004997968673706055,OutputReady,49 +0.004997968673706055,OutputReady,50 +0.004997968673706055,OutputReady,51 +0.004997968673706055,OutputReady,52 +0.004997968673706055,OutputReady,53 +0.004997968673706055,OutputReady,54 +0.004997968673706055,OutputReady,55 +0.004997968673706055,OutputReady,56 +0.004997968673706055,OutputReady,57 +0.004997968673706055,OutputReady,58 +0.004997968673706055,OutputReady,59 +0.004997968673706055,OutputReady,60 +0.004997968673706055,OutputReady,61 +0.004997968673706055,OutputReady,62 +0.004997968673706055,OutputReady,63 +0.004997968673706055,OutputReady,64 +0.004997968673706055,OutputReady,65 +0.004997968673706055,OutputReady,66 +0.004997968673706055,OutputReady,67 +0.004997968673706055,OutputReady,68 +0.004997968673706055,OutputReady,69 +0.004997968673706055,OutputReady,70 +0.004997968673706055,OutputReady,71 +0.004997968673706055,OutputReady,72 +0.004997968673706055,OutputReady,73 +0.004997968673706055,OutputReady,74 +0.005997657775878906,OutputReady,75 +0.005997657775878906,OutputReady,76 +0.005997657775878906,OutputReady,77 +0.005997657775878906,OutputReady,78 +0.005997657775878906,OutputReady,79 +0.005997657775878906,OutputReady,80 +0.005997657775878906,OutputReady,81 +0.005997657775878906,OutputReady,82 +0.005997657775878906,OutputReady,83 +0.005997657775878906,OutputReady,84 +0.005997657775878906,OutputReady,85 +0.005997657775878906,OutputReady,86 +0.005997657775878906,OutputReady,87 +0.005997657775878906,OutputReady,88 +0.006997108459472656,OutputReady,89 +0.006997108459472656,OutputReady,90 +0.006997108459472656,OutputReady,91 +0.006997108459472656,OutputReady,92 +0.006997108459472656,OutputReady,93 +0.006997108459472656,OutputReady,94 +0.006997108459472656,OutputReady,95 +0.006997108459472656,OutputReady,96 +0.006997108459472656,OutputReady,97 +0.006997108459472656,OutputReady,98 +0.006997108459472656,OutputReady,99 +6.154195547103882,LeavingClient,Client::0; t_entered::0.0010004043579101562; t_exited::6.144615411758423 +6.154195547103882,LeavingClient,Client::1; t_entered::1.5143980979919434; t_exited::6.144615411758423 +6.154195547103882,LeavingClient,Client::2; t_entered::3.0254054069519043; t_exited::6.144615411758423 +6.154195547103882,LeavingClient,Client::3; t_entered::4.526482820510864; t_exited::6.144615411758423 +6.155194282531738,OutputReady,0 +6.155194282531738,OutputReady,1 +6.155194282531738,OutputReady,2 +6.155194282531738,OutputReady,3 +7.81133770942688,LeavingClient,Client::4; t_entered::6.037675380706787; t_exited::7.800191164016724 +7.81133770942688,OutputReady,4 +9.323095083236694,LeavingClient,Client::5; t_entered::7.550443649291992; t_exited::9.31480860710144 +9.323095083236694,OutputReady,5 +10.857501029968262,LeavingClient,Client::6; t_entered::9.060180425643921; t_exited::10.855368852615356 +10.858490705490112,OutputReady,6 +12.320794343948364,LeavingClient,Client::7; t_entered::10.574152708053589; t_exited::12.308090209960938 +12.321786642074585,OutputReady,7 +13.847427368164062,LeavingClient,Client::8; t_entered::12.076507568359375; t_exited::13.841029405593872 +13.847427368164062,OutputReady,8 +15.358103036880493,LeavingClient,Client::9; t_entered::13.588427782058716; t_exited::15.35494351387024 +15.358103036880493,OutputReady,9 +16.872090578079224,LeavingClient,Client::10; t_entered::15.091552734375; t_exited::16.866944789886475 +16.872090578079224,OutputReady,10 +18.417977333068848,LeavingClient,Client::11; t_entered::16.601346492767334; t_exited::18.409807920455933 +18.417977333068848,OutputReady,11 +19.893912315368652,LeavingClient,Client::12; t_entered::18.112578630447388; t_exited::19.882812976837158 +19.894919633865356,OutputReady,12 +21.39572048187256,LeavingClient,Client::13; t_entered::19.62247347831726; t_exited::21.39280390739441 +21.39572048187256,OutputReady,13 +22.918955087661743,LeavingClient,Client::14; t_entered::21.137441873550415; t_exited::22.90504288673401 +22.919960021972656,OutputReady,14 +24.436033487319946,LeavingClient,Client::15; t_entered::22.651875495910645; t_exited::24.421077013015747 +24.436033487319946,OutputReady,15 +25.951406002044678,LeavingClient,Client::16; t_entered::24.159263849258423; t_exited::25.93886137008667 +25.951406002044678,OutputReady,16 +27.443901300430298,LeavingClient,Client::17; t_entered::25.66021490097046; t_exited::27.438814640045166 +27.443901300430298,OutputReady,17 +28.948714017868042,LeavingClient,Client::18; t_entered::27.16807770729065; t_exited::28.9385404586792 +28.948714017868042,OutputReady,18 +30.490768909454346,LeavingClient,Client::19; t_entered::28.66822123527527; t_exited::30.477773904800415 +30.490768909454346,OutputReady,19 +31.94276738166809,LeavingClient,Client::20; t_entered::30.16935157775879; t_exited::31.939603805541992 +31.94276738166809,OutputReady,20 +33.52330040931702,LeavingClient,Client::21; t_entered::31.6766140460968; t_exited::33.50881505012512 +33.52330040931702,OutputReady,21 +34.967496156692505,LeavingClient,Client::22; t_entered::33.18162560462952; t_exited::34.95107936859131 +34.96849489212036,OutputReady,22 +36.47815656661987,LeavingClient,Client::23; t_entered::34.69177031517029; t_exited::36.47166299819946 +36.47915601730347,OutputReady,23 +37.97754096984863,LeavingClient,Client::24; t_entered::36.19194841384888; t_exited::37.96942210197449 +37.97754096984863,OutputReady,24 +39.49344801902771,LeavingClient,Client::25; t_entered::37.69249367713928; t_exited::39.48461389541626 +39.49344801902771,OutputReady,25 +40.97653865814209,LeavingClient,Client::26; t_entered::39.20053577423096; t_exited::40.965234994888306 +40.977537631988525,OutputReady,26 +42.67374396324158,LeavingClient,Client::27; t_entered::40.7106728553772; t_exited::42.66540455818176 +42.674750566482544,OutputReady,27 +44.00695443153381,LeavingClient,Client::28; t_entered::42.22624850273132; t_exited::43.993196964263916 +44.00695443153381,OutputReady,28 +45.54259276390076,LeavingClient,Client::29; t_entered::43.72702383995056; t_exited::45.53678750991821 +45.54259276390076,OutputReady,29 diff --git a/xdevs/examples/store_cashier/queueSys.py b/xdevs/examples/store_cashier/queueSys.py index cef19eb..db7688e 100644 --- a/xdevs/examples/store_cashier/queueSys.py +++ b/xdevs/examples/store_cashier/queueSys.py @@ -31,8 +31,12 @@ def __init__(self, name=None): def parser_new_client(msg: str): + #print('¿?¿?¿?¿?¿?') client_id, t_entered = msg.split('?') - return NewClient(client_id, t_entered) + + c = NewClient(client_id=client_id,t_entered=t_entered) + #print(f'DEVUELVO:{c}, c_id = {client_id}, t = {t_entered}') + return c if __name__ == '__main__': @@ -46,15 +50,22 @@ def parser_new_client(msg: str): 'AvailableEmployee': lambda x: int(x) } - q_manager.add_input_handler('tcp_handler', port=5055, max_clients=5, msg_parsers=msg_parser) + subs_input = { + 'RTsys/Output/OutputReady': 0, + } - subs = { - 'AvailableEmployee': 0, + subs_output = { # QUITAR + 'RTsys/InputClient': 0, } + connections = { + 'OutputReady': 'AvailableEmployee' + } + + q_manager.add_input_handler('tcp_handler', port=4321, max_clients=5, msg_parsers=msg_parser, connections=connections) - q_manager.add_output_handler('mqtt_handler', ) + q_manager.add_input_handler('mqtt_handler', subscriptions=subs_input, connections=connections, msg_parsers=msg_parser) - q_manager.add_output_handler('mqtt_handler') + q_manager.add_output_handler('mqtt_handler', subscriptions=subs_output) q_coord = RealTimeCoordinator(q, q_manager) diff --git a/xdevs/examples/store_cashier/store_cashier.py b/xdevs/examples/store_cashier/store_cashier.py index 9b6a502..a12839f 100644 --- a/xdevs/examples/store_cashier/store_cashier.py +++ b/xdevs/examples/store_cashier/store_cashier.py @@ -14,8 +14,8 @@ class StoreCashier(Coupled): - def __init__(self, n_employees:int = 10000, mean_employees: float = 30, mean_clients: float = 1, - stddev_employees: float =0, stddev_clients:float =0, + def __init__(self, n_employees: int = 10000, mean_employees: float = 30, mean_clients: float = 1, + stddev_employees: float = 0, stddev_clients: float = 0, name=None): super().__init__(name) @@ -40,7 +40,7 @@ def __init__(self, n_employees:int = 10000, mean_employees: float = 30, mean_cli class GenSys(Coupled): - def __init__(self, mean_clients: float = 1, stddev_clients: float =0, name=None): + def __init__(self, mean_clients: float = 1, stddev_clients: float = 0, name=None): super().__init__(name) generator = ClientGenerator(mean_clients, stddev_clients) @@ -53,7 +53,7 @@ def __init__(self, mean_clients: float = 1, stddev_clients: float =0, name=None class StoreWithoutGen(Coupled): - def __init__(self, n_employees:int = 10000, mean_employees: float = 30, stddev_employees: float =0, + def __init__(self, n_employees: int = 10000, mean_employees: float = 30, stddev_employees: float = 0, name=None): super().__init__(name) @@ -77,16 +77,17 @@ def __init__(self, n_employees:int = 10000, mean_employees: float = 30, stddev_e self.add_coupling(queue.output_client_to_employee, employee.input_client) self.add_coupling(employee.output_ready, queue.input_available_employee) + def get_sec(time_str): h, m, s = time_str.split(':') return int(h) * 3600 + int(m) * 60 + int(s) if __name__ == '__main__': - sim_time: float = 35 + sim_time: float = 13 n_employees = 3 - mean_employees = 3 - mean_generator = 1 + mean_employees = 5 + mean_generator = 3 stddev_employees = 0 stddev_clients = 0 @@ -96,7 +97,8 @@ def get_sec(time_str): print("Program used with less arguments than accepted. Missing parameters will be set to their default value.") if len(sys.argv) != 8: print("Correct usage:") - print("\t" "python3 " + sys.argv[0] + " ") + print("\t" "python3 " + sys.argv[ + 0] + " ") try: sim_time = get_sec(sys.argv[1]) n_employees = int(sys.argv[2]) @@ -111,9 +113,12 @@ def get_sec(time_str): print("CONFIGURATION OF THE SCENARIO:") print("\tSimulation time: {} seconds".format(sim_time)) print("\tNumber of Employees: {}".format(n_employees)) - print("\tMean time required by employee to dispatch clients: {} seconds (standard deviation of {})".format(mean_employees, stddev_employees)) - print("\tMean time between new clients: {} seconds (standard deviation of {})".format(mean_generator, stddev_employees)) - """ + print("\tMean time required by employee to dispatch clients: {} seconds (standard deviation of {})".format( + mean_employees, stddev_employees)) + print("\tMean time between new clients: {} seconds (standard deviation of {})".format(mean_generator, + stddev_employees)) + +""" start = time.time() store = StoreCashier(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) middle = time.time() @@ -125,33 +130,39 @@ def get_sec(time_str): coord.simulate_time(sim_time) end = time.time() print("Simulation took: {} sec".format(end - start)) - """ - - # Real Time simulation: +""" +# Real Time simulation: +""" st = StoreWithoutGen(n_employees, mean_employees, stddev_employees) st_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) st_manager.add_input_handler('tcp_handler', PORT=5055) st_coord = RealTimeCoordinator(st, st_manager) - - - - """ - store = StoreCashier(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) - rt_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) - rt_manager.add_output_handler('csv_out_handler') - c = RealTimeCoordinator(store, rt_manager) - """ - +""" + +start = time.time() +store = StoreCashier(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) +middle = time.time() +print("Model Created. Elapsed time: {} sec".format(middle - start)) +rt_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) +c = RealTimeCoordinator(store, rt_manager) +middle = time.time() +print("Coordinator and Manager Created. Elapsed time: {} sec".format(middle - start)) +c.simulate(time_interv=sim_time) +end = time.time() +print(f' Simulation time (s) = {sim_time}') +print("Simulation took: {} sec".format(end - start)) +print(f' Error (%) = ' + f'{((time.time() - start - sim_time) / sim_time) * 100}') + +""" t_ini = time.time() print(f' >>> COMENZAMOS : {t_ini}') - st_coord.simulate(time_interv=sim_time) + c.simulate(time_interv=sim_time) print(f' >>> FIN : {time.time()}') print(f' Tiempo a ejecutar (s) = {sim_time }') print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') print(f' Error (%) = ' f'{((time.time() - t_ini - sim_time) / sim_time) * 100}') - - - +""" diff --git a/xdevs/examples/store_cashier/store_queue.py b/xdevs/examples/store_cashier/store_queue.py index cb8f081..f5edb4a 100644 --- a/xdevs/examples/store_cashier/store_queue.py +++ b/xdevs/examples/store_cashier/store_queue.py @@ -1,5 +1,6 @@ from _collections import deque from xdevs.models import Atomic, Port, INFINITY +import time import logging from msg import NewClient, ClientToEmployee @@ -31,6 +32,8 @@ def __init__(self, name: str = None): self.output_client_to_employee = Port(ClientToEmployee, 'OUTqueue') self.add_out_port(self.output_client_to_employee) + self.time_started = time.time() + def deltint(self): self.clock += self.ta self.state.pairings.clear() @@ -48,7 +51,10 @@ def deltext(self, e): self.state.pairings.appendleft(ClientToEmployee(new_client, self.state.employees.pop())) except IndexError: self.state.clients.appendleft(new_client) - print('({}) [{}]-> {}'.format(self.clock, self.name, str(self.state))) + # Para simulacion normal + # print('({}) [{}]-> {}'.format(self.clock, self.name, str(self.state))) + # Para RT + print('({:.4f}) [{}]-> {}'.format(time.time()-self.time_started, self.name, str(self.state))) timeout = 0 if self.state.pairings else INFINITY self.hold_in(self.phase, timeout) diff --git a/xdevs/examples/store_cashier/trial_CSV_store_cashier.py b/xdevs/examples/store_cashier/trial_CSV_store_cashier.py new file mode 100644 index 0000000..04547a9 --- /dev/null +++ b/xdevs/examples/store_cashier/trial_CSV_store_cashier.py @@ -0,0 +1,105 @@ +import sys +import threading + +from xdevs.examples.store_cashier.msg import NewClient, ClientToEmployee, LeavingClient +from xdevs.models import Coupled, Port +from xdevs.sim import Coordinator +from xdevs.rt_sim.rt_coord import RealTimeCoordinator +from xdevs.rt_sim.rt_manager import RealTimeManager +import time + +from client_generator import ClientGenerator +from store_queue import StoreQueue +from employee import Employee + + +class StoreCashier(Coupled): + def __init__(self, n_employees: int = 10000, mean_employees: float = 30, mean_clients: float = 1, + stddev_employees: float = 0, stddev_clients: float = 0, + name=None): + super().__init__(name) + + generator = ClientGenerator(mean_clients, stddev_clients) + queue = StoreQueue() + + self.output_port_queue = Port(ClientToEmployee, 'OP_LeavingQueue') + self.output_port_gen = Port(NewClient, 'OP_LeavingGenerator') + self.output_port_employee = Port(LeavingClient, 'OP_LeavingEmployee') + + self.add_out_port(self.output_port_queue) + self.add_out_port(self.output_port_gen) + self.add_out_port(self.output_port_employee) + + self.add_component(generator) + self.add_component(queue) + self.add_coupling(generator.output_new_client, queue.input_new_client) + + self.add_coupling(queue.output_client_to_employee, self.output_port_queue) + self.add_coupling(generator.output_new_client, self.output_port_gen) + + for i in range(n_employees): + employee = Employee(i, mean_employees, stddev_employees) + self.add_component(employee) + self.add_coupling(queue.output_client_to_employee, employee.input_client) + self.add_coupling(employee.output_ready, queue.input_available_employee) + self.add_coupling(employee.output_client, self.output_port_employee) + + +def get_sec(time_str): + h, m, s = time_str.split(':') + return int(h) * 3600 + int(m) * 60 + int(s) + + +if __name__ == '__main__': + sim_time: float = 52 + n_employees = 3 + mean_employees = 5 + mean_generator = 3 + stddev_employees = 0.8 + stddev_clients = 0.5 + + if len(sys.argv) > 8: + print("Program used with more arguments than accepted. Last arguments will be ignored.") + elif len(sys.argv) < 8: + print("Program used with less arguments than accepted. Missing parameters will be set to their default value.") + if len(sys.argv) != 8: + print("Correct usage:") + print("\t" "python3 " + sys.argv[ + 0] + " ") + try: + sim_time = get_sec(sys.argv[1]) + n_employees = int(sys.argv[2]) + mean_employees = float(sys.argv[3]) + mean_generator = float(sys.argv[4]) + stddev_employees = float(sys.argv[5]) + stddev_clients = float(sys.argv[6]) + force_chain = bool(int(sys.argv[7])) + except IndexError: + pass + + print("CONFIGURATION OF THE SCENARIO:") + print("\tSimulation time: {} seconds".format(sim_time)) + print("\tNumber of Employees: {}".format(n_employees)) + print("\tMean time required by employee to dispatch clients: {} seconds (standard deviation of {})".format( + mean_employees, stddev_employees)) + print("\tMean time between new clients: {} seconds (standard deviation of {})".format(mean_generator, + stddev_employees)) + +# Real Time simulation: + + +start = time.time() +store = StoreCashier(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) +middle = time.time() +print("Model Created. Elapsed time: {} sec".format(middle - start)) +rt_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) +rt_manager.add_output_handler('csv_out_handler', file='C:/Users/Usuario/Desktop/00 UNI/01 CUARTO/00 TFG/05 Resultados simulaciones/02 Simulacion CSV OH/52s_rt_csv') +c = RealTimeCoordinator(store, rt_manager) +middle = time.time() +print("Coordinator, Manager and Handlers Created. Elapsed time: {} sec".format(middle - start)) +c.simulate(time_interv=sim_time) +end = time.time() +print(f' Simulation time (s) = {sim_time}') +print("Simulation took: {} sec".format(end - start)) +print(f' Error (%) = ' + f'{((time.time() - start - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store_cashier/trial_TCP_store_cashier.py b/xdevs/examples/store_cashier/trial_TCP_store_cashier.py new file mode 100644 index 0000000..1bd0b2b --- /dev/null +++ b/xdevs/examples/store_cashier/trial_TCP_store_cashier.py @@ -0,0 +1,120 @@ +import sys +import threading + +from xdevs.examples.store_cashier.msg import NewClient, ClientToEmployee, LeavingClient +from xdevs.models import Coupled, Port +from xdevs.sim import Coordinator +from xdevs.rt_sim.rt_coord import RealTimeCoordinator +from xdevs.rt_sim.rt_manager import RealTimeManager +import time + +from client_generator import ClientGenerator +from store_queue import StoreQueue +from employee import Employee + + +class StoreCashier(Coupled): + def __init__(self, n_employees: int = 10000, mean_employees: float = 30, mean_clients: float = 1, + stddev_employees: float = 0, stddev_clients: float = 0, + name=None): + super().__init__(name) + + generator = ClientGenerator(mean_clients, stddev_clients) + queue = StoreQueue() + + self.add_component(generator) + self.add_component(queue) + self.add_coupling(generator.output_new_client, queue.input_new_client) + + self.new_client = Port(NewClient, 'NewClient') + self.add_in_port(self.new_client) + self.add_coupling(self.new_client, queue.input_new_client) + + for i in range(n_employees): + employee = Employee(i, mean_employees, stddev_employees) + self.add_component(employee) + self.add_coupling(queue.output_client_to_employee, employee.input_client) + self.add_coupling(employee.output_ready, queue.input_available_employee) + + + +def get_sec(time_str): + h, m, s = time_str.split(':') + return int(h) * 3600 + int(m) * 60 + int(s) + +def new_client_parser(msg: str): + #print('¿?¿?¿?¿?¿?') + client_id, t_entered = msg.split('?') + + c = NewClient(client_id=client_id,t_entered=time.time()) + #print(f'DEVUELVO:{c}, c_id = {client_id}, t = {t_entered} ahora {time.time()}') + return c + +def tcp_parser(event): + pass + + + +if __name__ == '__main__': + sim_time: float = 52 + n_employees = 3 + mean_employees = 5 + mean_generator = 3 + stddev_employees = 0.8 + stddev_clients = 0.5 + + if len(sys.argv) > 8: + print("Program used with more arguments than accepted. Last arguments will be ignored.") + elif len(sys.argv) < 8: + print("Program used with less arguments than accepted. Missing parameters will be set to their default value.") + if len(sys.argv) != 8: + print("Correct usage:") + print("\t" "python3 " + sys.argv[ + 0] + " ") + try: + sim_time = get_sec(sys.argv[1]) + n_employees = int(sys.argv[2]) + mean_employees = float(sys.argv[3]) + mean_generator = float(sys.argv[4]) + stddev_employees = float(sys.argv[5]) + stddev_clients = float(sys.argv[6]) + force_chain = bool(int(sys.argv[7])) + except IndexError: + pass + + print("CONFIGURATION OF THE SCENARIO:") + print("\tSimulation time: {} seconds".format(sim_time)) + print("\tNumber of Employees: {}".format(n_employees)) + print("\tMean time required by employee to dispatch clients: {} seconds (standard deviation of {})".format( + mean_employees, stddev_employees)) + print("\tMean time between new clients: {} seconds (standard deviation of {})".format(mean_generator, + stddev_clients)) + +##### + + +msg_parser = { + 'NewClient': new_client_parser, +} + + +# Real Time simulation: + + + +start = time.time() +store = StoreCashier(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) +middle = time.time() +print("Model Created. Elapsed time: {} sec".format(middle - start)) +rt_manager = RealTimeManager(max_jitter=0.2, event_window=3) +rt_manager.add_input_handler('tcp_handler', port=4321, max_clients=5, msg_parsers=msg_parser) + +c = RealTimeCoordinator(store, rt_manager) +middle = time.time() +print("Coordinator, Manager and Handlers Created. Elapsed time: {} sec".format(middle - start)) +c.simulate(time_interv=sim_time) +end = time.time() +print(f' Simulation time (s) = {sim_time}') +print("Simulation took: {} sec".format(end - start)) +print(f' Error (%) = ' + f'{((time.time() - start - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store_cashier/trial_two_models_MQTT_gen.py b/xdevs/examples/store_cashier/trial_two_models_MQTT_gen.py new file mode 100644 index 0000000..7967b9c --- /dev/null +++ b/xdevs/examples/store_cashier/trial_two_models_MQTT_gen.py @@ -0,0 +1,81 @@ +import sys +import threading + +from xdevs.examples.store_cashier.msg import NewClient, ClientToEmployee +from xdevs.models import Coupled, Port +from xdevs.sim import Coordinator +from xdevs.rt_sim.rt_coord import RealTimeCoordinator +from xdevs.rt_sim.rt_manager import RealTimeManager +import time + +from client_generator import ClientGenerator +from store_queue import StoreQueue +from employee import Employee + + +class GenSys(Coupled): + def __init__(self, mean_clients: float = 1, stddev_clients: float = 0, name=None): + super().__init__(name) + generator = ClientGenerator(mean_clients, stddev_clients) + + self.out_gen_port = Port(NewClient, 'Gen_ClientOut') + self.add_out_port(self.out_gen_port) + + self.add_component(generator) + + self.add_coupling(generator.output_new_client, self.out_gen_port) + +def get_sec(time_str): + h, m, s = time_str.split(':') + return int(h) * 3600 + int(m) * 60 + int(s) + +if __name__ == '__main__': + sim_time: float = 52 + n_employees = 3 + mean_employees = 5 + mean_generator = 3 + stddev_employees = 0.8 + stddev_clients = 0.5 + + if len(sys.argv) > 8: + print("Program used with more arguments than accepted. Last arguments will be ignored.") + elif len(sys.argv) < 8: + print("Program used with less arguments than accepted. Missing parameters will be set to their default value.") + if len(sys.argv) != 8: + print("Correct usage:") + print("\t" "python3 " + sys.argv[ + 0] + " ") + try: + sim_time = get_sec(sys.argv[1]) + n_employees = int(sys.argv[2]) + mean_employees = float(sys.argv[3]) + mean_generator = float(sys.argv[4]) + stddev_employees = float(sys.argv[5]) + stddev_clients = float(sys.argv[6]) + force_chain = bool(int(sys.argv[7])) + except IndexError: + pass + + print("CONFIGURATION OF THE SCENARIO:") + print("\tSimulation time: {} seconds".format(sim_time)) + print("\tNumber of Employees: {}".format(n_employees)) + print("\tMean time required by employee to dispatch clients: {} seconds (standard deviation of {})".format( + mean_employees, stddev_employees)) + print("\tMean time between new clients: {} seconds (standard deviation of {})".format(mean_generator, + stddev_employees)) + + start = time.time() + gensys = GenSys(mean_generator, stddev_clients) + middle = time.time() + print("Model Created. Elapsed time: {} sec".format(middle - start)) + rt_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) + rt_manager.add_output_handler('mqtt_handler') + c = RealTimeCoordinator(gensys, rt_manager) + middle = time.time() + print("Coordinator and Manager Created. Elapsed time: {} sec".format(middle - start)) + c.simulate(time_interv=sim_time) + end = time.time() + print(f' Simulation time (s) = {sim_time}') + print("Simulation took: {} sec".format(end - start)) + print(f' Error (%) = ' + f'{((time.time() - start - sim_time) / sim_time) * 100}') \ No newline at end of file diff --git a/xdevs/examples/store_cashier/trial_two_models_MQTT_store.py b/xdevs/examples/store_cashier/trial_two_models_MQTT_store.py new file mode 100644 index 0000000..6f19dcb --- /dev/null +++ b/xdevs/examples/store_cashier/trial_two_models_MQTT_store.py @@ -0,0 +1,110 @@ +import sys +import threading + +from xdevs.examples.store_cashier.msg import NewClient, ClientToEmployee +from xdevs.models import Coupled, Port +from xdevs.sim import Coordinator +from xdevs.rt_sim.rt_coord import RealTimeCoordinator +from xdevs.rt_sim.rt_manager import RealTimeManager +import time + +from client_generator import ClientGenerator +from store_queue import StoreQueue +from employee import Employee + +class StoreWithoutGen(Coupled): + def __init__(self, n_employees: int = 10000, mean_employees: float = 30, stddev_employees: float = 0, + name=None): + super().__init__(name) + + queue = StoreQueue() + + self.o_p_queue = Port(ClientToEmployee) + self.add_out_port(self.o_p_queue) + + self.i_port_gen = Port(NewClient, 'Queue_ClientGen') + self.add_in_port(self.i_port_gen) + + self.add_component(queue) + + self.add_coupling(self.i_port_gen, queue.input_new_client) + + self.add_coupling(queue.output_client_to_employee, self.o_p_queue) + + for i in range(n_employees): + employee = Employee(i, mean_employees, stddev_employees) + self.add_component(employee) + self.add_coupling(queue.output_client_to_employee, employee.input_client) + self.add_coupling(employee.output_ready, queue.input_available_employee) +def get_sec(time_str): + h, m, s = time_str.split(':') + return int(h) * 3600 + int(m) * 60 + int(s) + + +def mqtt_parser(msg: str): + c_id, t = msg.split(';') + return NewClient(c_id, t) + +if __name__ == '__main__': + sim_time: float = 52 + n_employees = 3 + mean_employees = 5 + mean_generator = 3 + stddev_employees = 0.8 + stddev_clients = 0.5 + + if len(sys.argv) > 8: + print("Program used with more arguments than accepted. Last arguments will be ignored.") + elif len(sys.argv) < 8: + print( + "Program used with less arguments than accepted. Missing parameters will be set to their default value.") + if len(sys.argv) != 8: + print("Correct usage:") + print("\t" "python3 " + sys.argv[ + 0] + " ") + try: + sim_time = get_sec(sys.argv[1]) + n_employees = int(sys.argv[2]) + mean_employees = float(sys.argv[3]) + mean_generator = float(sys.argv[4]) + stddev_employees = float(sys.argv[5]) + stddev_clients = float(sys.argv[6]) + force_chain = bool(int(sys.argv[7])) + except IndexError: + pass + + print("CONFIGURATION OF THE SCENARIO:") + print("\tSimulation time: {} seconds".format(sim_time)) + print("\tNumber of Employees: {}".format(n_employees)) + print( + "\tMean time required by employee to dispatch clients: {} seconds (standard deviation of {})".format( + mean_employees, stddev_employees)) + print("\tMean time between new clients: {} seconds (standard deviation of {})".format(mean_generator, + stddev_employees)) + #### + conexiones = { + 'Gen_ClientOut': 'Queue_ClientGen' + } + topics = {'RTsys/Output/Gen_ClientOut': 0} + + msg_parser = { + 'Queue_ClientGen': mqtt_parser, + } + + + start = time.time() + storeNOGEN = StoreWithoutGen(n_employees, mean_employees, stddev_employees) + middle = time.time() + print("Model Created. Elapsed time: {} sec".format(middle - start)) + rt_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) + rt_manager.add_input_handler('mqtt_handler', subscriptions=topics, connections=conexiones, msg_parsers=msg_parser) + c = RealTimeCoordinator(storeNOGEN, rt_manager) + middle = time.time() + print("Coordinator and Manager Created. Elapsed time: {} sec".format(middle - start)) + t_ini = time.time() + c.simulate(time_interv=sim_time) + end = time.time() + print(f' Simulation time (s) = {sim_time}') + print("Simulation took: {} sec".format(end - start)) + print(f' Error (%) = ' + f'{((time.time() - start - sim_time) / sim_time) * 100}') \ No newline at end of file diff --git a/xdevs/plugins/input_handlers/mqtt_input_handler.py b/xdevs/plugins/input_handlers/mqtt_input_handler.py index e6be409..48d02b0 100644 --- a/xdevs/plugins/input_handlers/mqtt_input_handler.py +++ b/xdevs/plugins/input_handlers/mqtt_input_handler.py @@ -1,3 +1,4 @@ +import datetime import queue import threading @@ -5,16 +6,10 @@ from xdevs.rt_sim.input_handler import InputHandler + # Desde este input handler me subscribo a topics para ver los mensajes que entran # ruta: RTsys/coupled_name/input/port_name y to_do lo que llegue a ese puerto se inyecta. -# Si y no, al final solo nos importa el nombre del puerto. El sistema es capaz de diferenciar entre puertos, por lo que -# si envio al final una tupla port,msg si dicho port no coincide con un nombre de un puerto de entrada del sistema -# deberia dar error. msg es el payload asoicado en el pacquete mqtt. Si port no coincide con ninguno no se inyecta nada. -# ¿No? -# Al final estaremos conectando un puerto de salida con uno de entrada. El puerto de salida debera publicar en -# RTsys/coupledAlqueInyecto/input/port_name y asi el input handler podra subscribirse a RTsys/coupledAlqueInyecto(que es -# el suyo)/input/#. ######################################################################### ######################################################################### @@ -62,9 +57,9 @@ def __init__(self, subscriptions: dict[str, int] = None, **kwargs): super().__init__(**kwargs) self.subscriptions = subscriptions - self.host = kwargs.get('host', 'test.mosquitto.org') - self.port = kwargs.get('port', 1883) - self.keepalive = kwargs.get('keepalive', 60) + self.host: str = kwargs.get('host', 'test.mosquitto.org') + self.port: int = kwargs.get('port', 1883) + self.keepalive: int = kwargs.get('keepalive', 60) self.event_queue: queue.SimpleQueue = queue.SimpleQueue() self.client = MQTTClient(event_queue=self.event_queue) @@ -81,7 +76,7 @@ def initialize(self): def run(self): while True: event = self.event_queue.get() - print(f'MQTT: Event pushed: {event}') + print(f'MQTT: Event pushed: {event} t = {datetime.datetime.now()}') self.push_event(event) if __name__ == '__main__': diff --git a/xdevs/plugins/input_handlers/tcp_input_handler.py b/xdevs/plugins/input_handlers/tcp_input_handler.py index 67fe57b..96b614d 100644 --- a/xdevs/plugins/input_handlers/tcp_input_handler.py +++ b/xdevs/plugins/input_handlers/tcp_input_handler.py @@ -44,7 +44,7 @@ def __init__(self, **kwargs): # create socket server to handle the communications self.server = SocketServer(self.server_address, self.server_socket, self.max_clients) - self.server_thread: threading.Thread = threading.Thread(target=self.server.start, daemon=True) + self.server_thread: threading.Thread = threading.Thread(target=self.server.start_ih, daemon=True) def initialize(self): self.server_thread.start() @@ -53,7 +53,7 @@ def run(self): """It just forwards messages from the server queue to the RT manager's queue.""" while True: event = self.server.input_queue.get() - print(f'Event pushed: [{event.decode()}]') # Porque no .decode() + print(f'TCP: Event pushed: [{event.decode()}]') # Porque no .decode() self.push_event(event) diff --git a/xdevs/plugins/output_handlers/csv_output_handler.py b/xdevs/plugins/output_handlers/csv_output_handler.py index c779b85..3e09701 100644 --- a/xdevs/plugins/output_handlers/csv_output_handler.py +++ b/xdevs/plugins/output_handlers/csv_output_handler.py @@ -1,4 +1,5 @@ import csv +import datetime import time from xdevs.rt_sim.output_handler import OutputHandler @@ -6,7 +7,7 @@ class CSVOutputHandler(OutputHandler): def __init__(self, **kwargs): """ - CSVOutputHandler store in a file the outgoing events in the form : time, port, msg. + CSVOutputHandler stores in a file the outgoing events in the form : time, port, msg. If not file is given, a default file is created by the name output.csv. :param file: path or name of the output file. By default, it is set to 'output.csv' @@ -17,6 +18,7 @@ def __init__(self, **kwargs): self.delimiter: str = kwargs.get('delimiter', ',') def run(self): + print('CSV running...') initial_time = time.time() # in general, it is not a good idea to append to an existing file when logging a simulation with open(self.file, 'w', newline='') as file: diff --git a/xdevs/plugins/output_handlers/mqtt_output_handler.py b/xdevs/plugins/output_handlers/mqtt_output_handler.py index 26a152b..bb20293 100644 --- a/xdevs/plugins/output_handlers/mqtt_output_handler.py +++ b/xdevs/plugins/output_handlers/mqtt_output_handler.py @@ -1,5 +1,6 @@ +import datetime import threading -import time +import time from typing import Callable, Any from xdevs.plugins.input_handlers.mqtt_input_handler import MQTTClient @@ -30,7 +31,7 @@ def run(self): while True: topic, payload = self.pop_event() self.client.publish(topic, payload) - print(f'MQTT sends : {topic} : {payload}') + print(f'MQTT sends : {topic} : {payload} > {datetime.datetime.now()}') if __name__ == '__main__': diff --git a/xdevs/plugins/output_handlers/tcp_output_handler.py b/xdevs/plugins/output_handlers/tcp_output_handler.py index a442925..380ea4e 100644 --- a/xdevs/plugins/output_handlers/tcp_output_handler.py +++ b/xdevs/plugins/output_handlers/tcp_output_handler.py @@ -3,6 +3,7 @@ import threading from typing import Any, Callable +from xdevs.plugins.util.socket_server import SocketServer from xdevs.rt_sim.output_handler import OutputHandler @@ -23,44 +24,51 @@ def __init__(self, **kwargs): """ super().__init__(**kwargs) - self.host: str = kwargs.get('host', 'LocalHost') - self.port: int = kwargs.get('port') - if self.port is None: - raise ValueError('TCP port is mandatory') + self.client_address: tuple[Any, ...] = kwargs.get('address') + if self.client_address is None: + host: str = kwargs.get('host', 'LocalHost') + port: int = kwargs.get('port') + if port is None: + raise ValueError('TCP port is mandatory') + self.client_address = (host, port) + + self.server_socket = kwargs.get('server_socket') + + self.client = SocketServer(server_address=self.client_address, server_socket=self.server_socket) + self.t_wait: float = kwargs.get('t_wait', 10) self.event_parser: Callable[[str, Any], str] = kwargs.get('event_parser', lambda port, msg: f'{port},{msg}') - self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + # self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.is_connected: bool = False - def exit(self): - print(f'Closing client to server {self.host} in port {self.port}...') - self.client_socket.close() - self.is_connected = False +# def exit(self): +# print(f'Closing client to server {host} in port {self.port}...') +# self.client_socket.close() +# self.is_connected = False def run(self): - timeout = 0 + timeout = 0 # Probar a poner aqui time.time() y ver que pasa while True: # Wait for an outgoing event event = self.pop_event() try: if self.is_connected: - #if self.client_socket.fileno() > 0: # TODO no podemos usar esto en lugar de self.is_connected? - # We can only send data if the client_socket is not close. Client_socket is closed when - # .fileno() return 0 - self.client_socket.sendall(event.encode()) + # self.client_socket.sendall(event.encode()) + self.client.output_queue.put(event) elif time.time() > timeout: try: - self.client_socket.connect((self.host, self.port)) - print('Connected to server...') + # self.client_socket.connect((self.host, self.port)) + # print('Connected to server...') + + self.client.start_oh() self.is_connected = True - # TODO el mensaje habría que inyectarlo!! - # LLega un msg -> probamos a conectarnos -> Exito! pero... el msg se envio antes de conectarse - # se puede tratar como instante inicial I guess. - self.client_socket.sendall(event.encode()) + # self.client_socket.sendall(event.encode()) + + self.client.output_queue.put(event) except ConnectionRefusedError: # If the connection is refused, wait for a time t_wait and try again. diff --git a/xdevs/plugins/util/socket_server.py b/xdevs/plugins/util/socket_server.py index 9ae0ecd..4fc3e70 100644 --- a/xdevs/plugins/util/socket_server.py +++ b/xdevs/plugins/util/socket_server.py @@ -42,7 +42,10 @@ def output_client_handler(client_socket: socket.socket, address: tuple[Any, ...] try: while True: event = output_queue.get() - client_socket.sendall(event) + client_socket.sendall(event.encode()) + except OSError as e: + # If a system error occurred when connecting, we assume that the server has been shut down. + print(f'Error while connecting to server: {e}') finally: print(f'socket output client disconnected from {address}') client_socket.close() @@ -67,9 +70,10 @@ def __init__(self, server_address: tuple[Any, ...], server_socket: socket.socket self.max_clients: int | None = max_clients self.clients: list[threading.Thread] = list() # TODO yo creo que nos lo podemos ahorrar - self.input_queue: queue.SimpleQueue = queue.SimpleQueue() + self.input_queue: queue.SimpleQueue = queue.SimpleQueue() # Para InputHandler + self.output_queue: queue.SimpleQueue = queue.SimpleQueue() # Para OutputHandler - def start(self): + def start_ih(self): self.server_socket.bind(self.server_address) self.server_socket.listen(self.max_clients) print(f'socket server with address {self.server_address} is listening...') @@ -79,4 +83,14 @@ def start(self): # TODO la hebra etc. la abre el handler de turno self.clients.append(threading.Thread(target=input_client_handler, daemon=True, args=(client_socket, address, self.input_queue))) + #print('TCP MSG ARRIVED') self.clients[-1].start() + + def start_oh(self): + self.server_socket.connect(self.server_address) + print('Connected to server...') + c_thread = threading.Thread(target=output_client_handler, daemon=True, + args=(self.server_socket, self.server_address, self.output_queue)) + c_thread.start() + + diff --git a/xdevs/rt_sim/input_handler.py b/xdevs/rt_sim/input_handler.py index aae7fc6..37cf22d 100644 --- a/xdevs/rt_sim/input_handler.py +++ b/xdevs/rt_sim/input_handler.py @@ -1,9 +1,13 @@ from __future__ import annotations + +import datetime from abc import ABC, abstractmethod from typing import ClassVar, Type, Callable, Any import sys import pkg_resources +from xdevs.rt_sim.mqtt_connector import Connector + class InputHandler(ABC): def __init__(self, **kwargs): @@ -24,6 +28,9 @@ def __init__(self, **kwargs): 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', None) + 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 @@ -41,22 +48,30 @@ 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 + #print(f'HAGO PUSH MSG DE {port},{msg}') 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.""" + #print(f'Entro en push_msg con port ->{port}') 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) + #print(f'EL msg = {msg}') except Exception as e: # if an exception is triggered while parsing the message, we ignore it - print(f'error parsing input msg ("{msg}"): {e}. Message will be ignored', file=sys.stderr) + print(f'error parsing input msg ("{msg}") in port {port}: {e}. Message will be ignored', file=sys.stderr) return + #print('###') + #print(f'EVENTO ENTRA EN LA COLA EN:{datetime.datetime.time()}') self.queue.put((port, msg)) + #print(f'COla = {self.queue}, puse en {port}:{msg}') class InputHandlers: diff --git a/xdevs/rt_sim/mqtt_connector.py b/xdevs/rt_sim/mqtt_connector.py new file mode 100644 index 0000000..029af14 --- /dev/null +++ b/xdevs/rt_sim/mqtt_connector.py @@ -0,0 +1,23 @@ +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): + #print(f'Le paso port = {port}') + #print(self.connections) + if self.connections is not None: + for key, value in self.connections.items(): + if port == key: + return value + #print(f'Devuelvo port = {port}') + return port + + diff --git a/xdevs/rt_sim/output_handler.py b/xdevs/rt_sim/output_handler.py index b23db52..ee51864 100644 --- a/xdevs/rt_sim/output_handler.py +++ b/xdevs/rt_sim/output_handler.py @@ -12,6 +12,14 @@ def __init__(self, **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 putted. + :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() @@ -32,10 +40,10 @@ def run(self): pass def pop_event(self) -> Any: - """Waits until it recevies an outgoing event and parses it with the desired format.""" + """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}') + # print(f'POP_EVENT: recibo port = {port} y msg = {msg}') try: event = self.event_parser(port, msg) except Exception as e: @@ -47,7 +55,7 @@ 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}') + # 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: diff --git a/xdevs/rt_sim/rt_manager.py b/xdevs/rt_sim/rt_manager.py index 10b8638..aa622aa 100644 --- a/xdevs/rt_sim/rt_manager.py +++ b/xdevs/rt_sim/rt_manager.py @@ -43,7 +43,7 @@ def __init__(self, max_jitter: float = None, time_scale: float = 1, event_window self.threads = list() # Queue for processing the external events that are being injecting in the system - self.input_queue = queue.SimpleQueue() + self.input_queue: queue.SimpleQueue = queue.SimpleQueue() # Lists for storing any handler self.input_handlers: list[InputHandler] = list() self.output_handlers: list[OutputHandler] = list() From d91b1ee1e3f18fa5f2567b68a4e39a3465a8856d Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Thu, 5 Oct 2023 09:32:53 +0200 Subject: [PATCH 36/60] LastCodeReview Needs cleaning --- xdevs/examples/store_cashier/employeesSys.py | 2 +- xdevs/plugins/input_handlers/mqtt_input_handler.py | 2 +- xdevs/plugins/input_handlers/tcp_input_handler.py | 4 ++-- xdevs/plugins/output_handlers/mqtt_output_handler.py | 2 +- xdevs/plugins/util/socket_server.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/xdevs/examples/store_cashier/employeesSys.py b/xdevs/examples/store_cashier/employeesSys.py index 310bdb1..693dc82 100644 --- a/xdevs/examples/store_cashier/employeesSys.py +++ b/xdevs/examples/store_cashier/employeesSys.py @@ -8,7 +8,7 @@ class EmployeesSys(Coupled): - def __init__(self, n_employees: int = 3, mean_employees: float = 5, + def __init__(self, n_employees: int = 3, mean_employees: float = 10, stddev_employees: float = 0.8, name=None): super().__init__(name) diff --git a/xdevs/plugins/input_handlers/mqtt_input_handler.py b/xdevs/plugins/input_handlers/mqtt_input_handler.py index 48d02b0..9037107 100644 --- a/xdevs/plugins/input_handlers/mqtt_input_handler.py +++ b/xdevs/plugins/input_handlers/mqtt_input_handler.py @@ -76,7 +76,7 @@ def initialize(self): def run(self): while True: event = self.event_queue.get() - print(f'MQTT: Event pushed: {event} t = {datetime.datetime.now()}') + print(f'MQTT: Event pushed') # {event} t = {datetime.datetime.now()}') self.push_event(event) if __name__ == '__main__': diff --git a/xdevs/plugins/input_handlers/tcp_input_handler.py b/xdevs/plugins/input_handlers/tcp_input_handler.py index 96b614d..259b3f8 100644 --- a/xdevs/plugins/input_handlers/tcp_input_handler.py +++ b/xdevs/plugins/input_handlers/tcp_input_handler.py @@ -40,7 +40,7 @@ def __init__(self, **kwargs): raise ValueError('TCP port is mandatory') self.server_address = (host, port) self.server_socket = kwargs.get('socket') - self.max_clients: int | None = kwargs.get('max_clients', 5) # Si no le paso nada da error en socket_server + self.max_clients: int | None = kwargs.get('max_clients', 5) # Si no le paso nada da error en socket_server # create socket server to handle the communications self.server = SocketServer(self.server_address, self.server_socket, self.max_clients) @@ -53,7 +53,7 @@ def run(self): """It just forwards messages from the server queue to the RT manager's queue.""" while True: event = self.server.input_queue.get() - print(f'TCP: Event pushed: [{event.decode()}]') # Porque no .decode() + print(f'TCP: Event pushed') #: [{event.decode()}]') # Porque no .decode() self.push_event(event) diff --git a/xdevs/plugins/output_handlers/mqtt_output_handler.py b/xdevs/plugins/output_handlers/mqtt_output_handler.py index bb20293..4ec0dc9 100644 --- a/xdevs/plugins/output_handlers/mqtt_output_handler.py +++ b/xdevs/plugins/output_handlers/mqtt_output_handler.py @@ -31,7 +31,7 @@ def run(self): while True: topic, payload = self.pop_event() self.client.publish(topic, payload) - print(f'MQTT sends : {topic} : {payload} > {datetime.datetime.now()}') + print(f'MQTT sends ') #: {topic} : {payload} > {datetime.datetime.now()}') if __name__ == '__main__': diff --git a/xdevs/plugins/util/socket_server.py b/xdevs/plugins/util/socket_server.py index 4fc3e70..6883eea 100644 --- a/xdevs/plugins/util/socket_server.py +++ b/xdevs/plugins/util/socket_server.py @@ -79,7 +79,7 @@ def start_ih(self): print(f'socket server with address {self.server_address} is listening...') while True: client_socket, address = self.server_socket.accept() - # TODO en vez de esto, añadimos el resultado de eaccept a la cola + # TODO en vez de esto, añadimos el resultado de accept a la cola # TODO la hebra etc. la abre el handler de turno self.clients.append(threading.Thread(target=input_client_handler, daemon=True, args=(client_socket, address, self.input_queue))) From 5fc964c3e08b1ae7643723749a0f31bb64dc7db6 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Fri, 6 Oct 2023 12:18:54 +0200 Subject: [PATCH 37/60] First approach JSON --- xdevs/coupled_model.json | 104 +++++++++++++++++++++++++++++++++++++++ xdevs/unwrap_json.py | 52 ++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 xdevs/coupled_model.json create mode 100644 xdevs/unwrap_json.py diff --git a/xdevs/coupled_model.json b/xdevs/coupled_model.json new file mode 100644 index 0000000..55de2be --- /dev/null +++ b/xdevs/coupled_model.json @@ -0,0 +1,104 @@ +{ + "components": { + "c0": { + "type": "Processor", + "components": {}, + "id": "p0", + "att": { + "name": "Proc", + "proc_time": 3 + } + }, + "c1": { + "type": "None", + "components": { + "c10": { + "type": "Generator", + "components": {}, + "id": "g0", + "att": { + "name": "Gen", + "period": 5 + } + }, + "c11": { + "type": "Transducer", + "components": {}, + "id": "t0", + "att": { + "name": "Trans", + "obs_time": 100 + } + }, + "eic": [ + [ + "c_in", + [ + "t0", + "i_solved" + ] + ] + ], + "ic": [ + [ + [ + "t0", + "o_out" + ], + [ + "g0", + "i_stop" + ] + ], + [ + [ + "g0", + "o_out" + ], + [ + "t0", + "i_arrived" + ] + ] + ], + "eoc": [ + [ + [ + "g0", + "o_out" + ], + "c_out" + ] + ] + }, + "id": "EF", + "att": {} + }, + + + "eic": [], + "ic": [ + [ + [ + "EF", + "c_out" + ], + [ + "p0", + "i_in" + ] + ], + [ + [ + "p0", + "o_out" + ], + [ + "EF", + "c_in" + ] + ] + ], + "eoc": [] + } +} diff --git a/xdevs/unwrap_json.py b/xdevs/unwrap_json.py new file mode 100644 index 0000000..9efc1f6 --- /dev/null +++ b/xdevs/unwrap_json.py @@ -0,0 +1,52 @@ +import distutils.cygwinccompiler +import json +from xdevs.examples.basic.basic import Generator, Processor, Transducer + +with open("coupled_model.json") as f: + file = json.load(f) + + +Components_json = list() # lista para guardar los dict de cada componente +Conex = list() # Lista para guardar todas las conexiones que tienen que hacerse +Comp = list() # Lista final para guardar los objetos creados +def unwrap_components(**dic: dict): + for k, v in dic.get("components").items(): + if isinstance(v, dict): + #print(f'{k} = {v}', flush=True) + if not v.get("components").items(): + Components_json.append(v) + unwrap_components(**v) + elif isinstance(v, list): + #print(f'Conexiones: {v}', flush=True) + Conex.append(v) + else: + pass + + +def create_components(comp: list[dict]): + # Creamos componentes como una tupla (id, objeto) Para luego hacer las conexiones ayudandonos del id que es el que + # usa en JSON + for item in comp: + if item.get("type") == "Processor": + Comp.append((item.get("id"), Processor(**item.get("att")))) + elif item.get("type") == "Generator": + Comp.append((item.get("id"), Generator(**item.get("att")))) + elif item.get("type") == "Transducer": + Comp.append((item.get("id"), Transducer(**item.get("att")))) + else: + pass + + + + +unwrap_components(**file) +create_components(Components_json) + +print(len(Comp)) +""" +print(Components_json, len(Components_json)) + +Conex = Conex[::-1] # Es necesario girar la lista de conexiones para ir de fuera a dentro. OJO porque cuando tengamos + # acoplados (distintas ramas hacia abajo) esto se va a romper. +print(Conex, len(Conex)) +""" \ No newline at end of file From b591a8ab94df5720cc6ebf8c6822f129005a6e76 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Tue, 17 Oct 2023 12:57:16 +0200 Subject: [PATCH 38/60] Coupled obtained from json The method "from_json" is able to extract a coupled model from a json file --- xdevs/coupled_model_v2.json | 81 ++++++++++++++++++++++++++ xdevs/unwrap_json_v2.py | 112 ++++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 xdevs/coupled_model_v2.json create mode 100644 xdevs/unwrap_json_v2.py diff --git a/xdevs/coupled_model_v2.json b/xdevs/coupled_model_v2.json new file mode 100644 index 0000000..f206551 --- /dev/null +++ b/xdevs/coupled_model_v2.json @@ -0,0 +1,81 @@ +{ + "coupled/MasterModel": { + "components": { + "name/GPT": { + "components": { + + "atomic/Processor": { + "name": "p0", + "class": "xdevs.core.examples.efp.Processor", + "kwargs": { + "proc_time": "3.0" + } + }, + "atomic/generator": { + "name": "g0", + "class": "xdevs.core.examples.efp.Generator", + "kwargs": { + "period": "1.0" + } + }, + "atomic/transducer": { + "name": "t0", + "class": "xdevs.core.examples.efp.Transducer", + "kwargs": { + "obs_time": "100.0" + } + } + }, + + "connections": [ + { + "componentFrom": "p0", + "portFrom": "o_out", + "componentTo": "t0", + "portTo": "i_solved" + }, + { + "componentFrom": "g0", + "portFrom": "o_out", + "componentTo": "p0", + "portTo": "i_in" + }, + { + "componentFrom": "g0", + "portFrom": "o_out", + "componentTo": "t0", + "portTo": "i_arrived" + }, + { + "componentFrom": "t0", + "portFrom": "o_out", + "componentTo": "g0", + "portTo": "i_stop" + }, + { + "componentFrom": null, + "portFrom": "i_in_gpt", + "componentTo": "g0", + "portTo": "i_stop" + } + ] + }, + "atomic/Processor": { + "name": "p1", + "class": "xdevs.core.examples.efp.Processor", + "kwargs": { + "proc_time": "300.0" + } + } + }, + + "connections": [ + { + "componentFrom": "p1", + "portFrom": "o_out", + "componentTo": "name/GPT", + "portTo": "i_in_gpt" + } + ] + } +} \ No newline at end of file diff --git a/xdevs/unwrap_json_v2.py b/xdevs/unwrap_json_v2.py new file mode 100644 index 0000000..9292e18 --- /dev/null +++ b/xdevs/unwrap_json_v2.py @@ -0,0 +1,112 @@ +# This approach is based on the java implementation in xdevs.java + +# Now we are dealing with a GPT model instead of an EFP. + +import importlib +import logging +import json + +from xdevs.models import Coupled, Atomic, Port +from xdevs.examples.basic.basic import Processor, Transducer, Generator + + +# TODO fabrica componentes +def create_atomic(info: dict): + + name = info.get('name', None) + clase: str = info.get('class', None) + valores = info.get('kwargs') + ato = None + # print(f'clase = {clase}') + # print(f'valores = {valores}') + + if clase.split('.')[-1] == 'Processor': + ato = Processor(name=name, proc_time=valores.get('proc_time')) + elif clase.split('.')[-1] == 'Transducer': + ato = Transducer(name=name, obs_time=valores.get('obs_time')) + elif clase.split('.')[-1] == 'Generator': + ato = Generator(name=name, period=valores.get('period')) + else: + pass + + return ato + +# @staticmethod de la clase Coupled +def from_json(data): + name = list(data.keys())[0] # Obtiene el nombre del componente acoplado actual + print(name) + padre = Coupled(name) + dict_comp = dict() # dict para almacenar los componentes + + for key in data[name].get('components', {}): + current_data = data[name]['components'][key] + + if 'components' in current_data: + + hijo = from_json({key: current_data}) + padre.add_component(hijo) + dict_comp[hijo.name] = hijo + + elif 'name' in current_data: + at = create_atomic(current_data) + Comp.append(at) + dict_comp[at.name] = at + padre.add_component(at) + + else: + raise Exception('No component found') + + if 'connections' in data[name]: + connections_data = data[name]['connections'] + # print(dict_comp) + for connection in connections_data: + # print(connection) + + # Connexion ic + if connection['componentFrom'] in dict_comp and connection['componentTo'] in dict_comp: + port_from = dict_comp[connection['componentFrom']].get_out_port(connection['portFrom']) + port_to = dict_comp[connection['componentTo']].get_in_port(connection['portTo']) + padre.add_coupling(port_from, port_to) + + # Connexion eic + elif connection['componentFrom'] is None and connection['componentTo'] in dict_comp: + print('!!!!!') + port_to = dict_comp[connection['componentTo']].get_in_port(connection['portTo']) + p_in = Port(p_type=type(port_to), name=connection['portFrom']) + padre.add_in_port(p_in) + padre.add_coupling(padre.get_in_port(p_in.name), port_to) + + # Connexion eoc + elif connection['componentFrom'] in dict_comp and connection['componentTo'] is None: + print('¡¡¡¡¡') + port_from = dict_comp[connection['componentFrom']].get_out_port(connection['portFrom']) + p_out = Port(p_type=type(port_from), name=connection['portOut']) + padre.add_out_port(p_out) + padre.add_coupling(port_from, padre.get_out_port(p_out.name)) + + else: + raise Exception('Ports not found') + + return padre + + + + + + + + +with open("coupled_model_v2.json") as f: + file = json.load(f) + +Comp = list() +Conections = list() + +PJ = Coupled(name='ModelTest') + +C = from_json(file) +print(C) + +print(f'Componentes añadidos: {Comp}') + +print(f'Conexiones añadidos: {Conections}') From dec580d4a7ddb350e23b5688f60656eefab1abe2 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Thu, 26 Oct 2023 10:52:01 +0200 Subject: [PATCH 39/60] Component Factory implementation --- setup.py | 8 +++++ xdevs/coupled_model_v2.json | 26 +++++++++++----- xdevs/unwrap_json_v2.py | 60 +++++++++++++++++++++++++------------ 3 files changed, 67 insertions(+), 27 deletions(-) diff --git a/setup.py b/setup.py index 86390dc..f42ec21 100644 --- a/setup.py +++ b/setup.py @@ -29,6 +29,14 @@ 'mqtt_handler = xdevs.plugins.output_handlers.mqtt_output_handler:MQTTOutputHandler', ], + 'xdevs': [ + 'Generator = xdevs.examples.basic.basic:Generator', + 'Transducer = xdevs.examples.basic.basic:Transducer', + 'Processor = xdevs.examples.basic.basic:Processor', + 'GPT = xdevs.examples.basic.basic:Gpt' + ], + + 'xdevs.plugins.wrappers': [ 'pypdevs = xdevs.plugins.wrappers.pypdevs:PyPDEVSWrapper' ]}, diff --git a/xdevs/coupled_model_v2.json b/xdevs/coupled_model_v2.json index f206551..5b9ef03 100644 --- a/xdevs/coupled_model_v2.json +++ b/xdevs/coupled_model_v2.json @@ -6,23 +6,23 @@ "atomic/Processor": { "name": "p0", - "class": "xdevs.core.examples.efp.Processor", + "class": "Processor", "kwargs": { - "proc_time": "3.0" + "proc_time": 3.0 } }, "atomic/generator": { "name": "g0", - "class": "xdevs.core.examples.efp.Generator", + "class": "Generator", "kwargs": { - "period": "1.0" + "period": 1.0 } }, "atomic/transducer": { "name": "t0", - "class": "xdevs.core.examples.efp.Transducer", + "class": "Transducer", "kwargs": { - "obs_time": "100.0" + "obs_time": 100.0 } } }, @@ -60,11 +60,21 @@ } ] }, + "atomic/Processor": { "name": "p1", - "class": "xdevs.core.examples.efp.Processor", + "class": "Processor", + "kwargs": { + "proc_time": 300.0 + } + }, + + "c/gpt" : { + "name": "gptTest", + "class": "GPT", "kwargs": { - "proc_time": "300.0" + "period": 3, + "obs_time": 100 } } }, diff --git a/xdevs/unwrap_json_v2.py b/xdevs/unwrap_json_v2.py index 9292e18..11ffdbc 100644 --- a/xdevs/unwrap_json_v2.py +++ b/xdevs/unwrap_json_v2.py @@ -5,31 +5,23 @@ import importlib import logging import json +import pkg_resources +from typing import ClassVar, Type from xdevs.models import Coupled, Atomic, Port from xdevs.examples.basic.basic import Processor, Transducer, Generator +from xdevs.models import Component +from xdevs.sim import Coordinator -# TODO fabrica componentes def create_atomic(info: dict): - name = info.get('name', None) clase: str = info.get('class', None) valores = info.get('kwargs') - ato = None - # print(f'clase = {clase}') - # print(f'valores = {valores}') - if clase.split('.')[-1] == 'Processor': - ato = Processor(name=name, proc_time=valores.get('proc_time')) - elif clase.split('.')[-1] == 'Transducer': - ato = Transducer(name=name, obs_time=valores.get('obs_time')) - elif clase.split('.')[-1] == 'Generator': - ato = Generator(name=name, period=valores.get('period')) - else: - pass + component = ComponentsFactory.create_component_handler(class_id=clase, name=name, **valores) + return component - return ato # @staticmethod de la clase Coupled def from_json(data): @@ -70,17 +62,17 @@ def from_json(data): # Connexion eic elif connection['componentFrom'] is None and connection['componentTo'] in dict_comp: - print('!!!!!') + # print('!!!!!') port_to = dict_comp[connection['componentTo']].get_in_port(connection['portTo']) - p_in = Port(p_type=type(port_to), name=connection['portFrom']) + p_in = Port(p_type=port_to.p_type, name=connection['portFrom']) padre.add_in_port(p_in) padre.add_coupling(padre.get_in_port(p_in.name), port_to) # Connexion eoc elif connection['componentFrom'] in dict_comp and connection['componentTo'] is None: - print('¡¡¡¡¡') + # print('¡¡¡¡¡') port_from = dict_comp[connection['componentFrom']].get_out_port(connection['portFrom']) - p_out = Port(p_type=type(port_from), name=connection['portOut']) + p_out = Port(p_type=port_from.p_type, name=connection['portOut']) padre.add_out_port(p_out) padre.add_coupling(port_from, padre.get_out_port(p_out.name)) @@ -90,10 +82,34 @@ def from_json(data): return padre +class ComponentsFactory: + _plugins: ClassVar[dict[str, Type[Component]]] = { + ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs')} + @staticmethod + def add_plugin(name: str, plugin: Type[Component]): + """ + Registers a custom component to the plugin system. + :param name: name used to identify the custom component. It must be unique. + :param plugin: custom component type. Note that it must not be an object, just the class. + """ + if name in ComponentsFactory._plugins: + raise ValueError('xDEVS component plugin with name "{}" already exists'.format(name)) + ComponentsFactory._plugins[name] = plugin + @staticmethod + def create_component_handler(class_id: str, **kwargs) -> Component: + """ + Creates a new component. + :param class_id: unique ID of the component to be created. + :param kwargs: any additional configuration parameter needed for creating the component. + :return: an instance of the Component class. + """ + if class_id not in ComponentsFactory._plugins: + raise ValueError('xDEVS component plugin with name "{}" not found'.format(class_id)) + return ComponentsFactory._plugins[class_id](**kwargs) with open("coupled_model_v2.json") as f: @@ -105,8 +121,14 @@ def from_json(data): PJ = Coupled(name='ModelTest') C = from_json(file) -print(C) + +# G = ComponentsFactory.create_component_handler('Generator', name='GenTest', period=3) +# P = ComponentsFactory.create_component_handler('Processor', name='PTest', proc_time=5) + +# print(C, P, G) print(f'Componentes añadidos: {Comp}') +for c in Comp: + print(c) print(f'Conexiones añadidos: {Conections}') From e282923a2b6ceeee43fe095be87ce2f7cad0228c Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Fri, 27 Oct 2023 11:36:00 +0200 Subject: [PATCH 40/60] Doc Added documentation and the EFP example --- setup.py | 4 +- xdevs/coupled_model.json | 130 +++++------------ xdevs/examples/basic/basic.py | 4 +- xdevs/examples/basic/efp_model.py | 54 +++++++ xdevs/examples/basic/json_gpt_model.json | 53 +++++++ xdevs/unwrap_json.py | 52 ------- xdevs/unwrap_json_v2.py | 173 ++++++++++++++--------- 7 files changed, 255 insertions(+), 215 deletions(-) create mode 100644 xdevs/examples/basic/efp_model.py create mode 100644 xdevs/examples/basic/json_gpt_model.json delete mode 100644 xdevs/unwrap_json.py diff --git a/setup.py b/setup.py index f42ec21..980a1d1 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,9 @@ 'Generator = xdevs.examples.basic.basic:Generator', 'Transducer = xdevs.examples.basic.basic:Transducer', 'Processor = xdevs.examples.basic.basic:Processor', - 'GPT = xdevs.examples.basic.basic:Gpt' + 'GPT = xdevs.examples.basic.basic:Gpt', + 'EF = xdevs.examples.basic.efp_model:EF', + 'EFP = xdevs.examples.basic.efp_model:EFP' ], diff --git a/xdevs/coupled_model.json b/xdevs/coupled_model.json index 55de2be..b838f1c 100644 --- a/xdevs/coupled_model.json +++ b/xdevs/coupled_model.json @@ -1,104 +1,40 @@ { - "components": { - "c0": { - "type": "Processor", - "components": {}, - "id": "p0", - "att": { - "name": "Proc", - "proc_time": 3 - } - }, - "c1": { - "type": "None", - "components": { - "c10": { - "type": "Generator", - "components": {}, - "id": "g0", - "att": { - "name": "Gen", - "period": 5 - } - }, - "c11": { - "type": "Transducer", - "components": {}, - "id": "t0", - "att": { - "name": "Trans", - "obs_time": 100 - } - }, - "eic": [ - [ - "c_in", - [ - "t0", - "i_solved" - ] - ] - ], - "ic": [ - [ - [ - "t0", - "o_out" - ], - [ - "g0", - "i_stop" - ] - ], - [ - [ - "g0", - "o_out" - ], - [ - "t0", - "i_arrived" - ] - ] - ], - "eoc": [ - [ - [ - "g0", - "o_out" - ], - "c_out" - ] - ] + "EFP" : { + "components": { + + "Processor": { + "name" : "P0", + "class": "Processor", + "kwargs": { + "proc_time": 10 + } }, - "id": "EF", - "att": {} + + "ef": { + "name": "ef0", + "class": "EF", + "kwargs": { + "period": 5, + "obs_time": 300 + } + + } }, + "connections" : [ + { + "componentFrom": "ef0", + "portFrom": "p_out_ef", + "componentTo": "P0", + "portTo": "i_in" + }, + { + "componentFrom": "P0", + "portFrom": "o_out", + "componentTo": "ef0", + "portTo": "p_in_ef" + } - "eic": [], - "ic": [ - [ - [ - "EF", - "c_out" - ], - [ - "p0", - "i_in" - ] - ], - [ - [ - "p0", - "o_out" - ], - [ - "EF", - "c_in" - ] - ] - ], - "eoc": [] + ] } } diff --git a/xdevs/examples/basic/basic.py b/xdevs/examples/basic/basic.py index 11653fb..011484c 100644 --- a/xdevs/examples/basic/basic.py +++ b/xdevs/examples/basic/basic.py @@ -4,7 +4,7 @@ from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger from xdevs.models import Atomic, Coupled, Port from xdevs.sim import Coordinator -from xdevs.rt_sim import CoordinatorRt +from xdevs.rt_sim import RealTimeCoordinator import time logger = get_logger(__name__, logging.DEBUG) @@ -187,7 +187,7 @@ def __init__(self, name, period, obs_time): if __name__ == '__main__': gpt = Gpt("gpt", 2, 100) - coord = CoordinatorRt(gpt, 1, 0.10) + coord = Coordinator(gpt) coord.initialize() # coord.simulate() coord.simulate() diff --git a/xdevs/examples/basic/efp_model.py b/xdevs/examples/basic/efp_model.py new file mode 100644 index 0000000..31f671a --- /dev/null +++ b/xdevs/examples/basic/efp_model.py @@ -0,0 +1,54 @@ +from xdevs.examples.basic.basic import Generator, Transducer, Job, Processor +from xdevs.models import Coupled, Port +from xdevs.sim import Coordinator + + +class EF(Coupled): + def __init__(self, name, period, obs_time): + super().__init__(name) + + if period < 1: + raise ValueError("period has to be greater than 0") + + if obs_time < 0: + raise ValueError("obs_time has to be greater or equal than 0") + + gen = Generator(name='EF_gen', period=period) + trans = Transducer(name='EF_trans', obs_time=obs_time) + + self.add_component(gen) + self.add_component(trans) + + self.p_in_ef = Port(Job, name='p_in_ef') + self.p_out_ef = Port(Job, name='p_out_ef') + + self.add_in_port(self.p_in_ef) + self.add_out_port(self.p_out_ef) + + self.add_coupling(gen.o_out, trans.i_arrived) + self.add_coupling(gen.o_out, self.p_out_ef) + self.add_coupling(trans.o_out, gen.i_stop) + self.add_coupling(self.p_in_ef, trans.i_solved) + + +class EFP(Coupled): + def __init__(self, name, period, obs_time, proc_time): + super().__init__(name) + + ef = Ef(name='EF', period=period, obs_time=obs_time) + + proc = Processor(name='EFP_proc', proc_time=proc_time) + + self.add_component(ef) + self.add_component(proc) + + self.add_coupling(ef.p_out_ef, proc.i_in) + self.add_coupling(proc.o_out, ef.p_in_ef) + + +if __name__ == '__main__': + + EFP = EFP('efp', 3, 100, 5) + Coord = Coordinator(EFP) + Coord.initialize() + Coord.simulate() diff --git a/xdevs/examples/basic/json_gpt_model.json b/xdevs/examples/basic/json_gpt_model.json new file mode 100644 index 0000000..e1f2cb1 --- /dev/null +++ b/xdevs/examples/basic/json_gpt_model.json @@ -0,0 +1,53 @@ +{ + "nameGPT": { + "components": { + "atomic/Processor": { + "name": "p0", + "class": "xdevs.core.examples.efp.Processor", + "kwargs": { + "proc_time": 3.0 + } + }, + "atomic/generator": { + "name": "g0", + "class": "xdevs.core.examples.efp.Generator", + "kwargs": { + "period": 1.0 + } + }, + "atomic/transducer": { + "name": "t0", + "class": "xdevs.core.examples.efp.Transducer", + "kwargs": { + "obs_time": 100.0 + } + } + }, + "connections": [ + { + "componentFrom": "p0", + "portFrom": "o_out", + "componentTo": "t0", + "portTo": "i_solved" + }, + { + "componentFrom": "g0", + "portFrom": "o_out", + "componentTo": "p0", + "portTo": "i_in" + }, + { + "componentFrom": "g0", + "portFrom": "o_out", + "componentTo": "t0", + "portTo": "i_arrived" + }, + { + "componentFrom": "t0", + "portFrom": "o_out", + "componentTo": "g0", + "portTo": "i_stop" + } + ] + } +} \ No newline at end of file diff --git a/xdevs/unwrap_json.py b/xdevs/unwrap_json.py deleted file mode 100644 index 9efc1f6..0000000 --- a/xdevs/unwrap_json.py +++ /dev/null @@ -1,52 +0,0 @@ -import distutils.cygwinccompiler -import json -from xdevs.examples.basic.basic import Generator, Processor, Transducer - -with open("coupled_model.json") as f: - file = json.load(f) - - -Components_json = list() # lista para guardar los dict de cada componente -Conex = list() # Lista para guardar todas las conexiones que tienen que hacerse -Comp = list() # Lista final para guardar los objetos creados -def unwrap_components(**dic: dict): - for k, v in dic.get("components").items(): - if isinstance(v, dict): - #print(f'{k} = {v}', flush=True) - if not v.get("components").items(): - Components_json.append(v) - unwrap_components(**v) - elif isinstance(v, list): - #print(f'Conexiones: {v}', flush=True) - Conex.append(v) - else: - pass - - -def create_components(comp: list[dict]): - # Creamos componentes como una tupla (id, objeto) Para luego hacer las conexiones ayudandonos del id que es el que - # usa en JSON - for item in comp: - if item.get("type") == "Processor": - Comp.append((item.get("id"), Processor(**item.get("att")))) - elif item.get("type") == "Generator": - Comp.append((item.get("id"), Generator(**item.get("att")))) - elif item.get("type") == "Transducer": - Comp.append((item.get("id"), Transducer(**item.get("att")))) - else: - pass - - - - -unwrap_components(**file) -create_components(Components_json) - -print(len(Comp)) -""" -print(Components_json, len(Components_json)) - -Conex = Conex[::-1] # Es necesario girar la lista de conexiones para ir de fuera a dentro. OJO porque cuando tengamos - # acoplados (distintas ramas hacia abajo) esto se va a romper. -print(Conex, len(Conex)) -""" \ No newline at end of file diff --git a/xdevs/unwrap_json_v2.py b/xdevs/unwrap_json_v2.py index 11ffdbc..a153a76 100644 --- a/xdevs/unwrap_json_v2.py +++ b/xdevs/unwrap_json_v2.py @@ -1,10 +1,7 @@ # This approach is based on the java implementation in xdevs.java - -# Now we are dealing with a GPT model instead of an EFP. - -import importlib -import logging import json +import sys + import pkg_resources from typing import ClassVar, Type @@ -14,36 +11,85 @@ from xdevs.sim import Coordinator -def create_atomic(info: dict): - name = info.get('name', None) - clase: str = info.get('class', None) - valores = info.get('kwargs') +def create_component(info: dict): + """ + A function to create components based on the ComponentsFactory - component = ComponentsFactory.create_component_handler(class_id=clase, name=name, **valores) + :param info: a dictionary that contains the class_id and the arguments to create the component + :return: a component (Coupled/Atomic) with the parameters set in info + """ + name = info.get('name', None) + class_id: str = info.get('class', None) + values = info.get('kwargs') + component = ComponentsFactory.create_component_handler(class_id=class_id, name=name, **values) return component # @staticmethod de la clase Coupled def from_json(data): - name = list(data.keys())[0] # Obtiene el nombre del componente acoplado actual + """ + A function to parser a json file into a DEVS model + + The structure of the json file must be as follows: + + { + "MasterCoupledName": { + "components" : { + + "ThisIsACoupledModel" :{ + "components" : {} + "connections" : [] + }, + + "ThisIsAAtomicModel" : { + "name": "AtomicName", + "class": "AtomicClassIdentifier", + "kwargs": { + "a_parameter" : , + ... + } + }, + + ... + + }, + "connections": [ + { + "componentFrom": "", + "portFrom": "", + "componentTo": "", + "portTo": "" + }, + + ... + + ] + } + } + + :param data: a loaded json file of the type dict + :return: a Coupled DEVS model + """ + + name = list(data.keys())[0] # Gets the actual component name print(name) - padre = Coupled(name) - dict_comp = dict() # dict para almacenar los componentes + parent = Coupled(name) + dict_comp = dict() # dict to storage the components for key in data[name].get('components', {}): current_data = data[name]['components'][key] if 'components' in current_data: - hijo = from_json({key: current_data}) - padre.add_component(hijo) - dict_comp[hijo.name] = hijo + child = from_json({key: current_data}) + parent.add_component(child) + dict_comp[child.name] = child elif 'name' in current_data: - at = create_atomic(current_data) - Comp.append(at) - dict_comp[at.name] = at - padre.add_component(at) + component = create_component(current_data) + Comp.append(component) + dict_comp[component.name] = component + parent.add_component(component) else: raise Exception('No component found') @@ -53,33 +99,37 @@ def from_json(data): # print(dict_comp) for connection in connections_data: # print(connection) - - # Connexion ic - if connection['componentFrom'] in dict_comp and connection['componentTo'] in dict_comp: - port_from = dict_comp[connection['componentFrom']].get_out_port(connection['portFrom']) - port_to = dict_comp[connection['componentTo']].get_in_port(connection['portTo']) - padre.add_coupling(port_from, port_to) - - # Connexion eic - elif connection['componentFrom'] is None and connection['componentTo'] in dict_comp: - # print('!!!!!') - port_to = dict_comp[connection['componentTo']].get_in_port(connection['portTo']) - p_in = Port(p_type=port_to.p_type, name=connection['portFrom']) - padre.add_in_port(p_in) - padre.add_coupling(padre.get_in_port(p_in.name), port_to) - - # Connexion eoc - elif connection['componentFrom'] in dict_comp and connection['componentTo'] is None: - # print('¡¡¡¡¡') - port_from = dict_comp[connection['componentFrom']].get_out_port(connection['portFrom']) - p_out = Port(p_type=port_from.p_type, name=connection['portOut']) - padre.add_out_port(p_out) - padre.add_coupling(port_from, padre.get_out_port(p_out.name)) - - else: - raise Exception('Ports not found') - - return padre + try: + # Connexion ic + if connection['componentFrom'] in dict_comp and connection['componentTo'] in dict_comp: + port_from = dict_comp[connection['componentFrom']].get_out_port(connection['portFrom']) + port_to = dict_comp[connection['componentTo']].get_in_port(connection['portTo']) + parent.add_coupling(port_from, port_to) + + # Connexion eic + elif connection['componentFrom'] is None and connection['componentTo'] in dict_comp: + # print('!!!!!') + port_to = dict_comp[connection['componentTo']].get_in_port(connection['portTo']) + p_in = Port(p_type=port_to.p_type, name=connection['portFrom']) + parent.add_in_port(p_in) + parent.add_coupling(parent.get_in_port(p_in.name), port_to) + + # Connexion eoc + elif connection['componentFrom'] in dict_comp and connection['componentTo'] is None: + # print('¡¡¡¡¡') + port_from = dict_comp[connection['componentFrom']].get_out_port(connection['portFrom']) + p_out = Port(p_type=port_from.p_type, name=connection['portOut']) + parent.add_out_port(p_out) + parent.add_coupling(port_from, parent.get_out_port(p_out.name)) + + else: + raise Exception(f'Connection Error! -> {connection}') + + except Exception as e: + print(f'Invalid connection in: {connection}. Reason: {e}', file=sys.stderr) + raise + + return parent class ComponentsFactory: @@ -112,23 +162,20 @@ def create_component_handler(class_id: str, **kwargs) -> Component: return ComponentsFactory._plugins[class_id](**kwargs) -with open("coupled_model_v2.json") as f: - file = json.load(f) - -Comp = list() -Conections = list() - -PJ = Coupled(name='ModelTest') - -C = from_json(file) +if __name__ == '__main__': + with open("coupled_model.json") as f: + file = json.load(f) -# G = ComponentsFactory.create_component_handler('Generator', name='GenTest', period=3) -# P = ComponentsFactory.create_component_handler('Processor', name='PTest', proc_time=5) + print(type(file)) + Comp = list() + Connections = list() -# print(C, P, G) + C = from_json(file) -print(f'Componentes añadidos: {Comp}') -for c in Comp: - print(c) + print(f'Componentes añadidos: {Comp}') + for c in Comp: + print(c) -print(f'Conexiones añadidos: {Conections}') + Coord = Coordinator(C) + Coord.initialize() + Coord.simulate() From 1ce5b3c61057c173ede5e35638e1a4455bb64ac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Fri, 27 Oct 2023 16:32:41 +0200 Subject: [PATCH 41/60] Make MQTT dependency optional --- setup.py | 90 ++++++------ xdevs/examples/basic/__init__.py | 0 xdevs/examples/basic/efp_model.py | 2 +- .../input_handlers/bad_dependencies.py | 15 ++ .../input_handlers/mqtt_input_handler.py | 137 ++++++++++-------- .../output_handlers/bad_dependencies.py | 15 ++ .../output_handlers/mqtt_output_handler.py | 83 ++++++----- xdevs/plugins/transducers/csv_transducer.py | 2 +- .../transducers/elasticsearch_transducer.py | 6 +- xdevs/plugins/transducers/sql_transducer.py | 4 +- xdevs/transducers.py | 5 +- xdevs/wrappers.py | 4 +- 12 files changed, 206 insertions(+), 157 deletions(-) create mode 100644 xdevs/examples/basic/__init__.py create mode 100644 xdevs/plugins/input_handlers/bad_dependencies.py create mode 100644 xdevs/plugins/output_handlers/bad_dependencies.py diff --git a/setup.py b/setup.py index 980a1d1..cbe0a95 100644 --- a/setup.py +++ b/setup.py @@ -1,49 +1,47 @@ from setuptools import setup, find_packages -setup(name='xdevs', - version='2.2.2', - description='xDEVS M&S framework', - url='https://github.com/iscar-ucm/xdevs.py', - author='Román Cárdenas, Kevin Henares', - author_email='r.cardenas@upm.es, khenares@ucm.es', - packages=find_packages(exclude=['xdevs.tests']), - entry_points={ - 'xdevs.plugins.transducers': [ - 'csv = xdevs.plugins.transducers.csv_transducer:CSVTransducer', - 'sql = xdevs.plugins.transducers.sql_transducer:SQLTransducer', - 'elasticsearch = xdevs.plugins.transducers.elasticsearch_transducer:ElasticsearchTransducer', - ], - 'xdevs.plugins.input_handlers': [ - - 'function = xdevs.plugins.input_handlers.callable_function:CallableFunction', - 'csv_handler = xdevs.plugins.input_handlers.csv_input_handler:CSVInputHandler', - 'tcp_handler = xdevs.plugins.input_handlers.tcp_input_handler:TCPInputHandler', - 'mqtt_handler = xdevs.plugins.input_handlers.mqtt_input_handler:MQTTInputHandler', - - ], - - 'xdevs.plugins.output_handlers': [ - 'csv_out_handler = xdevs.plugins.output_handlers.csv_output_handler:CSVOutputHandler', - 'tcp_out_handler = xdevs.plugins.output_handlers.tcp_output_handler:TCPOutputHandler', - 'mqtt_handler = xdevs.plugins.output_handlers.mqtt_output_handler:MQTTOutputHandler', - ], - - 'xdevs': [ - 'Generator = xdevs.examples.basic.basic:Generator', - 'Transducer = xdevs.examples.basic.basic:Transducer', - 'Processor = xdevs.examples.basic.basic:Processor', - 'GPT = xdevs.examples.basic.basic:Gpt', - 'EF = xdevs.examples.basic.efp_model:EF', - 'EFP = xdevs.examples.basic.efp_model:EFP' - ], - - - 'xdevs.plugins.wrappers': [ - 'pypdevs = xdevs.plugins.wrappers.pypdevs:PyPDEVSWrapper' - ]}, - extras_require={ - 'sql': ['sqlalchemy==1.3.22'], - 'elasticsearch': ['elasticsearch==7.10.1'], - }, - zip_safe=False) +setup( + name='xdevs', + version='2.2.2', + description='xDEVS M&S framework', + url='https://github.com/iscar-ucm/xdevs.py', + author='Román Cárdenas, Kevin Henares', + author_email='r.cardenas@upm.es, khenares@ucm.es', + packages=find_packages(exclude=['xdevs.tests']), + entry_points={ + 'xdevs.plugins.transducers': [ + 'csv = xdevs.plugins.transducers.csv_transducer:CSVTransducer', + 'sql = xdevs.plugins.transducers.sql_transducer:SQLTransducer', + 'elasticsearch = xdevs.plugins.transducers.elasticsearch_transducer:ElasticsearchTransducer', + ], + 'xdevs.plugins.input_handlers': [ + 'function = xdevs.plugins.input_handlers.callable_function:CallableFunction', + 'csv_handler = xdevs.plugins.input_handlers.csv_input_handler:CSVInputHandler', + 'tcp_handler = xdevs.plugins.input_handlers.tcp_input_handler:TCPInputHandler', + 'mqtt_handler = xdevs.plugins.input_handlers.mqtt_input_handler:MQTTInputHandler', + + ], + 'xdevs.plugins.output_handlers': [ + 'csv_out_handler = xdevs.plugins.output_handlers.csv_output_handler:CSVOutputHandler', + 'tcp_out_handler = xdevs.plugins.output_handlers.tcp_output_handler:TCPOutputHandler', + 'mqtt_handler = xdevs.plugins.output_handlers.mqtt_output_handler:MQTTOutputHandler', + ], + 'xdevs': [ + 'Generator = xdevs.examples.basic.basic:Generator', + 'Transducer = xdevs.examples.basic.basic:Transducer', + 'Processor = xdevs.examples.basic.basic:Processor', + 'GPT = xdevs.examples.basic.basic:Gpt', + 'EF = xdevs.examples.basic.efp_model:EF', + 'EFP = xdevs.examples.basic.efp_model:EFP' + ], + 'xdevs.plugins.wrappers': [ + 'pypdevs = xdevs.plugins.wrappers.pypdevs:PyPDEVSWrapper' + ]}, + extras_require={ + 'sql': ['sqlalchemy==1.3.22'], + 'elasticsearch': ['elasticsearch==7.10.1'], + 'mqtt': ['paho-mqtt==1.5.1'], + }, + zip_safe=False +) diff --git a/xdevs/examples/basic/__init__.py b/xdevs/examples/basic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xdevs/examples/basic/efp_model.py b/xdevs/examples/basic/efp_model.py index 31f671a..acb9dad 100644 --- a/xdevs/examples/basic/efp_model.py +++ b/xdevs/examples/basic/efp_model.py @@ -35,7 +35,7 @@ class EFP(Coupled): def __init__(self, name, period, obs_time, proc_time): super().__init__(name) - ef = Ef(name='EF', period=period, obs_time=obs_time) + ef = EF(name='EF', period=period, obs_time=obs_time) proc = Processor(name='EFP_proc', proc_time=proc_time) diff --git a/xdevs/plugins/input_handlers/bad_dependencies.py b/xdevs/plugins/input_handlers/bad_dependencies.py new file mode 100644 index 0000000..e972b1b --- /dev/null +++ b/xdevs/plugins/input_handlers/bad_dependencies.py @@ -0,0 +1,15 @@ +from abc import ABC +from xdevs.rt_sim.input_handler import InputHandler + + +class BadDependenciesHandler(InputHandler, ABC): + def __init__(self, **kwargs): + """ + Template input handler for using when dependencies are not imported. + :param str handler_type: transducer type. + """ + super().__init__(**kwargs) + raise ImportError(f'{kwargs.get('handler_type')} input handler specific dependencies are not imported') + + def run(self): + pass diff --git a/xdevs/plugins/input_handlers/mqtt_input_handler.py b/xdevs/plugins/input_handlers/mqtt_input_handler.py index 9037107..885a5af 100644 --- a/xdevs/plugins/input_handlers/mqtt_input_handler.py +++ b/xdevs/plugins/input_handlers/mqtt_input_handler.py @@ -2,93 +2,102 @@ import queue import threading -from paho.mqtt.client import Client -from xdevs.rt_sim.input_handler import InputHandler +try: + from paho.mqtt.client import Client + from xdevs.rt_sim.input_handler import InputHandler -# Desde este input handler me subscribo a topics para ver los mensajes que entran -# ruta: RTsys/coupled_name/input/port_name y to_do lo que llegue a ese puerto se inyecta. + # Desde este input handler me subscribo a topics para ver los mensajes que entran + # ruta: RTsys/coupled_name/input/port_name y to_do lo que llegue a ese puerto se inyecta. -######################################################################### -######################################################################### -######################################################################### -def on_connect(client, userdata, flags, rc): - print(f'MQTT client connected with mqtt: {rc}') # rc valor de exito o fracaso en la conexion - return rc + ######################################################################### + ######################################################################### + ######################################################################### + def on_connect(client, userdata, flags, rc): + print(f'MQTT client connected with mqtt: {rc}') # rc valor de exito o fracaso en la conexion + return rc -def on_message(client, userdata, msg): - # print(f'New msg arrived in {msg.topic} : {msg.payload.decode()} ') - client.event_queue.put(msg) + def on_message(client, userdata, msg): + # print(f'New msg arrived in {msg.topic} : {msg.payload.decode()} ') + client.event_queue.put(msg) -class MQTTClient(Client): - def __init__(self, event_queue: queue = None, **kwargs): - super().__init__(**kwargs) + class MQTTClient(Client): + def __init__(self, event_queue: queue = None, **kwargs): + super().__init__(**kwargs) - self.on_message = kwargs.get('on_message', on_message) - self.on_connect = kwargs.get('on_connect', on_connect) + self.on_message = kwargs.get('on_message', on_message) + self.on_connect = kwargs.get('on_connect', on_connect) - self.event_queue = event_queue + self.event_queue = event_queue -######################################################################### -######################################################################### -######################################################################### + ######################################################################### + ######################################################################### + ######################################################################### -def mqtt_parser(mqtt_msg): - topic = [item for item in mqtt_msg.topic.split('/')] - port = topic[-1] + def mqtt_parser(mqtt_msg): + topic = [item for item in mqtt_msg.topic.split('/')] + port = topic[-1] - msg = mqtt_msg.payload.decode() - return port, msg + msg = mqtt_msg.payload.decode() + return port, msg -class MQTTInputHandler(InputHandler): - def __init__(self, subscriptions: dict[str, int] = None, **kwargs): - """ + class MQTTInputHandler(InputHandler): + def __init__(self, subscriptions: dict[str, int] = None, **kwargs): + """ - :param subscriptions: diccionario con los topics y su qos - :param kwargs: - """ + :param subscriptions: diccionario con los topics y su qos + :param kwargs: + """ - kwargs['event_parser'] = kwargs.get('event_parser', mqtt_parser) + kwargs['event_parser'] = kwargs.get('event_parser', mqtt_parser) - super().__init__(**kwargs) + super().__init__(**kwargs) - self.subscriptions = subscriptions - self.host: str = kwargs.get('host', 'test.mosquitto.org') - self.port: int = kwargs.get('port', 1883) - self.keepalive: int = kwargs.get('keepalive', 60) + self.subscriptions = subscriptions + self.host: str = kwargs.get('host', 'test.mosquitto.org') + self.port: int = kwargs.get('port', 1883) + self.keepalive: int = kwargs.get('keepalive', 60) - self.event_queue: queue.SimpleQueue = queue.SimpleQueue() - self.client = MQTTClient(event_queue=self.event_queue) + self.event_queue: queue.SimpleQueue = queue.SimpleQueue() + self.client = MQTTClient(event_queue=self.event_queue) - self.client_thread: threading.Thread = threading.Thread(target=self.client.loop_forever, daemon=True) + self.client_thread: threading.Thread = threading.Thread(target=self.client.loop_forever, daemon=True) - def initialize(self): - self.client.connect(self.host, self.port, self.keepalive) - for topic, qos in self.subscriptions.items(): - self.client.subscribe(topic, qos) + def initialize(self): + self.client.connect(self.host, self.port, self.keepalive) + for topic, qos in self.subscriptions.items(): + self.client.subscribe(topic, qos) - self.client_thread.start() + self.client_thread.start() - def run(self): - while True: - event = self.event_queue.get() - print(f'MQTT: Event pushed') # {event} t = {datetime.datetime.now()}') - self.push_event(event) + def run(self): + while True: + event = self.event_queue.get() + print(f'MQTT: Event pushed') # {event} t = {datetime.datetime.now()}') + self.push_event(event) -if __name__ == '__main__': - input_queue = queue.SimpleQueue() - event_Q = queue.SimpleQueue() + if __name__ == '__main__': + input_queue = queue.SimpleQueue() + event_Q = queue.SimpleQueue() - sub: dict = { - 'ALSW/#': 0, - 'ALSW/TEP': 0, - 'RTsys/#': 0, - } - # C = MQTTClient(event_queue=event_Q) - IN = MQTTInputHandler(queue=input_queue, subscriptions=sub) - IN.initialize() - IN.run() + sub: dict = { + 'ALSW/#': 0, + 'ALSW/TEP': 0, + 'RTsys/#': 0, + } + # C = MQTTClient(event_queue=event_Q) + IN = MQTTInputHandler(queue=input_queue, subscriptions=sub) + IN.initialize() + IN.run() + +except ImportError: + from xdevs.plugins.input_handlers.bad_dependencies import BadDependenciesHandler + + + class MQTTInputHandler(BadDependenciesHandler): + def __init__(self, **kwargs): + super().__init__(handler_type='mqtt', **kwargs) diff --git a/xdevs/plugins/output_handlers/bad_dependencies.py b/xdevs/plugins/output_handlers/bad_dependencies.py new file mode 100644 index 0000000..2af008e --- /dev/null +++ b/xdevs/plugins/output_handlers/bad_dependencies.py @@ -0,0 +1,15 @@ +from abc import ABC +from xdevs.rt_sim.output_handler import OutputHandler + + +class BadDependenciesHandler(OutputHandler, ABC): + def __init__(self, **kwargs): + """ + Template input handler for using when dependencies are not imported. + :param str handler_type: transducer type. + """ + super().__init__(**kwargs) + raise ImportError(f'{kwargs.get('handler_type')} input handler specific dependencies are not imported') + + def run(self): + pass diff --git a/xdevs/plugins/output_handlers/mqtt_output_handler.py b/xdevs/plugins/output_handlers/mqtt_output_handler.py index 4ec0dc9..1457bd0 100644 --- a/xdevs/plugins/output_handlers/mqtt_output_handler.py +++ b/xdevs/plugins/output_handlers/mqtt_output_handler.py @@ -3,52 +3,61 @@ import time from typing import Callable, Any -from xdevs.plugins.input_handlers.mqtt_input_handler import MQTTClient -from xdevs.rt_sim.output_handler import OutputHandler +try: + from xdevs.plugins.input_handlers.mqtt_input_handler import MQTTClient + from xdevs.rt_sim.output_handler import OutputHandler -class MQTTOutputHandler(OutputHandler): - def __init__(self, **kwargs): - super().__init__(**kwargs) + class MQTTOutputHandler(OutputHandler): + def __init__(self, **kwargs): + super().__init__(**kwargs) - self.host = kwargs.get('host', 'test.mosquitto.org') - self.port = kwargs.get('port', 1883) - self.keepalive = kwargs.get('keepalive', 60) + self.host = kwargs.get('host', 'test.mosquitto.org') + self.port = kwargs.get('port', 1883) + self.keepalive = kwargs.get('keepalive', 60) - self.client = MQTTClient() + self.client = MQTTClient() - self.topic: str = kwargs.get('topic', 'RTsys') + self.topic: str = kwargs.get('topic', 'RTsys') - self.event_parser: Callable[[str, Any], str] = kwargs.get('event_parser', - lambda port, msg: (f'{self.topic}/Output/{port}', msg)) + self.event_parser: Callable[[str, Any], str] = kwargs.get('event_parser', + lambda port, msg: (f'{self.topic}/Output/{port}', msg)) - def initialize(self): - self.client.connect(self.host, self.port, self.keepalive) - print('MQTT connected') + def initialize(self): + self.client.connect(self.host, self.port, self.keepalive) + print('MQTT connected') - def run(self): - print('MQTT running...') - while True: - topic, payload = self.pop_event() - self.client.publish(topic, payload) - print(f'MQTT sends ') #: {topic} : {payload} > {datetime.datetime.now()}') + def run(self): + print('MQTT running...') + while True: + topic, payload = self.pop_event() + self.client.publish(topic, payload) + print(f'MQTT sends ') #: {topic} : {payload} > {datetime.datetime.now()}') -if __name__ == '__main__': - def inject_msg(): - print(f'Thread active') - for i in range(20): - OUT.queue.put(('Port', f' msg: {i} ')) - print(f'Msg in q and i = {i}') - if i == 3: - time.sleep(15) - else: - time.sleep(2.5) - print('Closing...') + if __name__ == '__main__': + def inject_msg(): + print(f'Thread active') + for i in range(20): + OUT.queue.put(('Port', f' msg: {i} ')) + print(f'Msg in q and i = {i}') + if i == 3: + time.sleep(15) + else: + time.sleep(2.5) + print('Closing...') - OUT = MQTTOutputHandler() - OUT.initialize() - t = threading.Thread(target=inject_msg, daemon=True) - t.start() - OUT.run() + OUT = MQTTOutputHandler() + OUT.initialize() + t = threading.Thread(target=inject_msg, daemon=True) + t.start() + OUT.run() + +except ImportError: + from xdevs.plugins.output_handlers.bad_dependencies import BadDependenciesHandler + + + class MQTTOutputHandler(BadDependenciesHandler): + def __init__(self, **kwargs): + super().__init__(handler_type='mqtt', **kwargs) diff --git a/xdevs/plugins/transducers/csv_transducer.py b/xdevs/plugins/transducers/csv_transducer.py index 80d44ac..f93ba46 100644 --- a/xdevs/plugins/transducers/csv_transducer.py +++ b/xdevs/plugins/transducers/csv_transducer.py @@ -2,7 +2,7 @@ import csv import os from typing import Any, Iterable, Type -from ...transducers import Transducer +from xdevs.transducers import Transducer class CSVTransducer(Transducer): diff --git a/xdevs/plugins/transducers/elasticsearch_transducer.py b/xdevs/plugins/transducers/elasticsearch_transducer.py index 02629bb..0e971e7 100644 --- a/xdevs/plugins/transducers/elasticsearch_transducer.py +++ b/xdevs/plugins/transducers/elasticsearch_transducer.py @@ -1,7 +1,6 @@ from __future__ import annotations import logging -from ...transducers import Transducer -from .bad_dependencies_transducer import BadDependenciesTransducer +from xdevs.transducers import Transducer try: from elasticsearch import Elasticsearch @@ -82,6 +81,9 @@ def create_index(self, index_name: str, field_properties: dict[str, dict[str, st except ModuleNotFoundError: + from .bad_dependencies_transducer import BadDependenciesTransducer + + class ElasticsearchTransducer(BadDependenciesTransducer): def __init__(self, **kwargs): super().__init__(transducer_type='elasticsearch', **kwargs) diff --git a/xdevs/plugins/transducers/sql_transducer.py b/xdevs/plugins/transducers/sql_transducer.py index 3977d65..b751ca2 100644 --- a/xdevs/plugins/transducers/sql_transducer.py +++ b/xdevs/plugins/transducers/sql_transducer.py @@ -1,6 +1,5 @@ from __future__ import annotations from xdevs.transducers import Transducer -from .bad_dependencies_transducer import BadDependenciesTransducer try: from sqlalchemy import create_engine, text, Column, Float, Integer, MetaData, String, Table @@ -86,6 +85,9 @@ def create_table(self, table_name: str, columns: list[Column], except ModuleNotFoundError: + from .bad_dependencies_transducer import BadDependenciesTransducer + + class SQLTransducer(BadDependenciesTransducer): def __init__(self, **kwargs): super().__init__(transducer_type='sql', **kwargs) diff --git a/xdevs/transducers.py b/xdevs/transducers.py index de3f8a1..6325edd 100644 --- a/xdevs/transducers.py +++ b/xdevs/transducers.py @@ -260,7 +260,6 @@ def _log_unknown_data(self, data_type: type, field_name: str): class Transducers: - _plugins: ClassVar[dict[str, Type[Transducer]]] = { ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs.plugins.transducers') } @@ -268,11 +267,11 @@ class Transducers: @staticmethod def add_plugin(name: str, plugin: Type[Transducer]): if name in Transducers._plugins: - raise ValueError('xDEVS transducer plugin with name "{}" already exists'.format(name)) + raise ValueError(f'xDEVS transducer plugin with name "{name}" already exists') Transducers._plugins[name] = plugin @staticmethod def create_transducer(name: str, **kwargs) -> Transducer: if name not in Transducers._plugins: - raise ValueError('xDEVS transducer plugin with name "{}" not found'.format(name)) + raise ValueError('xDEVS transducer plugin with name "{name}" not found') return Transducers._plugins[name](**kwargs) diff --git a/xdevs/wrappers.py b/xdevs/wrappers.py index 8eb8858..6b5aadb 100644 --- a/xdevs/wrappers.py +++ b/xdevs/wrappers.py @@ -12,11 +12,11 @@ class Wrappers: @staticmethod def add_plugin(name: str, plugin: Type[Atomic]): if name in Wrappers._plugins: - raise ValueError('xDEVS wrapper plugin with name "{}" already exists'.format(name)) + raise ValueError(f'xDEVS wrapper plugin with name "{name}" already exists') Wrappers._plugins[name] = plugin @staticmethod def get_wrapper(name: str) -> Type[Atomic]: if name not in Wrappers._plugins: - raise ValueError('xDEVS wrapper plugin with name "{}" not found'.format(name)) + raise ValueError(f'xDEVS wrapper plugin with name "{name}" not found') return Wrappers._plugins[name] From 3ec278ab94dbe1b476d4280ffaabb2880c955730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Fri, 27 Oct 2023 17:38:53 +0200 Subject: [PATCH 42/60] requests for change --- setup.py | 14 +-- xdevs/components.py | 21 +++++ xdevs/coupled_model.json | 16 ++-- xdevs/unwrap_json_v2.py | 178 +++++++++++++++------------------------ xdevs/wrappers.py | 2 +- 5 files changed, 102 insertions(+), 129 deletions(-) create mode 100644 xdevs/components.py diff --git a/setup.py b/setup.py index cbe0a95..b226a2e 100644 --- a/setup.py +++ b/setup.py @@ -27,13 +27,13 @@ 'tcp_out_handler = xdevs.plugins.output_handlers.tcp_output_handler:TCPOutputHandler', 'mqtt_handler = xdevs.plugins.output_handlers.mqtt_output_handler:MQTTOutputHandler', ], - 'xdevs': [ - 'Generator = xdevs.examples.basic.basic:Generator', - 'Transducer = xdevs.examples.basic.basic:Transducer', - 'Processor = xdevs.examples.basic.basic:Processor', - 'GPT = xdevs.examples.basic.basic:Gpt', - 'EF = xdevs.examples.basic.efp_model:EF', - 'EFP = xdevs.examples.basic.efp_model:EFP' + 'xdevs.plugins.components': [ + 'generator = xdevs.examples.basic.basic:Generator', + 'transducer = xdevs.examples.basic.basic:Transducer', + 'processor = xdevs.examples.basic.basic:Processor', + 'gpt = xdevs.examples.basic.basic:Gpt', + 'ef = xdevs.examples.basic.efp_model:EF', + 'efp = xdevs.examples.basic.efp_model:EFP' ], 'xdevs.plugins.wrappers': [ 'pypdevs = xdevs.plugins.wrappers.pypdevs:PyPDEVSWrapper' diff --git a/xdevs/components.py b/xdevs/components.py new file mode 100644 index 0000000..372a98d --- /dev/null +++ b/xdevs/components.py @@ -0,0 +1,21 @@ +import pkg_resources +from typing import ClassVar +from xdevs.models import Component + + +class Components: + _plugins: ClassVar[dict[str, type[Component]]] = { + ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs.plugins.components') + } + + @staticmethod + def add_plugin(component_id: str, plugin: type[Component]): + if component_id in Components._plugins: + raise ValueError(f'xDEVS component plugin with name "{component_id}" already exists') + Components._plugins[component_id] = plugin + + @staticmethod + def create_component(component_id: str, *args, **kwargs) -> type[Component]: + if component_id not in Components._plugins: + raise ValueError(f'xDEVS component plugin with name "{component_id}" not found') + return Components._plugins[component_id](*args, **kwargs) diff --git a/xdevs/coupled_model.json b/xdevs/coupled_model.json index b838f1c..26c1bca 100644 --- a/xdevs/coupled_model.json +++ b/xdevs/coupled_model.json @@ -2,17 +2,15 @@ "EFP" : { "components": { - "Processor": { - "name" : "P0", - "class": "Processor", + "processor": { + "component_id": "processor", "kwargs": { "proc_time": 10 } }, "ef": { - "name": "ef0", - "class": "EF", + "component_id": "ef", "kwargs": { "period": 5, "obs_time": 300 @@ -23,15 +21,15 @@ "connections" : [ { - "componentFrom": "ef0", + "componentFrom": "ef", "portFrom": "p_out_ef", - "componentTo": "P0", + "componentTo": "processor", "portTo": "i_in" }, { - "componentFrom": "P0", + "componentFrom": "processor", "portFrom": "o_out", - "componentTo": "ef0", + "componentTo": "ef", "portTo": "p_in_ef" } diff --git a/xdevs/unwrap_json_v2.py b/xdevs/unwrap_json_v2.py index a153a76..9bd66d6 100644 --- a/xdevs/unwrap_json_v2.py +++ b/xdevs/unwrap_json_v2.py @@ -1,32 +1,69 @@ # This approach is based on the java implementation in xdevs.java import json import sys - -import pkg_resources -from typing import ClassVar, Type - -from xdevs.models import Coupled, Atomic, Port -from xdevs.examples.basic.basic import Processor, Transducer, Generator -from xdevs.models import Component +from xdevs.models import Coupled, Port, Component from xdevs.sim import Coordinator - - -def create_component(info: dict): - """ - A function to create components based on the ComponentsFactory - - :param info: a dictionary that contains the class_id and the arguments to create the component - :return: a component (Coupled/Atomic) with the parameters set in info - """ - name = info.get('name', None) - class_id: str = info.get('class', None) - values = info.get('kwargs') - component = ComponentsFactory.create_component_handler(class_id=class_id, name=name, **values) +from xdevs.components import Components + + +def nested_component(name: str, config: dict) -> Component: + if 'component_id' in config: + # Predefined component, use factory + component_id: str = config['component_id'] + args = config.get('args', []) + kwargs = config.get('kwargs', {}) + kwargs['name'] = name + return Components.create_component(component_id, *args, **kwargs) + elif 'components' in config: + # It is a coupled model + component = Coupled(name) + children: dict[str, Component] = dict() + # Create children components + for component_name, component_config in config['components'].items(): + child = nested_component(component_name, component_config) + children[component_name] = child + component.add_component(child) + # Create connections + for connection in config.get('connections', []): + child_from = connection.get('componentFrom') + child_to = connection.get('componentTo') + if child_from is not None: + child_from = children[child_from] + port_from = child_from.get_out_port(connection['portFrom']) + if port_from is None: + raise Exception(f'Invalid connection in: {connection}. Reason: portFrom not found') + if child_to is not None: + # this is an IC + child_to = children[child_to] + port_to = child_to.get_in_port(connection['portTo']) + if port_to is None: + raise Exception(f'Invalid connection in: {connection}. Reason: portTo not found') + else: + # this is an EOC + port_to = child_to.get_in_port(connection['portTo']) + if port_to is None: + port_to = Port(p_type=port_from.p_type, name=connection['portTo']) + component.add_out_port(port_to) + elif child_to is not None: + child_to = children[child_to] + port_to = child_to.get_in_port(connection['portTo']) + if port_to is None: + raise Exception(f'Invalid connection in: {connection}. Reason: portTo not found') + port_from = component.get_in_port(connection['portFrom']) + if port_from is None: + port_from = Port(p_type=port_to.p_type, name=connection['portFrom']) + component.add_in_port(port_from) + else: + raise Exception(f'Invalid connection in: {connection}. Reason: componentFrom and componentTo are None') + + component.add_coupling(port_from, port_to) + else: + raise Exception('No component found') return component # @staticmethod de la clase Coupled -def from_json(data): +def from_json(file): """ A function to parser a json file into a DEVS model @@ -67,104 +104,21 @@ def from_json(data): } } - :param data: a loaded json file of the type dict + :param file: Path to the JSON file :return: a Coupled DEVS model """ + + with open("coupled_model.json") as f: + data = json.load(f) name = list(data.keys())[0] # Gets the actual component name - print(name) - parent = Coupled(name) - dict_comp = dict() # dict to storage the components - - for key in data[name].get('components', {}): - current_data = data[name]['components'][key] - - if 'components' in current_data: - - child = from_json({key: current_data}) - parent.add_component(child) - dict_comp[child.name] = child - - elif 'name' in current_data: - component = create_component(current_data) - Comp.append(component) - dict_comp[component.name] = component - parent.add_component(component) - - else: - raise Exception('No component found') - - if 'connections' in data[name]: - connections_data = data[name]['connections'] - # print(dict_comp) - for connection in connections_data: - # print(connection) - try: - # Connexion ic - if connection['componentFrom'] in dict_comp and connection['componentTo'] in dict_comp: - port_from = dict_comp[connection['componentFrom']].get_out_port(connection['portFrom']) - port_to = dict_comp[connection['componentTo']].get_in_port(connection['portTo']) - parent.add_coupling(port_from, port_to) - - # Connexion eic - elif connection['componentFrom'] is None and connection['componentTo'] in dict_comp: - # print('!!!!!') - port_to = dict_comp[connection['componentTo']].get_in_port(connection['portTo']) - p_in = Port(p_type=port_to.p_type, name=connection['portFrom']) - parent.add_in_port(p_in) - parent.add_coupling(parent.get_in_port(p_in.name), port_to) - - # Connexion eoc - elif connection['componentFrom'] in dict_comp and connection['componentTo'] is None: - # print('¡¡¡¡¡') - port_from = dict_comp[connection['componentFrom']].get_out_port(connection['portFrom']) - p_out = Port(p_type=port_from.p_type, name=connection['portOut']) - parent.add_out_port(p_out) - parent.add_coupling(port_from, parent.get_out_port(p_out.name)) - - else: - raise Exception(f'Connection Error! -> {connection}') - - except Exception as e: - print(f'Invalid connection in: {connection}. Reason: {e}', file=sys.stderr) - raise - - return parent - - -class ComponentsFactory: - _plugins: ClassVar[dict[str, Type[Component]]] = { - ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs')} - - @staticmethod - def add_plugin(name: str, plugin: Type[Component]): - """ - Registers a custom component to the plugin system. - - :param name: name used to identify the custom component. It must be unique. - :param plugin: custom component type. Note that it must not be an object, just the class. - """ - if name in ComponentsFactory._plugins: - raise ValueError('xDEVS component plugin with name "{}" already exists'.format(name)) - ComponentsFactory._plugins[name] = plugin - - @staticmethod - def create_component_handler(class_id: str, **kwargs) -> Component: - """ - Creates a new component. - - :param class_id: unique ID of the component to be created. - :param kwargs: any additional configuration parameter needed for creating the component. - :return: an instance of the Component class. - """ - if class_id not in ComponentsFactory._plugins: - raise ValueError('xDEVS component plugin with name "{}" not found'.format(class_id)) - return ComponentsFactory._plugins[class_id](**kwargs) + config = data[name] # Gets the actual component config + + return nested_component(name, config) if __name__ == '__main__': - with open("coupled_model.json") as f: - file = json.load(f) + file = "coupled_model.json" print(type(file)) Comp = list() diff --git a/xdevs/wrappers.py b/xdevs/wrappers.py index 6b5aadb..10f655d 100644 --- a/xdevs/wrappers.py +++ b/xdevs/wrappers.py @@ -16,7 +16,7 @@ def add_plugin(name: str, plugin: Type[Atomic]): Wrappers._plugins[name] = plugin @staticmethod - def get_wrapper(name: str) -> Type[Atomic]: + def create_wrapper(name: str) -> Type[Atomic]: if name not in Wrappers._plugins: raise ValueError(f'xDEVS wrapper plugin with name "{name}" not found') return Wrappers._plugins[name] From 14a2a7a4e51ee5e9aa22b413674ec2bfe1aa7618 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Mon, 30 Oct 2023 11:10:33 +0100 Subject: [PATCH 43/60] Code organization and documentation Move the code from unwrap_json_v2.py to components.py and improve the documentation. --- xdevs/components.py | 130 +++++++++++++++++++++++++++++++++- xdevs/coupled_model_v2.json | 43 +++++------- xdevs/unwrap_json_v2.py | 135 ------------------------------------ 3 files changed, 147 insertions(+), 161 deletions(-) delete mode 100644 xdevs/unwrap_json_v2.py diff --git a/xdevs/components.py b/xdevs/components.py index 372a98d..df2afea 100644 --- a/xdevs/components.py +++ b/xdevs/components.py @@ -1,9 +1,16 @@ import pkg_resources +import json from typing import ClassVar -from xdevs.models import Component +from xdevs.models import Component, Port, Coupled class Components: + """ + This class creates components from unique identifiers called "component_id". + + In order to identify each component_id check the "entry_point" dict in the file setup.py and look for the + list 'xdevs.plugins.components'. + """ _plugins: ClassVar[dict[str, type[Component]]] = { ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs.plugins.components') } @@ -15,7 +22,126 @@ def add_plugin(component_id: str, plugin: type[Component]): Components._plugins[component_id] = plugin @staticmethod - def create_component(component_id: str, *args, **kwargs) -> type[Component]: + def create_component(component_id: str, *args, **kwargs) -> Component: if component_id not in Components._plugins: raise ValueError(f'xDEVS component plugin with name "{component_id}" not found') return Components._plugins[component_id](*args, **kwargs) + + @staticmethod + def _nested_component(name: str, config: dict) -> Component: + if 'component_id' in config: + # Predefined component, use factory + component_id: str = config['component_id'] + args = config.get('args', []) + kwargs = config.get('kwargs', {}) + kwargs['name'] = name + return Components.create_component(component_id, *args, **kwargs) + elif 'components' in config: + # It is a coupled model + component = Coupled(name) + children: dict[str, Component] = dict() + # Create children components + for component_name, component_config in config['components'].items(): + child = Components._nested_component(component_name, component_config) + children[component_name] = child + component.add_component(child) + # Create connections + for connection in config.get('connections', []): + child_from = connection.get('componentFrom') + child_to = connection.get('componentTo') + if child_from is not None: + child_from = children[child_from] + port_from = child_from.get_out_port(connection['portFrom']) + if port_from is None: + raise Exception(f'Invalid connection in: {connection}. Reason: portFrom not found') + if child_to is not None: + # this is an IC + child_to = children[child_to] + port_to = child_to.get_in_port(connection['portTo']) + if port_to is None: + raise Exception(f'Invalid connection in: {connection}. Reason: portTo not found') + else: + # this is an EOC + port_to = child_to.get_in_port(connection['portTo']) + if port_to is None: + port_to = Port(p_type=port_from.p_type, name=connection['portTo']) + component.add_out_port(port_to) + elif child_to is not None: + # this is an EIC + child_to = children[child_to] + port_to = child_to.get_in_port(connection['portTo']) + if port_to is None: + raise Exception(f'Invalid connection in: {connection}. Reason: portTo not found') + port_from = component.get_in_port(connection['portFrom']) + if port_from is None: + port_from = Port(p_type=port_to.p_type, name=connection['portFrom']) + component.add_in_port(port_from) + else: + raise Exception( + f'Invalid connection in: {connection}. Reason: componentFrom and componentTo are None') + + component.add_coupling(port_from, port_to) + else: + raise Exception('No component found') + return component + + @staticmethod + def from_json(file_path: str): + """ + A function to parser a json file into a DEVS model + + Considering the following constrains, the json file structure is as follows: + + When adding a component if it contains the key "component_id", the component will be created using it and the + kwargs associated with it. The "component_id" value refers to the key to identify each component in the class + Components. + + When the component does not have the key "component_id" it must have the pair keys "components" and "connections". + This component will be implementing several components and their connections inside itself. + + The connections are created using four keys: + - If both componentFrom/To keys are added, the connection will be of the type IC. + - If componentFrom key is missing, the connection will be of the type EIC. + - If componentTo key is missing, the connection will be of the type EOC. + - If any portFrom/To value is missing the connections is not established. + + Structure: + + - 'MasterComponentName' (dict): The master component. + - 'components' (dict): A dictionary containing multiple components. + - 'ComponentName1' (dict): Iterative component. + - 'components' (dict): Nested components if any. + - 'connections' (list): List of connection dictionaries. + - 'componentFrom' (str): Name of the component where the connection starts. + - 'portFrom' (str): Port name from 'componentFrom'. + - 'componentTo' (str): Name of the component where the connection ends. + - 'portTo' (str): Port name from 'componentTo'. + - 'ComponentName2' (dict): Single component. + - 'component_id' (str): ID read from the factory for this component. + - 'kwargs' (dict): Keyword arguments for the component. + - 'a_parameter' (any): A parameter for the component. + - ... : Other keyword arguments if any. + - ... : Additional components if any. + - 'connections' (list): List of connection dictionaries at the top level. + - 'componentFrom' (str): Name of the component where the connection starts. + - 'portFrom' (str): Port name from 'componentFrom'. + - 'componentTo' (str): Name of the component where the connection ends. + - 'portTo' (str): Port name from 'componentTo'. + + :param file_path: Path to the JSON file + :return: a Coupled DEVS model + """ + + with open(file_path) as f: + data = json.load(f) + + name = list(data.keys())[0] # Gets the actual component name + config = data[name] # Gets the actual component config + + return Components._nested_component(name, config) + + +if __name__ == '__main__': + + C = Components.from_json('coupled_model_v2.json') + print(C) diff --git a/xdevs/coupled_model_v2.json b/xdevs/coupled_model_v2.json index 5b9ef03..56ebac4 100644 --- a/xdevs/coupled_model_v2.json +++ b/xdevs/coupled_model_v2.json @@ -4,23 +4,20 @@ "name/GPT": { "components": { - "atomic/Processor": { - "name": "p0", - "class": "Processor", + "Processor": { + "component_id": "processor", "kwargs": { "proc_time": 3.0 } }, - "atomic/generator": { - "name": "g0", - "class": "Generator", + "Generator": { + "component_id": "generator", "kwargs": { "period": 1.0 } }, - "atomic/transducer": { - "name": "t0", - "class": "Transducer", + "Transducer": { + "component_id": "transducer", "kwargs": { "obs_time": 100.0 } @@ -29,49 +26,47 @@ "connections": [ { - "componentFrom": "p0", + "componentFrom": "Processor", "portFrom": "o_out", - "componentTo": "t0", + "componentTo": "Transducer", "portTo": "i_solved" }, { - "componentFrom": "g0", + "componentFrom": "Generator", "portFrom": "o_out", - "componentTo": "p0", + "componentTo": "Processor", "portTo": "i_in" }, { - "componentFrom": "g0", + "componentFrom": "Generator", "portFrom": "o_out", - "componentTo": "t0", + "componentTo": "Transducer", "portTo": "i_arrived" }, { - "componentFrom": "t0", + "componentFrom": "Transducer", "portFrom": "o_out", - "componentTo": "g0", + "componentTo": "Generator", "portTo": "i_stop" }, { "componentFrom": null, "portFrom": "i_in_gpt", - "componentTo": "g0", + "componentTo": "Generator", "portTo": "i_stop" } ] }, - "atomic/Processor": { - "name": "p1", - "class": "Processor", + "Processor_2": { + "component_id": "processor", "kwargs": { "proc_time": 300.0 } }, "c/gpt" : { - "name": "gptTest", - "class": "GPT", + "component_id": "gpt", "kwargs": { "period": 3, "obs_time": 100 @@ -81,7 +76,7 @@ "connections": [ { - "componentFrom": "p1", + "componentFrom": "Processor_2", "portFrom": "o_out", "componentTo": "name/GPT", "portTo": "i_in_gpt" diff --git a/xdevs/unwrap_json_v2.py b/xdevs/unwrap_json_v2.py deleted file mode 100644 index 9bd66d6..0000000 --- a/xdevs/unwrap_json_v2.py +++ /dev/null @@ -1,135 +0,0 @@ -# This approach is based on the java implementation in xdevs.java -import json -import sys -from xdevs.models import Coupled, Port, Component -from xdevs.sim import Coordinator -from xdevs.components import Components - - -def nested_component(name: str, config: dict) -> Component: - if 'component_id' in config: - # Predefined component, use factory - component_id: str = config['component_id'] - args = config.get('args', []) - kwargs = config.get('kwargs', {}) - kwargs['name'] = name - return Components.create_component(component_id, *args, **kwargs) - elif 'components' in config: - # It is a coupled model - component = Coupled(name) - children: dict[str, Component] = dict() - # Create children components - for component_name, component_config in config['components'].items(): - child = nested_component(component_name, component_config) - children[component_name] = child - component.add_component(child) - # Create connections - for connection in config.get('connections', []): - child_from = connection.get('componentFrom') - child_to = connection.get('componentTo') - if child_from is not None: - child_from = children[child_from] - port_from = child_from.get_out_port(connection['portFrom']) - if port_from is None: - raise Exception(f'Invalid connection in: {connection}. Reason: portFrom not found') - if child_to is not None: - # this is an IC - child_to = children[child_to] - port_to = child_to.get_in_port(connection['portTo']) - if port_to is None: - raise Exception(f'Invalid connection in: {connection}. Reason: portTo not found') - else: - # this is an EOC - port_to = child_to.get_in_port(connection['portTo']) - if port_to is None: - port_to = Port(p_type=port_from.p_type, name=connection['portTo']) - component.add_out_port(port_to) - elif child_to is not None: - child_to = children[child_to] - port_to = child_to.get_in_port(connection['portTo']) - if port_to is None: - raise Exception(f'Invalid connection in: {connection}. Reason: portTo not found') - port_from = component.get_in_port(connection['portFrom']) - if port_from is None: - port_from = Port(p_type=port_to.p_type, name=connection['portFrom']) - component.add_in_port(port_from) - else: - raise Exception(f'Invalid connection in: {connection}. Reason: componentFrom and componentTo are None') - - component.add_coupling(port_from, port_to) - else: - raise Exception('No component found') - return component - - -# @staticmethod de la clase Coupled -def from_json(file): - """ - A function to parser a json file into a DEVS model - - The structure of the json file must be as follows: - - { - "MasterCoupledName": { - "components" : { - - "ThisIsACoupledModel" :{ - "components" : {} - "connections" : [] - }, - - "ThisIsAAtomicModel" : { - "name": "AtomicName", - "class": "AtomicClassIdentifier", - "kwargs": { - "a_parameter" : , - ... - } - }, - - ... - - }, - "connections": [ - { - "componentFrom": "", - "portFrom": "", - "componentTo": "", - "portTo": "" - }, - - ... - - ] - } - } - - :param file: Path to the JSON file - :return: a Coupled DEVS model - """ - - with open("coupled_model.json") as f: - data = json.load(f) - - name = list(data.keys())[0] # Gets the actual component name - config = data[name] # Gets the actual component config - - return nested_component(name, config) - - -if __name__ == '__main__': - file = "coupled_model.json" - - print(type(file)) - Comp = list() - Connections = list() - - C = from_json(file) - - print(f'Componentes añadidos: {Comp}') - for c in Comp: - print(c) - - Coord = Coordinator(C) - Coord.initialize() - Coord.simulate() From 1d5ec79800c5b1dd0e8dddd3fde31edf7ac287da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Tue, 14 Nov 2023 16:15:32 +0100 Subject: [PATCH 44/60] cleaning up repository --- xdevs/models.py | 70 +++++++------- xdevs/sim.py | 176 +++-------------------------------- xdevs/tests/test_devstone.py | 9 +- xdevs/tests/test_models.py | 32 +++++++ 4 files changed, 88 insertions(+), 199 deletions(-) create mode 100644 xdevs/tests/test_models.py diff --git a/xdevs/models.py b/xdevs/models.py index f1f66c0..3898a2f 100644 --- a/xdevs/models.py +++ b/xdevs/models.py @@ -3,26 +3,26 @@ import pickle from abc import ABC, abstractmethod from collections import deque, defaultdict -from typing import Generator, Generic, Iterator, Type, TypeVar +from typing import Generator, Generic, Iterator, Type, TypeVar, Optional from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, INFINITY T = TypeVar('T') class Port(Generic[T]): - def __init__(self, p_type: Type[T] = None, name: str = None, serve: bool = False): + def __init__(self, p_type: Optional[Type[T]] = None, name: Optional[str] = None, serve: bool = False): """ xDEVS implementation of DEVS Port. :param p_type: data type of events to be sent/received via the new port instance. :param name: name of the new port instance. Defaults to the name of the port's class. :param serve: set to True if the port is going to be accessible via RPC server. Defaults to False. """ - self.name: str = name if name else self.__class__.__name__ - self.p_type: Type[T] = p_type - self.serve: bool = serve - self.parent: Component | None = None # xDEVS Component that owns the port - self._values: deque[T] = deque() # Bag containing events directly written to the port - self._bag: list[Port[T]] = list() # Bag containing coupled ports containing events + self.name: str = name if name else self.__class__.__name__ # Name of the port + self.p_type: Optional[Type[T]] = p_type # Port type. If None, it can contain any type of event. + self.serve: bool = serve # True if port is going to be accessible via RPC server + self.parent: Component | None = None # xDEVS Component that owns the port + self._values: deque[T] = deque() # Bag containing events directly written to the port + self._bag: list[Port[T]] = list() # Bag containing coupled ports containing events def __bool__(self) -> bool: return not self.empty() @@ -31,7 +31,7 @@ def __len__(self) -> int: return sum((len(port) for port in self._bag), len(self._values)) def __str__(self) -> str: - return '%s.%s(%s)' % (self.parent.name, self.name, self.p_type or 'None') + return f'{self.name}<{self.p_type.__name__ if self.p_type is not None else 'None'}>' def __repr__(self) -> str: return str(self) @@ -66,7 +66,7 @@ def add(self, val: T): :raises TypeError: If event is not instance of port type. """ if self.p_type is not None and not isinstance(val, self.p_type): - raise TypeError('Value type is %s (%s expected)' % (type(val).__name__, self.p_type.__name__)) + raise TypeError(f'Value type is {type(val).__name__} ({self.p_type.__name__} expected)') self._values.append(val) def extend(self, vals: Iterator[T]): @@ -88,15 +88,18 @@ def add_to_bag(self, port: Port[T]): class Component(ABC): - def __init__(self, name: str = None): + def __init__(self, name: Optional[str] = None): """ Abstract Base Class for an xDEVS model. :param name: name of the xDEVS model. Defaults to the name of the component's class. """ self.name: str = name if name else self.__class__.__name__ - self.parent: Coupled | None = None # Parent component of this component - self.in_ports: list[Port] = list() # List containing all the component's input ports - self.out_ports: list[Port] = list() # List containing all the component's output ports + self.parent: Optional[Coupled] = None # Parent component of this component + self.input: dict[str, Port] = dict() # Dictionary containing all the component's input ports by name + self.output: dict[str, Port] = dict() # Dictionary containing all the component's output ports by name + # TODO make these lists private + self.in_ports: list[Port] = list() # List containing all the component's input ports (serialized for performance) + self.out_ports: list[Port] = list() # List containing all the component's output ports (serialized for performance) def __str__(self) -> str: in_str = " ".join([p.name for p in self.in_ports]) @@ -136,29 +139,33 @@ def add_in_port(self, port: Port): """ Adds an input port to the xDEVS model. :param port: port to be added to the model. + :panics NameError: if port name already exists. """ + if port.name in self.input: + raise NameError("Input port name already exists") port.parent = self + self.input[port.name] = port self.in_ports.append(port) def add_out_port(self, port: Port): """ Adds an output port to the xDEVS model :param port: port to be added to the model. + :panics NameError: if port name already exists. """ + if port.name in self.output: + raise ValueError("Output port name already exists") port.parent = self + self.output[port.name] = port self.out_ports.append(port) def get_in_port(self, name) -> Port | None: - for port in self.in_ports: - if port.name == name: - return port - return None + """:return: Input port with the given name. If port is not found, returns None.""" + self.input.get(name) def get_out_port(self, name) -> Port | None: - for port in self.out_ports: - if port.name == name: - return port - return None + """:return: Output port with the given name. If port is not found, returns None.""" + self.output.get(name) class Coupling(Generic[T]): @@ -168,16 +175,10 @@ def __init__(self, port_from: Port[T], port_to: Port[T], host=None): :param port_from: DEVS transmitter port. :param port_to: DEVS receiver port. :param host: TODO documentation for this - :raises ValueError: if coupling direction is incompatible with the target ports. + :raises ValueError: port types are incompatible. """ # Check that couplings are valid - comp_from: Component = port_from.parent - comp_to: Component = port_to.parent - if isinstance(comp_from, Atomic) and port_from in comp_from.in_ports: - raise ValueError("Input ports whose parent is an Atomic model can not be coupled to any other port") - if isinstance(comp_to, Atomic) and port_to in comp_from.out_ports: - raise ValueError("Output ports whose parent is an Atomic model can not be recipient of any other port") - if port_from.p_type is not None and port_to in inspect.getmro(port_from.p_type): + if port_from.p_type is not None and port_to.p_type is not None and port_to in inspect.getmro(port_from.p_type): raise ValueError("Ports don't share the same port type") self.port_from: Port = port_from @@ -185,7 +186,7 @@ def __init__(self, port_from: Port[T], port_to: Port[T], host=None): self.host = host # TODO identify host's variable type def __str__(self) -> str: - return "(%s -> %s)" % (self.port_from, self.port_to) + return f"({self.port_from} -> {self.port_to})" def __repr__(self) -> str: return str(self) @@ -201,7 +202,7 @@ def propagate(self): class Atomic(Component, ABC): - def __init__(self, name: str = None): + def __init__(self, name: Optional[str] = None): """ xDEVS implementation of DEVS Atomic Model. :param name: name of the atomic model. If no name is provided, it will take the class's name by default. @@ -217,7 +218,7 @@ def ta(self) -> float: return self.sigma def __str__(self) -> str: - return "%s(%s, %s)" % (self.name, str(self.phase), self.sigma) + return f'{self.name}({self.phase}, {self.sigma})' @abstractmethod def deltint(self): @@ -279,7 +280,7 @@ def continuef(self, e: float): class Coupled(Component, ABC): - def __init__(self, name: str = None): + def __init__(self, name: Optional[str] = None): """ xDEVS implementation of DEVS Coupled Model. :param name: name of the coupled model. If no name is provided, it will take the class's name by default. @@ -289,6 +290,7 @@ def __init__(self, name: str = None): self.ic: dict[Port, dict[Port, Coupling]] = dict() self.eic: dict[Port, dict[Port, Coupling]] = dict() self.eoc: dict[Port, dict[Port, Coupling]] = dict() + # TODO serialized versions of ic, eic and eoc for performance def initialize(self): pass diff --git a/xdevs/sim.py b/xdevs/sim.py index b236d64..123409d 100644 --- a/xdevs/sim.py +++ b/xdevs/sim.py @@ -8,7 +8,7 @@ from abc import ABC, abstractmethod from collections import defaultdict from concurrent import futures -from typing import Generator +from typing import Generator, Optional from xmlrpc.server import SimpleXMLRPCServer from xdevs import INFINITY @@ -23,13 +23,13 @@ def __init__(self, time: float = 0): class AbstractSimulator(ABC): def __init__(self, model: Component, clock: SimulationClock, - event_transducers_mapping: dict[Port, list[Transducer]] = None): + event_transducers_mapping: Optional[dict[Port, list[Transducer]]] = None): self.model: Component = model self.clock: SimulationClock = clock self.time_last: float = 0 self.time_next: float = 0 - self.event_transducers: dict[Port, list[Transducer]] | None = None + self.event_transducers: Optional[dict[Port, list[Transducer]]] = None if event_transducers_mapping: port_transducers: dict[Port, list[Transducer]] = dict() for port in itertools.chain(self.model.in_ports, self.model.out_ports): @@ -79,8 +79,8 @@ class Simulator(AbstractSimulator): model: Atomic def __init__(self, model: Atomic, clock: SimulationClock, - event_transducers_mapping: dict[Port, list[Transducer]] = None, - state_transducers_mapping: dict[Atomic, list[Transducer]] = None): + event_transducers_mapping: Optional[dict[Port, list[Transducer]]] = None, + state_transducers_mapping: Optional[dict[Atomic, list[Transducer]]] = None): super().__init__(model, clock, event_transducers_mapping) self.state_transducers: list[Transducer] | None = None if state_transducers_mapping: @@ -132,17 +132,18 @@ def clear(self): class Coordinator(AbstractSimulator): model: Coupled - def __init__(self, model: Coupled, clock: SimulationClock = None, flatten: bool = False, - event_transducers_mapping: dict[Port, list[Transducer]] = None, - state_transducers_mapping: dict[Atomic, list[Transducer]] = None): + def __init__(self, model: Coupled, clock: Optional[SimulationClock] = None, flatten: bool = False, + event_transducers_mapping: Optional[dict[Port, list[Transducer]]] = None, + state_transducers_mapping: Optional[dict[Atomic, list[Transducer]]] = None): super().__init__(model, clock or SimulationClock(), event_transducers_mapping) self.coordinators: list[Coordinator] = list() self.simulators: list[Simulator] = list() - self._transducers: list[Transducer] = [] if self.root_coordinator else None + self._transducers: Optional[list[Transducer]] = [] if self.root_coordinator else None if flatten: self.model.flatten() + # TODO we must fix transducers here! self.ports_to_serve = dict() self.__event_transducers_mapping: dict[Port, list[Transducer]] | None = None @@ -210,7 +211,7 @@ def initialize(self): self.time_last = self.clock.time self.time_next = self.time_last + self.ta() - if self.root_coordinator: + if self._transducers is not None: for transducer in self._transducers: transducer.initialize() @@ -243,7 +244,7 @@ def _build_hierarchy(self): self.ports_to_serve[port_name] = pts def add_transducer(self, transducer: Transducer): - if not self.root_coordinator: + if self._transducers is None: raise RuntimeError('Only the root coordinator can contain transducers') self._transducers.append(transducer) @@ -256,7 +257,7 @@ def exit(self): for processor in self.processors: processor.exit() - if self.root_coordinator: + if self._transducers is not None: for transducer in self._transducers: transducer.exit() @@ -355,154 +356,3 @@ def simulate_inf(self): def _execute_transducers(self): for transducer in self._transducers: transducer.trigger(self.clock.time) - - -# TODO we should review the parallel implementation -class ParallelCoordinator(Coordinator): - def __init__(self, model: Coupled, clock: SimulationClock = None, flatten: bool = False, - event_transducers_mapping: dict[Port, list[Transducer]] = None, - state_transducers_mapping: dict[Atomic, list[Transducer]] = None, executor=None): - super().__init__(model, clock, flatten, event_transducers_mapping=event_transducers_mapping, - state_transducers_mapping=state_transducers_mapping) - - self.executor = executor or futures.ThreadPoolExecutor(max_workers=8) # TODO calc max workers - - def _add_coordinator(self, coupled: Coupled): - coord = ParallelCoordinator(coupled, self.clock, executor=False) - self.coordinators.append(coord) - self.ports_to_serve.update(coord.ports_to_serve) - - def _lambdaf(self): - for coord in self.coordinators: - coord.lambdaf() - ex_futures = [] - for sim in self.simulators: - self.add_task_to_pool(sim.lambdaf) - - for future in futures.as_completed(ex_futures): - future.result() - - self.propagate_output() - - def deltfcn(self): - self.propagate_input() - - for coord in self.coordinators: - coord.deltfcn() - ex_futures = [] - for sim in self.simulators: - self.add_task_to_pool(sim.deltfcn) - - for future in futures.as_completed(ex_futures): - future.result() - - self.time_last = self.clock.time - self.time_next = self.time_last + self.ta() - - def add_task_to_pool(self, task): - self.executor.submit(task) - - -class ParallelProcessCoordinator(Coordinator): - coordinators: list[ParallelProcessCoordinator] - - def __init__(self, model: Coupled, clock: SimulationClock = None, - event_transducers_mapping: dict[Port, list[Transducer]] = None, - state_transducers_mapping: dict[Atomic, list[Transducer]] = None, - master=True, executor=None, executor_futures=None): - super().__init__(model, clock, event_transducers_mapping=event_transducers_mapping, - state_transducers_mapping=state_transducers_mapping) - self.master = master - - if master: - self.executor = futures.ProcessPoolExecutor() - self.executor_futures = dict() - else: - self.executor = executor - self.executor_futures = executor_futures - - def _add_coordinator(self, coupled: Coupled): - coord = ParallelProcessCoordinator(coupled, self.clock, master=False, executor=self.executor, - executor_futures=self.executor_futures) - self.coordinators.append(coord) - self.ports_to_serve.update(coord.ports_to_serve) - - def lambdaf(self): - - for coord in self.coordinators: - if coord.clock.time == coord.time_next: - coord.lambdaf() - - for sim in self.simulators: - if sim.clock.time == sim.time_next: - self.executor_futures[self.executor.submit(sim.lambdaf)] = (self, sim) - - if self.master: - for i, future in enumerate(self.executor_futures): - # logger.debug("D: Waiting... (%d/%d)" % (i+1, len(self.executor_futures))) - futures.wait((future,)) - - res = future.result() - if isinstance(res, Simulator): - coord, sim = self.executor_futures[future] - for model_port, new_model_port in zip(sim.model.out_ports, future.result().model.out_ports): - model_port.extend(list(new_model_port.values)) - - self.executor_futures.clear() - self.propagate_output() - - def deltfcn(self): - if self.master: - self.propagate_input() - - for coord in self.coordinators: - coord.deltfcn() - - for sim in self.simulators: - self.executor_futures[self.executor.submit(sim.deltfcn)] = (self, sim) - - if self.master: - for i, future in enumerate(self.executor_futures): - # logger.debug("D: Waiting... (%d/%d)" % (i+1, len(self.executor_futures))) - futures.wait((future,)) - - res = future.result() - if isinstance(res, Simulator): - coord, sim = self.executor_futures[future] - model = sim.model - new_sim = future.result() - new_model = new_sim.model - - # Copy new state - if hasattr(new_model, "__state__"): - for state_att in new_model.__state__: - setattr(model, state_att, getattr(new_model, state_att)) - - # Update simulator info - sim.model = model - sim.time_last = new_sim.time_last - sim.time_next = new_sim.time_next - - self.executor_futures.clear() - self.update_times() - - def propagate_output(self): - for coord in self.coordinators: - coord.propagate_output() - - super().propagate_output() - - def propagate_input(self): - super().propagate_input() - - for coord in self.coordinators: - coord.propagate_input() - - def update_times(self): - for coord in self.coordinators: - coord.update_times() - - self.time_last = self.clock.time - self.time_next = self.time_last + self.ta() - # logger.debug({proc.model.name:proc.time_next for proc in self.processors}) - # logger.debug("Deltfcn %s: TL: %s, TN: %s" % (self.model.name, self.time_last, self.time_next)) diff --git a/xdevs/tests/test_devstone.py b/xdevs/tests/test_devstone.py index 6b2d189..d3f8f1d 100644 --- a/xdevs/tests/test_devstone.py +++ b/xdevs/tests/test_devstone.py @@ -1,10 +1,10 @@ -from unittest import TestCase +import unittest from xdevs.sim import Coordinator from xdevs.examples.devstone.devstone import DEVStone, LI, HI, HO, HOmod import random -class DevstoneUtilsTestCase(TestCase): +class DevstoneUtilsTestCase(unittest.TestCase): def __init__(self, name, num_valid_params_sets: int = 10): super().__init__(name) @@ -245,3 +245,8 @@ def test_behavior(self): def test_invalid_inputs(self): super().check_invalid_inputs(HOmod) + + +if __name__ == '__main__': + import unittest + unittest.main() diff --git a/xdevs/tests/test_models.py b/xdevs/tests/test_models.py new file mode 100644 index 0000000..75e5693 --- /dev/null +++ b/xdevs/tests/test_models.py @@ -0,0 +1,32 @@ +import unittest +from xdevs.models import * + + +class TestModels(unittest.TestCase): + + def test_port(self): + p = Port(int, "test") + self.assertEqual(p.p_type, int) + self.assertEqual(p.name, "test") + self.assertEqual(str(p), "test") + self.assertIsNone(p.parent) + self.assertFalse(p) # __bool__ is !empty() + + p.add(1) + self.assertTrue(p) + self.assertEqual(p.get(), 1) + self.assertEqual(len(p), 1) + + p.add(2) + self.assertTrue(p) + self.assertEqual(p.get(), 1) + self.assertEqual(len(p), 2) + + p.clear() + self.assertFalse(p) + + self.assertRaises(TypeError, p.add, "test") + + +if __name__ == '__main__': + unittest.main() From 54884501fc99ffd6cc4c4bf99d861da570fd1709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Mon, 27 May 2024 21:40:16 +0200 Subject: [PATCH 45/60] cleaning everything for v3 --- .gitignore | 1 + .vscode/launch.json | 12 + .vscode/settings.json | 10 + LICENSE => LICENSE.txt | 0 pyproject.toml | 67 +++++ setup.py | 47 ---- xdevs/__init__.py | 33 ++- xdevs/abc/__init__.py | 2 + xdevs/abc/handler.py | 138 ++++++++++ xdevs/{transducers.py => abc/transducer.py} | 73 ++---- xdevs/components.py | 147 ----------- xdevs/coupled_model.json | 38 --- xdevs/coupled_model_v2.json | 86 ------- xdevs/examples/async_rt/basic.py | 6 +- xdevs/examples/basic/basic.py | 194 -------------- xdevs/examples/basic/basic_inter.py | 211 ---------------- xdevs/examples/basic/efp_model.py | 54 ---- xdevs/examples/basic/json_gpt_model.json | 53 ---- xdevs/examples/devstone/devstone.py | 4 +- xdevs/examples/devstone/execution_loop.py | 54 ---- xdevs/examples/devstone/generator.py | 34 +++ xdevs/examples/devstone/main.py | 32 ++- xdevs/examples/{basic => gpt}/__init__.py | 0 xdevs/examples/gpt/efp.py | 47 ++++ xdevs/examples/gpt/gpt.py | 198 +++++++++++++++ .../{store_cashier => json}/__init__.py | 0 xdevs/examples/json/efp.json | 33 +++ xdevs/examples/json/gpt.json | 50 ++++ xdevs/examples/json/main.py | 12 + xdevs/examples/store/1_vt_simulation.py | 58 +++++ .../2_rt_simulation_csv_output_handler.py | 60 +++++ .../3_rt_simulation_tcp_input_handler.py | 73 ++++++ .../4_1_rt_simulation_mqtt_input_handler.py | 68 +++++ .../4_2_rt_simulation_mqtt_output_handler.py} | 18 +- xdevs/examples/store/README.md | 46 ++++ xdevs/examples/store/__init__.py | 0 xdevs/examples/store/models/__init__.py | 0 .../models/clients.py} | 21 +- .../models}/employee.py | 16 +- .../{store_cashier => store/models}/msg.py | 4 +- .../store_queue.py => store/models/queue.py} | 14 +- xdevs/examples/store/models/store.py | 79 ++++++ .../gen.py => store/system_clients.py} | 22 +- .../system_employees.py} | 32 +-- .../queueSys.py => store/system_queue.py} | 25 +- xdevs/examples/store_cashier/STORE_CASHIER | Bin 3492944 -> 0 bytes .../examples/store_cashier/execution_loop.py | 35 --- xdevs/examples/store_cashier/output.csv | 161 ------------ xdevs/examples/store_cashier/store_cashier.py | 168 ------------- .../store_cashier/trial_CSV_store_cashier.py | 105 -------- .../store_cashier/trial_TCP_store_cashier.py | 120 --------- .../trial_two_models_MQTT_store.py | 110 -------- xdevs/factory.py | 238 ++++++++++++++++++ xdevs/models.py | 32 +-- .../input_handlers/bad_dependencies.py | 2 +- .../{csv_input_handler.py => csv.py} | 2 +- .../{callable_function.py => function.py} | 6 +- .../{mqtt_input_handler.py => mqtt.py} | 9 +- .../{tcp_input_handler.py => tcp.py} | 4 +- .../output_handlers/bad_dependencies.py | 4 +- .../{csv_output_handler.py => csv.py} | 7 +- xdevs/plugins/output_handlers/mqtt.py | 38 +++ .../output_handlers/mqtt_output_handler.py | 63 ----- .../{tcp_output_handler.py => tcp.py} | 25 +- ...cies_transducer.py => bad_dependencies.py} | 4 +- .../transducers/{csv_transducer.py => csv.py} | 8 +- ...csearch_transducer.py => elasticsearch.py} | 4 +- .../transducers/{sql_transducer.py => sql.py} | 4 +- xdevs/plugins/util/socket_server.py | 2 - xdevs/plugins/wrappers/bad_dependencies.py | 27 ++ xdevs/plugins/wrappers/pypdevs.py | 107 ++++---- xdevs/{rt_sim/rt_manager.py => rt.py} | 76 ++++-- xdevs/rt_sim/__init__.py | 2 - xdevs/rt_sim/input_handler.py | 106 -------- xdevs/rt_sim/mqtt_connector.py | 23 -- xdevs/rt_sim/output_handler.py | 97 ------- xdevs/rt_sim/rt_coord.py | 51 ---- xdevs/sim.py | 44 +--- xdevs/tests/test_csv_transducer.py | 41 +-- xdevs/tests/test_devstone.py | 9 +- xdevs/tests/test_elasticsearch_transducer.py | 34 --- xdevs/tests/test_sql_transducer.py | 36 --- xdevs/tests/test_transducible.py | 4 +- xdevs/utils.py | 26 -- xdevs/wrappers.py | 22 -- 85 files changed, 1607 insertions(+), 2421 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json rename LICENSE => LICENSE.txt (100%) create mode 100644 pyproject.toml delete mode 100644 setup.py create mode 100644 xdevs/abc/__init__.py create mode 100644 xdevs/abc/handler.py rename xdevs/{transducers.py => abc/transducer.py} (81%) delete mode 100644 xdevs/components.py delete mode 100644 xdevs/coupled_model.json delete mode 100644 xdevs/coupled_model_v2.json delete mode 100644 xdevs/examples/basic/basic.py delete mode 100644 xdevs/examples/basic/basic_inter.py delete mode 100644 xdevs/examples/basic/efp_model.py delete mode 100644 xdevs/examples/basic/json_gpt_model.json delete mode 100644 xdevs/examples/devstone/execution_loop.py create mode 100644 xdevs/examples/devstone/generator.py rename xdevs/examples/{basic => gpt}/__init__.py (100%) create mode 100644 xdevs/examples/gpt/efp.py create mode 100644 xdevs/examples/gpt/gpt.py rename xdevs/examples/{store_cashier => json}/__init__.py (100%) create mode 100644 xdevs/examples/json/efp.json create mode 100644 xdevs/examples/json/gpt.json create mode 100644 xdevs/examples/json/main.py create mode 100644 xdevs/examples/store/1_vt_simulation.py create mode 100644 xdevs/examples/store/2_rt_simulation_csv_output_handler.py create mode 100644 xdevs/examples/store/3_rt_simulation_tcp_input_handler.py create mode 100644 xdevs/examples/store/4_1_rt_simulation_mqtt_input_handler.py rename xdevs/examples/{store_cashier/trial_two_models_MQTT_gen.py => store/4_2_rt_simulation_mqtt_output_handler.py} (85%) create mode 100644 xdevs/examples/store/README.md create mode 100644 xdevs/examples/store/__init__.py create mode 100644 xdevs/examples/store/models/__init__.py rename xdevs/examples/{store_cashier/client_generator.py => store/models/clients.py} (74%) rename xdevs/examples/{store_cashier => store/models}/employee.py (83%) rename xdevs/examples/{store_cashier => store/models}/msg.py (95%) rename xdevs/examples/{store_cashier/store_queue.py => store/models/queue.py} (83%) create mode 100644 xdevs/examples/store/models/store.py rename xdevs/examples/{store_cashier/gen.py => store/system_clients.py} (63%) rename xdevs/examples/{store_cashier/employeesSys.py => store/system_employees.py} (68%) rename xdevs/examples/{store_cashier/queueSys.py => store/system_queue.py} (69%) delete mode 100644 xdevs/examples/store_cashier/STORE_CASHIER delete mode 100644 xdevs/examples/store_cashier/execution_loop.py delete mode 100644 xdevs/examples/store_cashier/output.csv delete mode 100644 xdevs/examples/store_cashier/store_cashier.py delete mode 100644 xdevs/examples/store_cashier/trial_CSV_store_cashier.py delete mode 100644 xdevs/examples/store_cashier/trial_TCP_store_cashier.py delete mode 100644 xdevs/examples/store_cashier/trial_two_models_MQTT_store.py create mode 100644 xdevs/factory.py rename xdevs/plugins/input_handlers/{csv_input_handler.py => csv.py} (97%) rename xdevs/plugins/input_handlers/{callable_function.py => function.py} (60%) rename xdevs/plugins/input_handlers/{mqtt_input_handler.py => mqtt.py} (95%) rename xdevs/plugins/input_handlers/{tcp_input_handler.py => tcp.py} (97%) rename xdevs/plugins/output_handlers/{csv_output_handler.py => csv.py} (80%) create mode 100644 xdevs/plugins/output_handlers/mqtt.py delete mode 100644 xdevs/plugins/output_handlers/mqtt_output_handler.py rename xdevs/plugins/output_handlers/{tcp_output_handler.py => tcp.py} (86%) rename xdevs/plugins/transducers/{bad_dependencies_transducer.py => bad_dependencies.py} (74%) rename xdevs/plugins/transducers/{csv_transducer.py => csv.py} (86%) rename xdevs/plugins/transducers/{elasticsearch_transducer.py => elasticsearch.py} (97%) rename xdevs/plugins/transducers/{sql_transducer.py => sql.py} (97%) create mode 100644 xdevs/plugins/wrappers/bad_dependencies.py rename xdevs/{rt_sim/rt_manager.py => rt.py} (66%) delete mode 100644 xdevs/rt_sim/__init__.py delete mode 100644 xdevs/rt_sim/input_handler.py delete mode 100644 xdevs/rt_sim/mqtt_connector.py delete mode 100644 xdevs/rt_sim/output_handler.py delete mode 100644 xdevs/rt_sim/rt_coord.py delete mode 100644 xdevs/tests/test_elasticsearch_transducer.py delete mode 100644 xdevs/tests/test_sql_transducer.py delete mode 100644 xdevs/utils.py delete mode 100644 xdevs/wrappers.py diff --git a/.gitignore b/.gitignore index 3f8454a..12a6b9d 100644 --- a/.gitignore +++ b/.gitignore @@ -136,3 +136,4 @@ dmypy.json # Log File *.log +*.csv diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8352304 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,12 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Module", + "type": "python", + "request": "launch", + "module": "tests.test_devstone", + // "env": {"PYTHONPATH": "${workspaceFolder}/libs/"} + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bfaa2cd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "python.autoComplete.extraPaths": ["${workspaceFolder}/xdevs"], + "python.testing.cwd": "${workspaceFolder/xdevs}", + "python.analysis.include": [ + "${workspaceFolder}/xdevs" + ], + "python.analysis.extraPaths": [ + "${workspaceFolder}" + ], +} \ No newline at end of file diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..df8a95e --- /dev/null +++ b/pyproject.toml @@ -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 = "r.cardenas@upm.es"}, +] +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*"] diff --git a/setup.py b/setup.py deleted file mode 100644 index b226a2e..0000000 --- a/setup.py +++ /dev/null @@ -1,47 +0,0 @@ -from setuptools import setup, find_packages - - -setup( - name='xdevs', - version='2.2.2', - description='xDEVS M&S framework', - url='https://github.com/iscar-ucm/xdevs.py', - author='Román Cárdenas, Kevin Henares', - author_email='r.cardenas@upm.es, khenares@ucm.es', - packages=find_packages(exclude=['xdevs.tests']), - entry_points={ - 'xdevs.plugins.transducers': [ - 'csv = xdevs.plugins.transducers.csv_transducer:CSVTransducer', - 'sql = xdevs.plugins.transducers.sql_transducer:SQLTransducer', - 'elasticsearch = xdevs.plugins.transducers.elasticsearch_transducer:ElasticsearchTransducer', - ], - 'xdevs.plugins.input_handlers': [ - 'function = xdevs.plugins.input_handlers.callable_function:CallableFunction', - 'csv_handler = xdevs.plugins.input_handlers.csv_input_handler:CSVInputHandler', - 'tcp_handler = xdevs.plugins.input_handlers.tcp_input_handler:TCPInputHandler', - 'mqtt_handler = xdevs.plugins.input_handlers.mqtt_input_handler:MQTTInputHandler', - - ], - 'xdevs.plugins.output_handlers': [ - 'csv_out_handler = xdevs.plugins.output_handlers.csv_output_handler:CSVOutputHandler', - 'tcp_out_handler = xdevs.plugins.output_handlers.tcp_output_handler:TCPOutputHandler', - 'mqtt_handler = xdevs.plugins.output_handlers.mqtt_output_handler:MQTTOutputHandler', - ], - 'xdevs.plugins.components': [ - 'generator = xdevs.examples.basic.basic:Generator', - 'transducer = xdevs.examples.basic.basic:Transducer', - 'processor = xdevs.examples.basic.basic:Processor', - 'gpt = xdevs.examples.basic.basic:Gpt', - 'ef = xdevs.examples.basic.efp_model:EF', - 'efp = xdevs.examples.basic.efp_model:EFP' - ], - 'xdevs.plugins.wrappers': [ - 'pypdevs = xdevs.plugins.wrappers.pypdevs:PyPDEVSWrapper' - ]}, - extras_require={ - 'sql': ['sqlalchemy==1.3.22'], - 'elasticsearch': ['elasticsearch==7.10.1'], - 'mqtt': ['paho-mqtt==1.5.1'], - }, - zip_safe=False -) diff --git a/xdevs/__init__.py b/xdevs/__init__.py index 77136ad..baf4dd9 100644 --- a/xdevs/__init__.py +++ b/xdevs/__init__.py @@ -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) @@ -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 diff --git a/xdevs/abc/__init__.py b/xdevs/abc/__init__.py new file mode 100644 index 0000000..ce2b8cf --- /dev/null +++ b/xdevs/abc/__init__.py @@ -0,0 +1,2 @@ +from .handler import InputHandler, OutputHandler +from .transducer import Transducer diff --git a/xdevs/abc/handler.py b/xdevs/abc/handler.py new file mode 100644 index 0000000..ed989bf --- /dev/null +++ b/xdevs/abc/handler.py @@ -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 diff --git a/xdevs/transducers.py b/xdevs/abc/transducer.py similarity index 81% rename from xdevs/transducers.py rename to xdevs/abc/transducer.py index 6325edd..c88d785 100644 --- a/xdevs/transducers.py +++ b/xdevs/abc/transducer.py @@ -1,18 +1,14 @@ -from __future__ import annotations import inspect import itertools import logging -import pkg_resources import re from abc import ABC, abstractmethod from math import isinf, isnan -from typing import Any, Callable, ClassVar, Type, TypeVar, Iterable +from typing import Any, Callable, Type, Iterable +from xdevs import T from xdevs.models import Atomic, Component, Coupled, Port -T = TypeVar('T') - - class Transducible(ABC): @staticmethod @abstractmethod @@ -25,10 +21,10 @@ class Transducer(ABC): state_mapper: dict[str, tuple[Type[T], Callable[[Atomic], T]]] event_mapper: dict[str, tuple[Type[T], Callable[[Any], T]]] - def __init__(self, **kwargs): + def __init__(self, *args, **kwargs): """ Transducer for the xDEVS M&S tool. - :param str transducer_id: ID of the transducer. + :param str transducer_id: ID of the transducer. This parameter is mandatory. :param str sim_time_id: ID of the data field containing the simulation time. By default, it is "sim_time". :param bool include_names: when True, the logs include DEVS port and component names. By default, it is True. :param str model_name_id: ID of the data field containing the DEVS model name. By default, it is "model_name". @@ -36,17 +32,14 @@ def __init__(self, **kwargs): :param bool exhaustive: determines if the output contains the state of the target components for each iteration (True) or only includes the change states (False). """ - self.active: bool = True - self.transducer_id: str = kwargs.get('transducer_id', None) - if self.transducer_id is None: - raise AttributeError("You must specify a transducer ID.") - + self.transducer_id: str = kwargs['transducer_id'] self.sim_time_id: str = kwargs.get('sim_time_id', 'sim_time') self.include_names: bool = kwargs.get('include_names', True) self.model_name_id: str = kwargs.get('model_name_id', 'model_name') self.port_name_id: str = kwargs.get('port_name_id', 'port_name') - self.exhaustive: bool = kwargs.get('exhaustive', False) + + self.active: bool = True self.target_components: set[Atomic] = set() self.target_ports: set[Port] = set() self.imminent_components: list[Atomic] | None = None if self.exhaustive else [] @@ -63,30 +56,32 @@ def __init__(self, **kwargs): self._remove_special_numbers: bool = False def activate_remove_special_numbers(self): - logging.warning('Transducer {} does not support special number values (e.g., infinity). ' - 'It will automatically substitute these values by None'.format(self.transducer_id)) + logging.warning(f'Transducer {self.transducer_id} does not support special number values (e.g., infinity). ' + 'It will automatically substitute these values with None') self._remove_special_numbers = True - def add_target_component(self, component: Atomic or Coupled, *filters): + def add_target_component(self, component: Component, *filters): components = self._iterate_components(component) self.target_components |= self._apply_filters(filters, components) - def _iterate_components(self, root_comp, include_coupled=False): + def _iterate_components(self, root_comp: Component, include_coupled: bool = False): if isinstance(root_comp, Atomic): yield root_comp - else: # Coupled + elif isinstance(root_comp, Coupled): # Coupled if include_coupled: yield root_comp for child_comp in root_comp.components: yield from self._iterate_components(child_comp, include_coupled=include_coupled) + else: + raise ValueError(f'Component {root_comp.name} is not an Atomic nor a Coupled') def add_target_port(self, port: Port): parent: Component | None = port.parent if parent is None: - raise ValueError('Port {} does not have a parent component'.format(port.name)) + raise ValueError(f'Port {port.name} does not have a parent component') self.target_ports.add(port) - def add_target_ports_by_component(self, component, component_filters=None, port_filters=None): + def add_target_ports_by_component(self, component: Component, component_filters=None, port_filters=None): components = self._iterate_components(component, include_coupled=True) filtered_components = Transducer._apply_filters(component_filters, components) for comp in filtered_components: @@ -94,11 +89,11 @@ def add_target_ports_by_component(self, component, component_filters=None, port_ filtered_comp_ports = Transducer._apply_filters(port_filters, comp_ports) self.target_ports |= filtered_comp_ports - def add_imminent_model(self, component): + def add_imminent_model(self, component: Atomic): if not self.exhaustive and self.active: self.imminent_components.append(component) - def add_imminent_port(self, port): + def add_imminent_port(self, port: Port): if not self.exhaustive and self.active: self.imminent_ports.append(port) @@ -114,9 +109,9 @@ def add_event_field(self, field_name: str, field_type: Type[T], field_getter: Ca :raise KeyError: if field name is already in event mapper. """ if field_name == self.sim_time_id: - raise KeyError('Field name {} is reserved for the simulation time field'.format(field_name)) + raise KeyError(f'Field name {field_name} is reserved for the simulation time field') elif self.include_names and field_name in (self.model_name_id, self.port_name_id): - raise KeyError('Field name {} is reserved for DEVS element name field'.format(field_name)) + raise KeyError(f'Field name {field_name} is reserved for DEVS element name field') self._add_field(self.event_mapper, field_name, field_type, field_getter) def add_state_field(self, field_name: str, field_type: Type[T], field_getter: Callable[[Atomic], T]): @@ -128,16 +123,16 @@ def add_state_field(self, field_name: str, field_type: Type[T], field_getter: Ca :raise KeyError: if field name is already in state mapper. """ if field_name == self.sim_time_id: - raise KeyError('Field name {} is reserved for the simulation time field'.format(field_name)) + raise KeyError(f'Field name {field_name} is reserved for the simulation time field') elif self.include_names and field_name == self.model_name_id: - raise KeyError('Field name {} is reserved for DEVS component name field'.format(field_name)) + raise KeyError(f'Field name {field_name} is reserved for DEVS component name field') self._add_field(self.state_mapper, field_name, field_type, field_getter) @staticmethod def _add_field(field_mapper: dict[str, tuple[Type[T], Callable[[Any], T]]], field_name: str, field_type: Type[T], field_getter: Callable[[Any], T]): if field_name in field_mapper: - raise KeyError('Field name {} is already included in field mapper'.format(field_name)) + raise KeyError(f'Field name {field_name} is already included in field mapper') field_mapper[field_name] = (field_type, field_getter) def drop_event_field(self, field_name: str): @@ -255,23 +250,5 @@ def map_extra_fields(self, target: Any, field_mapper: dict) -> dict[str, Any]: return extra_fields def _log_unknown_data(self, data_type: type, field_name: str): - logging.warning('Transducer {} does not support data type {} of field {}. ' - 'It will cast it to string'.format(self.transducer_id, data_type, field_name)) - - -class Transducers: - _plugins: ClassVar[dict[str, Type[Transducer]]] = { - ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs.plugins.transducers') - } - - @staticmethod - def add_plugin(name: str, plugin: Type[Transducer]): - if name in Transducers._plugins: - raise ValueError(f'xDEVS transducer plugin with name "{name}" already exists') - Transducers._plugins[name] = plugin - - @staticmethod - def create_transducer(name: str, **kwargs) -> Transducer: - if name not in Transducers._plugins: - raise ValueError('xDEVS transducer plugin with name "{name}" not found') - return Transducers._plugins[name](**kwargs) + logging.warning(f'Transducer {self.transducer_id} does not support data type {data_type} of field {field_name}. ' + 'It will cast it to string') diff --git a/xdevs/components.py b/xdevs/components.py deleted file mode 100644 index df2afea..0000000 --- a/xdevs/components.py +++ /dev/null @@ -1,147 +0,0 @@ -import pkg_resources -import json -from typing import ClassVar -from xdevs.models import Component, Port, Coupled - - -class Components: - """ - This class creates components from unique identifiers called "component_id". - - In order to identify each component_id check the "entry_point" dict in the file setup.py and look for the - list 'xdevs.plugins.components'. - """ - _plugins: ClassVar[dict[str, type[Component]]] = { - ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs.plugins.components') - } - - @staticmethod - def add_plugin(component_id: str, plugin: type[Component]): - if component_id in Components._plugins: - raise ValueError(f'xDEVS component plugin with name "{component_id}" already exists') - Components._plugins[component_id] = plugin - - @staticmethod - def create_component(component_id: str, *args, **kwargs) -> Component: - if component_id not in Components._plugins: - raise ValueError(f'xDEVS component plugin with name "{component_id}" not found') - return Components._plugins[component_id](*args, **kwargs) - - @staticmethod - def _nested_component(name: str, config: dict) -> Component: - if 'component_id' in config: - # Predefined component, use factory - component_id: str = config['component_id'] - args = config.get('args', []) - kwargs = config.get('kwargs', {}) - kwargs['name'] = name - return Components.create_component(component_id, *args, **kwargs) - elif 'components' in config: - # It is a coupled model - component = Coupled(name) - children: dict[str, Component] = dict() - # Create children components - for component_name, component_config in config['components'].items(): - child = Components._nested_component(component_name, component_config) - children[component_name] = child - component.add_component(child) - # Create connections - for connection in config.get('connections', []): - child_from = connection.get('componentFrom') - child_to = connection.get('componentTo') - if child_from is not None: - child_from = children[child_from] - port_from = child_from.get_out_port(connection['portFrom']) - if port_from is None: - raise Exception(f'Invalid connection in: {connection}. Reason: portFrom not found') - if child_to is not None: - # this is an IC - child_to = children[child_to] - port_to = child_to.get_in_port(connection['portTo']) - if port_to is None: - raise Exception(f'Invalid connection in: {connection}. Reason: portTo not found') - else: - # this is an EOC - port_to = child_to.get_in_port(connection['portTo']) - if port_to is None: - port_to = Port(p_type=port_from.p_type, name=connection['portTo']) - component.add_out_port(port_to) - elif child_to is not None: - # this is an EIC - child_to = children[child_to] - port_to = child_to.get_in_port(connection['portTo']) - if port_to is None: - raise Exception(f'Invalid connection in: {connection}. Reason: portTo not found') - port_from = component.get_in_port(connection['portFrom']) - if port_from is None: - port_from = Port(p_type=port_to.p_type, name=connection['portFrom']) - component.add_in_port(port_from) - else: - raise Exception( - f'Invalid connection in: {connection}. Reason: componentFrom and componentTo are None') - - component.add_coupling(port_from, port_to) - else: - raise Exception('No component found') - return component - - @staticmethod - def from_json(file_path: str): - """ - A function to parser a json file into a DEVS model - - Considering the following constrains, the json file structure is as follows: - - When adding a component if it contains the key "component_id", the component will be created using it and the - kwargs associated with it. The "component_id" value refers to the key to identify each component in the class - Components. - - When the component does not have the key "component_id" it must have the pair keys "components" and "connections". - This component will be implementing several components and their connections inside itself. - - The connections are created using four keys: - - If both componentFrom/To keys are added, the connection will be of the type IC. - - If componentFrom key is missing, the connection will be of the type EIC. - - If componentTo key is missing, the connection will be of the type EOC. - - If any portFrom/To value is missing the connections is not established. - - Structure: - - - 'MasterComponentName' (dict): The master component. - - 'components' (dict): A dictionary containing multiple components. - - 'ComponentName1' (dict): Iterative component. - - 'components' (dict): Nested components if any. - - 'connections' (list): List of connection dictionaries. - - 'componentFrom' (str): Name of the component where the connection starts. - - 'portFrom' (str): Port name from 'componentFrom'. - - 'componentTo' (str): Name of the component where the connection ends. - - 'portTo' (str): Port name from 'componentTo'. - - 'ComponentName2' (dict): Single component. - - 'component_id' (str): ID read from the factory for this component. - - 'kwargs' (dict): Keyword arguments for the component. - - 'a_parameter' (any): A parameter for the component. - - ... : Other keyword arguments if any. - - ... : Additional components if any. - - 'connections' (list): List of connection dictionaries at the top level. - - 'componentFrom' (str): Name of the component where the connection starts. - - 'portFrom' (str): Port name from 'componentFrom'. - - 'componentTo' (str): Name of the component where the connection ends. - - 'portTo' (str): Port name from 'componentTo'. - - :param file_path: Path to the JSON file - :return: a Coupled DEVS model - """ - - with open(file_path) as f: - data = json.load(f) - - name = list(data.keys())[0] # Gets the actual component name - config = data[name] # Gets the actual component config - - return Components._nested_component(name, config) - - -if __name__ == '__main__': - - C = Components.from_json('coupled_model_v2.json') - print(C) diff --git a/xdevs/coupled_model.json b/xdevs/coupled_model.json deleted file mode 100644 index 26c1bca..0000000 --- a/xdevs/coupled_model.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "EFP" : { - "components": { - - "processor": { - "component_id": "processor", - "kwargs": { - "proc_time": 10 - } - }, - - "ef": { - "component_id": "ef", - "kwargs": { - "period": 5, - "obs_time": 300 - } - - } - }, - - "connections" : [ - { - "componentFrom": "ef", - "portFrom": "p_out_ef", - "componentTo": "processor", - "portTo": "i_in" - }, - { - "componentFrom": "processor", - "portFrom": "o_out", - "componentTo": "ef", - "portTo": "p_in_ef" - } - - ] - } -} diff --git a/xdevs/coupled_model_v2.json b/xdevs/coupled_model_v2.json deleted file mode 100644 index 56ebac4..0000000 --- a/xdevs/coupled_model_v2.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "coupled/MasterModel": { - "components": { - "name/GPT": { - "components": { - - "Processor": { - "component_id": "processor", - "kwargs": { - "proc_time": 3.0 - } - }, - "Generator": { - "component_id": "generator", - "kwargs": { - "period": 1.0 - } - }, - "Transducer": { - "component_id": "transducer", - "kwargs": { - "obs_time": 100.0 - } - } - }, - - "connections": [ - { - "componentFrom": "Processor", - "portFrom": "o_out", - "componentTo": "Transducer", - "portTo": "i_solved" - }, - { - "componentFrom": "Generator", - "portFrom": "o_out", - "componentTo": "Processor", - "portTo": "i_in" - }, - { - "componentFrom": "Generator", - "portFrom": "o_out", - "componentTo": "Transducer", - "portTo": "i_arrived" - }, - { - "componentFrom": "Transducer", - "portFrom": "o_out", - "componentTo": "Generator", - "portTo": "i_stop" - }, - { - "componentFrom": null, - "portFrom": "i_in_gpt", - "componentTo": "Generator", - "portTo": "i_stop" - } - ] - }, - - "Processor_2": { - "component_id": "processor", - "kwargs": { - "proc_time": 300.0 - } - }, - - "c/gpt" : { - "component_id": "gpt", - "kwargs": { - "period": 3, - "obs_time": 100 - } - } - }, - - "connections": [ - { - "componentFrom": "Processor_2", - "portFrom": "o_out", - "componentTo": "name/GPT", - "portTo": "i_in_gpt" - } - ] - } -} \ No newline at end of file diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py index 2e83b16..5d2bf1c 100644 --- a/xdevs/examples/async_rt/basic.py +++ b/xdevs/examples/async_rt/basic.py @@ -5,7 +5,7 @@ from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger from xdevs.models import Atomic, Coupled, Port -from xdevs.rt_sim import RealTimeCoordinator, RealTimeManager +from xdevs.rt import RealTimeCoordinator, RealTimeManager logger = get_logger(__name__, logging.INFO) @@ -243,7 +243,7 @@ def inject_messages(q: queue.SimpleQueue): 'i_extern': lambda x: Job(x), # le digo al input handler como convertir el string a Job con una función 'tcp': lambda x: x.decode().split('.'), } - # manager.add_input_handler('csv_handler', file="prueba.csv", msg_parsers=parsers) + manager.add_input_handler('csv_handler', file="prueba.csv", msg_parsers=parsers) # manager.add_input_handler('function', function=inject_messages) # Si no quiero ir repitiendo parsers, se lo tendria que meter al manager @@ -263,7 +263,7 @@ def inject_messages(q: queue.SimpleQueue): c = RealTimeCoordinator(gpt, manager) t_ini = time.time() print(f' >>> COMENZAMOS : {t_ini}') - c.simulate(time_interv=execution_time) + c.simulate_iters(time_interv=execution_time) print(f' >>> FIN : {time.time()}') print(f' Tiempo a ejecutar (s) = {execution_time * time_scale}') print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') diff --git a/xdevs/examples/basic/basic.py b/xdevs/examples/basic/basic.py deleted file mode 100644 index 011484c..0000000 --- a/xdevs/examples/basic/basic.py +++ /dev/null @@ -1,194 +0,0 @@ -import logging - - -from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger -from xdevs.models import Atomic, Coupled, Port -from xdevs.sim import Coordinator -from xdevs.rt_sim import RealTimeCoordinator -import time - -logger = get_logger(__name__, logging.DEBUG) - -PHASE_DONE = "done" - - -class Job: - def __init__(self, name): - self.name = name - self.time = 0 - - -class Generator(Atomic): - - def __init__(self, name, period): - super().__init__(name) - self.i_start = Port(Job, "i_start") - self.i_stop = Port(Job, "i_stop") - self.o_out = Port(Job, "o_out") - - self.add_in_port(self.i_start) - self.add_in_port(self.i_stop) - self.add_out_port(self.o_out) - - self.period = period - self.job_counter = 1 - - def initialize(self): - self.hold_in(PHASE_ACTIVE, self.period) - - def exit(self): - pass - - def deltint(self): - self.job_counter += 1 - self.hold_in(PHASE_ACTIVE, self.period) - - def deltext(self, e): - self.passivate() - - def lambdaf(self): - self.o_out.add(Job(str(self.job_counter))) - - -class Processor(Atomic): - def __init__(self, name, proc_time): - super().__init__(name) - - self.i_in = Port(Job, "i_in") - self.o_out = Port(Job, "o_out") - - self.add_in_port(self.i_in) - self.add_out_port(self.o_out) - - self.current_job = None - self.proc_time = proc_time - - def initialize(self): - self.passivate() - - def exit(self): - pass - - def deltint(self): - self.passivate() - - def deltext(self, e): - if self.phase == PHASE_PASSIVE: - self.current_job = self.i_in.get() - self.hold_in(PHASE_ACTIVE, self.proc_time) - self.continuef(e) - - def lambdaf(self): - self.o_out.add(self.current_job) - - -class Transducer(Atomic): - - def __init__(self, name, obs_time): - super().__init__(name) - - self.i_arrived = Port(Job, "i_arrived") - self.i_solved = Port(Job, "i_solved") - self.o_out = Port(Job, "o_out") - - self.add_in_port(self.i_arrived) - self.add_in_port(self.i_solved) - self.add_out_port(self.o_out) - - self.jobs_arrived = [] - self.jobs_solved = [] - - self.total_ta = 0 - self.clock = 0 - self.obs_time = obs_time - - def initialize(self): - self.hold_in(PHASE_ACTIVE, self.obs_time) - - def exit(self): - pass - - def deltint(self): - self.clock += self.sigma - - if self.phase == PHASE_ACTIVE: - if self.jobs_solved: - avg_ta = self.total_ta / len(self.jobs_solved) - throughput = len(self.jobs_solved) / self.clock if self.clock > 0 else 0 - else: - avg_ta = 0 - throughput = 0 - - logger.info("End time: %f" % self.clock) - logger.info("Jobs arrived: %d" % len(self.jobs_arrived)) - logger.info("Jobs solved: %d" % len(self.jobs_solved)) - logger.info("Average TA: %f" % avg_ta) - logger.info("Throughput: %f\n" % throughput) - - self.hold_in(PHASE_DONE, 0) - else: - self.passivate() - - def deltext(self, e): - self.clock += e - - if self.phase == PHASE_ACTIVE: - if self.i_arrived: - job = self.i_arrived.get() - logger.info("Starting job %s @ t = %d @ t = %d" % (job.name, self.clock, time.time_ns())) - job.time = self.clock - self.jobs_arrived.append(job) - - if self.i_solved: - job = self.i_solved.get() - logger.info("Job %s finished @ t = %d @ t = %d" % (job.name, self.clock, time.time())) - self.total_ta += self.clock - job.time - self.jobs_solved.append(job) - - self.continuef(e) - - def lambdaf(self): - if self.phase == PHASE_DONE: - self.o_out.add(Job("null")) - - -class Gpt(Coupled): - def __init__(self, name, period, obs_time): - super().__init__(name) - - if period < 1: - raise ValueError("period has to be greater than 0") - - if obs_time < 0: - raise ValueError("obs_time has to be greater or equal than 0") - - gen = Generator("generator", period) - proc = Processor("processor", 3 * period) - trans = Transducer("transducer", obs_time) - - self.add_component(gen) - self.add_component(proc) - self.add_component(trans) - - self.add_coupling(gen.o_out, proc.i_in) - self.add_coupling(gen.o_out, trans.i_arrived) - self.add_coupling(proc.o_out, trans.i_solved) - self.add_coupling(trans.o_out, gen.i_stop) - - -class Wrap(Coupled): - def __init__(self, name, period, obs_time): - super().__init__("wrap") - - gpt = Gpt(name, period, obs_time) - - self.add_component(gpt) - - -if __name__ == '__main__': - gpt = Gpt("gpt", 2, 100) - coord = Coordinator(gpt) - coord.initialize() - # coord.simulate() - coord.simulate() - # coord.simulate_rt(50) diff --git a/xdevs/examples/basic/basic_inter.py b/xdevs/examples/basic/basic_inter.py deleted file mode 100644 index cc40eaa..0000000 --- a/xdevs/examples/basic/basic_inter.py +++ /dev/null @@ -1,211 +0,0 @@ -from pypdevs.DEVS import AtomicDEVS - -from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger -from xdevs.models import Atomic, Coupled, Port -from xdevs.plugins.wrappers.pypdevs import PyPDEVSWrapper -from xdevs.sim import Coordinator, ParallelCoordinator -from pypdevs.infinity import INFINITY -import logging - -logger = get_logger(__name__, logging.DEBUG) - -PHASE_DONE = "done" - - -class Job: - def __init__(self, name): - self.name = name - self.time = 0 - - -class Generator(Atomic): - - def __init__(self, name, period): - super().__init__(name) - self.i_start = Port(Job, "i_start") - self.i_stop = Port(Job, "i_stop") - self.o_out = Port(Job, "o_out") - - self.add_in_port(self.i_start) - self.add_in_port(self.i_stop) - self.add_out_port(self.o_out) - - self.period = period - self.job_counter = 1 - - def initialize(self): - self.hold_in(PHASE_ACTIVE, self.period) - - def exit(self): - pass - - def deltint(self): - self.job_counter += 1 - self.hold_in(PHASE_ACTIVE, self.period) - - def deltext(self, e): - self.passivate() - - def lambdaf(self): - self.o_out.add(Job(str(self.job_counter))) - - -# -# class Processor(Atomic): -# def __init__(self, name, proc_time): -# super().__init__(name) -# -# self.i_in = Port(Job, "i_in") -# self.o_out = Port(Job, "o_out") -# -# self.add_in_port(self.i_in) -# self.add_out_port(self.o_out) -# -# self.current_job = None -# self.proc_time = proc_time -# -# def initialize(self): -# self.passivate() -# -# def exit(self): -# pass -# -# def deltint(self): -# self.passivate() -# -# def deltext(self, e): -# if self.phase == PHASE_PASSIVE: -# self.current_job = self.i_in.get() -# self.hold_in(PHASE_ACTIVE, self.proc_time) -# -# def lambdaf(self): -# self.o_out.add(self.current_job) - -class Processor(AtomicDEVS): - def __init__(self, name, proc_time): - super().__init__(name) - - self.i_in = self.addInPort("i_in") - self.o_out = self.addOutPort("o_out") - - self.current_job = None - self.proc_time = proc_time - self.state = "passive" - - def intTransition(self): - return "passive" - - def extTransition(self, inputs): - if self.state == "passive": - self.current_job = inputs[self.i_in][0] - return "active" - - def timeAdvance(self): - if self.state == "active": - return self.proc_time - else: - return INFINITY - - def outputFnc(self): - return {self.o_out: [self.current_job]} - - -class Transducer(Atomic): - - def __init__(self, name, obs_time): - super().__init__(name) - - self.i_arrived = Port(Job, "i_arrived") - self.i_solved = Port(Job, "i_solved") - self.o_out = Port(Job, "o_out") - - self.add_in_port(self.i_arrived) - self.add_in_port(self.i_solved) - self.add_out_port(self.o_out) - - self.jobs_arrived = [] - self.jobs_solved = [] - - self.total_ta = 0 - self.clock = 0 - self.obs_time = obs_time - - def initialize(self): - self.hold_in(PHASE_ACTIVE, self.obs_time) - - def exit(self): - pass - - def deltint(self): - self.clock += self.sigma - - if self.phase == PHASE_ACTIVE: - if self.jobs_solved: - avg_ta = self.total_ta / len(self.jobs_solved) - throughput = len(self.jobs_solved) / self.clock if self.clock > 0 else 0 - else: - avg_ta = 0 - throughput = 0 - - logger.info("End time: %f" % self.clock) - logger.info("Jobs arrived: %d" % len(self.jobs_arrived)) - logger.info("Jobs solved: %d" % len(self.jobs_solved)) - logger.info("Average TA: %f" % avg_ta) - logger.info("Throughput: %f\n" % throughput) - - self.hold_in(PHASE_DONE, 0) - else: - self.passivate() - - def deltext(self, e): - self.clock += e - - if self.phase == PHASE_ACTIVE: - if self.i_arrived: - job = self.i_arrived.get() - logger.info("Starting job %s @ t = %d" % (job.name, self.clock)) - job.time = self.clock - self.jobs_arrived.append(job) - - if self.i_solved: - job = self.i_solved.get() - logger.info("Job %s finished @ t = %d" % (job.name, self.clock)) - self.total_ta += self.clock - job.time - self.jobs_solved.append(job) - - self.continuef(e) - - def lambdaf(self): - if self.phase == PHASE_DONE: - self.o_out.add(Job("null")) - - -class Gpt(Coupled): - def __init__(self, name, period, obs_time): - super().__init__(name) - - if period < 1: - raise ValueError("period has to be greater than 0") - - if obs_time < 0: - raise ValueError("obs_time has to be greater or equal than 0") - - gen = Generator("generator", period) - proc = PyPDEVSWrapper(Processor("processor", 3 * period)) - trans = Transducer("transducer", obs_time) - - self.add_component(gen) - self.add_component(proc) - self.add_component(trans) - - self.add_coupling(gen.o_out, proc.i_in) - self.add_coupling(gen.o_out, trans.i_arrived) - self.add_coupling(proc.o_out, trans.i_solved) - self.add_coupling(trans.o_out, gen.i_stop) - - -if __name__ == '__main__': - gpt = Gpt("gpt", 3, 1000) - coord = Coordinator(gpt, flatten=False, chain=False) - coord.initialize() - coord.simulate() diff --git a/xdevs/examples/basic/efp_model.py b/xdevs/examples/basic/efp_model.py deleted file mode 100644 index acb9dad..0000000 --- a/xdevs/examples/basic/efp_model.py +++ /dev/null @@ -1,54 +0,0 @@ -from xdevs.examples.basic.basic import Generator, Transducer, Job, Processor -from xdevs.models import Coupled, Port -from xdevs.sim import Coordinator - - -class EF(Coupled): - def __init__(self, name, period, obs_time): - super().__init__(name) - - if period < 1: - raise ValueError("period has to be greater than 0") - - if obs_time < 0: - raise ValueError("obs_time has to be greater or equal than 0") - - gen = Generator(name='EF_gen', period=period) - trans = Transducer(name='EF_trans', obs_time=obs_time) - - self.add_component(gen) - self.add_component(trans) - - self.p_in_ef = Port(Job, name='p_in_ef') - self.p_out_ef = Port(Job, name='p_out_ef') - - self.add_in_port(self.p_in_ef) - self.add_out_port(self.p_out_ef) - - self.add_coupling(gen.o_out, trans.i_arrived) - self.add_coupling(gen.o_out, self.p_out_ef) - self.add_coupling(trans.o_out, gen.i_stop) - self.add_coupling(self.p_in_ef, trans.i_solved) - - -class EFP(Coupled): - def __init__(self, name, period, obs_time, proc_time): - super().__init__(name) - - ef = EF(name='EF', period=period, obs_time=obs_time) - - proc = Processor(name='EFP_proc', proc_time=proc_time) - - self.add_component(ef) - self.add_component(proc) - - self.add_coupling(ef.p_out_ef, proc.i_in) - self.add_coupling(proc.o_out, ef.p_in_ef) - - -if __name__ == '__main__': - - EFP = EFP('efp', 3, 100, 5) - Coord = Coordinator(EFP) - Coord.initialize() - Coord.simulate() diff --git a/xdevs/examples/basic/json_gpt_model.json b/xdevs/examples/basic/json_gpt_model.json deleted file mode 100644 index e1f2cb1..0000000 --- a/xdevs/examples/basic/json_gpt_model.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "nameGPT": { - "components": { - "atomic/Processor": { - "name": "p0", - "class": "xdevs.core.examples.efp.Processor", - "kwargs": { - "proc_time": 3.0 - } - }, - "atomic/generator": { - "name": "g0", - "class": "xdevs.core.examples.efp.Generator", - "kwargs": { - "period": 1.0 - } - }, - "atomic/transducer": { - "name": "t0", - "class": "xdevs.core.examples.efp.Transducer", - "kwargs": { - "obs_time": 100.0 - } - } - }, - "connections": [ - { - "componentFrom": "p0", - "portFrom": "o_out", - "componentTo": "t0", - "portTo": "i_solved" - }, - { - "componentFrom": "g0", - "portFrom": "o_out", - "componentTo": "p0", - "portTo": "i_in" - }, - { - "componentFrom": "g0", - "portFrom": "o_out", - "componentTo": "t0", - "portTo": "i_arrived" - }, - { - "componentFrom": "t0", - "portFrom": "o_out", - "componentTo": "g0", - "portTo": "i_stop" - } - ] - } -} \ No newline at end of file diff --git a/xdevs/examples/devstone/devstone.py b/xdevs/examples/devstone/devstone.py index 2884f08..769787d 100644 --- a/xdevs/examples/devstone/devstone.py +++ b/xdevs/examples/devstone/devstone.py @@ -49,6 +49,8 @@ def exit(self): class AbstractDEVStone(Coupled, ABC): + components: list[AbstractDEVStone | DelayedAtomic] + def __init__(self, name: str, width: int, depth: int, int_delay: float, ext_delay: float, test: bool = False): super().__init__(name) if width < 1: @@ -290,4 +292,4 @@ def n_events(self) -> int: coord = Coordinator(root) coord.initialize() coord.inject(root.i_in, 0) - coord.simulate() + coord.simulate_iters() diff --git a/xdevs/examples/devstone/execution_loop.py b/xdevs/examples/devstone/execution_loop.py deleted file mode 100644 index bda1593..0000000 --- a/xdevs/examples/devstone/execution_loop.py +++ /dev/null @@ -1,54 +0,0 @@ -import csv -import sys -import time - -from devstone import LI, HI, HO -from xdevs.sim import Coordinator - -sys.setrecursionlimit(10000) - -sim_max_time = 1e10 -int_delay = 0 -ext_delay = 0 -flatten = False -num_execs = int(sys.argv[1]) if len(sys.argv) > 1 else 10 - -depths_widths = [(300, 10), (10, 300), (300, 300)] - -filename = "xdevs_devstone_%s_%dc_%di_%de_%d.csv" % ("flatten" if flatten else "noflatten", len(depths_widths), - int_delay, ext_delay, int(time.time())) - -with open(filename, "w") as csv_file: - csv_writer = csv.writer(csv_file, delimiter=';') - csv_writer.writerow(("model", "depth", "width", "chained", "model_time", "runner_time", "sim_time", "total_time")) - - for depth, width in depths_widths: - for model_type in (LI, HI, HO): - class_name = model_type.__name__ - row = (class_name, depth, width) - - for chain_activated in (False, True): - for i_exec in range(num_execs): - - start = time.time() - root_model = model_type("root", depth, width, int_delay, ext_delay) - middle = time.time() - coord = Coordinator(root_model, flatten=flatten, chain=chain_activated) - coord.initialize() - coord.inject(root_model.i_in, 0) # TODO How many input? - middle2 = time.time() - coord.simulate_time(sim_max_time) - end = time.time() - - model_time = middle - start - runner_time = middle2 - middle - sim_time = end - middle2 - total_time = end - start - # print("acc: %d" % i_exec, model_time, runner_time, sim_time, total_time) - - # row = row + (model_time/num_execs, runner_time/num_execs, sim_time/num_execs, total_time/num_execs) - curr_row = row + (chain_activated, model_time, runner_time, sim_time, total_time) - - print(curr_row) - csv_writer.writerow(curr_row) - diff --git a/xdevs/examples/devstone/generator.py b/xdevs/examples/devstone/generator.py new file mode 100644 index 0000000..aaddbeb --- /dev/null +++ b/xdevs/examples/devstone/generator.py @@ -0,0 +1,34 @@ +from xdevs.models import Atomic, Port + + +class Generator(Atomic): + def __init__(self, name: str, num_outputs: int = 1, period: float = float('inf'), num_ports: int = 1): + super(Generator, self).__init__(name=name) + if num_outputs < 1: + raise ValueError('Number of outputs must greater than zero') + if num_ports < 1: + raise ValueError('Number of ports must be greater than zero') + if period <= 0: + raise ValueError('Period must be greater than zero') + self.num_outputs: int = num_outputs + self.period: float = period + + self.o_out: list[Port[int]] = [Port(int, f'o_out_{i}') for i in range(num_ports)] + for port in self.o_out: + self.add_out_port(port) + + def deltint(self): + self.sigma = self.period + + def deltext(self, e: float): + pass + + def lambdaf(self): + for port in self.o_out: + port.extend(range(self.num_outputs)) + + def initialize(self): + self.activate() + + def exit(self): + pass diff --git a/xdevs/examples/devstone/main.py b/xdevs/examples/devstone/main.py index 0e305c9..2220214 100644 --- a/xdevs/examples/devstone/main.py +++ b/xdevs/examples/devstone/main.py @@ -5,27 +5,25 @@ from xdevs.sim import Coordinator from xdevs.examples.devstone.devstone import LI, HO, HI, HOmod +from xdevs.examples.devstone.generator import Generator from xdevs.models import Coupled -from xdevs.utils import Generator + sys.setrecursionlimit(10000) +MODEL_TYPES = ("LI", "HI", "HO", "HOmod") -class DEVStoneEnvironment(Coupled): - def __init__(self, name, devstone_model, num_gen_outputs=1, gen_period=None): +class DEVStoneEnvironment(Coupled): + def __init__(self, name, devstone_model, num_gen_outputs=1): super(DEVStoneEnvironment, self).__init__(name=name) - generator = Generator("generator", num_gen_outputs, gen_period) + generator = Generator("generator", num_gen_outputs) self.add_component(generator) self.add_component(devstone_model) - self.add_coupling(generator.o_out, devstone_model.i_in) - + self.add_coupling(generator.o_out[0], devstone_model.i_in) if isinstance(devstone_model, HO) or isinstance(devstone_model, HOmod): - self.add_coupling(generator.o_out, devstone_model.i_in2) - - -MODEL_TYPES = ("LI", "HI", "HO", "HOmod") + self.add_coupling(generator.o_out[0], devstone_model.i_in2) def parse_args(): @@ -47,11 +45,8 @@ def parse_args(): if __name__ == '__main__': - args = parse_args() - devstone_model = None - if args.model_type == "LI": devstone_model = LI("LI_root", args.depth, args.width, args.int_cycles, args.ext_cycles) elif args.model_type == "HI": @@ -60,6 +55,8 @@ def parse_args(): devstone_model = HO("HO_root", args.depth, args.width, args.int_cycles, args.ext_cycles) elif args.model_type == "HOmod": devstone_model = HOmod("HOmod_root", args.depth, args.width, args.int_cycles, args.ext_cycles) + else: + raise RuntimeError("Unrecognized model type.") start_time = time.time() env = DEVStoneEnvironment("DEVStoneEnvironment", devstone_model) @@ -69,10 +66,9 @@ def parse_args(): coord.initialize() engine_setup_time = time.time() - coord.simulate() + coord.simulate_iters() sim_time = time.time() - print("Model creation time: {}".format(model_created_time - start_time)) - print("Engine setup time: {}".format(engine_setup_time - model_created_time)) - print("Simulation time: {}".format(sim_time - engine_setup_time)) - + print(f"Model creation time: {model_created_time - start_time}") + print(f"Engine setup time: {engine_setup_time - model_created_time}") + print(f"Simulation time: {sim_time - engine_setup_time}") diff --git a/xdevs/examples/basic/__init__.py b/xdevs/examples/gpt/__init__.py similarity index 100% rename from xdevs/examples/basic/__init__.py rename to xdevs/examples/gpt/__init__.py diff --git a/xdevs/examples/gpt/efp.py b/xdevs/examples/gpt/efp.py new file mode 100644 index 0000000..87421ff --- /dev/null +++ b/xdevs/examples/gpt/efp.py @@ -0,0 +1,47 @@ +from xdevs.examples.gpt.gpt import Generator, Transducer, Job, Processor +from xdevs.models import Coupled, Port + + +class Ef(Coupled): + def __init__(self, name: str, gen_t: float, obs_t: float): + super().__init__(name) + + gen = Generator('generator', gen_t) + trans = Transducer('transducer', obs_t) + + self.add_component(gen) + self.add_component(trans) + + self.p_in_ef = Port(Job, name='p_in_ef') + self.p_out_ef = Port(Job, name='p_out_ef') + + self.add_in_port(self.p_in_ef) + self.add_out_port(self.p_out_ef) + + self.add_coupling(gen.o_job, trans.i_arrived) + self.add_coupling(gen.o_job, self.p_out_ef) + self.add_coupling(trans.o_out, gen.i_stop) + self.add_coupling(self.p_in_ef, trans.i_solved) + + +class Efp(Coupled): + def __init__(self, name: str, gen_t: float, proc_t: float, obs_t: float): + super().__init__(name) + + ef = Ef('ef', gen_t, obs_t) + proc = Processor('processor', proc_t) + + self.add_component(ef) + self.add_component(proc) + + self.add_coupling(ef.p_out_ef, proc.i_in) + self.add_coupling(proc.o_out, ef.p_in_ef) + + +if __name__ == '__main__': + from xdevs.sim import Coordinator + + efp = Efp('efp', 3, 5, 100) + coord = Coordinator(efp) + coord.initialize() + coord.simulate_iters() diff --git a/xdevs/examples/gpt/gpt.py b/xdevs/examples/gpt/gpt.py new file mode 100644 index 0000000..e46bba6 --- /dev/null +++ b/xdevs/examples/gpt/gpt.py @@ -0,0 +1,198 @@ +import logging +import time +from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger +from xdevs.models import Atomic, Coupled, Port + +logger = get_logger(__name__, logging.DEBUG) + +PHASE_DONE = "done" + + +class Job: + def __init__(self, name: str): + """ + Job event class. It represents a job sent by the generator and processed by the processor. + :param name: job name + """ + self.name: str = name + self.time: float = 0 + + +class Generator(Atomic): + def __init__(self, name: str, gen_t: float): + """ + Generator model. It generates jobs at a given period. + :param name: model name + :param gen_t: period between job generations + """ + super().__init__(name) + + if gen_t < 1: + raise ValueError('gen_t must be greater than 0') + + self.i_stop: Port[bool] = Port(bool, "i_stop") + self.o_job: Port[Job] = Port(Job, "o_out") + + self.add_in_port(self.i_stop) + self.add_out_port(self.o_job) + + self.gen_t: float = gen_t + self.job_counter: int = 1 + + def initialize(self): + self.hold_in(PHASE_ACTIVE, self.gen_t) + + def exit(self): + pass + + def deltint(self): + self.job_counter += 1 + self.hold_in(PHASE_ACTIVE, self.gen_t) + + def deltext(self, e): + self.continuef(e) + if self.i_stop.get(): + self.passivate() + elif self.sigma == float('inf'): + self.hold_in(PHASE_ACTIVE, self.gen_t) + + def lambdaf(self): + self.o_job.add(Job(str(self.job_counter))) + + +class Processor(Atomic): + def __init__(self, name: str, proc_t: float): + """ + Processor model. It processes jobs with a given processing time. + :param name: model name + :param proc_t: processing time + """ + super().__init__(name) + + if proc_t < 1: + raise ValueError('proc_t must be greater than 0') + + self.i_in: Port[Job] = Port(Job, "i_in") + self.o_out: Port[Job] = Port(Job, "o_out") + + self.add_in_port(self.i_in) + self.add_out_port(self.o_out) + + self.current_job: Job | None = None + self.proc_t: float = proc_t + + def initialize(self): + self.passivate() + + def exit(self): + pass + + def deltint(self): + self.passivate() + + def deltext(self, e): + if self.phase == PHASE_PASSIVE: + self.current_job = self.i_in.get() + self.hold_in(PHASE_ACTIVE, self.proc_t) + self.continuef(e) + + def lambdaf(self): + self.o_out.add(self.current_job) + + +class Transducer(Atomic): + def __init__(self, name: str, obs_t: float): + super().__init__(name) + + if obs_t < 0: + raise ValueError('obs_t must be greater or equal than 0') + + self.i_arrived: Port[Job] = Port(Job, "i_arrived") + self.i_solved: Port[Job] = Port(Job, "i_solved") + self.o_out: Port[bool] = Port(bool, "o_out") + + self.add_in_port(self.i_arrived) + self.add_in_port(self.i_solved) + self.add_out_port(self.o_out) + + self.jobs_arrived: list[Job] = [] + self.jobs_solved: list[Job] = [] + + self.total_ta: float = 0 + self.clock: float = 0 + self.obs_t: float = obs_t + + def initialize(self): + self.hold_in(PHASE_ACTIVE, self.obs_t) + + def exit(self): + pass + + def deltint(self): + self.clock += self.sigma + + if self.phase == PHASE_ACTIVE: + avg_ta = 0 + throughput = 0 + if self.jobs_solved: + avg_ta = self.total_ta / len(self.jobs_solved) + throughput = len(self.jobs_solved) / self.clock if self.clock > 0 else 0 + + logger.info(f'End time: {self.clock}') + logger.info(f'Jobs arrived: {len(self.jobs_arrived)}') + logger.info(f'Jobs solved: {len(self.jobs_solved)}') + logger.info(f'Average TA: {avg_ta}') + logger.info(f'Throughput: {throughput}') + + self.hold_in(PHASE_DONE, 0) + else: + self.passivate() + + def deltext(self, e): + self.clock += e + + if self.phase == PHASE_ACTIVE: + if self.i_arrived: + job = self.i_arrived.get() + logger.info(f'Starting job {job.name} @ t = {self.clock} @ t = {time.time_ns()}') + job.time = self.clock + self.jobs_arrived.append(job) + + if self.i_solved: + job = self.i_solved.get() + logger.info(f'Job {job.name} finished @ t = {self.clock} @ t = {time.time()}') + self.total_ta += self.clock - job.time + self.jobs_solved.append(job) + + self.continuef(e) + + def lambdaf(self): + if self.phase == PHASE_DONE: + self.o_out.add(True) + + +class Gpt(Coupled): + def __init__(self, name: str, gen_t: float, proc_t: float, obs_t: float): + super().__init__(name) + + gen = Generator('generator', gen_t) + proc = Processor('processor', proc_t) + trans = Transducer('transducer', obs_t) + + self.add_component(gen) + self.add_component(proc) + self.add_component(trans) + + self.add_coupling(gen.o_job, proc.i_in) + self.add_coupling(gen.o_job, trans.i_arrived) + self.add_coupling(proc.o_out, trans.i_solved) + self.add_coupling(trans.o_out, gen.i_stop) + + +if __name__ == '__main__': + from xdevs.sim import Coordinator + + gpt = Gpt("gpt", 3, 5, 100) + coord = Coordinator(gpt) + coord.initialize() + coord.simulate_iters() diff --git a/xdevs/examples/store_cashier/__init__.py b/xdevs/examples/json/__init__.py similarity index 100% rename from xdevs/examples/store_cashier/__init__.py rename to xdevs/examples/json/__init__.py diff --git a/xdevs/examples/json/efp.json b/xdevs/examples/json/efp.json new file mode 100644 index 0000000..a172b88 --- /dev/null +++ b/xdevs/examples/json/efp.json @@ -0,0 +1,33 @@ +{ + "efp": { + "components": { + "ef": { + "component_id": "ef", + "kwargs": { + "gen_t": 3.0, + "obs_t": 100.0 + } + }, + "processor": { + "component_id": "processor", + "kwargs": { + "proc_t": 5.0 + } + } + }, + "couplings": [ + { + "componentFrom": "ef", + "portFrom": "p_out_ef", + "componentTo": "processor", + "portTo": "i_in" + }, + { + "componentFrom": "processor", + "portFrom": "o_out", + "componentTo": "ef", + "portTo": "p_in_ef" + } + ] + } +} diff --git a/xdevs/examples/json/gpt.json b/xdevs/examples/json/gpt.json new file mode 100644 index 0000000..7480c41 --- /dev/null +++ b/xdevs/examples/json/gpt.json @@ -0,0 +1,50 @@ +{ + "gpt": { + "components": { + "generator": { + "component_id": "generator", + "kwargs": { + "gen_t": 3.0 + } + }, + "processor": { + "component_id": "processor", + "kwargs": { + "proc_t": 5.0 + } + }, + "transducer": { + "component_id": "transducer", + "kwargs": { + "obs_t": 100.0 + } + } + }, + "couplings": [ + { + "componentFrom": "processor", + "portFrom": "o_out", + "componentTo": "transducer", + "portTo": "i_solved" + }, + { + "componentFrom": "generator", + "portFrom": "o_out", + "componentTo": "processor", + "portTo": "i_in" + }, + { + "componentFrom": "generator", + "portFrom": "o_out", + "componentTo": "transducer", + "portTo": "i_arrived" + }, + { + "componentFrom": "transducer", + "portFrom": "o_out", + "componentTo": "generator", + "portTo": "i_stop" + } + ] + } +} diff --git a/xdevs/examples/json/main.py b/xdevs/examples/json/main.py new file mode 100644 index 0000000..f713acf --- /dev/null +++ b/xdevs/examples/json/main.py @@ -0,0 +1,12 @@ +import sys +from xdevs.sim import Coordinator +from xdevs.factory import Components + + +if __name__ == '__main__': + file_path = sys.argv[1] if len(sys.argv) > 1 else 'gpt.json' + + component = Components.from_json(file_path) + coord = Coordinator(component) + coord.initialize() + coord.simulate_iters() diff --git a/xdevs/examples/store/1_vt_simulation.py b/xdevs/examples/store/1_vt_simulation.py new file mode 100644 index 0000000..6a71e0a --- /dev/null +++ b/xdevs/examples/store/1_vt_simulation.py @@ -0,0 +1,58 @@ +import sys +import time +from xdevs.sim import Coordinator +from xdevs.examples.store.models.store import Store + + +def get_sec(time_str): + h, m, s = time_str.split(':') + return int(h) * 3600 + int(m) * 60 + int(s) + + +if __name__ == '__main__': + sim_time: float = 13 + n_employees = 3 + mean_employees = 5 + mean_generator = 3 + stddev_employees = 0 + stddev_clients = 0 + + if len(sys.argv) > 8: + print("Program used with more arguments than accepted. Last arguments will be ignored.") + elif len(sys.argv) < 8: + print("Program used with less arguments than accepted. Missing parameters will be set to their default value.") + if len(sys.argv) != 8: + print("Correct usage:") + print("\t" "python3 " + sys.argv[ + 0] + " ") + try: + sim_time = get_sec(sys.argv[1]) + n_employees = int(sys.argv[2]) + mean_employees = float(sys.argv[3]) + mean_generator = float(sys.argv[4]) + stddev_employees = float(sys.argv[5]) + stddev_clients = float(sys.argv[6]) + force_chain = bool(int(sys.argv[7])) + except IndexError: + pass + + print("CONFIGURATION OF THE SCENARIO:") + print("\tSimulation time: {} seconds".format(sim_time)) + print("\tNumber of Employees: {}".format(n_employees)) + print("\tMean time required by employee to dispatch clients: {} seconds (standard deviation of {})".format( + mean_employees, stddev_employees)) + print("\tMean time between new clients: {} seconds (standard deviation of {})".format(mean_generator, + stddev_employees)) + + # PURE SIMULATION + start = time.time() + store = Store(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) + middle = time.time() + print("Model Created. Elapsed time: {} sec".format(middle - start)) + coord = Coordinator(store) + coord.initialize() + middle = time.time() + print("Coordinator Created. Elapsed time: {} sec".format(middle - start)) + coord.simulate(sim_time) + end = time.time() + print("Simulation took: {} sec".format(end - start)) diff --git a/xdevs/examples/store/2_rt_simulation_csv_output_handler.py b/xdevs/examples/store/2_rt_simulation_csv_output_handler.py new file mode 100644 index 0000000..882a046 --- /dev/null +++ b/xdevs/examples/store/2_rt_simulation_csv_output_handler.py @@ -0,0 +1,60 @@ +import sys +from xdevs.rt import RealTimeCoordinator, RealTimeManager +import time + +from xdevs.examples.store.models.store import Store + + +def get_sec(time_str): + h, m, s = time_str.split(':') + return int(h) * 3600 + int(m) * 60 + int(s) + + +if __name__ == '__main__': + sim_time: float = 52 + n_employees = 3 + mean_employees = 5 + mean_generator = 3 + stddev_employees = 0.8 + stddev_clients = 0.5 + + if len(sys.argv) > 8: + print("Program used with more arguments than accepted. Last arguments will be ignored.") + elif len(sys.argv) < 8: + print("Program used with less arguments than accepted. Missing parameters will be set to their default value.") + if len(sys.argv) != 8: + print("Correct usage:") + print("\t" "python3 " + sys.argv[ + 0] + " " + " ") + try: + sim_time = get_sec(sys.argv[1]) + n_employees = int(sys.argv[2]) + mean_employees = float(sys.argv[3]) + mean_generator = float(sys.argv[4]) + stddev_employees = float(sys.argv[5]) + stddev_clients = float(sys.argv[6]) + force_chain = bool(int(sys.argv[7])) + except IndexError: + pass + + print("CONFIGURATION OF THE SCENARIO:") + print(f'\tSimulation time: {sim_time} seconds') + print(f'\tNumber of Employees: {n_employees}') + print(f'\tMean time required to dispatch clients: {mean_employees} seconds (stddev of {stddev_employees})') + print(f'\tMean time between new clients: {mean_generator} seconds (standard deviation of {stddev_employees})') + + start = time.time() + store = Store(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) + middle = time.time() + print(f'Model Created. Elapsed time: {middle - start} sec') + rt_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) + rt_manager.add_output_handler('csv', file='rt_simulation_csv_output_handler.csv') + c = RealTimeCoordinator(store, rt_manager) + middle = time.time() + print(f'Coordinator, Manager and Handlers Created. Elapsed time: {middle - start} sec') + c.simulate_iters(time_interv=sim_time) + end = time.time() + print(f'Simulation time (s) = {sim_time}') + print(f'Simulation took: {end - start} sec') + print(f'Error (%) = {((time.time() - start - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store/3_rt_simulation_tcp_input_handler.py b/xdevs/examples/store/3_rt_simulation_tcp_input_handler.py new file mode 100644 index 0000000..4780d4f --- /dev/null +++ b/xdevs/examples/store/3_rt_simulation_tcp_input_handler.py @@ -0,0 +1,73 @@ +import sys +import time +from xdevs.examples.store.models.msg import NewClient +from xdevs.rt import RealTimeCoordinator, RealTimeManager +from xdevs.examples.store.models.store import Store + + +def get_sec(time_str): + h, m, s = time_str.split(':') + return int(h) * 3600 + int(m) * 60 + int(s) + + +def new_client_parser(msg: str): + client_id, t_entered = msg.split('?') + + c = NewClient(client_id=client_id, t_entered=time.time()) + return c + + +if __name__ == '__main__': + sim_time: float = 52 + n_employees = 3 + mean_employees = 5 + mean_generator = 3 + stddev_employees = 0.8 + stddev_clients = 0.5 + + if len(sys.argv) > 8: + print("Program used with more arguments than accepted. Last arguments will be ignored.") + elif len(sys.argv) < 8: + print("Program used with less arguments than accepted. Missing parameters will be set to their default value.") + if len(sys.argv) != 8: + print("Correct usage:") + print("\t" "python3 " + sys.argv[0] + + " " + " ") + try: + sim_time = get_sec(sys.argv[1]) + n_employees = int(sys.argv[2]) + mean_employees = float(sys.argv[3]) + mean_generator = float(sys.argv[4]) + stddev_employees = float(sys.argv[5]) + stddev_clients = float(sys.argv[6]) + force_chain = bool(int(sys.argv[7])) + except IndexError: + pass + + print("CONFIGURATION OF THE SCENARIO:") + print(f'\tSimulation time: {sim_time} seconds') + print(f'\tNumber of Employees: {n_employees}') + print(f'\tMean time required to dispatch clients: {mean_employees} seconds (stddev of {stddev_employees})') + print(f'\tMean time between new clients: {mean_generator} seconds (standard deviation of {stddev_clients})') + + msg_parser = { + 'IP_NewClient': new_client_parser, + } + + # Real Time simulation: + start = time.time() + store = Store(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) + middle = time.time() + print(f'Model Created. Elapsed time: {middle - start} sec') + rt_manager = RealTimeManager(max_jitter=0.2, event_window=3) + rt_manager.add_input_handler('tcp', port=4321, max_clients=5, msg_parsers=msg_parser) + + c = RealTimeCoordinator(store, rt_manager) + middle = time.time() + print(f'Coordinator, Manager and Handlers Created. Elapsed time: {middle - start} sec') + c.simulate_iters(time_interv=sim_time) + end = time.time() + print(f'Simulation time (s) = {sim_time}') + print(f'Simulation took: {end - start} sec') + print(f'Error (%) = {((time.time() - start - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store/4_1_rt_simulation_mqtt_input_handler.py b/xdevs/examples/store/4_1_rt_simulation_mqtt_input_handler.py new file mode 100644 index 0000000..92f3eea --- /dev/null +++ b/xdevs/examples/store/4_1_rt_simulation_mqtt_input_handler.py @@ -0,0 +1,68 @@ +import sys +import time +from xdevs.examples.store.models.msg import NewClient +from xdevs.rt import RealTimeCoordinator, RealTimeManager +from xdevs.examples.store.models.store import StoreWithoutGen + + +def get_sec(time_str): + h, m, s = time_str.split(':') + return int(h) * 3600 + int(m) * 60 + int(s) + + +def mqtt_parser(msg: str): + c_id, t = msg.split(';') + return NewClient(c_id, t) + + +if __name__ == '__main__': + sim_time: float = 52 + n_employees = 3 + mean_employees = 5 + stddev_employees = 0.8 + + if len(sys.argv) > 8: + print("Program used with more arguments than accepted. Last arguments will be ignored.") + elif len(sys.argv) < 8: + print( + "Program used with less arguments than accepted. Missing parameters will be set to their default value.") + if len(sys.argv) != 8: + print("Correct usage:") + print("\tpython3 " + sys.argv[0] + " ") + try: + sim_time = get_sec(sys.argv[1]) + n_employees = int(sys.argv[2]) + mean_employees = float(sys.argv[3]) + stddev_employees = float(sys.argv[4]) + except IndexError: + pass + + print("CONFIGURATION OF THE SCENARIO:") + print(f"\tSimulation time: {sim_time} seconds") + print(f"\tNumber of Employees: {n_employees}") + print(f"\tMean time required to dispatch clients: {mean_employees} seconds (stddev of {stddev_employees})") + + conexiones = { + 'Gen_ClientOut': 'Queue_ClientGen' + } + topics = {'RTsys/Output/Gen_ClientOut': 0} + + msg_parser = { + 'Queue_ClientGen': mqtt_parser, + } + + start = time.time() + storeNOGEN = StoreWithoutGen(n_employees, mean_employees, stddev_employees) + middle = time.time() + print(f"Model Created. Elapsed time: {middle - start} sec") + rt_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) + rt_manager.add_input_handler('mqtt', subscriptions=topics, connections=conexiones, msg_parsers=msg_parser) + c = RealTimeCoordinator(storeNOGEN, rt_manager) + middle = time.time() + print(f"Coordinator and Manager Created. Elapsed time: {middle - start} sec") + t_ini = time.time() + c.simulate_iters(time_interv=sim_time) + end = time.time() + print(f' Simulation time (s) = {sim_time}') + print(f"Simulation took: {end - start} sec") + print(f' Error (%) = {((time.time() - start - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store_cashier/trial_two_models_MQTT_gen.py b/xdevs/examples/store/4_2_rt_simulation_mqtt_output_handler.py similarity index 85% rename from xdevs/examples/store_cashier/trial_two_models_MQTT_gen.py rename to xdevs/examples/store/4_2_rt_simulation_mqtt_output_handler.py index 7967b9c..23b0414 100644 --- a/xdevs/examples/store_cashier/trial_two_models_MQTT_gen.py +++ b/xdevs/examples/store/4_2_rt_simulation_mqtt_output_handler.py @@ -1,16 +1,11 @@ import sys -import threading -from xdevs.examples.store_cashier.msg import NewClient, ClientToEmployee +from xdevs.examples.store.models.msg import NewClient from xdevs.models import Coupled, Port -from xdevs.sim import Coordinator -from xdevs.rt_sim.rt_coord import RealTimeCoordinator -from xdevs.rt_sim.rt_manager import RealTimeManager +from xdevs.rt import RealTimeCoordinator, RealTimeManager import time -from client_generator import ClientGenerator -from store_queue import StoreQueue -from employee import Employee +from xdevs.examples.store.models.clients import ClientGenerator class GenSys(Coupled): @@ -25,10 +20,12 @@ def __init__(self, mean_clients: float = 1, stddev_clients: float = 0, name=None self.add_coupling(generator.output_new_client, self.out_gen_port) + def get_sec(time_str): h, m, s = time_str.split(':') return int(h) * 3600 + int(m) * 60 + int(s) + if __name__ == '__main__': sim_time: float = 52 n_employees = 3 @@ -73,9 +70,8 @@ def get_sec(time_str): c = RealTimeCoordinator(gensys, rt_manager) middle = time.time() print("Coordinator and Manager Created. Elapsed time: {} sec".format(middle - start)) - c.simulate(time_interv=sim_time) + c.simulate_iters(time_interv=sim_time) end = time.time() print(f' Simulation time (s) = {sim_time}') print("Simulation took: {} sec".format(end - start)) - print(f' Error (%) = ' - f'{((time.time() - start - sim_time) / sim_time) * 100}') \ No newline at end of file + print(f' Error (%) = {((time.time() - start - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store/README.md b/xdevs/examples/store/README.md new file mode 100644 index 0000000..3550d00 --- /dev/null +++ b/xdevs/examples/store/README.md @@ -0,0 +1,46 @@ +# Instructions for running the store examples + +## Virtual time (regular simulation) + +To run the store examples with virtual time, you can use the following command: + +```bash +$ cd xdevs/examples/store +$ python3 1_vt_simulation.py +``` + +## Real time with CSV output log + +This example runs the store model in real time and uses an output handler to store output events. +The CSV file is saved in the `output` directory. +You can run the example with the following command: + +```bash +$ cd xdevs/examples/store +$ python3 2_rt_simulation_csv_output_handler.py +``` + +## Real time with TCP input clients + +This example runs the store model in real time and uses a TCP input handler to inject extra clients. +You can run the example with the following command: + +```bash +$ cd xdevs/examples/store +$ python3 3_rt_simulation_tcp_input_handler.py +``` + +This executable will start a TCP server that listens for incoming connections on `localhost:4321`. +You can connect to the server using a TCP client, such as `netcat`: + +```bash +$ nc localhost 4321 +``` + +The client can send messages to the server in the following format: + +``` +,? +``` + +The model only has one input port, called `IP_NewClient`. \ No newline at end of file diff --git a/xdevs/examples/store/__init__.py b/xdevs/examples/store/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xdevs/examples/store/models/__init__.py b/xdevs/examples/store/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xdevs/examples/store_cashier/client_generator.py b/xdevs/examples/store/models/clients.py similarity index 74% rename from xdevs/examples/store_cashier/client_generator.py rename to xdevs/examples/store/models/clients.py index 3a94333..645f448 100644 --- a/xdevs/examples/store_cashier/client_generator.py +++ b/xdevs/examples/store/models/clients.py @@ -1,31 +1,30 @@ -import logging from random import gauss from xdevs.models import Atomic, Port import time -from msg import NewClient +from .msg import NewClient class ClientGeneratorStatus: def __init__(self): - self.next_client_id = 0 - self.time_to_next = 0 + self.next_client_id: int = 0 + self.time_to_next: float = 0 def __str__(self): - return ''.format(self.next_client_id, self.time_to_next) + return f'' class ClientGenerator(Atomic): - def __init__(self, mean: float = 10, stddev: float = 0, name: str = None): super().__init__(name) - self.mean = mean - self.stddev = stddev - self.clock = 0 + self.mean: float = mean + self.stddev: float = stddev + self.clock: float = 0 self.state: ClientGeneratorStatus = ClientGeneratorStatus() - self.output_new_client = Port(NewClient) + self.output_new_client: Port[NewClient] = Port(NewClient) self.add_out_port(self.output_new_client) - self.time_started = time.time() + + self.time_started: float = time.time() def deltint(self): self.clock += self.sigma diff --git a/xdevs/examples/store_cashier/employee.py b/xdevs/examples/store/models/employee.py similarity index 83% rename from xdevs/examples/store_cashier/employee.py rename to xdevs/examples/store/models/employee.py index 4a2da21..45d1851 100644 --- a/xdevs/examples/store_cashier/employee.py +++ b/xdevs/examples/store/models/employee.py @@ -1,10 +1,8 @@ -import datetime -import logging import time from random import gauss from xdevs.models import Atomic, Port, INFINITY -from msg import ClientToEmployee, LeavingClient +from .msg import ClientToEmployee, LeavingClient class EmployeeState: @@ -14,11 +12,10 @@ def __init__(self): self.time_remaining = 0 def __str__(self): - return ''.format(self.clients_so_far, bool(self.client), self.time_remaining) + return f'' class Employee(Atomic): - def __init__(self, employee_id: int, mean: float = 30, stddev: float = 0, name: str = None): super().__init__(name) self.name += str(employee_id) @@ -28,11 +25,11 @@ def __init__(self, employee_id: int, mean: float = 30, stddev: float = 0, name: self.clock = 0 self.state = EmployeeState() - self.input_client = Port(ClientToEmployee) + self.input_client = Port(ClientToEmployee, 'in_client') self.add_in_port(self.input_client) - self.output_ready = Port(int) - self.output_client = Port(LeavingClient) + self.output_ready = Port(int, 'out_ready') + self.output_client = Port(LeavingClient, 'out_client') self.add_out_port(self.output_ready) self.add_out_port(self.output_client) @@ -63,13 +60,12 @@ def deltext(self, e): def lambdaf(self): clock = self.clock + self.state.time_remaining - #print(f'METO LEAVING CLIENT EN :{datetime.datetime.now()}') if self.state.client is not None: self.output_client.add(LeavingClient(self.state.client.client_id, self.state.client.t_entered, clock)) self.output_ready.add(self.employee_id) def initialize(self): - self.activate(0) + self.activate() def exit(self): pass diff --git a/xdevs/examples/store_cashier/msg.py b/xdevs/examples/store/models/msg.py similarity index 95% rename from xdevs/examples/store_cashier/msg.py rename to xdevs/examples/store/models/msg.py index ec2b58f..67d0067 100644 --- a/xdevs/examples/store_cashier/msg.py +++ b/xdevs/examples/store/models/msg.py @@ -6,6 +6,7 @@ def __init__(self, client_id, t_entered): def __str__(self): return f'id::{self.client_id}; t_entered::{self.t_entered}' + class ClientToEmployee: def __init__(self, new_client, employee_id): self.client = new_client @@ -14,6 +15,7 @@ def __init__(self, new_client, employee_id): def __str__(self): return f'Client::{self.client} to Employee::{self.employee_id}' + class LeavingClient: def __init__(self, client_id, t_entered, t_exited): self.client_id = client_id @@ -21,4 +23,4 @@ def __init__(self, client_id, t_entered, t_exited): self.t_exited = t_exited def __str__(self): - return f'Client::{self.client_id}; t_entered::{self.t_entered}; t_exited::{self.t_exited}' \ No newline at end of file + return f'Client::{self.client_id}; t_entered::{self.t_entered}; t_exited::{self.t_exited}' diff --git a/xdevs/examples/store_cashier/store_queue.py b/xdevs/examples/store/models/queue.py similarity index 83% rename from xdevs/examples/store_cashier/store_queue.py rename to xdevs/examples/store/models/queue.py index f5edb4a..c93344a 100644 --- a/xdevs/examples/store_cashier/store_queue.py +++ b/xdevs/examples/store/models/queue.py @@ -1,9 +1,7 @@ from _collections import deque from xdevs.models import Atomic, Port, INFINITY import time -import logging - -from msg import NewClient, ClientToEmployee +from .msg import NewClient, ClientToEmployee class QueueStatus: @@ -13,19 +11,17 @@ def __init__(self): self.pairings = deque() def __str__(self): - return ''.format(len(self.clients), len(self.employees), - len(self.pairings)) + return f'' class StoreQueue(Atomic): - def __init__(self, name: str = None): super().__init__(name) self.clock = 0 self.state = QueueStatus() - self.input_new_client = Port(NewClient) - self.input_available_employee = Port(int) + self.input_new_client = Port(NewClient, 'in_new_client') + self.input_available_employee = Port(int, 'in_available_employee') self.add_in_port(self.input_new_client) self.add_in_port(self.input_available_employee) @@ -35,7 +31,7 @@ def __init__(self, name: str = None): self.time_started = time.time() def deltint(self): - self.clock += self.ta + self.clock += self.ta() self.state.pairings.clear() self.passivate() diff --git a/xdevs/examples/store/models/store.py b/xdevs/examples/store/models/store.py new file mode 100644 index 0000000..1dcb1ad --- /dev/null +++ b/xdevs/examples/store/models/store.py @@ -0,0 +1,79 @@ +from xdevs.models import Coupled, Port +from xdevs.examples.store.models.msg import NewClient, ClientToEmployee, LeavingClient +from xdevs.examples.store.models.clients import ClientGenerator +from xdevs.examples.store.models.queue import StoreQueue +from xdevs.examples.store.models.employee import Employee + + +class Store(Coupled): + def __init__(self, n_employees: int = 10000, mean_employees: float = 30, mean_clients: float = 1, + stddev_employees: float = 0, stddev_clients: float = 0, name=None): + super().__init__(name) + + generator = ClientGenerator(mean_clients, stddev_clients) + queue = StoreQueue() + + self.input_new_client = Port(NewClient, 'IP_NewClient') + self.add_in_port(self.input_new_client) + + self.output_port_queue = Port(ClientToEmployee, 'OP_LeavingQueue') + self.output_port_gen = Port(NewClient, 'OP_LeavingGenerator') + self.output_port_employee = Port(LeavingClient, 'OP_LeavingEmployee') + + self.add_out_port(self.output_port_queue) + self.add_out_port(self.output_port_gen) + self.add_out_port(self.output_port_employee) + + self.add_component(generator) + self.add_component(queue) + + self.add_coupling(self.input_new_client, queue.input_new_client) + self.add_coupling(generator.output_new_client, queue.input_new_client) + self.add_coupling(queue.output_client_to_employee, self.output_port_queue) + self.add_coupling(generator.output_new_client, self.output_port_gen) + + for i in range(n_employees): + employee = Employee(i, mean_employees, stddev_employees) + self.add_component(employee) + self.add_coupling(queue.output_client_to_employee, employee.input_client) + self.add_coupling(employee.output_ready, queue.input_available_employee) + self.add_coupling(employee.output_client, self.output_port_employee) + + +class GenSys(Coupled): + def __init__(self, mean_clients: float = 1, stddev_clients: float = 0, name=None): + super().__init__(name) + generator = ClientGenerator(mean_clients, stddev_clients) + + self.out_gen_port = Port(NewClient) + self.add_out_port(self.out_gen_port) + + self.add_component(generator) + + self.add_coupling(generator.output_new_client, self.out_gen_port) + + +class StoreWithoutGen(Coupled): + def __init__(self, n_employees: int = 10000, mean_employees: float = 30, stddev_employees: float = 0, + name=None): + super().__init__(name) + + queue = StoreQueue() + + self.o_p_queue = Port(ClientToEmployee) + self.add_out_port(self.o_p_queue) + + self.i_port_gen = Port(NewClient) + self.add_in_port(self.i_port_gen) + + self.add_component(queue) + + self.add_coupling(self.i_port_gen, queue.input_new_client) + + self.add_coupling(queue.output_client_to_employee, self.o_p_queue) + + for i in range(n_employees): + employee = Employee(i, mean_employees, stddev_employees) + self.add_component(employee) + self.add_coupling(queue.output_client_to_employee, employee.input_client) + self.add_coupling(employee.output_ready, queue.input_available_employee) diff --git a/xdevs/examples/store_cashier/gen.py b/xdevs/examples/store/system_clients.py similarity index 63% rename from xdevs/examples/store_cashier/gen.py rename to xdevs/examples/store/system_clients.py index e2aeeda..d6e4ffe 100644 --- a/xdevs/examples/store_cashier/gen.py +++ b/xdevs/examples/store/system_clients.py @@ -1,16 +1,10 @@ -import sys -import threading - -from xdevs.examples.store_cashier.msg import NewClient, ClientToEmployee +from xdevs.examples.store.models.msg import NewClient from xdevs.models import Coupled, Port -from xdevs.sim import Coordinator -from xdevs.rt_sim.rt_coord import RealTimeCoordinator -from xdevs.rt_sim.rt_manager import RealTimeManager +from xdevs.rt import RealTimeManager, RealTimeCoordinator import time -from client_generator import ClientGenerator -from store_queue import StoreQueue -from employee import Employee +from xdevs.examples.store.models.clients import ClientGenerator + class GenSys(Coupled): def __init__(self, mean_clients: float = 1, stddev_clients: float =0, name=None): @@ -33,16 +27,14 @@ def __init__(self, mean_clients: float = 1, stddev_clients: float =0, name=None gen = GenSys(mean_clients=mean_clients, stddev_clients=stddev_clients) gen_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) - gen_manager.add_output_handler('tcp_out_handler', PORT=5055) + gen_manager.add_output_handler('tcp', PORT=5055) gen_coord = RealTimeCoordinator(gen, gen_manager) - t_ini = time.time() print(f' >>> COMENZAMOS : {t_ini}') - gen_coord.simulate(time_interv=sim_time) + gen_coord.simulate_iters(time_interv=sim_time) print(f' >>> FIN : {time.time()}') print(f' Tiempo a ejecutar (s) = {sim_time }') print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') - print(f' Error (%) = ' - f'{((time.time() - t_ini - sim_time) / sim_time) * 100}') + print(f' Error (%) = {((time.time() - t_ini - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store_cashier/employeesSys.py b/xdevs/examples/store/system_employees.py similarity index 68% rename from xdevs/examples/store_cashier/employeesSys.py rename to xdevs/examples/store/system_employees.py index 693dc82..8f5bb23 100644 --- a/xdevs/examples/store_cashier/employeesSys.py +++ b/xdevs/examples/store/system_employees.py @@ -1,10 +1,10 @@ import datetime import time -from xdevs.examples.store_cashier.employee import Employee -from xdevs.examples.store_cashier.msg import LeavingClient, ClientToEmployee, NewClient +from xdevs.examples.store.models.employee import Employee +from xdevs.examples.store.models.msg import LeavingClient, ClientToEmployee, NewClient from xdevs.models import Coupled, Port -from xdevs.rt_sim import RealTimeManager, RealTimeCoordinator +from xdevs.rt import RealTimeManager, RealTimeCoordinator class EmployeesSys(Coupled): @@ -32,21 +32,13 @@ def __init__(self, n_employees: int = 3, mean_employees: float = 10, self.add_coupling(employee.output_ready, self.output_ready) self.add_coupling(employee.output_client, self.output_client) - # class ClientToEmployee: - # def __init__(self, new_client, employee_id): - # self.client = new_client - # self.employee_id = employee_id - - # Estoy pasando : MqttClient?i?t def input_client_parser(msg: str): - # ("Client::id::3; t_entered::time.time to Employee::3") Formato de entrada client = msg.split("::id::")[1].split(";")[0] - #t = time.time() - float(msg.split("t_entered::")[1].split(" t")[0]) + # t = time.time() - float(msg.split("t_entered::")[1].split(" t")[0]) t = time.time() - t_ini e_id = msg.split("Employee::")[1] - return ClientToEmployee(NewClient(client, t), int(e_id)) @@ -55,7 +47,6 @@ def input_client_parser(msg: str): E = EmployeesSys() - e_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) msg_parser = { 'InputClient': input_client_parser, @@ -73,20 +64,17 @@ def input_client_parser(msg: str): 'Client2Employee': 'InputClient' } - e_manager.add_input_handler('mqtt_handler', subscriptions=sub_input, msg_parsers=msg_parser, connections=connections) - e_manager.add_output_handler('mqtt_handler', subscriptions=sub_output) - - file = 'C:/Users/Usuario/Desktop/00 UNI/01 CUARTO/00 TFG/05 Resultados simulaciones/05 Simulacion final/csv_fin.csv' - - e_manager.add_output_handler('csv_out_handler', file=file) + e_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) + e_manager.add_input_handler('mqtt', subscriptions=sub_input, msg_parsers=msg_parser, connections=connections) + e_manager.add_output_handler('mqtt', subscriptions=sub_output) + e_manager.add_output_handler('csv', file='employees.csv') e_coord = RealTimeCoordinator(E, e_manager) t_ini = time.time() print(f' >>> COMENZAMOS : {t_ini} : {datetime.datetime.now()}') - e_coord.simulate(time_interv=sim_time) + e_coord.simulate_iters(time_interv=sim_time) print(f' >>> FIN : {time.time()}') print(f' Tiempo a ejecutar (s) = {sim_time}') print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') - print(f' Error (%) = ' - f'{((time.time() - t_ini - sim_time) / sim_time) * 100}') + print(f' Error (%) = {((time.time() - t_ini - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store_cashier/queueSys.py b/xdevs/examples/store/system_queue.py similarity index 69% rename from xdevs/examples/store_cashier/queueSys.py rename to xdevs/examples/store/system_queue.py index db7688e..8a239a6 100644 --- a/xdevs/examples/store_cashier/queueSys.py +++ b/xdevs/examples/store/system_queue.py @@ -1,9 +1,8 @@ import time - -from xdevs.examples.store_cashier.msg import ClientToEmployee, NewClient -from xdevs.examples.store_cashier.store_queue import StoreQueue +from xdevs.examples.store.models.msg import ClientToEmployee, NewClient +from xdevs.examples.store.models.queue import StoreQueue from xdevs.models import Coupled, Port -from xdevs.rt_sim import RealTimeManager, RealTimeCoordinator +from xdevs.rt import RealTimeManager, RealTimeCoordinator class QueueSys(Coupled): @@ -31,11 +30,8 @@ def __init__(self, name=None): def parser_new_client(msg: str): - #print('¿?¿?¿?¿?¿?') client_id, t_entered = msg.split('?') - - c = NewClient(client_id=client_id,t_entered=t_entered) - #print(f'DEVUELVO:{c}, c_id = {client_id}, t = {t_entered}') + c = NewClient(client_id=client_id, t_entered=t_entered) return c @@ -61,19 +57,16 @@ def parser_new_client(msg: str): 'OutputReady': 'AvailableEmployee' } - q_manager.add_input_handler('tcp_handler', port=4321, max_clients=5, msg_parsers=msg_parser, connections=connections) - - q_manager.add_input_handler('mqtt_handler', subscriptions=subs_input, connections=connections, msg_parsers=msg_parser) - - q_manager.add_output_handler('mqtt_handler', subscriptions=subs_output) + q_manager.add_input_handler('tcp', port=4321, max_clients=5, msg_parsers=msg_parser, connections=connections) + q_manager.add_input_handler('mqtt', subscriptions=subs_input, connections=connections, msg_parsers=msg_parser) + q_manager.add_output_handler('mqtt', subscriptions=subs_output) q_coord = RealTimeCoordinator(q, q_manager) t_ini = time.time() print(f' >>> COMENZAMOS : {t_ini}') - q_coord.simulate(time_interv=sim_time) + q_coord.simulate_iters(time_interv=sim_time) print(f' >>> FIN : {time.time()}') print(f' Tiempo a ejecutar (s) = {sim_time}') print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') - print(f' Error (%) = ' - f'{((time.time() - t_ini - sim_time) / sim_time) * 100}') + print(f' Error (%) = {((time.time() - t_ini - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store_cashier/STORE_CASHIER b/xdevs/examples/store_cashier/STORE_CASHIER deleted file mode 100644 index 60e2bbac27bd9ff1a0994fddfd8bd07f17f9b7a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3492944 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

qaU3wC0`lc3gdK>4W- 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
PjfStM1Bdk##aLW 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%0AQ{4NG_e+)91^O4=OKexBA!OSmj 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

    e+SbtD%2%-H=qLq=R?k|+H@4bDw`n~ z&Jg{&mQ{PsUxG%{7ucu(TvFGwYEQWbke!(zm}iKr+H-eUxJrHlpsfURlN+_CCPogv z&NVoXZoeSX=^h-LSA~&-K9}Dt6dp*uUe^;;q%5ijg=*NX0A!s5rr@p0(Pu z<(()&Zro%|K=qy2TJ2c?bE)A3w*&OHjcwGP`iW3m1|fKqiK*LPt3Au_;APWHU<)l? zh~-18Jtf`*z7g0qgB!IcycDi{p=|#G4m`8 z1kiAatlHx}i2B$Ef)kdAhG(nxM2*6HdIQ)UgDcMjFmPWKgOz&ZC)fkP0FhC9?i9dr?-u~Iv@okZs!waRrw^>kUGIWmgeBs#IaOoSo;SaO7BvG@ z3oIoOS+(a-hB_GS+5~)up|NVuxi+f7TxS9OX<#J+s%5p>GhmCGtJf7VHAv*dlSKry z+EeL;2p)4>Pl4z;BD7sTQ0=)^43*gw1aDYwIVtB_?P*yuR3+FK@K8%a)nL?~5!tY{ zjNh&qfWHYKkxXaQo?4YXViWKKmWG!M0cuaH)xqKd@H>Gtj@pxH>39$e#Om^Am#BVJ zM2O^-7ZYGC(ktmRC=Giy28;AB*z!3ITVzWP_gPz6n7%V}68Q;1_@QTq7^ zPNxp4N4`qw7#oYb@F`6Ry+KGiA;!j{_u^>L3!(k-xZWVd*jTjwC|rz3=rn_O_>F;OxfEoq{}rz)vNyX?~jIv=>kk6P>iT?(ZJUX>TH6Dg(;WV#-L5 z_#D4aI5Cu^#jkJ0h`9i6+Zfu?;wVn;gd#uDcw7sKm9ezQ^D-RuBDAzYxZ-tBR+bhE zmO>$^0sKWv!@1Fx7L%G`7zFZ;MVC?n#?s#B zXiv^G+Ai4dJIh84Y5#2CeBc;Yb zO#U+dLrTmqdGc#9)ZJg2WxG3@?dhrG>~o=F0Qh}~$5oC@`*e5jJ}F*ILEvly$fpU_ z-97tsXa(N^`oY509C%RKVc5$&4Csu7YdO4!J~$dJZUJ#4Pn>Ah?tU2BW&=b?28nD; zadmeuo)Ir7C*=s>G_wjAcXuwOhw92#>0CTd9iPs|)IlN6)KPOUT&9BEhs5qRO2TeOc3XiKDA!h2hK0Ze5M(7cPs7f_Aw_SkeXpb7~N zpW{gFrD#w;#w14Zt1({j^C6Z=scU6Q>6?dW52W<09}-iZQp%4v-IP*yzm#16p?@&H zw65_ge+&F#<@gV=mQepq?urW#Wa$Zq5h?oiWI^t;OMli4UxNx^My!J` z!Z)@UdINHN-7m*4z%8ZlU2cJmM4iUK zUCMAMN}&)gA{9g=&%@GImR{Nzn398KjvTCTk_Dy@bs2LujUfLE0iUZ>{k|s5c8=!%C{h9`-}1_X*#R zPh;K!x9ffg^)TTZ*#v88AmLv+dCLK#KHls;ubyMse?S+_WN=}yh|^A0l

    q4KqP`!;uUd9c6Z(E7>N)d%L1=N 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:=K4 zBbubW`*^TALY@?&)8+bJL3H#Xyb-}@ld7)fZ@{fMTJTlSmCA=3;rVt!5mcbFYT2M7 z`imqg=RclB3g=AH7a8~=yrX*|jh=Gw)9{`K{8>*xDLIoA&Y291 z%dCWV)lYZ}nN6l9N@mas47j6PVv$;}NQ@|1C-T)qF##xqyA)dvvn}^9gy_@3-0(b= zcnhD55Gfh%6SWh#x>p;fnPek%f(KVB)wwz#RQzE$Q(t7xXNfcSOv$3c%blb~*saM? z+xJ|epx-f}x?2*R{OL_<_a81qQQAeYFfHtYml)@w_6 zi;!M3R@|`$ntw1Y=DY=diW2Ghf+r4);g}&|{}wY(EZkBHKr#H*Q}V0Q)S7kRnV`

    UC-_460P#h2Ye#8pu6TtYWANcs>-X%C{%YUkj% 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

    Twj*atfpT51SKpeT{}1m!7)@Cf>}!lj^p$xR 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

    *`h^6)V+kJ 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%0AtK7ZyhL8p}f{$*c%Mu|rlhijRTu~nT5BFk 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℞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 z zZ|YaD(~KBFb_ss1@{KYYgC0dGp52jsy| zkRQ!2yAMDge;WSEPuAbT2h0QSNT2TH!!jXG?Xx`~51az7ec<4O@F^aRcocsR^0E(L ztbL*B5%uK`W{=lH5DH4_CsK1oRvv@6f`@R=b{t)79 z?T>Ob&-OO_ZR6NI>Ibhzy$;a7>|yvUY>T|{_L#SU{HM|2<*EOdyfXIz)Q;zlz-Oql zU)u2~xVyeuL*DWX+E?wE_89aplKb-77uzR~=mO=ez_SoD}ud{j3G#?)y#I6QJ*!0rCGt|0(1vI3Yxk z-yzTTA@ozpYnb`T_J`26h)3(I3FK|-pVUR)(#z4fC9)*LGsQw8P7EU`L84I#{EO}&oZ~5A2B2MXhkx$~fRO7~B z@+s!MMtdkf`5wY&aX5U`E-lEH~*D zuYIt0pMf9bI$8NyN#6D`47A=$^&I+BeBMdpc{B1@?tdztdE{FI&=0kyIxe3f$bU-4 znd=4oukiT_Q#n5m0p5-Tn{V$fPcXgcppdl`{d*JeLt1^ z)hp;P_ClP>+t1|g`wqtqxAW#c=R1%EUc)DL6XI8%yO77TT~uz0H_)F;j=br*uNHZP z`S0wZcovZt;C-J^=HVa1?fDzX`(;`Wm-!F=?sux!kw?yhe|Gw0Hv1H|{|xGmJVcP! zFx=L+$PZX2EGC~%eR0Rt>x;kU?#jzWi0xrNd}AIR$__5U&C=Q1N6mAi(#>_PAW z?4M7_-y4ejs9$wU5C0^S!F9eQkh}A*e+KB|&2@%7G`>wEci)@4PwxJnP7U+Bm^KgF z*&lS@@D%yt#qgiVc1dmaFRcDR8B|E)TNHWoOW^8PFUgm!K^}tWb2l@5&T#)+`k`6C zD<6QqDD^2mKtE~m2>Dq;zRGYLPjmp{3?YAPxXvA-#6PLbK84jccfLz}g4{m{@v9#e z&kB7v#-sL{MeaWTnLvJGif?{C%m$yb91rg@s9xkbzd(J1$+KpM{?#JXOXE_1@=-a! z)!*XC`>?&F&uN~UvGskYCH%EMKS2Jp1M;cstU4cJJ{sig|MxW9`Yf&iAGOzU@@E^s zXEFZoazO8X*ZKl^!hYymQU8g#p11zDhoWBcA3^Tsa~F9SpTAYT{v#jGaZmkcd?5VY z?__?Q3*7y#%XRX>?;}p_Yn2RwKBk#(`$mw*@_CHPtk+BO$-J(KAn$ATLv4MNbKgMY z>LT)W=DQ2_P(Li68~RfF(O$am7De7N72RCG;k>VYlH+6e?7Ik`aOxw;uW)@5 zO}>qMp6Orq(70X3_}cg@?LfI2C!3Ss{2cLXe11&6JsUW;N4=E!;4>#3dDc&VB)`;-zWJ5KK6I-?IHi5VDLg_Jhq3%^KZ!S9|g~s_^+Nfjr_zc zaIMGAkr!A2uJboXVfbg@{-pd5k%#3&e^9^run6?Ye*{emATrc~5iQV-JmoQ^+5(zo}l|?MY$gm$cr)ℑP_7?SvXClvf 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

    wyVps4V|>Bx82+~LqP87 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 Kxgh!W&i@7XDUZ 8: - print("Program used with more arguments than accepted. Last arguments will be ignored.") - elif len(sys.argv) < 8: - print("Program used with less arguments than accepted. Missing parameters will be set to their default value.") - if len(sys.argv) != 8: - print("Correct usage:") - print("\t" "python3 " + sys.argv[ - 0] + " ") - try: - sim_time = get_sec(sys.argv[1]) - n_employees = int(sys.argv[2]) - mean_employees = float(sys.argv[3]) - mean_generator = float(sys.argv[4]) - stddev_employees = float(sys.argv[5]) - stddev_clients = float(sys.argv[6]) - force_chain = bool(int(sys.argv[7])) - except IndexError: - pass - - print("CONFIGURATION OF THE SCENARIO:") - print("\tSimulation time: {} seconds".format(sim_time)) - print("\tNumber of Employees: {}".format(n_employees)) - print("\tMean time required by employee to dispatch clients: {} seconds (standard deviation of {})".format( - mean_employees, stddev_employees)) - print("\tMean time between new clients: {} seconds (standard deviation of {})".format(mean_generator, - stddev_employees)) - -""" - start = time.time() - store = StoreCashier(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) - middle = time.time() - print("Model Created. Elapsed time: {} sec".format(middle - start)) - coord = Coordinator(store, flatten=True) - coord.initialize() - middle = time.time() - print("Coordinator Created. Elapsed time: {} sec".format(middle - start)) - coord.simulate_time(sim_time) - end = time.time() - print("Simulation took: {} sec".format(end - start)) - -""" -# Real Time simulation: -""" - st = StoreWithoutGen(n_employees, mean_employees, stddev_employees) - - st_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) - st_manager.add_input_handler('tcp_handler', PORT=5055) - st_coord = RealTimeCoordinator(st, st_manager) -""" - -start = time.time() -store = StoreCashier(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) -middle = time.time() -print("Model Created. Elapsed time: {} sec".format(middle - start)) -rt_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) -c = RealTimeCoordinator(store, rt_manager) -middle = time.time() -print("Coordinator and Manager Created. Elapsed time: {} sec".format(middle - start)) -c.simulate(time_interv=sim_time) -end = time.time() -print(f' Simulation time (s) = {sim_time}') -print("Simulation took: {} sec".format(end - start)) -print(f' Error (%) = ' - f'{((time.time() - start - sim_time) / sim_time) * 100}') - -""" - t_ini = time.time() - print(f' >>> COMENZAMOS : {t_ini}') - c.simulate(time_interv=sim_time) - print(f' >>> FIN : {time.time()}') - print(f' Tiempo a ejecutar (s) = {sim_time }') - print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') - print(f' Error (%) = ' - f'{((time.time() - t_ini - sim_time) / sim_time) * 100}') -""" diff --git a/xdevs/examples/store_cashier/trial_CSV_store_cashier.py b/xdevs/examples/store_cashier/trial_CSV_store_cashier.py deleted file mode 100644 index 04547a9..0000000 --- a/xdevs/examples/store_cashier/trial_CSV_store_cashier.py +++ /dev/null @@ -1,105 +0,0 @@ -import sys -import threading - -from xdevs.examples.store_cashier.msg import NewClient, ClientToEmployee, LeavingClient -from xdevs.models import Coupled, Port -from xdevs.sim import Coordinator -from xdevs.rt_sim.rt_coord import RealTimeCoordinator -from xdevs.rt_sim.rt_manager import RealTimeManager -import time - -from client_generator import ClientGenerator -from store_queue import StoreQueue -from employee import Employee - - -class StoreCashier(Coupled): - def __init__(self, n_employees: int = 10000, mean_employees: float = 30, mean_clients: float = 1, - stddev_employees: float = 0, stddev_clients: float = 0, - name=None): - super().__init__(name) - - generator = ClientGenerator(mean_clients, stddev_clients) - queue = StoreQueue() - - self.output_port_queue = Port(ClientToEmployee, 'OP_LeavingQueue') - self.output_port_gen = Port(NewClient, 'OP_LeavingGenerator') - self.output_port_employee = Port(LeavingClient, 'OP_LeavingEmployee') - - self.add_out_port(self.output_port_queue) - self.add_out_port(self.output_port_gen) - self.add_out_port(self.output_port_employee) - - self.add_component(generator) - self.add_component(queue) - self.add_coupling(generator.output_new_client, queue.input_new_client) - - self.add_coupling(queue.output_client_to_employee, self.output_port_queue) - self.add_coupling(generator.output_new_client, self.output_port_gen) - - for i in range(n_employees): - employee = Employee(i, mean_employees, stddev_employees) - self.add_component(employee) - self.add_coupling(queue.output_client_to_employee, employee.input_client) - self.add_coupling(employee.output_ready, queue.input_available_employee) - self.add_coupling(employee.output_client, self.output_port_employee) - - -def get_sec(time_str): - h, m, s = time_str.split(':') - return int(h) * 3600 + int(m) * 60 + int(s) - - -if __name__ == '__main__': - sim_time: float = 52 - n_employees = 3 - mean_employees = 5 - mean_generator = 3 - stddev_employees = 0.8 - stddev_clients = 0.5 - - if len(sys.argv) > 8: - print("Program used with more arguments than accepted. Last arguments will be ignored.") - elif len(sys.argv) < 8: - print("Program used with less arguments than accepted. Missing parameters will be set to their default value.") - if len(sys.argv) != 8: - print("Correct usage:") - print("\t" "python3 " + sys.argv[ - 0] + " ") - try: - sim_time = get_sec(sys.argv[1]) - n_employees = int(sys.argv[2]) - mean_employees = float(sys.argv[3]) - mean_generator = float(sys.argv[4]) - stddev_employees = float(sys.argv[5]) - stddev_clients = float(sys.argv[6]) - force_chain = bool(int(sys.argv[7])) - except IndexError: - pass - - print("CONFIGURATION OF THE SCENARIO:") - print("\tSimulation time: {} seconds".format(sim_time)) - print("\tNumber of Employees: {}".format(n_employees)) - print("\tMean time required by employee to dispatch clients: {} seconds (standard deviation of {})".format( - mean_employees, stddev_employees)) - print("\tMean time between new clients: {} seconds (standard deviation of {})".format(mean_generator, - stddev_employees)) - -# Real Time simulation: - - -start = time.time() -store = StoreCashier(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) -middle = time.time() -print("Model Created. Elapsed time: {} sec".format(middle - start)) -rt_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) -rt_manager.add_output_handler('csv_out_handler', file='C:/Users/Usuario/Desktop/00 UNI/01 CUARTO/00 TFG/05 Resultados simulaciones/02 Simulacion CSV OH/52s_rt_csv') -c = RealTimeCoordinator(store, rt_manager) -middle = time.time() -print("Coordinator, Manager and Handlers Created. Elapsed time: {} sec".format(middle - start)) -c.simulate(time_interv=sim_time) -end = time.time() -print(f' Simulation time (s) = {sim_time}') -print("Simulation took: {} sec".format(end - start)) -print(f' Error (%) = ' - f'{((time.time() - start - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store_cashier/trial_TCP_store_cashier.py b/xdevs/examples/store_cashier/trial_TCP_store_cashier.py deleted file mode 100644 index 1bd0b2b..0000000 --- a/xdevs/examples/store_cashier/trial_TCP_store_cashier.py +++ /dev/null @@ -1,120 +0,0 @@ -import sys -import threading - -from xdevs.examples.store_cashier.msg import NewClient, ClientToEmployee, LeavingClient -from xdevs.models import Coupled, Port -from xdevs.sim import Coordinator -from xdevs.rt_sim.rt_coord import RealTimeCoordinator -from xdevs.rt_sim.rt_manager import RealTimeManager -import time - -from client_generator import ClientGenerator -from store_queue import StoreQueue -from employee import Employee - - -class StoreCashier(Coupled): - def __init__(self, n_employees: int = 10000, mean_employees: float = 30, mean_clients: float = 1, - stddev_employees: float = 0, stddev_clients: float = 0, - name=None): - super().__init__(name) - - generator = ClientGenerator(mean_clients, stddev_clients) - queue = StoreQueue() - - self.add_component(generator) - self.add_component(queue) - self.add_coupling(generator.output_new_client, queue.input_new_client) - - self.new_client = Port(NewClient, 'NewClient') - self.add_in_port(self.new_client) - self.add_coupling(self.new_client, queue.input_new_client) - - for i in range(n_employees): - employee = Employee(i, mean_employees, stddev_employees) - self.add_component(employee) - self.add_coupling(queue.output_client_to_employee, employee.input_client) - self.add_coupling(employee.output_ready, queue.input_available_employee) - - - -def get_sec(time_str): - h, m, s = time_str.split(':') - return int(h) * 3600 + int(m) * 60 + int(s) - -def new_client_parser(msg: str): - #print('¿?¿?¿?¿?¿?') - client_id, t_entered = msg.split('?') - - c = NewClient(client_id=client_id,t_entered=time.time()) - #print(f'DEVUELVO:{c}, c_id = {client_id}, t = {t_entered} ahora {time.time()}') - return c - -def tcp_parser(event): - pass - - - -if __name__ == '__main__': - sim_time: float = 52 - n_employees = 3 - mean_employees = 5 - mean_generator = 3 - stddev_employees = 0.8 - stddev_clients = 0.5 - - if len(sys.argv) > 8: - print("Program used with more arguments than accepted. Last arguments will be ignored.") - elif len(sys.argv) < 8: - print("Program used with less arguments than accepted. Missing parameters will be set to their default value.") - if len(sys.argv) != 8: - print("Correct usage:") - print("\t" "python3 " + sys.argv[ - 0] + " ") - try: - sim_time = get_sec(sys.argv[1]) - n_employees = int(sys.argv[2]) - mean_employees = float(sys.argv[3]) - mean_generator = float(sys.argv[4]) - stddev_employees = float(sys.argv[5]) - stddev_clients = float(sys.argv[6]) - force_chain = bool(int(sys.argv[7])) - except IndexError: - pass - - print("CONFIGURATION OF THE SCENARIO:") - print("\tSimulation time: {} seconds".format(sim_time)) - print("\tNumber of Employees: {}".format(n_employees)) - print("\tMean time required by employee to dispatch clients: {} seconds (standard deviation of {})".format( - mean_employees, stddev_employees)) - print("\tMean time between new clients: {} seconds (standard deviation of {})".format(mean_generator, - stddev_clients)) - -##### - - -msg_parser = { - 'NewClient': new_client_parser, -} - - -# Real Time simulation: - - - -start = time.time() -store = StoreCashier(n_employees, mean_employees, mean_generator, stddev_employees, stddev_clients) -middle = time.time() -print("Model Created. Elapsed time: {} sec".format(middle - start)) -rt_manager = RealTimeManager(max_jitter=0.2, event_window=3) -rt_manager.add_input_handler('tcp_handler', port=4321, max_clients=5, msg_parsers=msg_parser) - -c = RealTimeCoordinator(store, rt_manager) -middle = time.time() -print("Coordinator, Manager and Handlers Created. Elapsed time: {} sec".format(middle - start)) -c.simulate(time_interv=sim_time) -end = time.time() -print(f' Simulation time (s) = {sim_time}') -print("Simulation took: {} sec".format(end - start)) -print(f' Error (%) = ' - f'{((time.time() - start - sim_time) / sim_time) * 100}') diff --git a/xdevs/examples/store_cashier/trial_two_models_MQTT_store.py b/xdevs/examples/store_cashier/trial_two_models_MQTT_store.py deleted file mode 100644 index 6f19dcb..0000000 --- a/xdevs/examples/store_cashier/trial_two_models_MQTT_store.py +++ /dev/null @@ -1,110 +0,0 @@ -import sys -import threading - -from xdevs.examples.store_cashier.msg import NewClient, ClientToEmployee -from xdevs.models import Coupled, Port -from xdevs.sim import Coordinator -from xdevs.rt_sim.rt_coord import RealTimeCoordinator -from xdevs.rt_sim.rt_manager import RealTimeManager -import time - -from client_generator import ClientGenerator -from store_queue import StoreQueue -from employee import Employee - -class StoreWithoutGen(Coupled): - def __init__(self, n_employees: int = 10000, mean_employees: float = 30, stddev_employees: float = 0, - name=None): - super().__init__(name) - - queue = StoreQueue() - - self.o_p_queue = Port(ClientToEmployee) - self.add_out_port(self.o_p_queue) - - self.i_port_gen = Port(NewClient, 'Queue_ClientGen') - self.add_in_port(self.i_port_gen) - - self.add_component(queue) - - self.add_coupling(self.i_port_gen, queue.input_new_client) - - self.add_coupling(queue.output_client_to_employee, self.o_p_queue) - - for i in range(n_employees): - employee = Employee(i, mean_employees, stddev_employees) - self.add_component(employee) - self.add_coupling(queue.output_client_to_employee, employee.input_client) - self.add_coupling(employee.output_ready, queue.input_available_employee) -def get_sec(time_str): - h, m, s = time_str.split(':') - return int(h) * 3600 + int(m) * 60 + int(s) - - -def mqtt_parser(msg: str): - c_id, t = msg.split(';') - return NewClient(c_id, t) - -if __name__ == '__main__': - sim_time: float = 52 - n_employees = 3 - mean_employees = 5 - mean_generator = 3 - stddev_employees = 0.8 - stddev_clients = 0.5 - - if len(sys.argv) > 8: - print("Program used with more arguments than accepted. Last arguments will be ignored.") - elif len(sys.argv) < 8: - print( - "Program used with less arguments than accepted. Missing parameters will be set to their default value.") - if len(sys.argv) != 8: - print("Correct usage:") - print("\t" "python3 " + sys.argv[ - 0] + " ") - try: - sim_time = get_sec(sys.argv[1]) - n_employees = int(sys.argv[2]) - mean_employees = float(sys.argv[3]) - mean_generator = float(sys.argv[4]) - stddev_employees = float(sys.argv[5]) - stddev_clients = float(sys.argv[6]) - force_chain = bool(int(sys.argv[7])) - except IndexError: - pass - - print("CONFIGURATION OF THE SCENARIO:") - print("\tSimulation time: {} seconds".format(sim_time)) - print("\tNumber of Employees: {}".format(n_employees)) - print( - "\tMean time required by employee to dispatch clients: {} seconds (standard deviation of {})".format( - mean_employees, stddev_employees)) - print("\tMean time between new clients: {} seconds (standard deviation of {})".format(mean_generator, - stddev_employees)) - #### - conexiones = { - 'Gen_ClientOut': 'Queue_ClientGen' - } - topics = {'RTsys/Output/Gen_ClientOut': 0} - - msg_parser = { - 'Queue_ClientGen': mqtt_parser, - } - - - start = time.time() - storeNOGEN = StoreWithoutGen(n_employees, mean_employees, stddev_employees) - middle = time.time() - print("Model Created. Elapsed time: {} sec".format(middle - start)) - rt_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) - rt_manager.add_input_handler('mqtt_handler', subscriptions=topics, connections=conexiones, msg_parsers=msg_parser) - c = RealTimeCoordinator(storeNOGEN, rt_manager) - middle = time.time() - print("Coordinator and Manager Created. Elapsed time: {} sec".format(middle - start)) - t_ini = time.time() - c.simulate(time_interv=sim_time) - end = time.time() - print(f' Simulation time (s) = {sim_time}') - print("Simulation took: {} sec".format(end - start)) - print(f' Error (%) = ' - f'{((time.time() - start - sim_time) / sim_time) * 100}') \ No newline at end of file diff --git a/xdevs/factory.py b/xdevs/factory.py new file mode 100644 index 0000000..74671c1 --- /dev/null +++ b/xdevs/factory.py @@ -0,0 +1,238 @@ +from __future__ import annotations +import json +from importlib.metadata import entry_points +from typing import ClassVar +from xdevs.abc import InputHandler, OutputHandler, Transducer +from xdevs.models import Atomic, Component, Port, Coupled + + +class InputHandlers: + _plugins: ClassVar[dict[str, type[InputHandler]]] = { + ep.name: ep.load() for ep in entry_points(group='xdevs.input_handlers') + } + + @staticmethod + def add_plugin(name: str, plugin: type[InputHandler]): + """ + Registers a custom input handler to the plugin system. + + :param name: name used to identify the custom input handler. It must be unique. + :param plugin: custom input handler type. Note that it must not be an object, just the class. + """ + if name in InputHandlers._plugins: + raise ValueError(f'xDEVS input_handler plugin with name "{name}" already exists') + InputHandlers._plugins[name] = plugin + + @staticmethod + def create_input_handler(name: str, *args, **kwargs) -> InputHandler: + """ + Creates a new input handler. Note that this is done by the real-time manager. + Users do not directly create input handlers using this method. + + :param name: unique ID of the input handler to be created. + :param kwargs: any additional configuration parameter needed for creating the input handler. + :return: an instance of the InputHandler class. + """ + if name not in InputHandlers._plugins: + raise ValueError(f'xDEVS input_handler plugin with name "{name}" not found') + return InputHandlers._plugins[name](*args, **kwargs) + + +class OutputHandlers: + _plugins: ClassVar[dict[str, type[OutputHandler]]] = { + ep.name: ep.load() for ep in entry_points(group='xdevs.output_handlers') + } + + @staticmethod + def add_plugin(name: str, plugin: type[OutputHandler]): + """ + Registers a custom output handler to the plugin system. + + :param name: name used to identify the custom input handler. It must be unique. + :param plugin: custom input handler type. Note that it must not be an object, just the class. + """ + if name in OutputHandlers._plugins: + raise ValueError(f'xDEVS output_handler plugin with name "{name}" already exists') + OutputHandlers._plugins[name] = plugin + + @staticmethod + def create_output_handler(name: str, *args, **kwargs) -> OutputHandler: + """ + + Creates a new output handler. Note that this is done by the real-time manager. + Users do not directly create output handlers using this method. + + :param name: unique ID of the output handler to be created. + :param kwargs: any additional configuration parameter needed for creating the output handler. + :return: an instance of the OutputHandler class. + """ + if name not in OutputHandlers._plugins: + raise ValueError(f'xDEVS output_handler plugin with name "{name}" not found') + return OutputHandlers._plugins[name](*args, **kwargs) + + +class Wrappers: + _plugins: ClassVar[dict[str, type[Atomic]]] = { + ep.name: ep.load() for ep in entry_points(group='xdevs.wrappers') + } + + @staticmethod + def add_plugin(name: str, plugin: type[Atomic]): + if name in Wrappers._plugins: + raise ValueError(f'xDEVS wrapper plugin with name "{name}" already exists') + Wrappers._plugins[name] = plugin + + @staticmethod + def create_wrapper(name: str, *args, **kwargs) -> Atomic: + if name not in Wrappers._plugins: + raise ValueError(f'xDEVS wrapper plugin with name "{name}" not found') + return Wrappers._plugins[name](*args, **kwargs) + + +class Transducers: + _plugins: ClassVar[dict[str, type[Transducer]]] = { + ep.name: ep.load() for ep in entry_points(group='xdevs.transducers') + } + + @staticmethod + def add_plugin(name: str, plugin: type[Transducer]): + if name in Transducers._plugins: + raise ValueError(f'xDEVS transducer plugin with name "{name}" already exists') + Transducers._plugins[name] = plugin + + @staticmethod + def create_transducer(name: str, *args, **kwargs) -> Transducer: + if name not in Transducers._plugins: + raise ValueError(f'xDEVS transducer plugin with name "{name}" not found') + return Transducers._plugins[name](*args, **kwargs) + + +class Components: + """This class creates components from unique identifiers called "component_id".""" + _plugins: ClassVar[dict[str, type[Component]]] = { + ep.name: ep.load() for ep in entry_points(group='xdevs.components') + } + + @staticmethod + def add_plugin(component_id: str, plugin: type[Component]): + if component_id in Components._plugins: + raise ValueError(f'xDEVS component plugin with name "{component_id}" already exists') + Components._plugins[component_id] = plugin + + @staticmethod + def create_component(component_id: str, *args, **kwargs) -> Component: + if component_id not in Components._plugins: + raise ValueError(f'xDEVS component plugin with name "{component_id}" not found') + return Components._plugins[component_id](*args, **kwargs) + + @staticmethod + def _nested_component(name: str, config: dict) -> Component: + if 'component_id' in config: + # Predefined component, use factory + component_id: str = config['component_id'] + args = config.get('args', []) + kwargs = config.get('kwargs', {}) + kwargs['name'] = name + return Components.create_component(component_id, *args, **kwargs) + elif 'components' in config: + # It is a coupled model + component = Coupled(name) + children: dict[str, Component] = dict() + # Create children components + for component_name, component_config in config['components'].items(): + child = Components._nested_component(component_name, component_config) + children[component_name] = child + component.add_component(child) + # Create connections + for coupling in config.get('couplings', []): + child_from = coupling.get('componentFrom') + child_to = coupling.get('componentTo') + if child_from is not None: + child_from = children[child_from] + port_from = child_from.get_out_port(coupling['portFrom']) + if port_from is None: + raise Exception(f'Invalid coupling in: {coupling}. Reason: portFrom not found') + if child_to is not None: + # this is an IC + child_to = children[child_to] + port_to = child_to.get_in_port(coupling['portTo']) + if port_to is None: + raise Exception(f'Invalid coupling in: {coupling}. Reason: portTo not found') + else: + # this is an EOC + port_to = child_to.get_in_port(coupling['portTo']) + if port_to is None: + port_to = Port(p_type=port_from.p_type, name=coupling['portTo']) + component.add_out_port(port_to) + elif child_to is not None: + # this is an EIC + child_to = children[child_to] + port_to = child_to.get_in_port(coupling['portTo']) + if port_to is None: + raise Exception(f'Invalid coupling in: {coupling}. Reason: portTo not found') + port_from = component.get_in_port(coupling['portFrom']) + if port_from is None: + port_from = Port(p_type=port_to.p_type, name=coupling['portFrom']) + component.add_in_port(port_from) + else: + raise Exception( + f'Invalid coupling in: {coupling}. Reason: componentFrom and componentTo are None') + + component.add_coupling(port_from, port_to) + else: + raise Exception('No component found') + return component + + @staticmethod + def from_json(file_path: str): + """ + A function to parser a JSON file into a DEVS model. The JSON file structure should follow the next rules: + + When adding a component, if it contains the key "component_id", the component will be created using it and the + args and kwargs associated with it. The "component_id" value refers to the key to identify each component in + the class Components. + + When the component does not have the key "component_id", it is assumed to be a coupled model. + Therefore, it must have the keys "components" and "couplings". + This component will be implementing several components and their couplings inside itself. + + The couplings are created using four keys: + - If both componentFrom/To keys are added, the connection will be of the type IC. + - If componentFrom key is missing, the connection will be of the type EIC. + - If componentTo key is missing, the connection will be of the type EOC. + - If any portFrom/To value is missing the connections is not valid. + + Structure: + + - 'MasterComponentName' (dict): The master component. + - 'components' (dict): A dictionary containing multiple components. + - 'ComponentName1' (dict): Iterative component. + - 'components' (dict): Nested components if any. + - 'couplings' (list): List of connection dictionaries. + - 'componentFrom' (str): Name of the component where the connection starts. + - 'portFrom' (str): Port name from 'componentFrom'. + - 'componentTo' (str): Name of the component where the connection ends. + - 'portTo' (str): Port name from 'componentTo'. + - 'ComponentName2' (dict): Single component. + - 'component_id' (str): ID read from the factory for this component. + - 'args' (list): Positional arguments for the component. + - 'kwargs' (dict): Keyword arguments for the component. + - 'a_parameter' (any): A parameter for the component. + - ... : Other keyword arguments if any. + - ... : Additional components if any. + - 'couplings' (list): List of couplings. + - 'componentFrom' (str): Name of the component where the connection starts. + - 'portFrom' (str): Port name from 'componentFrom'. + - 'componentTo' (str): Name of the component where the connection ends. + - 'portTo' (str): Port name from 'componentTo'. + + :param file_path: Path to the JSON file + :return: a DEVS model according to the JSON file + """ + with open(file_path) as f: + data = json.load(f) + + name = list(data.keys())[0] # Gets the actual component name + config = data[name] # Gets the actual component config + + return Components._nested_component(name, config) diff --git a/xdevs/models.py b/xdevs/models.py index 3898a2f..692cbaa 100644 --- a/xdevs/models.py +++ b/xdevs/models.py @@ -3,14 +3,12 @@ import pickle from abc import ABC, abstractmethod from collections import deque, defaultdict -from typing import Generator, Generic, Iterator, Type, TypeVar, Optional -from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, INFINITY - -T = TypeVar('T') +from typing import Generator, Generic, Iterator +from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, INFINITY, T class Port(Generic[T]): - def __init__(self, p_type: Optional[Type[T]] = None, name: Optional[str] = None, serve: bool = False): + def __init__(self, p_type: type[T] | None = None, name: str = None, serve: bool = False): """ xDEVS implementation of DEVS Port. :param p_type: data type of events to be sent/received via the new port instance. @@ -18,7 +16,7 @@ def __init__(self, p_type: Optional[Type[T]] = None, name: Optional[str] = None, :param serve: set to True if the port is going to be accessible via RPC server. Defaults to False. """ self.name: str = name if name else self.__class__.__name__ # Name of the port - self.p_type: Optional[Type[T]] = p_type # Port type. If None, it can contain any type of event. + self.p_type: type[T] | None = p_type # Port type. If None, it can contain any type of event. self.serve: bool = serve # True if port is going to be accessible via RPC server self.parent: Component | None = None # xDEVS Component that owns the port self._values: deque[T] = deque() # Bag containing events directly written to the port @@ -88,13 +86,13 @@ def add_to_bag(self, port: Port[T]): class Component(ABC): - def __init__(self, name: Optional[str] = None): + def __init__(self, name: str = None): """ Abstract Base Class for an xDEVS model. :param name: name of the xDEVS model. Defaults to the name of the component's class. """ self.name: str = name if name else self.__class__.__name__ - self.parent: Optional[Coupled] = None # Parent component of this component + self.parent: Coupled | None = None # Parent component of this component self.input: dict[str, Port] = dict() # Dictionary containing all the component's input ports by name self.output: dict[str, Port] = dict() # Dictionary containing all the component's output ports by name # TODO make these lists private @@ -104,7 +102,7 @@ def __init__(self, name: Optional[str] = None): def __str__(self) -> str: in_str = " ".join([p.name for p in self.in_ports]) out_str = " ".join([p.name for p in self.out_ports]) - return '%s: InPorts[%s] OutPorts[%s]' % (self.name, in_str, out_str) + return f'{self.name}: InPorts[{in_str}] OutPorts[{out_str}]' def __repr__(self): return self.name @@ -161,11 +159,11 @@ def add_out_port(self, port: Port): def get_in_port(self, name) -> Port | None: """:return: Input port with the given name. If port is not found, returns None.""" - self.input.get(name) + return self.input.get(name) def get_out_port(self, name) -> Port | None: """:return: Output port with the given name. If port is not found, returns None.""" - self.output.get(name) + return self.output.get(name) class Coupling(Generic[T]): @@ -202,7 +200,7 @@ def propagate(self): class Atomic(Component, ABC): - def __init__(self, name: Optional[str] = None): + def __init__(self, name: str = None): """ xDEVS implementation of DEVS Atomic Model. :param name: name of the atomic model. If no name is provided, it will take the class's name by default. @@ -212,7 +210,6 @@ def __init__(self, name: Optional[str] = None): self.phase: str = PHASE_PASSIVE self.sigma: float = INFINITY - @property def ta(self) -> float: """:return: remaining time for the atomic model's internal transition.""" return self.sigma @@ -238,11 +235,8 @@ def lambdaf(self): """Describes the output function of the atomic model.""" pass - def deltcon(self, e: float): - """ - Describes the confluent transitions of the atomic model. By default, the internal transition is triggered first. - :param e: elapsed time between last transition and the confluent transition. - """ + def deltcon(self): + """Confluent transitions of the atomic model. By default, internal transition is triggered first.""" self.deltint() self.deltext(0) @@ -280,7 +274,7 @@ def continuef(self, e: float): class Coupled(Component, ABC): - def __init__(self, name: Optional[str] = None): + def __init__(self, name: str = None): """ xDEVS implementation of DEVS Coupled Model. :param name: name of the coupled model. If no name is provided, it will take the class's name by default. diff --git a/xdevs/plugins/input_handlers/bad_dependencies.py b/xdevs/plugins/input_handlers/bad_dependencies.py index e972b1b..6c864c3 100644 --- a/xdevs/plugins/input_handlers/bad_dependencies.py +++ b/xdevs/plugins/input_handlers/bad_dependencies.py @@ -1,5 +1,5 @@ from abc import ABC -from xdevs.rt_sim.input_handler import InputHandler +from xdevs.abc.handler import InputHandler class BadDependenciesHandler(InputHandler, ABC): diff --git a/xdevs/plugins/input_handlers/csv_input_handler.py b/xdevs/plugins/input_handlers/csv.py similarity index 97% rename from xdevs/plugins/input_handlers/csv_input_handler.py rename to xdevs/plugins/input_handlers/csv.py index 2d8b5b9..581614f 100644 --- a/xdevs/plugins/input_handlers/csv_input_handler.py +++ b/xdevs/plugins/input_handlers/csv.py @@ -1,7 +1,7 @@ import csv import sys import time -from xdevs.rt_sim.input_handler import InputHandler +from xdevs.abc.handler import InputHandler class CSVInputHandler(InputHandler): diff --git a/xdevs/plugins/input_handlers/callable_function.py b/xdevs/plugins/input_handlers/function.py similarity index 60% rename from xdevs/plugins/input_handlers/callable_function.py rename to xdevs/plugins/input_handlers/function.py index ac8d671..880b0d2 100644 --- a/xdevs/plugins/input_handlers/callable_function.py +++ b/xdevs/plugins/input_handlers/function.py @@ -1,12 +1,10 @@ -from xdevs.rt_sim.input_handler import InputHandler +from xdevs.abc.handler import InputHandler class CallableFunction(InputHandler): def __init__(self, **kwargs): super().__init__(**kwargs) - self.function = kwargs.get('function') - if self.function is None: - raise ValueError('function is mandatory') + self.function = kwargs['function'] self.args = kwargs.get('f_args', list()) self.kwargs = kwargs.get('f_kwargs', dict()) diff --git a/xdevs/plugins/input_handlers/mqtt_input_handler.py b/xdevs/plugins/input_handlers/mqtt.py similarity index 95% rename from xdevs/plugins/input_handlers/mqtt_input_handler.py rename to xdevs/plugins/input_handlers/mqtt.py index 885a5af..0cb0e2e 100644 --- a/xdevs/plugins/input_handlers/mqtt_input_handler.py +++ b/xdevs/plugins/input_handlers/mqtt.py @@ -1,12 +1,9 @@ -import datetime import queue import threading try: from paho.mqtt.client import Client - from xdevs.rt_sim.input_handler import InputHandler - - + from xdevs.abc.handler import InputHandler # Desde este input handler me subscribo a topics para ver los mensajes que entran # ruta: RTsys/coupled_name/input/port_name y to_do lo que llegue a ese puerto se inyecta. @@ -95,9 +92,9 @@ def run(self): IN.run() except ImportError: - from xdevs.plugins.input_handlers.bad_dependencies import BadDependenciesHandler + from .bad_dependencies import BadDependenciesHandler + - class MQTTInputHandler(BadDependenciesHandler): def __init__(self, **kwargs): super().__init__(handler_type='mqtt', **kwargs) diff --git a/xdevs/plugins/input_handlers/tcp_input_handler.py b/xdevs/plugins/input_handlers/tcp.py similarity index 97% rename from xdevs/plugins/input_handlers/tcp_input_handler.py rename to xdevs/plugins/input_handlers/tcp.py index 259b3f8..2b1a2b5 100644 --- a/xdevs/plugins/input_handlers/tcp_input_handler.py +++ b/xdevs/plugins/input_handlers/tcp.py @@ -1,12 +1,12 @@ from __future__ import annotations import queue -import socket import threading from typing import Any from xdevs.plugins.util.socket_server import SocketServer -from xdevs.rt_sim.input_handler import InputHandler +from xdevs.abc.handler import InputHandler import socket + class TCPInputHandler(InputHandler): # TODO cambiar a SocketServerInputHandler (más generico que TCP, abre la puerta a SocketClientInputHandler) def __init__(self, **kwargs): """ diff --git a/xdevs/plugins/output_handlers/bad_dependencies.py b/xdevs/plugins/output_handlers/bad_dependencies.py index 2af008e..b1fe605 100644 --- a/xdevs/plugins/output_handlers/bad_dependencies.py +++ b/xdevs/plugins/output_handlers/bad_dependencies.py @@ -1,5 +1,5 @@ from abc import ABC -from xdevs.rt_sim.output_handler import OutputHandler +from xdevs.abc.handler import OutputHandler class BadDependenciesHandler(OutputHandler, ABC): @@ -9,7 +9,7 @@ def __init__(self, **kwargs): :param str handler_type: transducer type. """ super().__init__(**kwargs) - raise ImportError(f'{kwargs.get('handler_type')} input handler specific dependencies are not imported') + raise ImportError(f'{kwargs['handler_type']} input handler specific dependencies are not imported') def run(self): pass diff --git a/xdevs/plugins/output_handlers/csv_output_handler.py b/xdevs/plugins/output_handlers/csv.py similarity index 80% rename from xdevs/plugins/output_handlers/csv_output_handler.py rename to xdevs/plugins/output_handlers/csv.py index 3e09701..701b0ac 100644 --- a/xdevs/plugins/output_handlers/csv_output_handler.py +++ b/xdevs/plugins/output_handlers/csv.py @@ -1,7 +1,6 @@ import csv -import datetime import time -from xdevs.rt_sim.output_handler import OutputHandler +from xdevs.abc.handler import OutputHandler class CSVOutputHandler(OutputHandler): @@ -14,11 +13,10 @@ def __init__(self, **kwargs): :param str delimiter: column delimiter in CSV file. By default, it is set to ','. """ super().__init__() - self.file = kwargs.get('file', 'output.csv') # better to have a fixed name for automation + self.file = kwargs.get('file', 'output.csv') self.delimiter: str = kwargs.get('delimiter', ',') def run(self): - print('CSV running...') initial_time = time.time() # in general, it is not a good idea to append to an existing file when logging a simulation with open(self.file, 'w', newline='') as file: @@ -28,4 +26,3 @@ def run(self): port, msg = self.queue.get() # blocks indefinitely until it receives a message writer.writerow((time.time() - initial_time, port, msg)) file.flush() - # no need to close the file, the with open block does the trick diff --git a/xdevs/plugins/output_handlers/mqtt.py b/xdevs/plugins/output_handlers/mqtt.py new file mode 100644 index 0000000..a6d6040 --- /dev/null +++ b/xdevs/plugins/output_handlers/mqtt.py @@ -0,0 +1,38 @@ +from typing import Callable, Any + +try: + from xdevs.abc.handler import OutputHandler + from ..input_handlers import MQTTClient + + + class MQTTOutputHandler(OutputHandler): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self.host = kwargs.get('host', 'test.mosquitto.org') + self.port = kwargs.get('port', 1883) + self.keepalive = kwargs.get('keepalive', 60) + + self.client = MQTTClient() + + self.topic: str = kwargs.get('topic', 'RTsys') + + self.event_parser: Callable[[str, Any], str] = kwargs.get('event_parser', + lambda port, msg: (f'{self.topic}/output/{port}', msg)) + + def initialize(self): + self.client.connect(self.host, self.port, self.keepalive) + + def run(self): + while True: + topic, payload = self.pop_event() + self.client.publish(topic, payload) + + +except ImportError: + from .bad_dependencies import BadDependenciesHandler + + + class MQTTOutputHandler(BadDependenciesHandler): + def __init__(self, **kwargs): + super().__init__(handler_type='mqtt', **kwargs) diff --git a/xdevs/plugins/output_handlers/mqtt_output_handler.py b/xdevs/plugins/output_handlers/mqtt_output_handler.py deleted file mode 100644 index 1457bd0..0000000 --- a/xdevs/plugins/output_handlers/mqtt_output_handler.py +++ /dev/null @@ -1,63 +0,0 @@ -import datetime -import threading -import time -from typing import Callable, Any - -try: - from xdevs.plugins.input_handlers.mqtt_input_handler import MQTTClient - from xdevs.rt_sim.output_handler import OutputHandler - - - class MQTTOutputHandler(OutputHandler): - def __init__(self, **kwargs): - super().__init__(**kwargs) - - self.host = kwargs.get('host', 'test.mosquitto.org') - self.port = kwargs.get('port', 1883) - self.keepalive = kwargs.get('keepalive', 60) - - self.client = MQTTClient() - - self.topic: str = kwargs.get('topic', 'RTsys') - - self.event_parser: Callable[[str, Any], str] = kwargs.get('event_parser', - lambda port, msg: (f'{self.topic}/Output/{port}', msg)) - - def initialize(self): - self.client.connect(self.host, self.port, self.keepalive) - print('MQTT connected') - - def run(self): - print('MQTT running...') - while True: - topic, payload = self.pop_event() - self.client.publish(topic, payload) - print(f'MQTT sends ') #: {topic} : {payload} > {datetime.datetime.now()}') - - - if __name__ == '__main__': - def inject_msg(): - print(f'Thread active') - for i in range(20): - OUT.queue.put(('Port', f' msg: {i} ')) - print(f'Msg in q and i = {i}') - if i == 3: - time.sleep(15) - else: - time.sleep(2.5) - print('Closing...') - - - OUT = MQTTOutputHandler() - OUT.initialize() - t = threading.Thread(target=inject_msg, daemon=True) - t.start() - OUT.run() - -except ImportError: - from xdevs.plugins.output_handlers.bad_dependencies import BadDependenciesHandler - - - class MQTTOutputHandler(BadDependenciesHandler): - def __init__(self, **kwargs): - super().__init__(handler_type='mqtt', **kwargs) diff --git a/xdevs/plugins/output_handlers/tcp_output_handler.py b/xdevs/plugins/output_handlers/tcp.py similarity index 86% rename from xdevs/plugins/output_handlers/tcp_output_handler.py rename to xdevs/plugins/output_handlers/tcp.py index 380ea4e..1b78f66 100644 --- a/xdevs/plugins/output_handlers/tcp_output_handler.py +++ b/xdevs/plugins/output_handlers/tcp.py @@ -1,10 +1,8 @@ -import socket import time -import threading from typing import Any, Callable from xdevs.plugins.util.socket_server import SocketServer -from xdevs.rt_sim.output_handler import OutputHandler +from xdevs.abc.handler import OutputHandler class TCPOutputHandler(OutputHandler): # TODO cambiar a SocketClientOutputHandler (más generico que TCP, abre la puerta a SocketServerOutputHandler) @@ -82,24 +80,3 @@ def run(self): # If a system error occurred when connecting, we assume that the server has been shut down. print(f'Error while connecting to server: {e}') break - - -if __name__ == '__main__': - - def inject_msg(): - print(f'Thread active') - for i in range(20): - TCP.queue.put(('Port', f' msg: {i} ')) - print(f'Msg in q and i = {i}') - if i == 3: - time.sleep(15) - else: - time.sleep(2.5) - TCP.exit() - print('Closing...') - - - TCP = TCPOutputHandler(host='LocalHost', port=4321) - t = threading.Thread(target=inject_msg, daemon=True) - t.start() - TCP.run() diff --git a/xdevs/plugins/transducers/bad_dependencies_transducer.py b/xdevs/plugins/transducers/bad_dependencies.py similarity index 74% rename from xdevs/plugins/transducers/bad_dependencies_transducer.py rename to xdevs/plugins/transducers/bad_dependencies.py index 6981433..d03cbf2 100644 --- a/xdevs/plugins/transducers/bad_dependencies_transducer.py +++ b/xdevs/plugins/transducers/bad_dependencies.py @@ -1,5 +1,5 @@ from abc import ABC -from xdevs.transducers import Transducer +from xdevs.abc.transducer import Transducer class BadDependenciesTransducer(Transducer, ABC): @@ -9,7 +9,7 @@ def __init__(self, **kwargs): :param str transducer_type: transducer type. """ super().__init__(**kwargs) - raise ImportError('{} transducer specific dependencies are not imported'.format(kwargs.get('transducer_type'))) + raise ImportError(f'{kwargs.get('transducer_type')} transducer specific dependencies are not imported') def create_known_data_types_map(self): pass diff --git a/xdevs/plugins/transducers/csv_transducer.py b/xdevs/plugins/transducers/csv.py similarity index 86% rename from xdevs/plugins/transducers/csv_transducer.py rename to xdevs/plugins/transducers/csv.py index f93ba46..aa2bdc2 100644 --- a/xdevs/plugins/transducers/csv_transducer.py +++ b/xdevs/plugins/transducers/csv.py @@ -2,7 +2,7 @@ import csv import os from typing import Any, Iterable, Type -from xdevs.transducers import Transducer +from xdevs.abc.transducer import Transducer class CSVTransducer(Transducer): @@ -54,12 +54,6 @@ def bulk_data(self, sim_time: float): self.event_csv_writer.writerow([event_insert[field] for field in self.event_header]) def _create_csv_file(self, filename: str, header: list[str]): - # 1. If output file already exist, we ask the use if he/she wants to overwrite it. - # if os.path.exists(filename): - # print('Transducer output file {} already exists.'.format(filename)) - # if input('Do you want to overwrite it? [Y/n] >').lower() in ['n', 'no']: - # raise FileExistsError('File already exists and user does not want to overwrite it') - # 2. If directory that will contain the file does not exist, we create it. os.makedirs(os.path.dirname(filename), exist_ok=True) diff --git a/xdevs/plugins/transducers/elasticsearch_transducer.py b/xdevs/plugins/transducers/elasticsearch.py similarity index 97% rename from xdevs/plugins/transducers/elasticsearch_transducer.py rename to xdevs/plugins/transducers/elasticsearch.py index 0e971e7..b0cb412 100644 --- a/xdevs/plugins/transducers/elasticsearch_transducer.py +++ b/xdevs/plugins/transducers/elasticsearch.py @@ -1,6 +1,6 @@ from __future__ import annotations import logging -from xdevs.transducers import Transducer +from xdevs.abc.transducer import Transducer try: from elasticsearch import Elasticsearch @@ -81,7 +81,7 @@ def create_index(self, index_name: str, field_properties: dict[str, dict[str, st except ModuleNotFoundError: - from .bad_dependencies_transducer import BadDependenciesTransducer + from .bad_dependencies import BadDependenciesTransducer class ElasticsearchTransducer(BadDependenciesTransducer): diff --git a/xdevs/plugins/transducers/sql_transducer.py b/xdevs/plugins/transducers/sql.py similarity index 97% rename from xdevs/plugins/transducers/sql_transducer.py rename to xdevs/plugins/transducers/sql.py index b751ca2..e8cd62c 100644 --- a/xdevs/plugins/transducers/sql_transducer.py +++ b/xdevs/plugins/transducers/sql.py @@ -1,5 +1,5 @@ from __future__ import annotations -from xdevs.transducers import Transducer +from xdevs.abc.transducer import Transducer try: from sqlalchemy import create_engine, text, Column, Float, Integer, MetaData, String, Table @@ -85,7 +85,7 @@ def create_table(self, table_name: str, columns: list[Column], except ModuleNotFoundError: - from .bad_dependencies_transducer import BadDependenciesTransducer + from .bad_dependencies import BadDependenciesTransducer class SQLTransducer(BadDependenciesTransducer): diff --git a/xdevs/plugins/util/socket_server.py b/xdevs/plugins/util/socket_server.py index 6883eea..502200d 100644 --- a/xdevs/plugins/util/socket_server.py +++ b/xdevs/plugins/util/socket_server.py @@ -92,5 +92,3 @@ def start_oh(self): c_thread = threading.Thread(target=output_client_handler, daemon=True, args=(self.server_socket, self.server_address, self.output_queue)) c_thread.start() - - diff --git a/xdevs/plugins/wrappers/bad_dependencies.py b/xdevs/plugins/wrappers/bad_dependencies.py new file mode 100644 index 0000000..d37c58e --- /dev/null +++ b/xdevs/plugins/wrappers/bad_dependencies.py @@ -0,0 +1,27 @@ +from abc import ABC +from xdevs.models import Atomic + + +class BadDependenciesWrapper(Atomic, ABC): + def __init__(self, **kwargs): + """ + Template wrapper for using when dependencies are not installed. + :param str wrapper_type: wrapper type. + """ + super().__init__(**kwargs) + raise ImportError(f'{kwargs['wrapper_type']} wrapper specific dependencies are not installed') + + def deltint(self): + pass + + def deltext(self, e: float): + pass + + def lambdaf(self): + pass + + def initialize(self): + pass + + def exit(self): + pass diff --git a/xdevs/plugins/wrappers/pypdevs.py b/xdevs/plugins/wrappers/pypdevs.py index 224f1f8..01eb3d2 100644 --- a/xdevs/plugins/wrappers/pypdevs.py +++ b/xdevs/plugins/wrappers/pypdevs.py @@ -1,71 +1,76 @@ -import logging -from xdevs.models import Atomic, Port - try: from pypdevs.DEVS import AtomicDEVS from pypdevs.minimal import AtomicDEVS as AtomicDEVSMin -except ImportError: - logging.warning("pypdevs module not installed.") + from xdevs.models import Atomic, Port + + + def update_sigma_on_state_change(delt_func): + def inner(self, *args, **kwargs): + prev_state = self.phase + delt_func(self, *args, **kwargs) + if prev_state != self.phase: + self.sigma = self.atomic.timeAdvance() + return inner -def update_sigma_on_state_change(delt_func): - def inner(self, *args, **kwargs): - prev_state = self.phase - delt_func(self, *args, **kwargs) - if prev_state != self.phase: - self.sigma = self.atomic.timeAdvance() - return inner + class PyPDEVSWrapper(Atomic): + def __init__(self, atomic: AtomicDEVS or AtomicDEVSMin): + super().__init__() + self.atomic: AtomicDEVS or AtomicDEVSMin = atomic + self.pypdevs_in_ports = {} # IO ports dictionaries to efficiently manage pypdevs ports + self.xdevs_out_ports = {} -class PyPDEVSWrapper(Atomic): + for pypdevs_in_port in self.atomic.IPorts: + xdevs_in_port = Port(None, pypdevs_in_port.name) + self.add_in_port(xdevs_in_port) + self.pypdevs_in_ports[pypdevs_in_port.name] = pypdevs_in_port + setattr(self, pypdevs_in_port.name, xdevs_in_port) - def __init__(self, atomic: AtomicDEVS or AtomicDEVSMin): - super().__init__() - self.atomic: AtomicDEVS or AtomicDEVSMin = atomic - self.pypdevs_in_ports = {} # IO ports dictionaries to efficiently manage pypdevs ports - self.xdevs_out_ports = {} + for pypdevs_out_port in self.atomic.OPorts: + xdevs_out_port = Port(None, pypdevs_out_port.name) + self.add_out_port(xdevs_out_port) + self.xdevs_out_ports[pypdevs_out_port.name] = xdevs_out_port + setattr(self, pypdevs_out_port.name, xdevs_out_port) - for pypdevs_in_port in self.atomic.IPorts: - xdevs_in_port = Port(None, pypdevs_in_port.name) - self.add_in_port(xdevs_in_port) - self.pypdevs_in_ports[pypdevs_in_port.name] = pypdevs_in_port - setattr(self, pypdevs_in_port.name, xdevs_in_port) + @update_sigma_on_state_change + def deltint(self): + self.phase = self.atomic.state = self.atomic.intTransition() - for pypdevs_out_port in self.atomic.OPorts: - xdevs_out_port = Port(None, pypdevs_out_port.name) - self.add_out_port(xdevs_out_port) - self.xdevs_out_ports[pypdevs_out_port.name] = xdevs_out_port - setattr(self, pypdevs_out_port.name, xdevs_out_port) + @update_sigma_on_state_change + def deltext(self, e: float): + self.phase = self.atomic.state = self.atomic.extTransition(self._inputs_to_dict()) + self.continuef(e) - @update_sigma_on_state_change - def deltint(self): - self.phase = self.atomic.state = self.atomic.intTransition() + def lambdaf(self) -> None: + outputs = self.atomic.outputFnc() - @update_sigma_on_state_change - def deltext(self, e: float): - self.phase = self.atomic.state = self.atomic.extTransition(self._inputs_to_dict()) - self.continuef(e) + for pypdevs_out_port, values in outputs.items(): + if len(values) > 0: + xdevs_out_port = self.xdevs_out_ports[pypdevs_out_port.name] + xdevs_out_port.extend(values) - def lambdaf(self) -> None: - outputs = self.atomic.outputFnc() + def initialize(self) -> None: + pass - for pypdevs_out_port, values in outputs.items(): - if len(values) > 0: - xdevs_out_port = self.xdevs_out_ports[pypdevs_out_port.name] - xdevs_out_port.extend(values) + def exit(self) -> None: + pass - def initialize(self) -> None: - pass + def _inputs_to_dict(self): + in_values = {} - def exit(self) -> None: - pass + for in_port in self.in_ports: + in_port_values = list(in_port.values) + in_values[self.pypdevs_in_ports[in_port.name]] = in_port_values - def _inputs_to_dict(self): - in_values = {} + return in_values + + +except ImportError: + from .bad_dependencies import BadDependenciesWrapper - for in_port in self.in_ports: - in_port_values = list(in_port.values) - in_values[self.pypdevs_in_ports[in_port.name]] = in_port_values - return in_values + class PyPDEVSWrapper(BadDependenciesWrapper): + def __init__(self, **kwargs): + super().__init__(wrapper_type='pypdevs') diff --git a/xdevs/rt_sim/rt_manager.py b/xdevs/rt.py similarity index 66% rename from xdevs/rt_sim/rt_manager.py rename to xdevs/rt.py index aa622aa..e800e97 100644 --- a/xdevs/rt_sim/rt_manager.py +++ b/xdevs/rt.py @@ -4,9 +4,9 @@ import threading import time from typing import Any -from xdevs.models import Port -from xdevs.rt_sim.input_handler import InputHandler, InputHandlers -from xdevs.rt_sim.output_handler import OutputHandler, OutputHandlers +from xdevs.factory import InputHandler, InputHandlers, OutputHandler, OutputHandlers +from xdevs.models import Coupled, Port +from xdevs.sim import Coordinator def run_handler(handler: InputHandler | OutputHandler): @@ -48,24 +48,24 @@ def __init__(self, max_jitter: float = None, time_scale: float = 1, event_window self.input_handlers: list[InputHandler] = list() self.output_handlers: list[OutputHandler] = list() - def add_input_handler(self, handler_id: str, **kwargs): + def add_input_handler(self, handler_id: str, *args, **kwargs): """ Add a new InputHandler to the system. :param handler_id: unique ID of the input handler to be created. :param kwargs: any additional configuration parameter needed for creating the input handler. """ - i_handler = InputHandlers.create_input_handler(handler_id, **kwargs, queue=self.input_queue) + i_handler = InputHandlers.create_input_handler(handler_id, *args, **kwargs, queue=self.input_queue) self.input_handlers.append(i_handler) - def add_output_handler(self, handler_id: str, **kwargs): + def add_output_handler(self, handler_id: str, *args, **kwargs): """ Add a new OutputHandler to the system. :param handler_id: unique ID of the output handler to be created. :param kwargs: any additional configuration parameter needed for creating the output handler. """ - o_handler = OutputHandlers.create_output_handler(handler_id, **kwargs) + o_handler = OutputHandlers.create_output_handler(handler_id, *args, **kwargs) self.output_handlers.append(o_handler) def initialize(self, initial_t: float): @@ -87,12 +87,12 @@ def initialize(self, initial_t: float): def exit(self, final_t: float): self.last_v_time = final_t - def sleep(self, next_v_time: float) -> tuple[float, list[tuple[Any, Any]]]: + def wait_until(self, next_v_time: float) -> tuple[float, list[tuple[str, Any]]]: """ Function that implements the real time specification by waiting for ingoing events to the system. - :param next_v_time: simulation time of the next cycle. - :return: a tuple of: actual simulation time when function returned and list of ingoing events. + :param next_v_time: simulation time of the next internal event in the simulation. + :return: a tuple of: actual simulation time when function returned and list of input events. """ next_r_time = self.last_r_time + (next_v_time - self.last_v_time) * self.time_scale events: list[tuple[str, Any]] = list() @@ -107,20 +107,18 @@ def sleep(self, next_v_time: float) -> tuple[float, list[tuple[Any, Any]]]: except queue.Empty: break # event window timeout, we are done with messages # Finally, we compute the current time. Must be between last_r_time and next_r_time - r_time = min(next_r_time, time.time()) - v_time = (r_time - self.initial_r_time) / self.time_scale - self.last_v_time = v_time - self.last_r_time = r_time + self.last_r_time = min(next_r_time, time.time()) + self.last_v_time = min(next_v_time, (self.last_r_time - self.initial_r_time) / self.time_scale) except queue.Empty: # we did not receive any message, just update the time - self.last_v_time = next_v_time self.last_r_time = next_r_time + self.last_v_time = next_v_time # If needed, we check that the jitter is not too big if self.max_jitter is not None and abs(time.time() - self.last_r_time) > self.max_jitter: raise RuntimeError('maximum jitter exceeded.') return self.last_v_time, events - def output_messages(self, port: Port): + def propagate_output(self, port: Port): """ An outgoing event is inserted in the queues of all OutputHandlers. @@ -129,3 +127,49 @@ def output_messages(self, port: Port): for o_handler in self.output_handlers: for msg in port.values: o_handler.queue.put((port.name, msg)) + + +class RealTimeCoordinator(Coordinator): + def __init__(self, model: Coupled, manager: RealTimeManager): + super().__init__(model) + self.manager: RealTimeManager = manager + + def initialize(self): + super().initialize() + self.manager.initialize(self.clock.time) + + def exit(self): + self.manager.exit(self.clock.time) + super().exit() + + def simulate_iters(self, time_interv: float = float("inf")): + self.initialize() + while self.clock.time < time_interv: + if self.time_next == float("inf") and not self.manager.input_handlers: + break + # SLEEP UNTIL NEXT STATE TRANSITION + t, msgs = self.manager.wait_until(min(time_interv, self.time_next)) + # INJECT EXTERNAL EVENTS (if any) + for port_id, msg in msgs: + port = self.model.get_in_port(port_id) + if port is not None: + try: + port.add(msg) + except TypeError as e: + print(f'invalid message type: {e}', file=sys.stderr) + else: + print(f'input port "{port_id}" does not exit', file=sys.stderr) + # UPDATE SIMULATION CLOCK + self.clock.time = t + # EXECUTE NEXT CYCLE (if applies) + if self.clock.time == self.time_next: + self.lambdaf() + self.deltfcn() + # EXECUTE TRANSDUCERS (if any) + self._execute_transducers() + # EJECT NEW OUTPUT EVENTS + for port in self.model.out_ports: + self.manager.propagate_output(port) + # CLEAR THE PORTS OF THE MODEL + self.clear() + self.exit() diff --git a/xdevs/rt_sim/__init__.py b/xdevs/rt_sim/__init__.py deleted file mode 100644 index 4e4f02e..0000000 --- a/xdevs/rt_sim/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .rt_coord import RealTimeCoordinator -from .rt_manager import RealTimeManager diff --git a/xdevs/rt_sim/input_handler.py b/xdevs/rt_sim/input_handler.py deleted file mode 100644 index 37cf22d..0000000 --- a/xdevs/rt_sim/input_handler.py +++ /dev/null @@ -1,106 +0,0 @@ -from __future__ import annotations - -import datetime -from abc import ABC, abstractmethod -from typing import ClassVar, Type, Callable, Any -import sys -import pkg_resources - -from xdevs.rt_sim.mqtt_connector import Connector - - -class InputHandler(ABC): - def __init__(self, **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', None) - 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 - #print(f'HAGO PUSH MSG DE {port},{msg}') - 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.""" - #print(f'Entro en push_msg con port ->{port}') - 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) - #print(f'EL msg = {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 - #print('###') - #print(f'EVENTO ENTRA EN LA COLA EN:{datetime.datetime.time()}') - self.queue.put((port, msg)) - #print(f'COla = {self.queue}, puse en {port}:{msg}') - - -class InputHandlers: - _plugins: ClassVar[dict[str, Type[InputHandler]]] = { - ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs.plugins.input_handlers') - } - - @staticmethod - def add_plugin(name: str, plugin: Type[InputHandler]): - """ - Registers a custom input handler to the plugin system. - - :param name: name used to identify the custom input handler. It must be unique. - :param plugin: custom input handler type. Note that it must not be an object, just the class. - """ - if name in InputHandlers._plugins: - raise ValueError('xDEVS input_handler plugin with name "{}" already exists'.format(name)) - InputHandlers._plugins[name] = plugin - - @staticmethod - def create_input_handler(name: str, **kwargs) -> InputHandler: - """ - Creates a new input handler. Note that this is done by the real-time manager. - Users do not directly create input handlers using this method. - - :param name: unique ID of the input handler to be created. - :param kwargs: any additional configuration parameter needed for creating the input handler. - :return: an instance of the InputHandler class. - """ - if name not in InputHandlers._plugins: - raise ValueError('xDEVS input_handler plugin with name "{}" not found'.format(name)) - return InputHandlers._plugins[name](**kwargs) diff --git a/xdevs/rt_sim/mqtt_connector.py b/xdevs/rt_sim/mqtt_connector.py deleted file mode 100644 index 029af14..0000000 --- a/xdevs/rt_sim/mqtt_connector.py +++ /dev/null @@ -1,23 +0,0 @@ -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): - #print(f'Le paso port = {port}') - #print(self.connections) - if self.connections is not None: - for key, value in self.connections.items(): - if port == key: - return value - #print(f'Devuelvo port = {port}') - return port - - diff --git a/xdevs/rt_sim/output_handler.py b/xdevs/rt_sim/output_handler.py deleted file mode 100644 index ee51864..0000000 --- a/xdevs/rt_sim/output_handler.py +++ /dev/null @@ -1,97 +0,0 @@ -from __future__ import annotations -import queue -from abc import ABC, abstractmethod -import sys -from typing import Any, Callable, ClassVar, Type - -import pkg_resources - - -class OutputHandler(ABC): - def __init__(self, **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 putted. - :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 - - -class OutputHandlers: - _plugins: ClassVar[dict[str, Type[OutputHandler]]] = { - ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs.plugins.output_handlers') - } - - @staticmethod - def add_plugin(name: str, plugin: Type[OutputHandler]): - """ - Registers a custom output handler to the plugin system. - - :param name: name used to identify the custom input handler. It must be unique. - :param plugin: custom input handler type. Note that it must not be an object, just the class. - """ - if name in OutputHandlers._plugins: - raise ValueError('xDEVS output_handler plugin with name "{}" already exists'.format(name)) - OutputHandlers._plugins[name] = plugin - - @staticmethod - def create_output_handler(name: str, **kwargs) -> OutputHandler: - """ - - Creates a new output handler. Note that this is done by the real-time manager. - Users do not directly create output handlers using this method. - - :param name: unique ID of the output handler to be created. - :param kwargs: any additional configuration parameter needed for creating the output handler. - :return: an instance of the OutputHandler class. - """ - if name not in OutputHandlers._plugins: - raise ValueError('xDEVS output_handler plugin with name "{}" not found'.format(name)) - return OutputHandlers._plugins[name](**kwargs) diff --git a/xdevs/rt_sim/rt_coord.py b/xdevs/rt_sim/rt_coord.py deleted file mode 100644 index fb55401..0000000 --- a/xdevs/rt_sim/rt_coord.py +++ /dev/null @@ -1,51 +0,0 @@ -import sys -from xdevs.models import Coupled -from xdevs.rt_sim.rt_manager import RealTimeManager -from xdevs.sim import Coordinator - - -class RealTimeCoordinator(Coordinator): - def __init__(self, model: Coupled, manager: RealTimeManager): - super().__init__(model) - self.manager: RealTimeManager = manager - - def initialize(self): - super().initialize() - self.manager.initialize(self.clock.time) - - def exit(self): - self.manager.exit(self.clock.time) - super().exit() - - def simulate(self, time_interv: float = 10000): - self.initialize() - while self.clock.time < time_interv: - if self.time_next == float("inf") and not self.manager.input_handlers: - print('infinity reached') - break - # SLEEP UNTIL NEXT STATE TRANSITION - t, msgs = self.manager.sleep(min(time_interv, self.time_next)) - # INJECT EXTERNAL EVENTS (if any) - for port_id, msg in msgs: - port = self.model.get_in_port(port_id) - if port is not None: - try: - port.add(msg) - except TypeError as e: - print(f'invalid message type: {e}', file=sys.stderr) - else: - print(f'input port "{port_id}" does not exit', file=sys.stderr) - # UPDATE SIMULATION CLOCK - self.clock.time = t - # EXECUTE NEXT CYCLE (if applies) - if self.clock.time == self.time_next: - self.lambdaf() - self.deltfcn() - # EJECT NEW OUTPUT EVENTS - for port in self.model.out_ports: - self.manager.output_messages(port) - # EXECUTE TRANSDUCERS (if any) - self._execute_transducers() - # CLEAR THE PORTS OF THE MODEL - self.clear() - self.exit() diff --git a/xdevs/sim.py b/xdevs/sim.py index 123409d..3e5fa77 100644 --- a/xdevs/sim.py +++ b/xdevs/sim.py @@ -4,16 +4,16 @@ import itertools import pickle import logging +import warnings from abc import ABC, abstractmethod from collections import defaultdict -from concurrent import futures from typing import Generator, Optional from xmlrpc.server import SimpleXMLRPCServer -from xdevs import INFINITY -from xdevs.models import Atomic, Coupled, Component, Port, T -from xdevs.transducers import Transducer +from xdevs import INFINITY, T +from xdevs.models import Atomic, Coupled, Component, Port +from xdevs.abc import Transducer class SimulationClock: @@ -88,22 +88,22 @@ def __init__(self, model: Atomic, clock: SimulationClock, @property def ta(self) -> float: - return self.model.ta + return self.model.ta() def initialize(self): self.model.initialize() self.time_last = self.clock.time - self.time_next = self.time_last + self.model.ta + self.time_next = self.time_last + self.model.ta() def exit(self): self.model.exit() def deltfcn(self) -> Simulator | None: # TODO if not self.model.in_empty(): - e = self.clock.time - self.time_last if self.clock.time == self.time_next: - self.model.deltcon(e) + self.model.deltcon() else: + e = self.clock.time - self.time_last self.model.deltext(e) elif self.clock.time == self.time_next: self.model.deltint() @@ -117,7 +117,7 @@ def deltfcn(self) -> Simulator | None: # TODO self.trigger_event_transducers() self.time_last = self.clock.time - self.time_next = self.time_last + self.model.ta + self.time_next = self.time_last + self.model.ta() return self def lambdaf(self): @@ -154,21 +154,6 @@ def __init__(self, model: Coupled, clock: Optional[SimulationClock] = None, flat self.event_transducers_mapping = event_transducers_mapping self.state_transducers_mapping = state_transducers_mapping - # A log named logger is created. Logs will be sent to the file SimulationsLogs.log. The file can be find in - # xdevs/examples/basic/ - - self.logger = logging.getLogger("Simulation_Coordinator") - - fh = logging.FileHandler("SimulationLogs.log", mode='w') # for overwrite the file add mode='w' - - self.logger.setLevel(logging.DEBUG) - - formatter = logging.Formatter('%(asctime)s %(name)s - %(levelname)s - %(message)s') - - fh.setFormatter(formatter) - - self.logger.addHandler(fh) - @property def root_coordinator(self) -> bool: return self.model.parent is None @@ -325,7 +310,6 @@ def inject(self, port: str | Port[T], values: T | list[T], e: float = 0) -> bool def simulate(self, num_iters: int = 10000): self.clock.time = self.time_next cont = 0 - while cont < num_iters and self.clock.time < INFINITY: self.lambdaf() self.deltfcn() @@ -334,7 +318,7 @@ def simulate(self, num_iters: int = 10000): self.clock.time = self.time_next cont += 1 - def simulate_time(self, time_interv: float = 10000): + def simulate_time(self, time_interv: float = INFINITY): self.clock.time = self.time_next tf = self.clock.time + time_interv @@ -345,14 +329,6 @@ def simulate_time(self, time_interv: float = 10000): self.clear() self.clock.time = self.time_next - def simulate_inf(self): - while True: - self.lambdaf() - self.deltfcn() - self._execute_transducers() - self.clear() - self.clock.time = self.time_next - def _execute_transducers(self): for transducer in self._transducers: transducer.trigger(self.clock.time) diff --git a/xdevs/tests/test_csv_transducer.py b/xdevs/tests/test_csv_transducer.py index 2093045..795166a 100644 --- a/xdevs/tests/test_csv_transducer.py +++ b/xdevs/tests/test_csv_transducer.py @@ -6,15 +6,15 @@ from xdevs.sim import Coordinator from xdevs.examples.devstone.devstone import LI, DelayedAtomic, HI -from xdevs.transducers import Transducers -from xdevs.examples.basic.basic import Job, Processor, Gpt +from xdevs.factory import Transducers +from xdevs.examples.gpt.gpt import Job, Processor, Gpt class TestCsvTransducer(TestCase): def test_component_filtering_by_type(self): csv = Transducers.create_transducer('csv', transducer_id='tt') - gpt = Gpt("gpt", 3, 100) + gpt = Gpt("gpt", 3, 9, 100) csv.add_target_component(gpt) # csv.add_target_port() # csv.add_target_port_by_components(gpt, component_filter=[Coupled, "coupled_.*"], port_filter=OutPort) @@ -29,7 +29,7 @@ def test_component_filtering_by_type(self): def test_component_filtering_by_regex(self): csv = Transducers.create_transducer('csv', transducer_id='tt') - gpt = Gpt("gpt", 3, 100) + gpt = Gpt("gpt", 3, 9, 100) csv.add_target_component(gpt) csv.filter_components(".*or") self.assertEqual(len(csv.target_components), 2) @@ -42,45 +42,45 @@ def test_component_filtering_by_regex(self): def test_component_filtering_by_callable(self): csv = Transducers.create_transducer('csv', transducer_id='tt') - gpt = Gpt("gpt", 3, 100) + gpt = Gpt("gpt", 3, 9, 100) csv.add_target_component(gpt) - csv.filter_components(lambda comp: hasattr(comp, "proc_time")) + csv.filter_components(lambda comp: hasattr(comp, "proc_t")) self.assertEqual(1, len(csv.target_components)) csv = Transducers.create_transducer('csv', transducer_id='tt') li = LI("LI_root", depth=10, width=10, int_delay=0, ext_delay=0) csv.add_target_component(li) - csv.filter_components(lambda comp: isinstance(comp, DelayedAtomic) and comp.name[-1] == "0") + csv.filter_components(lambda comp: isinstance(comp, DelayedAtomic) and comp.name[-3] == "0") self.assertEqual(10, len(csv.target_components)) def test_ports_filtering_by_type(self): csv = Transducers.create_transducer('csv', transducer_id='tt') - gpt = Gpt("gpt", 3, 100) + gpt = Gpt("gpt", 3, 9, 100) csv.add_target_component(gpt) csv.add_target_ports_by_component(gpt, port_filters=Port) - self.assertEqual(8, len(csv.target_ports)) + self.assertEqual(7, len(csv.target_ports)) def test_ports_filtering_by_regex(self): csv = Transducers.create_transducer('csv', transducer_id='tt') - gpt = Gpt("gpt", 3, 100) + gpt = Gpt("gpt", 3, 9, 100) csv.add_target_component(gpt) csv.add_target_ports_by_component(gpt, port_filters="i_.*") - self.assertEqual(5, len(csv.target_ports)) + self.assertEqual(4, len(csv.target_ports)) csv.target_ports.clear() csv.add_target_ports_by_component(gpt, port_filters="[io]_.{3,5}ed") self.assertEqual(2, len(csv.target_ports)) csv.target_ports.clear() - csv.add_target_ports_by_component(gpt, port_filters=".*start.*") + csv.add_target_ports_by_component(gpt, port_filters=".*stop.*") self.assertEqual(1, len(csv.target_ports)) csv.target_ports.clear() def test_ports_filtering_by_callable(self): csv = Transducers.create_transducer('csv', transducer_id='tt') - gpt = Gpt("gpt", 3, 100) + gpt = Gpt("gpt", 3, 9, 100) csv.add_target_component(gpt) filter_func = lambda port: port.name.startswith("i_") and port.name.endswith("ed") @@ -89,11 +89,11 @@ def test_ports_filtering_by_callable(self): def test_ports_filtering_mixed(self): csv = Transducers.create_transducer('csv', transducer_id='tt') - gpt = Gpt("gpt", 3, 100) + gpt = Gpt("gpt", 3, 9, 100) csv.add_target_component(gpt) csv.add_target_ports_by_component(gpt) - self.assertEqual(8, len(csv.target_ports)) + self.assertEqual(7, len(csv.target_ports)) csv.target_ports.clear() input_ports_filter = lambda port: port.name.startswith("i_") @@ -106,7 +106,7 @@ def test_ports_filtering_mixed2(self): csv = Transducers.create_transducer('csv', transducer_id='tt') hi = HI("HI_root", depth=10, width=10, int_delay=0, ext_delay=0) - comp_filters = (lambda comp: isinstance(comp, DelayedAtomic), ".*[0-2]$") + comp_filters = (lambda comp: isinstance(comp, DelayedAtomic), ".*[0-2]_.*$") csv.add_target_component(hi, *comp_filters) print(csv.target_components) @@ -173,13 +173,13 @@ def test_behavior(self): trans_id = "%s_test_behavior" % self.__class__.__name__ csv_transducer = Transducers.create_transducer('csv', transducer_id=trans_id, exhaustive=True) - gpt = Gpt("gpt", 3, 1000) + gpt = Gpt("gpt", 3, 9, 1000) csv_transducer.add_target_component(gpt) coord = Coordinator(gpt, flatten=False) coord.add_transducer(csv_transducer) coord.initialize() - coord.simulate_time() + coord.simulate() coord.exit() self.assertTrue(os.path.exists(csv_transducer.state_filename)) @@ -187,3 +187,8 @@ def test_behavior(self): # TODO: continue when state changes are register appropiately # TODO: def test_pause_resume(self): + + +if __name__ == '__main__': + import unittest + unittest.main() diff --git a/xdevs/tests/test_devstone.py b/xdevs/tests/test_devstone.py index d3f8f1d..026168b 100644 --- a/xdevs/tests/test_devstone.py +++ b/xdevs/tests/test_devstone.py @@ -1,4 +1,5 @@ import unittest +from xdevs import INFINITY from xdevs.sim import Coordinator from xdevs.examples.devstone.devstone import DEVStone, LI, HI, HO, HOmod import random @@ -66,7 +67,7 @@ def test_behavior(self): coord = Coordinator(root) coord.initialize() # coord.inject(li_root.i_in, 0) - coord.simulate() + coord.simulate_time(INFINITY) self.assertEqual(root.n_internals, (params["width"] - 1) * (params["depth"] - 1) + 1) self.assertEqual(root.n_externals, (params["width"] - 1) * (params["depth"] - 1) + 1) @@ -116,7 +117,7 @@ def test_behavior(self): root = DEVStone("HI_root", **params) coord = Coordinator(root) coord.initialize() - coord.simulate() + coord.simulate_time(INFINITY) self.assertEqual(root.n_internals, (((params["width"] - 1) * params["width"]) / 2) * (params["depth"] - 1) + 1) self.assertEqual(root.n_externals, (((params["width"] - 1) * params["width"]) / 2) * (params["depth"] - 1) + 1) @@ -165,7 +166,7 @@ def test_behavior(self): coord = Coordinator(root) coord.initialize() # TODO aqui n_externals debería ser igual a n_atomics (pero no lo es...) - coord.simulate() + coord.simulate_time(INFINITY) self.assertEqual(root.n_internals, (params["width"] - 1) * params["width"] / 2 * (params["depth"] - 1) + 1) self.assertEqual(root.n_externals, (params["width"] - 1) * params["width"] / 2 * (params["depth"] - 1) + 1) @@ -221,7 +222,7 @@ def test_behavior(self): root = DEVStone("HOmod_root", **params) coord = Coordinator(root) coord.initialize() - coord.simulate() + coord.simulate_time(INFINITY) calc_in = lambda x, w: 1 + (x - 1)*(w - 1) exp_trans = 1 diff --git a/xdevs/tests/test_elasticsearch_transducer.py b/xdevs/tests/test_elasticsearch_transducer.py deleted file mode 100644 index 66a4973..0000000 --- a/xdevs/tests/test_elasticsearch_transducer.py +++ /dev/null @@ -1,34 +0,0 @@ -from xdevs.transducers import Transducers -from xdevs.examples.basic.basic import Job, Processor - - -if __name__ == '__main__': - es = Transducers.create_transducer('elasticsearch', - transducer_id='transducer_test', - exhaustive=True, - url='http://localhost:9200') - - model = Processor('processor', 100) - es.add_target_component(model) - es.add_target_port(model.o_out) - - # Try to comment and uncomment the mapper lines to see the effect on the output file - # csv.state_mapper = {'current_job': (str, lambda x: str(x.current_job))} - es.state_mapper['current_job'] = (str, lambda x: str(x.current_job)) - es.event_mapper = {'name': (int, lambda x: x.name), 'time': (int, lambda x: x.time)} - - es.initialize() - clock = 0 - es.bulk_data(clock) - - model.i_in.add(Job(0)) - model.deltext(1) - clock += 1 - model.i_in.clear() - es.bulk_data(1) - clock += model.sigma - model.lambdaf() - model.deltint() - es.bulk_data(clock) - - print('done') diff --git a/xdevs/tests/test_sql_transducer.py b/xdevs/tests/test_sql_transducer.py deleted file mode 100644 index e12d087..0000000 --- a/xdevs/tests/test_sql_transducer.py +++ /dev/null @@ -1,36 +0,0 @@ -from xdevs.transducers import Transducers -from xdevs.examples.basic.basic import Job, Processor - - -if __name__ == '__main__': - sql = Transducers.create_transducer('sql', - transducer_id='transducer_test', - sim_time_id='time', - include_names=True, - exhaustive=True, - url='mysql+pymysql://root@localhost/test') - - model = Processor('processor', 100) - sql.add_target_component(model) - sql.add_target_port(model.o_out) - - # Try to comment and uncomment the mapper lines to see the effect on the output file - # csv.state_mapper = {'current_job': (str, lambda x: str(x.current_job))} - sql.state_mapper['current_job'] = (Job, lambda x: x.current_job) - sql.event_mapper = {'name': (int, lambda x: x.name), 'time': (int, lambda x: x.time)} - - sql.initialize() - clock = 0 - sql.bulk_data(clock) - - model.i_in.add(Job(0)) - model.deltext(1) - clock += 1 - model.i_in.clear() - sql.bulk_data(1) - clock += model.sigma - model.lambdaf() - model.deltint() - sql.bulk_data(clock) - - print('done') diff --git a/xdevs/tests/test_transducible.py b/xdevs/tests/test_transducible.py index cc2f905..0a94a30 100644 --- a/xdevs/tests/test_transducible.py +++ b/xdevs/tests/test_transducible.py @@ -1,7 +1,9 @@ from __future__ import annotations import unittest from typing import Dict, Tuple, Type, Callable -from xdevs.transducers import Transducer, Transducers, Transducible, T +from xdevs import T +from xdevs.abc.transducer import Transducer, Transducible +from xdevs.factory import Transducers class NonTransducibleClass: diff --git a/xdevs/utils.py b/xdevs/utils.py deleted file mode 100644 index fd48800..0000000 --- a/xdevs/utils.py +++ /dev/null @@ -1,26 +0,0 @@ -from xdevs.models import Atomic, Port - - -class Generator(Atomic): - def __init__(self, name: str, num_outputs: int = 1, period: float = None): - super(Generator, self).__init__(name=name) - self.num_outputs: int = num_outputs - self.period: float = period - - self.o_out: Port[int] = Port(int, 'o_out') - self.add_out_port(self.o_out) - - def deltint(self): - self.hold_in('active', self.period) if self.period else self.passivate() - - def deltext(self, e: float): - pass - - def lambdaf(self): - self.o_out.extend(range(self.num_outputs)) - - def initialize(self): - self.activate() - - def exit(self): - pass diff --git a/xdevs/wrappers.py b/xdevs/wrappers.py deleted file mode 100644 index 10f655d..0000000 --- a/xdevs/wrappers.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import annotations -import pkg_resources -from typing import ClassVar, Type -from xdevs.models import Atomic - - -class Wrappers: - _plugins: ClassVar[dict[str, Type[Atomic]]] = { - ep.name: ep.load() for ep in pkg_resources.iter_entry_points('xdevs.plugins.wrappers') - } - - @staticmethod - def add_plugin(name: str, plugin: Type[Atomic]): - if name in Wrappers._plugins: - raise ValueError(f'xDEVS wrapper plugin with name "{name}" already exists') - Wrappers._plugins[name] = plugin - - @staticmethod - def create_wrapper(name: str) -> Type[Atomic]: - if name not in Wrappers._plugins: - raise ValueError(f'xDEVS wrapper plugin with name "{name}" not found') - return Wrappers._plugins[name] From c1fd4f64ac641a50d122e160c2a7ebbe43fd3c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Mon, 27 May 2024 21:50:16 +0200 Subject: [PATCH 46/60] cleaning everything for v3 --- .github/workflows/test.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..82c66a3 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,35 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Python application + +on: + push: + branches: [ "main", "dev-json" ] + pull_request: + branches: [ "main" ] + merge_group: + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11"] + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest + python -m install + - name: Test with pytest + run: | + pytest From 5813aab25adc6bf830f598765fd9677025f8c39e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Mon, 27 May 2024 21:52:46 +0200 Subject: [PATCH 47/60] setting up CI --- .github/workflows/test.yml | 16 +++++------- CHANGELOG.md | 26 +++++++++++++++++++ README.md | 3 ++- pyproject.toml | 2 +- xdevs/__init__.py | 1 + xdevs/factory.py | 20 +++++++++----- xdevs/models.py | 3 ++- .../input_handlers/bad_dependencies.py | 4 ++- xdevs/plugins/input_handlers/csv.py | 1 + xdevs/plugins/input_handlers/function.py | 1 + xdevs/plugins/input_handlers/mqtt.py | 1 + .../output_handlers/bad_dependencies.py | 4 ++- xdevs/plugins/output_handlers/csv.py | 1 + xdevs/plugins/output_handlers/mqtt.py | 1 + xdevs/plugins/output_handlers/tcp.py | 1 + xdevs/plugins/transducers/bad_dependencies.py | 4 ++- xdevs/plugins/wrappers/bad_dependencies.py | 4 ++- xdevs/plugins/wrappers/pypdevs.py | 2 ++ xdevs/sim.py | 1 - 19 files changed, 73 insertions(+), 23 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 82c66a3..f080a3a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,24 +1,19 @@ # This workflow will install Python dependencies, run tests and lint with a single version of Python # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python -name: Python application - +name: Build and test on: push: - branches: [ "main", "dev-json" ] + branches: [ "main" ] pull_request: branches: [ "main" ] merge_group: - -permissions: - contents: read - jobs: build: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 - name: Setup Python @@ -28,8 +23,11 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip + pip install --upgrade importlib-metadata pip install pytest - python -m install + - name: install xDEVS + run: | + python -m pip install . - name: Test with pytest run: | pytest diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..913d23f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,26 @@ +# Change Log + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + +## [Unreleased] + +### Added + +- Add real-time simulation capabilities, including input and output handlers +- CI/CD pipeline for automated testing + +### Changed + +- Transitioned from setup.py to pyproject.toml for package configuration +- Updated dependencies to latest versions +- deltcon now does not accept any input arguments +- All abstract classes are now defined in the `abc` module +- All plugin factories are now defined in the `factory` module +- Minimum Python version is now 3.9 + +### Removed + +- Faulty parallel simulator diff --git a/README.md b/README.md index 46cbf14..046e062 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ -# xdevs.py +# `xdevs.py` + Version of the xDEVS simulator for Python projects diff --git a/pyproject.toml b/pyproject.toml index df8a95e..0509310 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "xdevs" version = "3.0.0" -requires-python = ">=3.8" +requires-python = ">=3.9" authors = [ {name = "Román Cárdenas"}, {name = "Óscar Fernández Sebastián"}, diff --git a/xdevs/__init__.py b/xdevs/__init__.py index baf4dd9..04cb2d7 100644 --- a/xdevs/__init__.py +++ b/xdevs/__init__.py @@ -1,3 +1,4 @@ +from __future__ import annotations import functools import logging import math diff --git a/xdevs/factory.py b/xdevs/factory.py index 74671c1..49690c6 100644 --- a/xdevs/factory.py +++ b/xdevs/factory.py @@ -1,14 +1,22 @@ from __future__ import annotations import json -from importlib.metadata import entry_points +import sys +from importlib.metadata import entry_points, EntryPoint from typing import ClassVar from xdevs.abc import InputHandler, OutputHandler, Transducer from xdevs.models import Atomic, Component, Port, Coupled +def load_entry_points(group: str) -> list[EntryPoint]: + if sys.version_info < (3, 10): + return entry_points().get(group, []) + else: + return entry_points(group=group) + + class InputHandlers: _plugins: ClassVar[dict[str, type[InputHandler]]] = { - ep.name: ep.load() for ep in entry_points(group='xdevs.input_handlers') + ep.name: ep.load() for ep in load_entry_points('xdevs.input_handlers') } @staticmethod @@ -40,7 +48,7 @@ def create_input_handler(name: str, *args, **kwargs) -> InputHandler: class OutputHandlers: _plugins: ClassVar[dict[str, type[OutputHandler]]] = { - ep.name: ep.load() for ep in entry_points(group='xdevs.output_handlers') + ep.name: ep.load() for ep in load_entry_points('xdevs.output_handlers') } @staticmethod @@ -73,7 +81,7 @@ def create_output_handler(name: str, *args, **kwargs) -> OutputHandler: class Wrappers: _plugins: ClassVar[dict[str, type[Atomic]]] = { - ep.name: ep.load() for ep in entry_points(group='xdevs.wrappers') + ep.name: ep.load() for ep in load_entry_points('xdevs.wrappers') } @staticmethod @@ -91,7 +99,7 @@ def create_wrapper(name: str, *args, **kwargs) -> Atomic: class Transducers: _plugins: ClassVar[dict[str, type[Transducer]]] = { - ep.name: ep.load() for ep in entry_points(group='xdevs.transducers') + ep.name: ep.load() for ep in load_entry_points('xdevs.transducers') } @staticmethod @@ -110,7 +118,7 @@ def create_transducer(name: str, *args, **kwargs) -> Transducer: class Components: """This class creates components from unique identifiers called "component_id".""" _plugins: ClassVar[dict[str, type[Component]]] = { - ep.name: ep.load() for ep in entry_points(group='xdevs.components') + ep.name: ep.load() for ep in load_entry_points('xdevs.components') } @staticmethod diff --git a/xdevs/models.py b/xdevs/models.py index 692cbaa..b859e56 100644 --- a/xdevs/models.py +++ b/xdevs/models.py @@ -29,7 +29,8 @@ def __len__(self) -> int: return sum((len(port) for port in self._bag), len(self._values)) def __str__(self) -> str: - return f'{self.name}<{self.p_type.__name__ if self.p_type is not None else 'None'}>' + p_type = self.p_type.__name__ if self.p_type is not None else 'None' + return f'{self.name}<{p_type}>' def __repr__(self) -> str: return str(self) diff --git a/xdevs/plugins/input_handlers/bad_dependencies.py b/xdevs/plugins/input_handlers/bad_dependencies.py index 6c864c3..8c3c8f2 100644 --- a/xdevs/plugins/input_handlers/bad_dependencies.py +++ b/xdevs/plugins/input_handlers/bad_dependencies.py @@ -1,3 +1,4 @@ +from __future__ import annotations from abc import ABC from xdevs.abc.handler import InputHandler @@ -9,7 +10,8 @@ def __init__(self, **kwargs): :param str handler_type: transducer type. """ super().__init__(**kwargs) - raise ImportError(f'{kwargs.get('handler_type')} input handler specific dependencies are not imported') + handler_type = kwargs['handler_type'] + raise ImportError(f'{handler_type} input handler specific dependencies are not imported') def run(self): pass diff --git a/xdevs/plugins/input_handlers/csv.py b/xdevs/plugins/input_handlers/csv.py index 581614f..b69e24e 100644 --- a/xdevs/plugins/input_handlers/csv.py +++ b/xdevs/plugins/input_handlers/csv.py @@ -1,3 +1,4 @@ +from __future__ import annotations import csv import sys import time diff --git a/xdevs/plugins/input_handlers/function.py b/xdevs/plugins/input_handlers/function.py index 880b0d2..ac09420 100644 --- a/xdevs/plugins/input_handlers/function.py +++ b/xdevs/plugins/input_handlers/function.py @@ -1,3 +1,4 @@ +from __future__ import annotations from xdevs.abc.handler import InputHandler diff --git a/xdevs/plugins/input_handlers/mqtt.py b/xdevs/plugins/input_handlers/mqtt.py index 0cb0e2e..5917942 100644 --- a/xdevs/plugins/input_handlers/mqtt.py +++ b/xdevs/plugins/input_handlers/mqtt.py @@ -1,3 +1,4 @@ +from __future__ import annotations import queue import threading diff --git a/xdevs/plugins/output_handlers/bad_dependencies.py b/xdevs/plugins/output_handlers/bad_dependencies.py index b1fe605..201313d 100644 --- a/xdevs/plugins/output_handlers/bad_dependencies.py +++ b/xdevs/plugins/output_handlers/bad_dependencies.py @@ -1,3 +1,4 @@ +from __future__ import annotations from abc import ABC from xdevs.abc.handler import OutputHandler @@ -9,7 +10,8 @@ def __init__(self, **kwargs): :param str handler_type: transducer type. """ super().__init__(**kwargs) - raise ImportError(f'{kwargs['handler_type']} input handler specific dependencies are not imported') + handler_type = kwargs['handler_type'] + raise ImportError(f'{handler_type} input handler specific dependencies are not imported') def run(self): pass diff --git a/xdevs/plugins/output_handlers/csv.py b/xdevs/plugins/output_handlers/csv.py index 701b0ac..b77d365 100644 --- a/xdevs/plugins/output_handlers/csv.py +++ b/xdevs/plugins/output_handlers/csv.py @@ -1,3 +1,4 @@ +from __future__ import annotations import csv import time from xdevs.abc.handler import OutputHandler diff --git a/xdevs/plugins/output_handlers/mqtt.py b/xdevs/plugins/output_handlers/mqtt.py index a6d6040..70e115a 100644 --- a/xdevs/plugins/output_handlers/mqtt.py +++ b/xdevs/plugins/output_handlers/mqtt.py @@ -1,3 +1,4 @@ +from __future__ import annotations from typing import Callable, Any try: diff --git a/xdevs/plugins/output_handlers/tcp.py b/xdevs/plugins/output_handlers/tcp.py index 1b78f66..a26c019 100644 --- a/xdevs/plugins/output_handlers/tcp.py +++ b/xdevs/plugins/output_handlers/tcp.py @@ -1,3 +1,4 @@ +from __future__ import annotations import time from typing import Any, Callable diff --git a/xdevs/plugins/transducers/bad_dependencies.py b/xdevs/plugins/transducers/bad_dependencies.py index d03cbf2..432c757 100644 --- a/xdevs/plugins/transducers/bad_dependencies.py +++ b/xdevs/plugins/transducers/bad_dependencies.py @@ -1,3 +1,4 @@ +from __future__ import annotations from abc import ABC from xdevs.abc.transducer import Transducer @@ -9,7 +10,8 @@ def __init__(self, **kwargs): :param str transducer_type: transducer type. """ super().__init__(**kwargs) - raise ImportError(f'{kwargs.get('transducer_type')} transducer specific dependencies are not imported') + transducer_type = kwargs['transducer_type'] + raise ImportError(f'{transducer_type} transducer specific dependencies are not imported') def create_known_data_types_map(self): pass diff --git a/xdevs/plugins/wrappers/bad_dependencies.py b/xdevs/plugins/wrappers/bad_dependencies.py index d37c58e..116ad49 100644 --- a/xdevs/plugins/wrappers/bad_dependencies.py +++ b/xdevs/plugins/wrappers/bad_dependencies.py @@ -1,3 +1,4 @@ +from __future__ import annotations from abc import ABC from xdevs.models import Atomic @@ -9,7 +10,8 @@ def __init__(self, **kwargs): :param str wrapper_type: wrapper type. """ super().__init__(**kwargs) - raise ImportError(f'{kwargs['wrapper_type']} wrapper specific dependencies are not installed') + wrapper_type = kwargs['wrapper_type'] + raise ImportError(f'{wrapper_type} wrapper specific dependencies are not installed') def deltint(self): pass diff --git a/xdevs/plugins/wrappers/pypdevs.py b/xdevs/plugins/wrappers/pypdevs.py index 01eb3d2..789577f 100644 --- a/xdevs/plugins/wrappers/pypdevs.py +++ b/xdevs/plugins/wrappers/pypdevs.py @@ -1,3 +1,5 @@ +from __future__ import annotations + try: from pypdevs.DEVS import AtomicDEVS from pypdevs.minimal import AtomicDEVS as AtomicDEVSMin diff --git a/xdevs/sim.py b/xdevs/sim.py index 3e5fa77..36c7115 100644 --- a/xdevs/sim.py +++ b/xdevs/sim.py @@ -4,7 +4,6 @@ import itertools import pickle import logging -import warnings from abc import ABC, abstractmethod from collections import defaultdict From 20f9695a241bbd849a268295c456a592fea8a2a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Tue, 25 Jun 2024 15:54:08 +0200 Subject: [PATCH 48/60] Add Cell-DEVS support --- pyproject.toml | 5 + xdevs/abc/__init__.py | 1 + xdevs/abc/celldevs.py | 58 ++++ xdevs/abc/transducer.py | 4 +- xdevs/celldevs/__init__.py | 6 + xdevs/celldevs/cell.py | 192 +++++++++++++ xdevs/celldevs/coupled.py | 90 ++++++ xdevs/celldevs/grid.py | 288 ++++++++++++++++++++ xdevs/celldevs/inout.py | 52 ++++ xdevs/examples/async_rt/basic.py | 271 ------------------ xdevs/examples/async_rt/csv_output_v3.csv | 40 --- xdevs/examples/async_rt/prueba.csv | 10 - xdevs/examples/celldevs_sir/__init__.py | 0 xdevs/examples/celldevs_sir/main.py | 37 +++ xdevs/examples/celldevs_sir/scenario.json | 49 ++++ xdevs/examples/celldevs_sir/sir_cell.py | 68 +++++ xdevs/examples/celldevs_sir/sir_coupled.py | 36 +++ xdevs/examples/celldevs_sir/sir_sink.py | 53 ++++ xdevs/factory.py | 22 +- xdevs/plugins/celldevs_outputs/__init__.py | 0 xdevs/plugins/celldevs_outputs/hybrid.py | 26 ++ xdevs/plugins/celldevs_outputs/inertial.py | 22 ++ xdevs/plugins/celldevs_outputs/transport.py | 27 ++ 23 files changed, 1033 insertions(+), 324 deletions(-) create mode 100644 xdevs/abc/celldevs.py create mode 100644 xdevs/celldevs/__init__.py create mode 100644 xdevs/celldevs/cell.py create mode 100644 xdevs/celldevs/coupled.py create mode 100644 xdevs/celldevs/grid.py create mode 100644 xdevs/celldevs/inout.py delete mode 100644 xdevs/examples/async_rt/basic.py delete mode 100644 xdevs/examples/async_rt/csv_output_v3.csv delete mode 100644 xdevs/examples/async_rt/prueba.csv create mode 100644 xdevs/examples/celldevs_sir/__init__.py create mode 100644 xdevs/examples/celldevs_sir/main.py create mode 100644 xdevs/examples/celldevs_sir/scenario.json create mode 100644 xdevs/examples/celldevs_sir/sir_cell.py create mode 100644 xdevs/examples/celldevs_sir/sir_coupled.py create mode 100644 xdevs/examples/celldevs_sir/sir_sink.py create mode 100644 xdevs/plugins/celldevs_outputs/__init__.py create mode 100644 xdevs/plugins/celldevs_outputs/hybrid.py create mode 100644 xdevs/plugins/celldevs_outputs/inertial.py create mode 100644 xdevs/plugins/celldevs_outputs/transport.py diff --git a/pyproject.toml b/pyproject.toml index 0509310..2d6b8ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,6 +59,11 @@ efp = "xdevs.examples.gpt.efp:Efp" [project.entry-points."xdevs.wrappers"] pypdevs = "xdevs.plugins.wrappers.pypdevs:PyPDEVSWrapper" +[project.entry-points."xdevs.celldevs_outputs"] +hybrid = "xdevs.plugins.celldevs_outputs.hybrid:HybridDelayedOutput" +inertial = "xdevs.plugins.celldevs_outputs.inertial:InertialDelayedOutput" +transport = "xdevs.plugins.celldevs_outputs.transport:TransportDelayedOutput" + [tool.setuptools] include-package-data = false diff --git a/xdevs/abc/__init__.py b/xdevs/abc/__init__.py index ce2b8cf..8882ca6 100644 --- a/xdevs/abc/__init__.py +++ b/xdevs/abc/__init__.py @@ -1,2 +1,3 @@ +from .celldevs import DelayedOutput from .handler import InputHandler, OutputHandler from .transducer import Transducer diff --git a/xdevs/abc/celldevs.py b/xdevs/abc/celldevs.py new file mode 100644 index 0000000..195b336 --- /dev/null +++ b/xdevs/abc/celldevs.py @@ -0,0 +1,58 @@ +from abc import ABC, abstractmethod +from typing import Generic +from xdevs import INFINITY +from xdevs.models import Port +from xdevs.celldevs import C, S + + +class DelayedOutput(Generic[C, S], ABC): + def __init__(self, cell_id: C, serve: bool = False): + """ + Cell-DEVS delayed output port. This is an abstract base class. + :param cell_id: ID of the cell that owns this delayed output. + :param serve: set to True if the port is going to be accessible via RPC server. Defaults to False. + """ + from xdevs.celldevs.inout import CellMessage + self.cell_id = cell_id + self.port: Port[CellMessage[C, S]] = Port(CellMessage, 'out_celldevs', serve) + + @abstractmethod + def add_to_buffer(self, when: float, state: S): + """ + Schedules a cell state to send events. + :param when: time at which the events must be sent. + :param state: cell state. Events will be obtained by mapping this state. + """ + pass + + @abstractmethod + def next_time(self) -> float: + """:return: next time at which events must be sent.""" + pass + + @abstractmethod + def next_state(self) -> S: + """:return: next cell state used to generate events.""" + pass + + @abstractmethod + def pop_state(self): + """removes schedule state from the delayed output.""" + pass + + def send_events(self, time: float): + """ + If there is an scheduled state, it sends a new event via every Cell-DEVS output port. + :param time: current simulation time. + """ + from xdevs.celldevs.inout import CellMessage + if self.next_time() <= time: + self.port.add(CellMessage(self.cell_id, self.next_state())) + + def clean(self, time: float): + """ + It cleans all the outdated scheduled cell states. + :param time: current simulation time. + """ + while self.next_time() < INFINITY and self.next_time() <= time: + self.pop_state() diff --git a/xdevs/abc/transducer.py b/xdevs/abc/transducer.py index c88d785..7154a72 100644 --- a/xdevs/abc/transducer.py +++ b/xdevs/abc/transducer.py @@ -10,9 +10,9 @@ class Transducible(ABC): - @staticmethod + @classmethod @abstractmethod - def transducer_map() -> dict[str, tuple[Type[T], Callable[[Any], T]]]: + def transducer_map(cls) -> dict[str, tuple[Type[T], Callable[[Any], T]]]: pass diff --git a/xdevs/celldevs/__init__.py b/xdevs/celldevs/__init__.py new file mode 100644 index 0000000..6d7d5bd --- /dev/null +++ b/xdevs/celldevs/__init__.py @@ -0,0 +1,6 @@ +from __future__ import annotations +from typing import TypeVar + +C = TypeVar('C') # Variable type used for cell IDs +S = TypeVar('S') # Variable type used for cell states +V = TypeVar('V') # Variable type used for cell vicinities diff --git a/xdevs/celldevs/cell.py b/xdevs/celldevs/cell.py new file mode 100644 index 0000000..dcf69f1 --- /dev/null +++ b/xdevs/celldevs/cell.py @@ -0,0 +1,192 @@ +from __future__ import annotations +from abc import ABC, abstractmethod +from copy import deepcopy +from typing import Any, Generic +from xdevs.celldevs import C, S, V +from xdevs.celldevs.inout import CellMessage, InPort +from xdevs.models import Atomic +from xdevs.factory import DelayedOutputs, DelayedOutput + + +class CellConfig(Generic[C, S, V]): + def __init__(self, config_id: str, c_type: type[C], s_type: type[S], v_type: type[V], **kwargs): + """ + Cell-DEVS configuration structure. + :param config_id: identifier of the configuration. + :param c_type: type used to identify cells. + :param s_type: type used to represent cell states. + :param v_type: type used to represent vicinity between cells. + :param cell_type: identifier of the cell type. + :param delay: identifier of the delay buffer implemented by the cell. By default, it is set to inertial. + :param config: any additional configuration parameters. + :param state: parameters required to create the initial state of the cell. + :param neighborhood: representation of the cell neighborhood. By default, it is empty. + :param cell_map: list of cells that implement this configuration. By default, it is empty. + :param eic: list of external input couplings. By default, it is empty. + :param eoc: list of external output couplings. By default, it is empty. + """ + self.config_id: str = config_id + self.c_type: type[C] = c_type + self.s_type: type[S] = s_type + self.v_type: type[V] = v_type + CellMessage.state_t = s_type + + self.cell_type: str = kwargs['cell_type'] + self.delay_type: str = kwargs.get('delay', 'inertial') + self.cell_config = kwargs.get('config') + self.state = kwargs.get('state') + self.raw_neighborhood: list[dict] = kwargs.get('neighborhood', list()) + self.cell_map: list[C] | None = None if self.default else self._load_map(*kwargs.get('cell_map', list())) + self.eic: list[tuple[str, str]] = self._parse_couplings(kwargs.get('eic', list())) + self.ic: list[tuple[str, str]] = [('out_celldevs', 'in_celldevs')] + self.eoc: list[tuple[str, str]] = self._parse_couplings(kwargs.get('eoc', list())) + + @property + def default(self) -> bool: + """:return: true if this configuration profile is the default one.""" + return self.config_id == 'default' + + def apply_patch(self, config_id: str, **kwargs): + """ + Applies a configuration patch. This method is used for non-default configurations. + :param config_id: configuration ID. + :param cell_type: identifier of the cell type. + :param delay: identifier of the delay buffer implemented by the cell. By default, it is set to inertial. + :param config: any additional configuration parameters. + :param state: parameters required to create the initial state of the cell. + :param neighborhood: representation of the cell neighborhood. By default, it is empty. + :param cell_map: list of cells that implement this configuration. By default, it is empty. + :param eic: list of external input couplings. By default, it is empty. + :param ic: list of internal couplings. By default, it is empty. # TODO remove this? + :param eoc: list of external output couplings. By default, it is empty. + """ + self.config_id = config_id + self.cell_type = kwargs.get('cell_type', self.cell_type) + self.delay_type = kwargs.get('delay', self.delay_type) + if 'config' in kwargs: + self.cell_config = self._patch_dict(self.cell_config, kwargs['config']) \ + if isinstance(self.cell_config, dict) else kwargs['config'] + if 'state' in kwargs: + self.state = self._patch_dict(self.state, kwargs['state']) \ + if isinstance(self.state, dict) else kwargs['state'] + self.raw_neighborhood = kwargs.get('neighborhood', self.raw_neighborhood) + if 'cell_map' in kwargs: + self.cell_map = self._load_map(*kwargs['cell_map']) + if 'eic' in kwargs: + self.eic = self._parse_couplings(kwargs['eic']) + if 'ic' in kwargs: + self.ic = self._parse_couplings(kwargs['ic']) + if 'eoc' in kwargs: + self.eoc = self._parse_couplings(kwargs['eoc']) + + def load_state(self) -> S: + """:return: a new initial state structure.""" + return self._load_value(self.s_type, self.state) + + def load_neighborhood(self) -> dict[C, V]: + """:return: a new neighborhood.""" + neighbors: dict[C, V] = dict() + for neighborhood in self.raw_neighborhood: + for neighbor, vicinity in neighborhood.items(): + neighbors[self.c_type(neighbor)] = self._load_vicinity(vicinity) + return neighbors + + def _load_map(self, *args) -> list[C]: + return [self.c_type(self.config_id)] + + def _load_vicinity(self, vicinity: Any): + return self._load_value(self.v_type, vicinity) + + @classmethod + def _patch_dict(cls, d: dict, patch: dict) -> dict: + for k, v in patch.items(): + d[k] = cls._patch_dict(d[k], v) if isinstance(v, dict) and k in d and isinstance(d[k], dict) else v + return d + + @staticmethod + def _parse_couplings(couplings: list[list[str]]) -> list[tuple[str, str]]: + return [(coupling[0], coupling[1]) for coupling in couplings] + + @staticmethod + def _load_value(t_type, params: Any): + params = deepcopy(params) + if isinstance(params, dict): + return t_type(**params) + elif isinstance(params, list): + return t_type(*params) + elif params is not None: + return t_type(params) + return t_type() + + +class Cell(Atomic, ABC, Generic[C, S, V]): + def __init__(self, cell_id: C, config: CellConfig[C, S, V]): + """ + Abstract Base Class for a Cell-DEVS cell. + :param cell_id: cell identifier. + :param config: cell configuration structure. + """ + super().__init__(str(cell_id)) + self._clock: float = 0 + self._config: CellConfig = config + self.ics = config.eic + self.cell_id: C = cell_id + self.cell_state: S = config.load_state() + self.neighborhood: dict[C, V] = self._load_neighborhood() + + self.in_celldevs: InPort[C, S] = InPort(self.cell_id) + self.out_celldevs: DelayedOutput[C, S] = DelayedOutputs.create_delayed_output(config.delay_type, self.cell_id) + self.add_in_port(self.in_celldevs.port) + self.add_out_port(self.out_celldevs.port) + + @property + def neighbors_state(self) -> dict[C, S]: + return self.in_celldevs.history + + @abstractmethod + def local_computation(self, cell_state: S) -> S: + """ + Computes new cell state depending on its previous state. + :param cell_state: current cell state. + :return: new cell state. + """ + pass + + @abstractmethod + def output_delay(self, cell_state: S) -> float: + """ + Returns delay to be applied to output messages related to new cell state. + :param cell_state: new cell state. + :return: delay to be applied. + """ + pass + + def deltint(self): + self._clock += self.sigma + self.out_celldevs.clean(self._clock) + self.sigma = self.out_celldevs.next_time() - self._clock + + def deltext(self, e: float): + self._clock += e + self.sigma -= e + self.in_celldevs.read_new_events() + + new_state = self.local_computation(deepcopy(self.cell_state)) + if new_state != self.cell_state: + state = deepcopy(new_state) + self.out_celldevs.add_to_buffer(self._clock + self.output_delay(state), state) + self.sigma = self.out_celldevs.next_time() - self._clock + self.cell_state = new_state + + def lambdaf(self): + self.out_celldevs.send_events(self._clock + self.sigma) + + def initialize(self): + self.out_celldevs.add_to_buffer(0, self.cell_state) + self.activate() + + def exit(self): + pass + + def _load_neighborhood(self) -> dict[C, V]: + return self._config.load_neighborhood() diff --git a/xdevs/celldevs/coupled.py b/xdevs/celldevs/coupled.py new file mode 100644 index 0000000..fa25e00 --- /dev/null +++ b/xdevs/celldevs/coupled.py @@ -0,0 +1,90 @@ +from __future__ import annotations +import json +from abc import abstractmethod, ABC +from copy import deepcopy +from typing import Dict, Generic, Optional, Tuple, Type +from xdevs.celldevs import C, S, V +from xdevs.celldevs.cell import Cell, CellConfig +from xdevs.celldevs.grid import GridCell, GridCellConfig, GridScenario +from xdevs.models import Coupled + + +class CoupledCellDEVS(Coupled, ABC, Generic[C, S, V]): + def __init__(self, c_type: Type[C], s_type: Type[S], v_type: Type[V], config_file: str, name: Optional[str] = None): + super().__init__(name) + self.c_type: Type[C] = c_type + self.s_type: Type[S] = s_type + self.v_type: Type[V] = v_type + with open(config_file) as file: + self.raw_config = json.load(file) + self._configs: Dict[str, CellConfig[C, S, V]] = dict() + self._cells: Dict[C, Tuple[Cell[C, S, V], CellConfig]] = dict() + + def load_config(self): + raw_configs = self.raw_config['cells'] + default_config: CellConfig[C, S, V] = self._load_default_config(raw_configs['default']) + self._configs = {'default': default_config} + for config_id, raw_config in raw_configs.items(): + if config_id != 'default': + config = deepcopy(default_config) + config.apply_patch(config_id, **raw_config) + self._configs[config_id] = config + + def load_cells(self): + for cell_config in self._configs.values(): + if not cell_config.default: + for cell_id in cell_config.cell_map: + if cell_id in self._cells: + raise ValueError('cell with the same ID already exists') + cell: Cell[C, S, V] = self.create_cell(cell_config.cell_type, cell_id, cell_config) + self._cells[cell_id] = (cell, cell_config) + self.add_component(cell) + + def load_couplings(self): + for cell_to, cell_config in self._cells.values(): + for port_from, port_to in cell_config.eic: + self.add_coupling(self.get_in_port(port_from), cell_to.get_in_port(port_to)) + for neighbor in cell_to.neighborhood: + cell_from = self._cells[neighbor][0] + for port_from, port_to in cell_config.ic: + self.add_coupling(cell_from.get_out_port(port_from), cell_to.get_in_port(port_to)) + for port_from, port_to in cell_config.eoc: + self.add_coupling(cell_to.get_out_port(port_from), self.get_out_port(port_to)) + + def _load_default_config(self, raw_config: Dict) -> CellConfig[C, S, V]: + return CellConfig('default', self.c_type, self.s_type, self.v_type, **raw_config) + + @abstractmethod + def create_cell(self, cell_type: str, cell_id: C, cell_config: CellConfig[C, S, V]) -> Cell[C, S, V]: + pass + + +class CoupledGridCellDEVS(CoupledCellDEVS[Tuple[int, ...], S, V], ABC, Generic[S, V]): + + _configs: Dict[str, GridCellConfig] + + def __init__(self, s_type: Type[S], v_type: Type[V], config_file: str): + super().__init__(tuple, s_type, v_type, config_file) + + scenario_config = self.raw_config['scenario'] + shape = tuple(scenario_config['shape']) + origin = tuple(scenario_config['origin']) if 'origin' in scenario_config else None + wrapped = scenario_config.get('wrapped', False) + self.scenario: GridScenario = GridScenario(shape, origin, wrapped) + + def load_cells(self): + super().load_cells() + default_config = self._configs['default'] + for cell_id in self.scenario.iter_cells(): + if cell_id not in self._cells: + cell: GridCell[S, V] = self.create_cell(default_config.cell_type, cell_id, default_config) + self._cells[cell_id] = (cell, default_config) + self.add_component(cell) + + def _load_default_config(self, raw_config: Dict) -> GridCellConfig[S, V]: + return GridCellConfig(self.scenario, 'default', self.s_type, self.v_type, **raw_config) + + @abstractmethod + def create_cell(self, cell_type: str, cell_id: Tuple[int, ...], + cell_config: GridCellConfig[S, V]) -> GridCell[S, V]: + pass diff --git a/xdevs/celldevs/grid.py b/xdevs/celldevs/grid.py new file mode 100644 index 0000000..27f28fa --- /dev/null +++ b/xdevs/celldevs/grid.py @@ -0,0 +1,288 @@ +from __future__ import annotations +import math +from abc import ABC +from math import copysign, isinf +from typing import Dict, Generic, Iterator, List, Optional, Tuple, Type, Union +from xdevs.celldevs import S, V +from xdevs.celldevs.cell import Cell, CellConfig + +C = Tuple[int, ...] # Cell IDs in grids are tuples of integers + + +class GridScenario: + def __init__(self, shape: C, origin: Optional[C] = None, wrapped: bool = False): + """ + Grid scenario configuration. + :param shape: tuple describing the dimension of the scenario. + :param origin: tuple describing the origin of the scenario. By default, it is set to (0, 0, ...). + :param wrapped: if true, the scenario wraps the edges. It defaults to False. + """ + if len(shape) < 1: + raise ValueError('scenario dimension is invalid') + for dim in shape: + if dim <= 0: + raise ValueError('scenario shape is invalid') + self.shape = tuple(shape) + if origin is None: + origin = tuple(0 for _ in range(self.dimension)) + if len(origin) != len(shape): + raise ValueError('scenario shape and origin must have the same dimension') + self.origin = tuple(origin) + self.wrapped = wrapped + + @property + def dimension(self) -> int: + """:return: number of dimensions of the scenario.""" + return len(self.shape) + + def cell_in_scenario(self, cell: C) -> bool: + """ + Checks if a cell is inside the scenario. + :param cell: coordinates of the cell under study. + :return: True if the coordinates of the cell are inside the scenario. + """ + return self._cell_in_scenario(cell, self.shape, self.origin) + + def distance_vector(self, cell_from: C, cell_to: C) -> C: + """ + Computes the distance vector between two cells. + :param cell_from: origin cell. + :param cell_to: destination cell. + :return: relative distance vector. + """ + return self._distance_vector(cell_from, cell_to, self.shape, self.origin, self.wrapped) + + def cell_to(self, cell_from: C, distance_vector: C): + """ + Deduces destination cell according to an origin cell and a distance vector. + :param cell_from: origin cell. + :param distance_vector: distance vector. + :return: destination cell. + """ + if not self.cell_in_scenario(cell_from): + raise ValueError('cell_from is not part of the scenario') + elif len(distance_vector) != self.dimension: + raise ValueError('scenario shape and distance_vector must have the same dimension') + cell_to: C = tuple(cell_from[i] + distance_vector[i] for i in range(self.dimension)) + if self.wrapped: + cell_to = tuple((cell_to[i] + self.shape[i]) % self.shape[i] for i in range(self.dimension)) + if not self.cell_in_scenario(cell_to): + raise OverflowError('cell_to is not part of the scenario') + return cell_to + + def minkowski_distance(self, p: int, cell_from: C, cell_to: C) -> Union[int, float]: + """ + Computes Minkowski distance between two cells. + :param p: Minkowski distance order. + :param cell_from: origin cell. + :param cell_to: destination cell. + :return: Minkowski distance between cells. + """ + return self._minkowski_distance(p, cell_from, cell_to, self.shape, self.origin, self.wrapped) + + def moore_neighborhood(self, r: int = 1) -> List[C]: + """ + Creates a Moore neighborhood of the desired range. + :param r: neighborhood range. + :return: List with relative distance vectors of neighbors. + """ + return self._moore_neighborhood(self.dimension, r) + + def von_neumann_neighborhood(self, r: int = 1) -> List[C]: + """ + Creates a von Neumann neighborhood of the desired range. + :param r: neighborhood range. + :return: List with relative distance vectors of neighbors. + """ + return self._von_neumann_neighborhood(self.dimension, r) + + def iter_cells(self) -> Iterator[C]: + """:return: iterator that goes through all the cells in the scenario.""" + return self._iter_cells(self.shape, self.origin) + + @staticmethod + def _cell_in_scenario(cell: C, shape: C, origin: C) -> bool: + if len(cell) != len(shape): + raise ValueError('scenario shape and cell location must have the same dimension') + return all(0 <= cell[i] - origin[i] < shape[i] for i in range(len(shape))) + + @classmethod + def _distance_vector(cls, cell_from: C, cell_to: C, shape: C, origin: C, wrapped: bool) -> C: + if not cls._cell_in_scenario(cell_from, shape, origin) or not cls._cell_in_scenario(cell_to, shape, origin): + raise ValueError('cell_from and/or cell_to are not part of the scenario') + dimension = len(shape) + distance: C = tuple(cell_to[i] - cell_from[i] for i in range(dimension)) + if wrapped: + distance = tuple(distance[i] - copysign(shape[i], distance[i]) if abs(distance[i]) > shape[i] / 2 + else distance[i] for i in range(dimension)) + return distance + + @classmethod + def _minkowski_distance(cls, p: int, cell_from: C, cell_to: C, shape: C, + origin: C, wrapped: bool) -> Union[int, float]: + if p <= 0: + raise ValueError('Minkowski distance is only valid for p greater than 0') + d_vector: C = cls._distance_vector(cell_from, cell_to, shape, origin, wrapped) + if p == 1: + return sum(abs(d) for d in d_vector) + elif isinf(p): + return max(abs(d) for d in d_vector) + else: + return sum(abs(d) ** p for d in d_vector) ** (1 / p) + + @classmethod + def _moore_neighborhood(cls, dim: int, r: int) -> List[C]: + if dim < 0: + raise ValueError('invalid number of dimensions') + if r < 0: + raise ValueError('neighborhood range must be greater than or equal to 0') + n_shape: C = tuple(2 * r + 1 for _ in range(dim)) + n_origin: C = tuple(-r for _ in range(dim)) + return list(cls._iter_cells(n_shape, n_origin)) + + @classmethod + def _von_neumann_neighborhood(cls, dim: int, r: int) -> List[C]: + moore: List[C] = cls._moore_neighborhood(dim, r) + n_shape: C = tuple(2 * r + 1 for _ in range(dim)) + n_origin: C = tuple(-r for _ in range(dim)) + center: C = tuple(0 for _ in range(dim)) + neighborhood: List[C] = list() + for neighbor in moore: + if cls._minkowski_distance(1, center, neighbor, n_shape, n_origin, False) <= r: + neighborhood.append(neighbor) + return neighborhood + + @classmethod + def _next_cell(cls, prev_cell: C, shape: C, origin: C, d: int) -> Optional[C]: + if cls._cell_in_scenario(prev_cell, shape, origin): + if prev_cell[d] - origin[d] < shape[d] - 1: + return tuple(prev_cell[i] if i != d else prev_cell[i] + 1 for i in range(len(shape))) + elif d < len(shape) - 1: + prev_cell = tuple(prev_cell[i] if i != d else origin[i] for i in range(len(shape))) + return cls._next_cell(prev_cell, shape, origin, d + 1) + + @classmethod + def _iter_cells(cls, shape: C, origin: C) -> Iterator[C]: + cell: C = origin + while cell is not None: + yield cell + cell = cls._next_cell(cell, shape, origin, 0) + + +class GridCellConfig(CellConfig[C, S, V], Generic[S, V]): + def __init__(self, scenario: GridScenario, config_id: str, s_type: Type[S], v_type: Type[V], **kwargs): + """ + Grid cell configuration structure. + :param scenario: grid scenario structure. + :param config_id: identifier of the configuration. + :param s_type: type used to represent cell states. + :param v_type: type used to represent vicinity between cells. + :param kwargs: any additional configuration parameters required for creating a cell configuration structure. + """ + self.scenario: GridScenario = scenario + super().__init__(config_id, tuple, s_type, v_type, **kwargs) + + def _load_map(self, *args) -> List[C]: + return [tuple(cell_id) for cell_id in args] + + def load_cell_neighborhood(self, cell: C) -> Dict[C, V]: + """ + Creates the neighborhood corresponding to a given cell. + :param cell: target cell tu create the neighborhood. + :return: dictionary {neighbor cell: vicinity} + """ + neighbors: Dict[C, V] = dict() + for neighborhood in self.raw_neighborhood: + vicinity = neighborhood.get('vicinity') + n_type: str = neighborhood.get('type', 'absolute') + if n_type == 'absolute': + for neighbor in neighborhood.get('neighbors', list()): + neighbor = tuple(neighbor) + if not self.scenario.cell_in_scenario(neighbor): + raise OverflowError('absolute neighbor is not part of the scenario') + neighbors[neighbor] = self._load_vicinity(vicinity) + else: + if n_type == 'relative': + relative: List[C] = [tuple(neighbor) for neighbor in neighborhood.get('neighbors', list())] + elif n_type == 'moore': + relative: List[C] = self.scenario.moore_neighborhood(neighborhood.get('range', 1)) + elif n_type == 'von_neumann': + relative: List[C] = self.scenario.von_neumann_neighborhood(neighborhood.get('range', 1)) + else: + raise ValueError('unknown neighborhood type') + for neighbor in relative: + try: + neighbors[self.scenario.cell_to(cell, tuple(neighbor))] = self._load_vicinity(vicinity) + except OverflowError: + continue + return neighbors + + +class GridCell(Cell[C, S, V], ABC, Generic[S, V]): + + _config: GridCellConfig[S, V] + + def __init__(self, cell_id: C, config: GridCellConfig): + """ + Grid Cell class for Cell-DEVS scenarios. + :param config: configuration structure for grid cells. + """ + super().__init__(cell_id, config) + self.scenario = config.scenario + + @property + def location(self) -> C: + """:return: location of the cell.""" + return self.cell_id + + def minkowski_distance(self, p: int, other: C) -> float: + """ + Computes Minkowski distance from cell to another cell. + :param p: Minkowski distance order. + :param other: destination cell. + :return: Minkowski distance. + """ + return self.scenario.minkowski_distance(p, self.location, other) + + def manhattan_distance(self, other: C) -> int: + """ + Computes Manhattan distance from cell to another cell + :param other: destination cell. + :return: Manhattan distance. + """ + return self.scenario.minkowski_distance(1, self.location, other) + + def euclidean_distance(self, other: C) -> float: + """ + Computes Euclidean distance from cell to another cell. + :param other: destination cell. + :return: Euclidean distance. + """ + return self.scenario.minkowski_distance(2, self.location, other) + + def chebyshev_distance(self, other: C) -> int: + """ + Computes Chebyshev distance from cell to another cell. + :param other: destination cell. + :return: Chebyshev distance. + """ + return self.scenario.minkowski_distance(math.inf, self.location, other) + + def neighbor(self, relative: C) -> C: + """ + Computes the coordinates of a neighboring cell from a relative distance vector. + :param relative: relative distance vector. + :return: coordinates of neighboring cell. + """ + return self.scenario.cell_to(self.location, relative) + + def relative(self, neighbor: C) -> C: + """ + Computes the relative distance vector from the coordinates of a neighboring cell. + :param neighbor: coordinates of a neighboring cell. + :return: relative distance vector. + """ + return self.scenario.distance_vector(self.location, neighbor) + + def _load_neighborhood(self) -> Dict[C, V]: + return self._config.load_cell_neighborhood(self.cell_id) diff --git a/xdevs/celldevs/inout.py b/xdevs/celldevs/inout.py new file mode 100644 index 0000000..c15faa0 --- /dev/null +++ b/xdevs/celldevs/inout.py @@ -0,0 +1,52 @@ +from __future__ import annotations +from typing import ClassVar, Dict, Generic, Optional, Type, Callable, Any +from xdevs.models import Port +from xdevs.abc.transducer import Transducible, T +from xdevs.celldevs import C, S + + +class CellMessage(Transducible, Generic[C, S]): + + state_t: ClassVar[Type[S]] = None + + def __init__(self, cell_id: C, cell_state: S): + self.cell_id = cell_id + self.cell_state = cell_state + + @classmethod + def transducer_map(cls) -> dict[str, tuple[Type[T], Callable[[Any], T]]]: + if issubclass(cls.state_t, Transducible): + res = {'cell_id': (str, lambda x: x.cell_id)} + for field, (t, l) in cls.state_t.transducer_map().items(): + res[field] = (t, lambda x: l(x.cell_state)) + return res + # return { + # 'cell_id': (str, lambda x: x.cell_id), + # **{field: (t, lambda x: l(x.cell_state)) for field, (t, l) in cls.state_t.transducer_map().items()} + #} + return { + 'cell_id': (str, lambda x: x.cell_id), + 'cell_state': (str, lambda x: x.cell_state), + } + +class InPort(Generic[C, S]): + def __init__(self, serve: bool = False): + """ + Cell-DEVS in port. + :param serve: set to True if the port is going to be accessible via RPC server. Defaults to False. + """ + self.port: Port[CellMessage[C, S]] = Port(CellMessage, 'in_celldevs', serve) + self.history: Dict[C, S] = dict() + + def read_new_events(self): + """It stores the latest incoming events into self.history""" + for cell_message in self.port.values: + self.history[cell_message.cell_id] = cell_message.cell_state + + def get(self, cell_id: C) -> Optional[S]: + """ + Returns latest received event. + :param cell_id: ID of the cell that sent the event. + :return: latest received event. If no event has been received, it returns None. + """ + return self.history.get(cell_id) diff --git a/xdevs/examples/async_rt/basic.py b/xdevs/examples/async_rt/basic.py deleted file mode 100644 index 5d2bf1c..0000000 --- a/xdevs/examples/async_rt/basic.py +++ /dev/null @@ -1,271 +0,0 @@ -import logging -import queue -import random -import time - -from xdevs import PHASE_ACTIVE, PHASE_PASSIVE, get_logger -from xdevs.models import Atomic, Coupled, Port -from xdevs.rt import RealTimeCoordinator, RealTimeManager - -logger = get_logger(__name__, logging.INFO) - -PHASE_DONE = "done" - - -class Job: - def __init__(self, name): - self.name = name - self.time = 0 - - def __str__(self): - return f'Job:: {self.name}' - - -class Generator(Atomic): - - def __init__(self, name, period): - super().__init__(name) - self.i_start = Port(Job, "i_start") - self.i_extern = Port(Job, "i_extern") # receives additional jobs from outside - self.i_stop = Port(Job, "i_stop") - self.o_out = Port(Job, "o_out") - - self.add_in_port(self.i_start) - self.add_in_port(self.i_stop) - self.add_in_port(self.i_extern) - self.add_out_port(self.o_out) - - self.period = period - self.job_counter = 1 - self.extern_jobs = list() # stores external jobs - - self.generate = True - - def initialize(self): - self.hold_in(PHASE_ACTIVE, self.period) - - def exit(self): - pass - - def deltint(self): - self.job_counter += 1 - self.extern_jobs.clear() - self.hold_in(PHASE_ACTIVE, self.period) - - def deltext(self, e): - self.sigma -= e - for msg in self.i_extern.values: - logger.info("Generator received external job. It will forward it in the next lambda") - self.extern_jobs.append(msg) - if not self.i_stop.empty(): - self.generate = False - - def lambdaf(self): - if self.generate: - job = Job(str(self.job_counter)) - self.o_out.add(job) - logger.info("Starting job %s @ t_r = %f" % (job.name, time.time())) - for msg in self.extern_jobs: # we also forward external messages - self.o_out.add(msg) - logger.info("Starting job %s @ t_r = %f" % (msg.name, time.time())) - - -class Processor(Atomic): - def __init__(self, name, proc_time): - super().__init__(name) - - self.i_in = Port(Job, "i_in") - self.o_out = Port(Job, "o_out") - - self.add_in_port(self.i_in) - self.add_out_port(self.o_out) - - self.current_job = None - self.proc_time = proc_time - - def initialize(self): - self.passivate() - - def exit(self): - pass - - def deltint(self): - self.passivate() - - def deltext(self, e): - if self.phase == PHASE_PASSIVE: - self.current_job = self.i_in.get() - self.hold_in(PHASE_ACTIVE, self.proc_time) - self.continuef(e) - - def lambdaf(self): - self.o_out.add(self.current_job) - logger.info("Job %s finished @ t_r = %f" % (self.current_job.name, time.time())) - - -class Transducer(Atomic): - - def __init__(self, name, obs_time): - super().__init__(name) - - self.i_arrived = Port(Job, "i_arrived") - self.i_solved = Port(Job, "i_solved") - self.o_out = Port(Job, "o_out") - - self.add_in_port(self.i_arrived) - self.add_in_port(self.i_solved) - self.add_out_port(self.o_out) - - self.jobs_arrived = [] - self.jobs_solved = [] - - self.total_ta = 0 - self.clock = 0 - self.obs_time = obs_time - - def initialize(self): - self.hold_in(PHASE_ACTIVE, self.obs_time) - - def exit(self): - pass - - def deltint(self): - self.clock += self.sigma - - if self.phase == PHASE_ACTIVE: - if self.jobs_solved: - avg_ta = self.total_ta / len(self.jobs_solved) - throughput = len(self.jobs_solved) / self.clock if self.clock > 0 else 0 - else: - avg_ta = 0 - throughput = 0 - - logger.info("End time: %f" % self.clock) - logger.info("Jobs arrived: %d" % len(self.jobs_arrived)) - logger.info("Jobs solved: %d" % len(self.jobs_solved)) - logger.info("Average TA: %f" % avg_ta) - logger.info("Throughput: %f\n" % throughput) - - self.hold_in(PHASE_DONE, 0) - else: - self.passivate() - - def deltext(self, e): - self.clock += e - - if self.phase == PHASE_ACTIVE: - for job in self.i_arrived.values: - # logger.info("Starting job %s @ t = %d @ t_r = %f" % (job.name, self.clock, time.time())) - job.time = self.clock - self.jobs_arrived.append(job) - - if self.i_solved: - job = self.i_solved.get() - # logger.info("Job %s finished @ t = %d @ t_r = %f" % (job.name, self.clock, time.time())) - self.total_ta += self.clock - job.time - self.jobs_solved.append(job) - - self.continuef(e) - - def lambdaf(self): - if self.phase == PHASE_DONE: - self.o_out.add(Job("null")) - - -class RTGpt(Coupled): - def __init__(self, name, period, obs_time): - super().__init__(name) - - if period < 1: - raise ValueError("period has to be greater than 0") - - if obs_time < 0: - raise ValueError("obs_time has to be greater or equal than 0") - - gen = Generator("generator", period) - proc = Processor("processor", 3 * period) - trans = Transducer("transducer", obs_time) - - self.add_component(gen) - self.add_component(proc) - self.add_component(trans) - - # new input port for receiving input events - self.i_extern = Port(Job, "i_extern") - self.add_in_port(self.i_extern) - # new coupling for forwarding messages to generator - self.add_coupling(self.i_extern, gen.i_extern) - - # new output port for sending solved jobs - self.o_extern_proc = Port(Job, 'o_extern_proc') - self.o_extern_gen = Port(Job, 'o_extern_gen') - self.o_extern_trans = Port(Job, 'o_extern_trans') - self.add_out_port(self.o_extern_proc) - self.add_out_port(self.o_extern_gen) - self.add_out_port(self.o_extern_trans) - # new coupling - self.add_coupling(proc.o_out, self.o_extern_proc) - self.add_coupling(gen.o_out, self.o_extern_gen) - self.add_coupling(trans.o_out, self.o_extern_trans) - - self.add_coupling(gen.o_out, proc.i_in) - self.add_coupling(gen.o_out, trans.i_arrived) - self.add_coupling(proc.o_out, trans.i_solved) - self.add_coupling(trans.o_out, gen.i_stop) - - -def inject_messages(q: queue.SimpleQueue): - i = -1 - while True: - f = round(random.gauss(3, 0.6), 2) - # f = 3 - time.sleep(f) # duermo f segundos - # la cola espera tuplas (port_name, msg) - q.put(("i_extern", Job(i))) - i -= 1 - # test ventana manager - # time.sleep(0.3) - # q.put(("i_extern", Job(i))) - # i -= 1 - - -if __name__ == '__main__': - execution_time = 31 - time_scale = 1 - max_jitter = 0.2 - event_window = 0.5 - - gpt = RTGpt("gpt", 2, 3600) - - manager = RealTimeManager(max_jitter=max_jitter, time_scale=time_scale, event_window=event_window) - - parsers = { - 'i_extern': lambda x: Job(x), # le digo al input handler como convertir el string a Job con una función - 'tcp': lambda x: x.decode().split('.'), - } - manager.add_input_handler('csv_handler', file="prueba.csv", msg_parsers=parsers) - - # manager.add_input_handler('function', function=inject_messages) - # Si no quiero ir repitiendo parsers, se lo tendria que meter al manager - # manager.add_input_handler('tcp_handler', HOST='LocalHost', PORT=5055, parsers=parsers) - - # manager.add_output_handler('csv_out_handler', file='csv_output_v3.csv') - - # manager.add_output_handler('tcp_out_handler', PORT=1234) - - sub: dict = { - 'RTsys/i_extern': 0, - } - #manager.add_input_handler('mqtt_handler', subscriptions=sub, msg_parsers=parsers) - - #manager.add_output_handler('mqtt_handler') - - c = RealTimeCoordinator(gpt, manager) - t_ini = time.time() - print(f' >>> COMENZAMOS : {t_ini}') - c.simulate_iters(time_interv=execution_time) - print(f' >>> FIN : {time.time()}') - print(f' Tiempo a ejecutar (s) = {execution_time * time_scale}') - print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') - print(f' Error (%) = ' - f'{((time.time() - t_ini - (execution_time * time_scale)) / (execution_time * time_scale)) * 100}') diff --git a/xdevs/examples/async_rt/csv_output_v3.csv b/xdevs/examples/async_rt/csv_output_v3.csv deleted file mode 100644 index 1276501..0000000 --- a/xdevs/examples/async_rt/csv_output_v3.csv +++ /dev/null @@ -1,40 +0,0 @@ -t,port,msg -2.011455774307251,o_extern_gen,Job:: 1 -4.015483379364014,o_extern_gen,Job:: 2 -4.015483379364014,o_extern_gen,Job:: -1_csv -4.015483379364014,o_extern_gen,Job:: -1 -6.0036680698394775,o_extern_proc,Job:: 1 -6.004634857177734,o_extern_gen,Job:: 3 -6.004634857177734,o_extern_gen,Job:: -2 -8.01294207572937,o_extern_gen,Job:: 4 -8.01294207572937,o_extern_gen,Job:: -2_csv -10.01301908493042,o_extern_gen,Job:: 5 -10.01401972770691,o_extern_gen,Job:: -3_csv -10.01401972770691,o_extern_gen,Job:: -3 -12.008435010910034,o_extern_proc,Job:: 3 -12.008435010910034,o_extern_gen,Job:: 6 -14.014714479446411,o_extern_gen,Job:: 7 -14.014714479446411,o_extern_gen,Job:: -4_csv -14.014714479446411,o_extern_gen,Job:: -4 -16.0102641582489,o_extern_gen,Job:: 8 -16.011170864105225,o_extern_gen,Job:: -5_csv -16.011170864105225,o_extern_gen,Job:: -5 -18.002914667129517,o_extern_proc,Job:: 6 -18.002914667129517,o_extern_gen,Job:: 9 -20.006149291992188,o_extern_gen,Job:: 10 -20.006149291992188,o_extern_gen,Job:: -6 -20.006149291992188,o_extern_gen,Job:: -6_csv -22.01274347305298,o_extern_gen,Job:: 11 -22.01274347305298,o_extern_gen,Job:: -7 -22.01274347305298,o_extern_gen,Job:: -7_csv -24.00597643852234,o_extern_proc,Job:: 9 -24.00597643852234,o_extern_gen,Job:: 12 -24.00597643852234,o_extern_gen,Job:: -8 -26.00346088409424,o_extern_gen,Job:: 13 -26.00346088409424,o_extern_gen,Job:: -8_csv -28.013100385665894,o_extern_gen,Job:: 14 -28.013100385665894,o_extern_gen,Job:: -9 -28.013100385665894,o_extern_gen,Job:: -9_csv -30.007229804992676,o_extern_proc,Job:: 12 -30.007229804992676,o_extern_gen,Job:: 15 -30.007229804992676,o_extern_gen,Job:: -10 diff --git a/xdevs/examples/async_rt/prueba.csv b/xdevs/examples/async_rt/prueba.csv deleted file mode 100644 index 454c49a..0000000 --- a/xdevs/examples/async_rt/prueba.csv +++ /dev/null @@ -1,10 +0,0 @@ -t,port,msg -3,i_extern,-1_csv -3,i_extern,-2_csv -3,i_extern,-3_csv -3,i_extern,-4_csv -3,i_extern,-5_csv -3,i_extern,-6_csv -3,i_extern,-7_csv -3,i_extern,-8_csv -3,i_extern,-9_csv \ No newline at end of file diff --git a/xdevs/examples/celldevs_sir/__init__.py b/xdevs/examples/celldevs_sir/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xdevs/examples/celldevs_sir/main.py b/xdevs/examples/celldevs_sir/main.py new file mode 100644 index 0000000..52ee898 --- /dev/null +++ b/xdevs/examples/celldevs_sir/main.py @@ -0,0 +1,37 @@ +import math +from xdevs.celldevs.inout import CellMessage +from xdevs.models import Coupled +from xdevs.sim import Coordinator +from xdevs.factory import Transducer, Transducers +from sir_coupled import SIRGridCoupled +from sir_sink import SIRSink, State + + +class SIRModel(Coupled): + def __init__(self, config_path: str): + super().__init__() + self.celldevs = SIRGridCoupled(config_path) + self.sink = SIRSink('sink') + + self.add_component(self.celldevs) + self.add_component(self.sink) + self.add_coupling(self.celldevs.sink_port, self.sink.in_sink) + + +if __name__ == '__main__': + model = SIRModel('scenario.json') + + celldevs_transducer: Transducer = Transducers.create_transducer('csv', transducer_id='celldevs', + event_type=CellMessage, include_names=False, + exhaustive=True) + celldevs_transducer.add_target_port(model.celldevs.sink_port) + + sink_transducer: Transducer = Transducers.create_transducer('csv', transducer_id='sink', + event_type=State, include_names=False) + sink_transducer.add_target_port(model.sink.out_sink) + + coordinator = Coordinator(model) + coordinator.add_transducer(celldevs_transducer) + coordinator.add_transducer(sink_transducer) + coordinator.initialize() + coordinator.simulate_time(math.inf) diff --git a/xdevs/examples/celldevs_sir/scenario.json b/xdevs/examples/celldevs_sir/scenario.json new file mode 100644 index 0000000..2354737 --- /dev/null +++ b/xdevs/examples/celldevs_sir/scenario.json @@ -0,0 +1,49 @@ +{ + "scenario":{ + "shape": [25, 25], + "origin": [-12, -12], + "wrapped": false + }, + "cells": { + "default": { + "delay": "inertial", + "cell_type": "hoya", + "neighborhood": [ + { + "type": "von_neumann", + "range": 1, + "vicinity": { + "connectivity": 1, + "mobility": 0.5 + } + }, + { + "type": "relative", + "neighbors": [[0, 0]], + "vicinity": { + "connectivity": 1, + "mobility": 1 + } + } + ], + "state": { + "population": 100, + "susceptible": 1, + "infected": 0, + "recovered": 0 + }, + "config": { + "virulence": 0.6, + "recovery": 0.4 + }, + "eoc": [["out_celldevs", "out_sink"]] + }, + "epicenter": { + "state": { + "susceptible": 0.7, + "infected": 0.3 + }, + "cell_map": [[0, 0]] + } + } +} diff --git a/xdevs/examples/celldevs_sir/sir_cell.py b/xdevs/examples/celldevs_sir/sir_cell.py new file mode 100644 index 0000000..46f1a30 --- /dev/null +++ b/xdevs/examples/celldevs_sir/sir_cell.py @@ -0,0 +1,68 @@ +from __future__ import annotations +from typing import Callable, Any +from xdevs.celldevs.cell import S +from xdevs.celldevs.grid import C, GridCell, GridCellConfig +from xdevs.abc.transducer import Transducible, T + + +class State(Transducible): + def __init__(self, population: int, susceptible: float, infected: float, recovered: float): + self.population: int = population + self.susceptible: float = susceptible + self.infected: float = infected + self.recovered: float = recovered + + def __eq__(self, other: State): + return self.population == other.population and self.susceptible == other.susceptible \ + and self.infected == other.infected and self.recovered == other.recovered + + @classmethod + def transducer_map(cls) -> dict[str, tuple[type[T], Callable[[Any], T]]]: + return { + 'population': (int, lambda x: x.population), + 'susceptible': (float, lambda x: x.susceptible), + 'infected': (float, lambda x: x.infected), + 'recovered': (float, lambda x: x.recovered) + } + + +class Vicinity: + def __init__(self, connectivity: float, mobility: float): + self.connectivity: float = connectivity + self.mobility: float = mobility + + @property + def correlation(self) -> float: + return self.connectivity * self.mobility + + +class Config: + def __init__(self, virulence: float, recovery: float): + self.virulence = virulence + self.recovery = recovery + + +class SIRGridCell(GridCell[State, Vicinity]): + def __init__(self, cell_id: C, config: GridCellConfig): + super().__init__(cell_id, config) + self.config: Config = Config(**config.cell_config) + + def local_computation(self, cell_state: S) -> S: + new_infections = self.new_infections(cell_state) + new_recoveries = self.new_recoveries(cell_state) + cell_state.recovered = round((cell_state.recovered + new_recoveries) * 100) / 100 + cell_state.infected = round((cell_state.infected + new_infections - new_recoveries) * 100) / 100 + cell_state.susceptible = 1 - cell_state.infected - cell_state.recovered + return cell_state + + def new_infections(self, state: State) -> float: + neighbor_effect = sum(state.infected * state.population * self.neighborhood[neighbor].correlation + for neighbor, state in self.neighbors_state.items()) + new_infections = state.susceptible * self.config.virulence * neighbor_effect / state.population + return min(state.susceptible, new_infections) + + def new_recoveries(self, state: State) -> float: + return state.infected * self.config.recovery + + def output_delay(self, cell_state: S) -> float: + return 1 diff --git a/xdevs/examples/celldevs_sir/sir_coupled.py b/xdevs/examples/celldevs_sir/sir_coupled.py new file mode 100644 index 0000000..dbac3d1 --- /dev/null +++ b/xdevs/examples/celldevs_sir/sir_coupled.py @@ -0,0 +1,36 @@ +import math + +from xdevs.celldevs.coupled import CoupledGridCellDEVS +from xdevs.celldevs.grid import C, GridCellConfig, GridCell +from xdevs.celldevs.inout import CellMessage +from xdevs.models import Port +from xdevs.sim import Coordinator +from xdevs.factory import Transducer, Transducers +from sir_cell import State, Vicinity, SIRGridCell + + +class SIRGridCoupled(CoupledGridCellDEVS[State, Vicinity]): + def __init__(self, config_file: str): + super().__init__(State, Vicinity, config_file) + self.sink_port = Port(CellMessage, 'out_sink') + self.add_out_port(self.sink_port) + + self.load_config() + self.load_cells() + self.load_couplings() + + def create_cell(self, cell_type: str, cell_id: C, cell_config: GridCellConfig[State, Vicinity]) -> GridCell[State, Vicinity]: + return SIRGridCell(cell_id, cell_config) + + +if __name__ == '__main__': + model = SIRGridCoupled('scenario.json') + + transducer: Transducer = Transducers.create_transducer('csv', transducer_id='transducer', event_type=CellMessage, + include_names=False, exhaustive=True) + transducer.add_target_port(model.sink_port) + + coordinator = Coordinator(model) + coordinator.add_transducer(transducer) + coordinator.initialize() + coordinator.simulate_time(math.inf) diff --git a/xdevs/examples/celldevs_sir/sir_sink.py b/xdevs/examples/celldevs_sir/sir_sink.py new file mode 100644 index 0000000..105cae4 --- /dev/null +++ b/xdevs/examples/celldevs_sir/sir_sink.py @@ -0,0 +1,53 @@ +from typing import Dict, NoReturn, Optional, Tuple +from sir_cell import State +from xdevs.celldevs.inout import CellMessage +from xdevs.models import Atomic, Port + + +class SIRSink(Atomic): + def __init__(self, name: str = None): + super().__init__(name) + self.started: bool = False + self.cell_reports: Dict[Tuple[int, ...], State] = dict() + self.scenario_report: Optional[State] = None + + self.in_sink: Port[CellMessage[Tuple[int, ...], State]] = Port(CellMessage, 'in_sink') + self.out_sink: Port[State] = Port(State, 'out_sink') + self.add_in_port(self.in_sink) + self.add_out_port(self.out_sink) + + def deltint(self) -> NoReturn: + self.passivate() + self.started = True + + def deltext(self, e: float) -> NoReturn: + self.activate() + for msg in self.in_sink.values: + if self.started: + prev_report = self.cell_reports[msg.cell_id] + delta_s = msg.cell_state.susceptible - prev_report.susceptible + delta_i = msg.cell_state.infected - prev_report.infected + delta_r = msg.cell_state.recovered - prev_report.recovered + self.scenario_report.susceptible += delta_s * prev_report.population / self.scenario_report.population + self.scenario_report.infected += delta_i * prev_report.population / self.scenario_report.population + self.scenario_report.recovered += delta_r * prev_report.population / self.scenario_report.population + self.cell_reports[msg.cell_id] = msg.cell_state + if not self.started: + self.scenario_report: State = State(0, 0, 0, 0) + for cell_state in self.cell_reports.values(): + self.scenario_report.population += cell_state.population + self.scenario_report.susceptible += cell_state.population * cell_state.susceptible + self.scenario_report.infected += cell_state.population * cell_state.infected + self.scenario_report.recovered += cell_state.population * cell_state.recovered + self.scenario_report.susceptible /= self.scenario_report.population + self.scenario_report.infected /= self.scenario_report.population + self.scenario_report.recovered /= self.scenario_report.population + + def lambdaf(self) -> NoReturn: + self.out_sink.add(self.scenario_report) + + def initialize(self) -> NoReturn: + self.passivate() + + def exit(self) -> NoReturn: + pass diff --git a/xdevs/factory.py b/xdevs/factory.py index 49690c6..3ec298b 100644 --- a/xdevs/factory.py +++ b/xdevs/factory.py @@ -3,7 +3,8 @@ import sys from importlib.metadata import entry_points, EntryPoint from typing import ClassVar -from xdevs.abc import InputHandler, OutputHandler, Transducer +from xdevs.abc import InputHandler, OutputHandler, Transducer, DelayedOutput +from xdevs.celldevs import C from xdevs.models import Atomic, Component, Port, Coupled @@ -244,3 +245,22 @@ def from_json(file_path: str): config = data[name] # Gets the actual component config return Components._nested_component(name, config) + + +class DelayedOutputs: + + _plugins: ClassVar[dict[str, type[DelayedOutput]]] = { + ep.name: ep.load() for ep in load_entry_points('xdevs.celldevs_outputs') + } + + @staticmethod + def add_plugin(delay_id: str, plugin: type[DelayedOutput]): + if delay_id in DelayedOutputs._plugins: + raise ValueError('xDEVS Cell-DEVS delayed output plugin with name "{}" already exists'.format(delay_id)) + DelayedOutputs._plugins[delay_id] = plugin + + @staticmethod + def create_delayed_output(delay_id: str, cell_id: C, serve: bool = False) -> DelayedOutput: + if delay_id not in DelayedOutputs._plugins: + raise ValueError('xDEVS Cell-DEVS delayed output plugin with name "{}" not found'.format(delay_id)) + return DelayedOutputs._plugins[delay_id](cell_id, serve) diff --git a/xdevs/plugins/celldevs_outputs/__init__.py b/xdevs/plugins/celldevs_outputs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xdevs/plugins/celldevs_outputs/hybrid.py b/xdevs/plugins/celldevs_outputs/hybrid.py new file mode 100644 index 0000000..85c8d57 --- /dev/null +++ b/xdevs/plugins/celldevs_outputs/hybrid.py @@ -0,0 +1,26 @@ +from __future__ import annotations +from collections import deque +from typing import Deque, Generic +from xdevs.abc.celldevs import C, S, DelayedOutput, INFINITY + + +class HybridDelayedOutput(DelayedOutput[C, S], Generic[C, S]): + def __init__(self, cell_id: C, serve: bool = False): + super().__init__(cell_id, serve) + self.last_state: S | None = None + self.next_states: Deque[tuple[float, S]] = deque() + + def add_to_buffer(self, when: float, state: S): + while self.next_states and self.next_states[-1][0] >= when: + self.next_states.pop() + self.next_states.append((when, state)) + + def next_time(self) -> float: + return INFINITY if not self.next_states else self.next_states[0][0] + + def next_state(self) -> S: + return self.last_state if not self.next_states else self.next_states[0][1] + + def pop_state(self): + if self.next_states: + self.last_state = self.next_states.popleft()[1] diff --git a/xdevs/plugins/celldevs_outputs/inertial.py b/xdevs/plugins/celldevs_outputs/inertial.py new file mode 100644 index 0000000..34f7808 --- /dev/null +++ b/xdevs/plugins/celldevs_outputs/inertial.py @@ -0,0 +1,22 @@ +from __future__ import annotations +from typing import Generic +from xdevs.abc.celldevs import C, S, DelayedOutput, INFINITY + + +class InertialDelayedOutput(DelayedOutput[C, S], Generic[C, S]): + def __init__(self, cell_id: C, serve: bool = False): + super().__init__(cell_id, serve) + self.last_state: S | None = None + self.next_t: float = INFINITY + + def add_to_buffer(self, when: float, state: S): + self.next_t, self.last_state = when, state + + def next_time(self) -> float: + return self.next_t + + def next_state(self) -> S: + return self.last_state + + def pop_state(self): + self.next_t = INFINITY diff --git a/xdevs/plugins/celldevs_outputs/transport.py b/xdevs/plugins/celldevs_outputs/transport.py new file mode 100644 index 0000000..acdc285 --- /dev/null +++ b/xdevs/plugins/celldevs_outputs/transport.py @@ -0,0 +1,27 @@ +from __future__ import annotations +from typing import Generic +from queue import PriorityQueue +from xdevs.abc.celldevs import C, S, DelayedOutput, INFINITY + + +class TransportDelayedOutput(DelayedOutput[C, S], Generic[C, S]): + def __init__(self, cell_id: C, serve: bool = False): + super().__init__(cell_id, serve) + self.last_state: S | None = None + self.schedule: PriorityQueue = PriorityQueue() + self.next_states: dict[float, S] = dict() + + def add_to_buffer(self, when: float, state: S): + if when not in self.next_states: + self.schedule.put(when) + self.next_states[when] = state + + def next_time(self) -> float: + return self.schedule.queue[0] if self.next_states else INFINITY + + def next_state(self) -> S: + return self.next_states[self.schedule.queue[0]] if self.next_states else self.last_state + + def pop_state(self): + if not self.schedule.empty(): + self.last_state = self.next_states.pop(self.schedule.get()) From c6c38ae7571197aa4b470e8fa7ada129096d4ed5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Tue, 25 Jun 2024 15:57:28 +0200 Subject: [PATCH 49/60] Fix Processor model --- xdevs/examples/gpt/gpt.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xdevs/examples/gpt/gpt.py b/xdevs/examples/gpt/gpt.py index e46bba6..2324d0d 100644 --- a/xdevs/examples/gpt/gpt.py +++ b/xdevs/examples/gpt/gpt.py @@ -94,7 +94,8 @@ def deltext(self, e): if self.phase == PHASE_PASSIVE: self.current_job = self.i_in.get() self.hold_in(PHASE_ACTIVE, self.proc_t) - self.continuef(e) + else: + self.continuef(e) def lambdaf(self): self.o_out.add(self.current_job) @@ -195,4 +196,4 @@ def __init__(self, name: str, gen_t: float, proc_t: float, obs_t: float): gpt = Gpt("gpt", 3, 5, 100) coord = Coordinator(gpt) coord.initialize() - coord.simulate_iters() + coord.simulate_time() From 7ad49b96c3bae5965f852ebaba6922f1b4abf33f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Tue, 25 Jun 2024 17:47:42 +0200 Subject: [PATCH 50/60] Fix transducible inheritance in Cell-DEVS messages --- xdevs/celldevs/inout.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/xdevs/celldevs/inout.py b/xdevs/celldevs/inout.py index c15faa0..ec71bbb 100644 --- a/xdevs/celldevs/inout.py +++ b/xdevs/celldevs/inout.py @@ -18,12 +18,10 @@ def transducer_map(cls) -> dict[str, tuple[Type[T], Callable[[Any], T]]]: if issubclass(cls.state_t, Transducible): res = {'cell_id': (str, lambda x: x.cell_id)} for field, (t, l) in cls.state_t.transducer_map().items(): - res[field] = (t, lambda x: l(x.cell_state)) + # f is a fake lambda input parameter to capture the current value of l + # We need this to avoid the late binding problem in lambda functions + res[field] = (t, lambda x, f=l: f(x.cell_state)) return res - # return { - # 'cell_id': (str, lambda x: x.cell_id), - # **{field: (t, lambda x: l(x.cell_state)) for field, (t, l) in cls.state_t.transducer_map().items()} - #} return { 'cell_id': (str, lambda x: x.cell_id), 'cell_state': (str, lambda x: x.cell_state), From b9055be8e8c9f1a379153e9801a101fc908d5fd3 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Wed, 26 Jun 2024 14:39:07 +0200 Subject: [PATCH 51/60] GPT example doc --- pyproject.toml | 12 +-- xdevs/examples/gpt/README.md | 93 ++++++++++++++++++++++++ xdevs/examples/gpt/efp.py | 44 +---------- xdevs/examples/gpt/gpt_rt_ih_oh_sim.py | 18 +++++ xdevs/examples/gpt/gpt_rt_ih_sim.py | 16 ++++ xdevs/examples/gpt/gpt_rt_oh_sim.py | 12 +++ xdevs/examples/gpt/gpt_rt_sim.py | 8 ++ xdevs/examples/gpt/gpt_v_sim.py | 10 +++ xdevs/examples/gpt/{gpt.py => models.py} | 78 ++++++++++++++++++-- 9 files changed, 237 insertions(+), 54 deletions(-) create mode 100644 xdevs/examples/gpt/README.md create mode 100644 xdevs/examples/gpt/gpt_rt_ih_oh_sim.py create mode 100644 xdevs/examples/gpt/gpt_rt_ih_sim.py create mode 100644 xdevs/examples/gpt/gpt_rt_oh_sim.py create mode 100644 xdevs/examples/gpt/gpt_rt_sim.py create mode 100644 xdevs/examples/gpt/gpt_v_sim.py rename xdevs/examples/gpt/{gpt.py => models.py} (70%) diff --git a/pyproject.toml b/pyproject.toml index 2d6b8ba..4ecf1d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,12 +49,12 @@ 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" +generator = "xdevs.examples.gpt.models:Generator" +transducer = "xdevs.examples.gpt.models:Transducer" +processor = "xdevs.examples.gpt.models:Processor" +gpt = "xdevs.examples.gpt.models:Gpt" +ef = "xdevs.examples.gpt.models:Ef" +efp = "xdevs.examples.gpt.models:Efp" [project.entry-points."xdevs.wrappers"] pypdevs = "xdevs.plugins.wrappers.pypdevs:PyPDEVSWrapper" diff --git a/xdevs/examples/gpt/README.md b/xdevs/examples/gpt/README.md new file mode 100644 index 0000000..09fee53 --- /dev/null +++ b/xdevs/examples/gpt/README.md @@ -0,0 +1,93 @@ +# GPT examples + +This folder stores several examples in order to illustrate some of the possibilities offered by the repository. + + +## `xDEVS.py` Virtual Simulation + +A virtual simulation is carried out only taking into account the virtual environment. + +### Example: + + +```bash +$ cd xdevs/examples/gpt +$ python3 gpt_v_sim.py +``` + +## `xDEVS` Real-Time Simulation + +This section aims to show a collection of examples based on the methodology followed in this repository for achieving the wall-clock behaviour. + +### Examples: + +1. #### No handlers real-time simulation + +```bash +$ cd xdevs/examples/gpt +$ python3 gpt_rt_sim.py +``` +2. #### Input handler real-time simulation + +```bash +$ cd xdevs/examples/gpt +$ python3 gpt_rt_ih_sim.py +``` +However, in order to be able to completely understand the system you must execute a TCP client to send the input events to the model. +The following code, show a basic example of a TCP client that sends an input event to the model. +It is important to keep in mind that the expect message of the TCP Input handler is `Port_name,message` as it is specified in `xdevs.plugins.input_handlers.tcp` +```bash +import socket + +HOST = 'LocalHost' +PORT = 4321 + +c = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # AF_INET: IPv4, SOCK_STREAM: TCP + +c.connect((HOST,PORT)) + +c.sendall('ih_in,TCP'.encode()) # The data is 'port_name,msg' +``` + +3. #### Output handler real-time simulation + +```bash +$ cd xdevs/examples/gpt +$ python3 gpt_rt_oh_sim.py +``` +In order to capture the outgoing events of the model, you must execute a TCP server to receive the output events. +A basic TCP server is shown below: +```bash +import socket + +HOST = 'LocalHost' +PORT = 4321 + +s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # AF_INET: IPv4, SOCK_STREAM: TCP + +s.bind((HOST,PORT)) +s.listen() + +s_con, add = s.accept() +print(f'Connected to: {add}') +while True: + try: + data = s_con.recv(1024) # 1024 is the buffer size + data = data.decode() + if not data: + print(f'Client disconnected: {add}') + break + print(f'data received is: {data}') + except ConnectionResetError: + print(f'Client closed unexpectedly: {add}') + break +```` + +4. #### Input and Output handlers real-time simulation + +````bash +$ cd xdevs/examples/gpt +$ python3 gpt_rt_ih_oh_sim.py +```` +If you want to test the system properly, you will have to execute the server TCP first, then run the example and later +execute the client TCP to send the input events to the model. \ No newline at end of file diff --git a/xdevs/examples/gpt/efp.py b/xdevs/examples/gpt/efp.py index 87421ff..67dbda2 100644 --- a/xdevs/examples/gpt/efp.py +++ b/xdevs/examples/gpt/efp.py @@ -1,47 +1,9 @@ -from xdevs.examples.gpt.gpt import Generator, Transducer, Job, Processor -from xdevs.models import Coupled, Port - - -class Ef(Coupled): - def __init__(self, name: str, gen_t: float, obs_t: float): - super().__init__(name) - - gen = Generator('generator', gen_t) - trans = Transducer('transducer', obs_t) - - self.add_component(gen) - self.add_component(trans) - - self.p_in_ef = Port(Job, name='p_in_ef') - self.p_out_ef = Port(Job, name='p_out_ef') - - self.add_in_port(self.p_in_ef) - self.add_out_port(self.p_out_ef) - - self.add_coupling(gen.o_job, trans.i_arrived) - self.add_coupling(gen.o_job, self.p_out_ef) - self.add_coupling(trans.o_out, gen.i_stop) - self.add_coupling(self.p_in_ef, trans.i_solved) - - -class Efp(Coupled): - def __init__(self, name: str, gen_t: float, proc_t: float, obs_t: float): - super().__init__(name) - - ef = Ef('ef', gen_t, obs_t) - proc = Processor('processor', proc_t) - - self.add_component(ef) - self.add_component(proc) - - self.add_coupling(ef.p_out_ef, proc.i_in) - self.add_coupling(proc.o_out, ef.p_in_ef) - +from xdevs.sim import Coordinator +from xdevs.examples.gpt.models import Efp if __name__ == '__main__': - from xdevs.sim import Coordinator efp = Efp('efp', 3, 5, 100) coord = Coordinator(efp) coord.initialize() - coord.simulate_iters() + coord.simulate() diff --git a/xdevs/examples/gpt/gpt_rt_ih_oh_sim.py b/xdevs/examples/gpt/gpt_rt_ih_oh_sim.py new file mode 100644 index 0000000..1e626df --- /dev/null +++ b/xdevs/examples/gpt/gpt_rt_ih_oh_sim.py @@ -0,0 +1,18 @@ +from xdevs.rt import RealTimeCoordinator, RealTimeManager +from xdevs.examples.gpt.models import GptIHOH, Job + +if __name__ == '__main__': + gpt = GptIHOH("gpt", 20, 1, 100) + manager = RealTimeManager(max_jitter=0.02,time_scale=1,event_window=.05) + + # The InputHandler under study will be a TCP one + # msg_parser: How must the arrived messages adapt to the system, in this case they are converted into Jobs named + # after the receiving message + msg_parser = {"ih_in" : lambda x : Job(str(x))} + # We pass the identifier and the required arguments for the TCP handler + manager.add_input_handler("tcp", port=4321, msg_parsers=msg_parser) + + manager.add_output_handler('tcp', port=1234) + + coord = RealTimeCoordinator(gpt, manager) + coord.simulate_rt() \ No newline at end of file diff --git a/xdevs/examples/gpt/gpt_rt_ih_sim.py b/xdevs/examples/gpt/gpt_rt_ih_sim.py new file mode 100644 index 0000000..2a957ab --- /dev/null +++ b/xdevs/examples/gpt/gpt_rt_ih_sim.py @@ -0,0 +1,16 @@ +from xdevs.rt import RealTimeCoordinator, RealTimeManager +from xdevs.examples.gpt.models import GptIHOH, Job + +if __name__ == '__main__': + gpt = GptIHOH("gpt", 20, 1, 100) + manager = RealTimeManager(max_jitter=0.02,time_scale=1,event_window=.05) + + # The InputHandler under study will be a TCP one + # msg_parser: How must the arrived messages adapt to the system, in this case they are converted into Jobs named + # after the receiving message + msg_parser = {"ih_in" : lambda x : Job(str(x))} + # We pass the identifier and the required arguments for the TCP handler + manager.add_input_handler("tcp", port=4321, msg_parsers=msg_parser) + + coord = RealTimeCoordinator(gpt, manager) + coord.simulate_rt() diff --git a/xdevs/examples/gpt/gpt_rt_oh_sim.py b/xdevs/examples/gpt/gpt_rt_oh_sim.py new file mode 100644 index 0000000..87a9d64 --- /dev/null +++ b/xdevs/examples/gpt/gpt_rt_oh_sim.py @@ -0,0 +1,12 @@ +from xdevs.rt import RealTimeCoordinator, RealTimeManager +from xdevs.examples.gpt.models import GptIHOH, Job + +if __name__ == '__main__': + gpt = GptIHOH("gpt", 5, 3, 100) + manager = RealTimeManager(max_jitter=0.02,time_scale=1,event_window=.05) + + manager.add_output_handler('tcp',port=4321) + + coord = RealTimeCoordinator(gpt, manager) + coord.simulate_rt() + diff --git a/xdevs/examples/gpt/gpt_rt_sim.py b/xdevs/examples/gpt/gpt_rt_sim.py new file mode 100644 index 0000000..2c98c77 --- /dev/null +++ b/xdevs/examples/gpt/gpt_rt_sim.py @@ -0,0 +1,8 @@ +from xdevs.rt import RealTimeCoordinator, RealTimeManager +from xdevs.examples.gpt.models import Gpt + +if __name__ == '__main__': + gpt = Gpt("gpt", 2, 7, 100) + manager = RealTimeManager(0.02,1,0.05) + coordinator = RealTimeCoordinator(gpt, manager) + coordinator.simulate_rt(100) diff --git a/xdevs/examples/gpt/gpt_v_sim.py b/xdevs/examples/gpt/gpt_v_sim.py new file mode 100644 index 0000000..27801de --- /dev/null +++ b/xdevs/examples/gpt/gpt_v_sim.py @@ -0,0 +1,10 @@ +from xdevs.sim import Coordinator +from xdevs.examples.gpt.models import Gpt + +if __name__ == '__main__': + + gpt = Gpt("gpt", 3, 5, 100) + coord = Coordinator(gpt) + coord.initialize() + coord.simulate() + diff --git a/xdevs/examples/gpt/gpt.py b/xdevs/examples/gpt/models.py similarity index 70% rename from xdevs/examples/gpt/gpt.py rename to xdevs/examples/gpt/models.py index 2324d0d..a0000fd 100644 --- a/xdevs/examples/gpt/gpt.py +++ b/xdevs/examples/gpt/models.py @@ -16,7 +16,8 @@ def __init__(self, name: str): """ self.name: str = name self.time: float = 0 - + def __str__(self): + return self.name class Generator(Atomic): def __init__(self, name: str, gen_t: float): @@ -171,7 +172,40 @@ def lambdaf(self): if self.phase == PHASE_DONE: self.o_out.add(True) +class Ef(Coupled): + def __init__(self, name: str, gen_t: float, obs_t: float): + super().__init__(name) + + gen = Generator('generator', gen_t) + trans = Transducer('transducer', obs_t) + + self.add_component(gen) + self.add_component(trans) + + self.p_in_ef = Port(Job, name='p_in_ef') + self.p_out_ef = Port(Job, name='p_out_ef') + + self.add_in_port(self.p_in_ef) + self.add_out_port(self.p_out_ef) + + self.add_coupling(gen.o_job, trans.i_arrived) + self.add_coupling(gen.o_job, self.p_out_ef) + self.add_coupling(trans.o_out, gen.i_stop) + self.add_coupling(self.p_in_ef, trans.i_solved) + + +class Efp(Coupled): + def __init__(self, name: str, gen_t: float, proc_t: float, obs_t: float): + super().__init__(name) + ef = Ef('ef', gen_t, obs_t) + proc = Processor('processor', proc_t) + + self.add_component(ef) + self.add_component(proc) + + self.add_coupling(ef.p_out_ef, proc.i_in) + self.add_coupling(proc.o_out, ef.p_in_ef) class Gpt(Coupled): def __init__(self, name: str, gen_t: float, proc_t: float, obs_t: float): super().__init__(name) @@ -189,11 +223,41 @@ def __init__(self, name: str, gen_t: float, proc_t: float, obs_t: float): self.add_coupling(proc.o_out, trans.i_solved) self.add_coupling(trans.o_out, gen.i_stop) +class GptIHOH(Coupled): + + # Adaptation of the GPT DEVS model for injecting events via a new input port and for ejection of events via a new + # output port. + + def __init__(self, name: str, gen_t: float, proc_t: float, obs_t: float): + super().__init__(name) + + gen = Generator('generator', gen_t) + proc = Processor('processor', proc_t) + trans = Transducer('transducer', obs_t) + + # New input handler port + self.ih_in = Port(Job, name='ih_in') + self.add_in_port(self.ih_in) + + # New output handler port + self.oh_out = Port(Job, name='ih_in') + self.add_out_port(self.oh_out) + + self.add_component(gen) + self.add_component(proc) + self.add_component(trans) + + # New coupling for the input handler + self.add_coupling(self.ih_in, proc.i_in) + + # New coupling for the output handler + self.add_coupling(proc.o_out, self.oh_out) + + self.add_coupling(gen.o_job, proc.i_in) + self.add_coupling(gen.o_job, trans.i_arrived) + self.add_coupling(proc.o_out, trans.i_solved) + self.add_coupling(trans.o_out, gen.i_stop) + + -if __name__ == '__main__': - from xdevs.sim import Coordinator - gpt = Gpt("gpt", 3, 5, 100) - coord = Coordinator(gpt) - coord.initialize() - coord.simulate_time() From eeeb767930dddb147efa2f520e297af82c064f2a Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Wed, 26 Jun 2024 17:19:51 +0200 Subject: [PATCH 52/60] Renamed RTCoord simulate_rt method and refractor --- xdevs/examples/store/2_rt_simulation_csv_output_handler.py | 2 +- xdevs/examples/store/3_rt_simulation_tcp_input_handler.py | 2 +- xdevs/examples/store/4_1_rt_simulation_mqtt_input_handler.py | 2 +- xdevs/examples/store/4_2_rt_simulation_mqtt_output_handler.py | 2 +- xdevs/examples/store/system_clients.py | 2 +- xdevs/examples/store/system_employees.py | 2 +- xdevs/examples/store/system_queue.py | 2 +- xdevs/rt.py | 2 +- xdevs/tests/test_csv_transducer.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/xdevs/examples/store/2_rt_simulation_csv_output_handler.py b/xdevs/examples/store/2_rt_simulation_csv_output_handler.py index 882a046..88b436f 100644 --- a/xdevs/examples/store/2_rt_simulation_csv_output_handler.py +++ b/xdevs/examples/store/2_rt_simulation_csv_output_handler.py @@ -53,7 +53,7 @@ def get_sec(time_str): c = RealTimeCoordinator(store, rt_manager) middle = time.time() print(f'Coordinator, Manager and Handlers Created. Elapsed time: {middle - start} sec') - c.simulate_iters(time_interv=sim_time) + c.simulate_rt(time_interv=sim_time) end = time.time() print(f'Simulation time (s) = {sim_time}') print(f'Simulation took: {end - start} sec') diff --git a/xdevs/examples/store/3_rt_simulation_tcp_input_handler.py b/xdevs/examples/store/3_rt_simulation_tcp_input_handler.py index 4780d4f..21ac052 100644 --- a/xdevs/examples/store/3_rt_simulation_tcp_input_handler.py +++ b/xdevs/examples/store/3_rt_simulation_tcp_input_handler.py @@ -66,7 +66,7 @@ def new_client_parser(msg: str): c = RealTimeCoordinator(store, rt_manager) middle = time.time() print(f'Coordinator, Manager and Handlers Created. Elapsed time: {middle - start} sec') - c.simulate_iters(time_interv=sim_time) + c.simulate_rt(time_interv=sim_time) end = time.time() print(f'Simulation time (s) = {sim_time}') print(f'Simulation took: {end - start} sec') diff --git a/xdevs/examples/store/4_1_rt_simulation_mqtt_input_handler.py b/xdevs/examples/store/4_1_rt_simulation_mqtt_input_handler.py index 92f3eea..8703834 100644 --- a/xdevs/examples/store/4_1_rt_simulation_mqtt_input_handler.py +++ b/xdevs/examples/store/4_1_rt_simulation_mqtt_input_handler.py @@ -61,7 +61,7 @@ def mqtt_parser(msg: str): middle = time.time() print(f"Coordinator and Manager Created. Elapsed time: {middle - start} sec") t_ini = time.time() - c.simulate_iters(time_interv=sim_time) + c.simulate_rt(time_interv=sim_time) end = time.time() print(f' Simulation time (s) = {sim_time}') print(f"Simulation took: {end - start} sec") diff --git a/xdevs/examples/store/4_2_rt_simulation_mqtt_output_handler.py b/xdevs/examples/store/4_2_rt_simulation_mqtt_output_handler.py index 23b0414..1e9689e 100644 --- a/xdevs/examples/store/4_2_rt_simulation_mqtt_output_handler.py +++ b/xdevs/examples/store/4_2_rt_simulation_mqtt_output_handler.py @@ -70,7 +70,7 @@ def get_sec(time_str): c = RealTimeCoordinator(gensys, rt_manager) middle = time.time() print("Coordinator and Manager Created. Elapsed time: {} sec".format(middle - start)) - c.simulate_iters(time_interv=sim_time) + c.simulate_rt(time_interv=sim_time) end = time.time() print(f' Simulation time (s) = {sim_time}') print("Simulation took: {} sec".format(end - start)) diff --git a/xdevs/examples/store/system_clients.py b/xdevs/examples/store/system_clients.py index d6e4ffe..c62bb32 100644 --- a/xdevs/examples/store/system_clients.py +++ b/xdevs/examples/store/system_clients.py @@ -33,7 +33,7 @@ def __init__(self, mean_clients: float = 1, stddev_clients: float =0, name=None t_ini = time.time() print(f' >>> COMENZAMOS : {t_ini}') - gen_coord.simulate_iters(time_interv=sim_time) + gen_coord.simulate_rt(time_interv=sim_time) print(f' >>> FIN : {time.time()}') print(f' Tiempo a ejecutar (s) = {sim_time }') print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') diff --git a/xdevs/examples/store/system_employees.py b/xdevs/examples/store/system_employees.py index 8f5bb23..95eaad3 100644 --- a/xdevs/examples/store/system_employees.py +++ b/xdevs/examples/store/system_employees.py @@ -73,7 +73,7 @@ def input_client_parser(msg: str): t_ini = time.time() print(f' >>> COMENZAMOS : {t_ini} : {datetime.datetime.now()}') - e_coord.simulate_iters(time_interv=sim_time) + e_coord.simulate_rt(time_interv=sim_time) print(f' >>> FIN : {time.time()}') print(f' Tiempo a ejecutar (s) = {sim_time}') print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') diff --git a/xdevs/examples/store/system_queue.py b/xdevs/examples/store/system_queue.py index 8a239a6..4827900 100644 --- a/xdevs/examples/store/system_queue.py +++ b/xdevs/examples/store/system_queue.py @@ -65,7 +65,7 @@ def parser_new_client(msg: str): t_ini = time.time() print(f' >>> COMENZAMOS : {t_ini}') - q_coord.simulate_iters(time_interv=sim_time) + q_coord.simulate_rt(time_interv=sim_time) print(f' >>> FIN : {time.time()}') print(f' Tiempo a ejecutar (s) = {sim_time}') print(f' Tiempo ejecutado (s) = {(time.time() - t_ini)}') diff --git a/xdevs/rt.py b/xdevs/rt.py index e800e97..925bd81 100644 --- a/xdevs/rt.py +++ b/xdevs/rt.py @@ -142,7 +142,7 @@ def exit(self): self.manager.exit(self.clock.time) super().exit() - def simulate_iters(self, time_interv: float = float("inf")): + def simulate_rt(self, time_interv: float = float("inf")): self.initialize() while self.clock.time < time_interv: if self.time_next == float("inf") and not self.manager.input_handlers: diff --git a/xdevs/tests/test_csv_transducer.py b/xdevs/tests/test_csv_transducer.py index 795166a..293fbb2 100644 --- a/xdevs/tests/test_csv_transducer.py +++ b/xdevs/tests/test_csv_transducer.py @@ -7,7 +7,7 @@ from xdevs.examples.devstone.devstone import LI, DelayedAtomic, HI from xdevs.factory import Transducers -from xdevs.examples.gpt.gpt import Job, Processor, Gpt +from xdevs.examples.gpt.models import Job, Processor, Gpt class TestCsvTransducer(TestCase): From d8b435651c4b6a10f0b4ecc0bd8e63b6ea7102ec Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Thu, 27 Jun 2024 11:30:19 +0200 Subject: [PATCH 53/60] Fixed Tests --- xdevs/examples/gpt/models.py | 5 +++-- xdevs/plugins/transducers/csv.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/xdevs/examples/gpt/models.py b/xdevs/examples/gpt/models.py index a0000fd..3433493 100644 --- a/xdevs/examples/gpt/models.py +++ b/xdevs/examples/gpt/models.py @@ -14,11 +14,12 @@ def __init__(self, name: str): Job event class. It represents a job sent by the generator and processed by the processor. :param name: job name """ - self.name: str = name + self.name: str = str(name) self.time: float = 0 def __str__(self): return self.name + class Generator(Atomic): def __init__(self, name: str, gen_t: float): """ @@ -240,7 +241,7 @@ def __init__(self, name: str, gen_t: float, proc_t: float, obs_t: float): self.add_in_port(self.ih_in) # New output handler port - self.oh_out = Port(Job, name='ih_in') + self.oh_out = Port(Job, name='oh_out') self.add_out_port(self.oh_out) self.add_component(gen) diff --git a/xdevs/plugins/transducers/csv.py b/xdevs/plugins/transducers/csv.py index aa2bdc2..1a569bf 100644 --- a/xdevs/plugins/transducers/csv.py +++ b/xdevs/plugins/transducers/csv.py @@ -50,6 +50,7 @@ def bulk_data(self, sim_time: float): for state_insert in self._iterate_state_inserts(sim_time): self.state_csv_writer.writerow([state_insert[field] for field in self.state_header]) + for event_insert in self._iterate_event_inserts(sim_time): self.event_csv_writer.writerow([event_insert[field] for field in self.event_header]) @@ -58,7 +59,7 @@ def _create_csv_file(self, filename: str, header: list[str]): os.makedirs(os.path.dirname(filename), exist_ok=True) # 3. Create output CSV file and write the header row. - csv_file = open(filename, 'w') + csv_file = open(filename,newline='', mode='w') writer = csv.writer(csv_file, delimiter=self.delimiter) writer.writerow(header) From 7be80fde1b4a82bb57421c203731128952b4efa6 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Thu, 27 Jun 2024 18:01:50 +0200 Subject: [PATCH 54/60] README for abc and json --- xdevs/abc/README.md | 24 +++++++++ xdevs/examples/json/README.md | 91 +++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 xdevs/abc/README.md create mode 100644 xdevs/examples/json/README.md diff --git a/xdevs/abc/README.md b/xdevs/abc/README.md new file mode 100644 index 0000000..42cd9cc --- /dev/null +++ b/xdevs/abc/README.md @@ -0,0 +1,24 @@ +# What is a Factory + +A factory is a software methodology for designing and implementing objects that have a common behavior but several possible implementations. This type of methodology is widely used in this repository for creating different components in the simulations such as transducers, handlers, models, etc. + +## Example 1 +_(Focused on handlers, transducer and celldevs)_ + +1. Firstly, a father abstract class is defined, which states the common behavior of the component under developing. (The class `InputHandler` may be found in `xdevs.abc.handler`) +2. A child class that inherits the behavior of its father defines the particular implementation. (The folder `xdevs.plugins.input_handlers.` stores several implementations for this class) +3. An entry point is defined. This name will play the role of a key that links the name to the desired implementation. (In the script `xdevs.pyproject`, the keys for Input Handlers are found after the `[project.entry-points."xdevs.input_handlers"]` statement) +4. Creating an instance of each implementation is as easy as passing to a method of the factory class the desired key and the required parameters (if any) for that specific implementation (Using the class `InputHandlers` in `xdevs.factory` and calling the method `create_input_handler`). + +## Example 2 + +_(Focused on `JSON` to `DEVS` components)_ + +In case of the `JSON` to `DEVS` model conversion, the factory methodology is used to create the components defined in the `JSON` file. The `Factory` class is in charge of creating the different types of implementations of an abstract class based on a key that is linked to the desired implementation. + +1. The `DEVS` model must be defined (i.e. `EFP` in `xdevs.examples.gpt.models`). +2. The entry point must be created in pyproject.toml after `[project.entry-points."xdevs.components"]` + +With this methodology, adding several instances of a component for a range of simulations is easier. +Defining a key that links to the desired implementation allows for a more straightforward way to create the components +and avoids having to create and define each component for each simulation. \ No newline at end of file diff --git a/xdevs/examples/json/README.md b/xdevs/examples/json/README.md new file mode 100644 index 0000000..815b9ae --- /dev/null +++ b/xdevs/examples/json/README.md @@ -0,0 +1,91 @@ +# JSON to `xDEVS` Simulations + +The `json` folder contains two examples of how to generate a `DEVS` model using a `JSON` file, these examples are the `efp.json` and `gpt.json`. + +Next, a concise guide on using the `from_json` method from `xdevs.factory` to parse a `JSON` file into a `DEVS` model is presented. +For detailed information, please refer to the method's documentation. + +## Overview + +The `from_json` method allows you to parse a `JSON` file into a `DEVS` model. The `JSON` file must follow specific rules to correctly define components and their couplings. + +**ATTENTION PLEASE** ❗❗ + + Take into account that the `component_id` inside the `JSON` file must be identified as an `entry-point` of `Components` in `xdevs.factory `. (See the `Factory` section in `xdevs.abc` for more information). + + +## JSON Structure + +### Master Component + +The top-level JSON object represents the master component. + +```json +{ + "MasterComponentName": { + "components": { + // Nested components + }, + "couplings": [ + // List of couplings + ] + } +} +``` + +### Components + +Components can be either already registered in the `entry-points` or couple: + +* Coupled: Contains `components` and `couplings` keys. +* Component: Identified by the `component_id` key. It must be already defined in the `entry-points`. + + +```json +"components": { + "CoupledModel1": { + "components": { + // Nested components + }, + "couplings": [ + // List of connection dictionaries + ] + }, + "Component2": { + "component_id": "ID_from_factory", + "args": [/* positional arguments */], + "kwargs": { + "a_parameter": "value", + // Other keyword arguments + } + } + // Additional components +} + +``` + +### Couplings +Couplings define connections between components: + +* IC (Internal Coupling): Both componentFrom and componentTo are specified. +* EIC (External Input Coupling): componentFrom is missing. (A new input port is created) +* EOC (External Output Coupling): componentTo is missing. (A new output port is created) + +```json +"couplings": [ + { + "componentFrom": "Model1", + "portFrom": "PortA", // Port name defined in Model1 + "componentTo": "Model2", + "portTo": "PortB" + } + // Additional couplings +] +``` + +## Example + +```bash +$ cd xdevs/examples/json +$ python3 main.py +``` From 95838c19e71bd2d2d94b3f6f73fba5c2877f1beb Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Thu, 27 Jun 2024 18:14:52 +0200 Subject: [PATCH 55/60] Renamed json example --- xdevs/examples/json/README.md | 2 +- xdevs/examples/json/json2xdevs_example.py | 21 +++++++++++++++++++++ xdevs/examples/json/main.py | 12 ------------ 3 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 xdevs/examples/json/json2xdevs_example.py delete mode 100644 xdevs/examples/json/main.py diff --git a/xdevs/examples/json/README.md b/xdevs/examples/json/README.md index 815b9ae..8b94e12 100644 --- a/xdevs/examples/json/README.md +++ b/xdevs/examples/json/README.md @@ -87,5 +87,5 @@ Couplings define connections between components: ```bash $ cd xdevs/examples/json -$ python3 main.py +$ python3 json2xdevs_example.py ``` diff --git a/xdevs/examples/json/json2xdevs_example.py b/xdevs/examples/json/json2xdevs_example.py new file mode 100644 index 0000000..a637e40 --- /dev/null +++ b/xdevs/examples/json/json2xdevs_example.py @@ -0,0 +1,21 @@ +import sys + +from xdevs.rt import RealTimeCoordinator, RealTimeManager +from xdevs.factory import Components + + +if __name__ == '__main__': + file_path = sys.argv[1] if len(sys.argv) > 1 else 'gpt.json' + + model = Components.from_json(file_path) + """ + # Virtual simulation + coord_v_sim = Coordinator(model) + coord_v_sim.initialize() + coord_v_sim.simulate() + """ + # Wall-clock simulation + m_rt = RealTimeManager(0.2,1) + coord_rt_sim = RealTimeCoordinator(model, m_rt) + coord_rt_sim.simulate_rt() + diff --git a/xdevs/examples/json/main.py b/xdevs/examples/json/main.py deleted file mode 100644 index f713acf..0000000 --- a/xdevs/examples/json/main.py +++ /dev/null @@ -1,12 +0,0 @@ -import sys -from xdevs.sim import Coordinator -from xdevs.factory import Components - - -if __name__ == '__main__': - file_path = sys.argv[1] if len(sys.argv) > 1 else 'gpt.json' - - component = Components.from_json(file_path) - coord = Coordinator(component) - coord.initialize() - coord.simulate_iters() From b139a1f6a2c9e69a470c865c4d8d64a396cf7b3b Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Thu, 27 Jun 2024 20:54:33 +0200 Subject: [PATCH 56/60] Fixed MQTT examples + some doc --- xdevs/abc/handler.py | 11 +++--- .../4_1_rt_simulation_mqtt_input_handler.py | 14 ++++---- .../4_2_rt_simulation_mqtt_output_handler.py | 6 ++-- xdevs/examples/store/models/store.py | 6 ++-- xdevs/plugins/input_handlers/mqtt.py | 36 +++++-------------- xdevs/plugins/output_handlers/mqtt.py | 13 ++++++- 6 files changed, 40 insertions(+), 46 deletions(-) diff --git a/xdevs/abc/handler.py b/xdevs/abc/handler.py index ed989bf..d74b67b 100644 --- a/xdevs/abc/handler.py +++ b/xdevs/abc/handler.py @@ -5,14 +5,13 @@ class Connector: - def __init__(self, conections: dict[str, str]): + def __init__(self, connections: dict[str, str]): """ - Función para conectar de forma correcta los puertos (que usen protocolo MQTT) + Function to connect ports correctly (using MQTT protocol) - :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. + :param connections: dict[key: str, value: str]. Where the key is the port I am connecting to (via MQTT) and the value is the port of my coupled. """ - self.connections: dict[str, str] = conections + self.connections: dict[str, str] = connections def input_handler(self, port: str): if self.connections is not None: @@ -42,7 +41,7 @@ def __init__(self, *args, **kwargs): 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) + self.connector = Connector(connections=self.connections) def initialize(self): """Performs any task before calling the run method. It is implementation-specific. By default, it is empty.""" diff --git a/xdevs/examples/store/4_1_rt_simulation_mqtt_input_handler.py b/xdevs/examples/store/4_1_rt_simulation_mqtt_input_handler.py index 8703834..a842c28 100644 --- a/xdevs/examples/store/4_1_rt_simulation_mqtt_input_handler.py +++ b/xdevs/examples/store/4_1_rt_simulation_mqtt_input_handler.py @@ -42,13 +42,15 @@ def mqtt_parser(msg: str): print(f"\tNumber of Employees: {n_employees}") print(f"\tMean time required to dispatch clients: {mean_employees} seconds (stddev of {stddev_employees})") - conexiones = { - 'Gen_ClientOut': 'Queue_ClientGen' + # Map of the port of I am subscribing to and the port of the model + connections = { + 'Gen_ClientOut': 'i_ExternalGen' } - topics = {'RTsys/Output/Gen_ClientOut': 0} - + # Topics I am subscribing to + topics = {'RTsys/output/Gen_ClientOut': 0} + # Parser of the port of my model to the desired Port Type msg_parser = { - 'Queue_ClientGen': mqtt_parser, + 'i_ExternalGen': mqtt_parser, } start = time.time() @@ -56,7 +58,7 @@ def mqtt_parser(msg: str): middle = time.time() print(f"Model Created. Elapsed time: {middle - start} sec") rt_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) - rt_manager.add_input_handler('mqtt', subscriptions=topics, connections=conexiones, msg_parsers=msg_parser) + rt_manager.add_input_handler('mqtt', subscriptions=topics, connections=connections, msg_parsers=msg_parser) c = RealTimeCoordinator(storeNOGEN, rt_manager) middle = time.time() print(f"Coordinator and Manager Created. Elapsed time: {middle - start} sec") diff --git a/xdevs/examples/store/4_2_rt_simulation_mqtt_output_handler.py b/xdevs/examples/store/4_2_rt_simulation_mqtt_output_handler.py index 1e9689e..64322f3 100644 --- a/xdevs/examples/store/4_2_rt_simulation_mqtt_output_handler.py +++ b/xdevs/examples/store/4_2_rt_simulation_mqtt_output_handler.py @@ -62,12 +62,12 @@ def get_sec(time_str): stddev_employees)) start = time.time() - gensys = GenSys(mean_generator, stddev_clients) + gens = GenSys(mean_generator, stddev_clients) middle = time.time() print("Model Created. Elapsed time: {} sec".format(middle - start)) rt_manager = RealTimeManager(max_jitter=0.2, event_window=0.5) - rt_manager.add_output_handler('mqtt_handler') - c = RealTimeCoordinator(gensys, rt_manager) + rt_manager.add_output_handler('mqtt') + c = RealTimeCoordinator(gens, rt_manager) middle = time.time() print("Coordinator and Manager Created. Elapsed time: {} sec".format(middle - start)) c.simulate_rt(time_interv=sim_time) diff --git a/xdevs/examples/store/models/store.py b/xdevs/examples/store/models/store.py index 1dcb1ad..ad3c2f0 100644 --- a/xdevs/examples/store/models/store.py +++ b/xdevs/examples/store/models/store.py @@ -63,12 +63,12 @@ def __init__(self, n_employees: int = 10000, mean_employees: float = 30, stddev_ self.o_p_queue = Port(ClientToEmployee) self.add_out_port(self.o_p_queue) - self.i_port_gen = Port(NewClient) - self.add_in_port(self.i_port_gen) + self.i_ExternalGen = Port(NewClient, 'i_ExternalGen') + self.add_in_port(self.i_ExternalGen) self.add_component(queue) - self.add_coupling(self.i_port_gen, queue.input_new_client) + self.add_coupling(self.i_ExternalGen, queue.input_new_client) self.add_coupling(queue.output_client_to_employee, self.o_p_queue) diff --git a/xdevs/plugins/input_handlers/mqtt.py b/xdevs/plugins/input_handlers/mqtt.py index 5917942..15f7c12 100644 --- a/xdevs/plugins/input_handlers/mqtt.py +++ b/xdevs/plugins/input_handlers/mqtt.py @@ -6,15 +6,10 @@ from paho.mqtt.client import Client from xdevs.abc.handler import InputHandler - # Desde este input handler me subscribo a topics para ver los mensajes que entran - # ruta: RTsys/coupled_name/input/port_name y to_do lo que llegue a ese puerto se inyecta. - ######################################################################### - ######################################################################### - ######################################################################### def on_connect(client, userdata, flags, rc): - print(f'MQTT client connected with mqtt: {rc}') # rc valor de exito o fracaso en la conexion + print(f'MQTT client connected with mqtt: {rc}') # rc value for success or failure return rc def on_message(client, userdata, msg): @@ -31,11 +26,6 @@ def __init__(self, event_queue: queue = None, **kwargs): self.event_queue = event_queue - - ######################################################################### - ######################################################################### - ######################################################################### - def mqtt_parser(mqtt_msg): topic = [item for item in mqtt_msg.topic.split('/')] port = topic[-1] @@ -46,9 +36,14 @@ def mqtt_parser(mqtt_msg): class MQTTInputHandler(InputHandler): def __init__(self, subscriptions: dict[str, int] = None, **kwargs): """ - - :param subscriptions: diccionario con los topics y su qos - :param kwargs: + This input handler is the implementation of the MQTT protocol. + It subscribes to the desired topics and pushes the messages received to the system + + :param dict[str, int] subscriptions: dict of topics and their QoS. Default is None + :param str host: desired MQTT broker. Default is 'test.mosquitto.org' + :param int port: port of the MQTT broker to be used. Default is 1883 + :param int keepalive: keepalive time for the MQTT connection. Default is 60 + :param Callable[[mqtt.Message], str, str] event_parser: from the received message obtain the topic and the message payload. Default is mqtt_parser """ kwargs['event_parser'] = kwargs.get('event_parser', mqtt_parser) @@ -78,19 +73,6 @@ def run(self): print(f'MQTT: Event pushed') # {event} t = {datetime.datetime.now()}') self.push_event(event) - if __name__ == '__main__': - input_queue = queue.SimpleQueue() - event_Q = queue.SimpleQueue() - - sub: dict = { - 'ALSW/#': 0, - 'ALSW/TEP': 0, - 'RTsys/#': 0, - } - # C = MQTTClient(event_queue=event_Q) - IN = MQTTInputHandler(queue=input_queue, subscriptions=sub) - IN.initialize() - IN.run() except ImportError: from .bad_dependencies import BadDependenciesHandler diff --git a/xdevs/plugins/output_handlers/mqtt.py b/xdevs/plugins/output_handlers/mqtt.py index 70e115a..1487578 100644 --- a/xdevs/plugins/output_handlers/mqtt.py +++ b/xdevs/plugins/output_handlers/mqtt.py @@ -3,10 +3,20 @@ try: from xdevs.abc.handler import OutputHandler - from ..input_handlers import MQTTClient + from ..input_handlers.mqtt import MQTTClient class MQTTOutputHandler(OutputHandler): + """ + This output handler is the implementation of the MQTT protocol. + It publishes events to the desired topics. + + :param str host: desired MQTT broker. Default is 'test.mosquitto.org' + :param int port: port of the MQTT broker to be used. Default is 1883 + :param int keepalive: keepalive time for the MQTT connection. Default is 60 + :param str topic: desired topic to publish the events. Default is 'RTsys' and generate the topic as 'RTsys/output/' + :param Callable[[str, str], Tuple[str,str]] event_parser: function to obtain the topic and message payload to be published. + """ def __init__(self, **kwargs): super().__init__(**kwargs) @@ -27,6 +37,7 @@ def initialize(self): def run(self): while True: topic, payload = self.pop_event() + print(f'Publishing {payload} to {topic}') self.client.publish(topic, payload) From 497765ce5e6d461ff2a95cbfd87f4d50ab592703 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Fri, 28 Jun 2024 01:02:46 +0200 Subject: [PATCH 57/60] Project README + additional doc --- README.md | 142 +++++++++++++++++++++++++++++ xdevs/examples/gpt/README.md | 7 +- xdevs/examples/store/README.md | 19 +++- xdevs/images/sysoverview_small.png | Bin 0 -> 33436 bytes xdevs/rt.py | 12 ++- 5 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 xdevs/images/sysoverview_small.png diff --git a/README.md b/README.md index 046e062..2bc4a04 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,145 @@ # `xdevs.py` Version of the xDEVS simulator for Python projects + +The aim of this repository is to be able to simulate DEVS systems in both virtual and real-time environments using python. +However, additional features are being developed. + +## Sections +1. [Quick Start](#quick-start) +2. [What to Expect](#what-to-expect) +3. [DEVS in a Nutshell](#devs-in-a-nutshell) +4. [Deepening the repository](#deepening-the-repository) + + +## Quick Start + +1. Clone the repository: +````text +git clone https://github.com/iscar-ucm/xdevs.py.git +```` +2. Navigate to the project directory +```text +cd xdevs +``` +3. Install the package +```text +pip install . +``` + +**Now you're ready to start using xDEVS.py for your DEVS simulations!** + +In case additional dependencies (`sql`, `elasticsearch` or `mqtt`) are required, they can be installed. +To add MQTT support with paho-mqtt: +```text +pip install .[mqtt] +``` + + +## What to Expect + +This section summarizes what you may find in the repository folder structure. + +* ### Folder abc: + * The abstract classes folder contains the handler and transducer files. These folders contain the classes that define the general behavior and structure that each I/O handler or Transducer must follow. + +* ### Folder celldevs: + * Contains the implementation of CellDEVS. + +* ### Folder examples + + * Inside this folder, you will find a collection of examples to try and execute in both virtual and wall-clock simulations. Each sub-folder represents an independent case study: + - CellDevs + - Devstone + - Gpt + - Json + - Store + + +* ### Folder plugins + + * This folder encapsulates a collection of folders. Each subfolder stores the implementations of each of the abstract classes. For example, in the `input_handlers` subfolder, you will find several implementations for the Input Handler. + +* ### Folder tests + + * This folder is dedicated to storing the tests for GitHub Actions. + +* ### Factory.py + + * This script is in charge of creating the different types of implementations of an abstract class based on a key that is linked to the desired implementation. + +* ### Models.py + + * It has the basic `DEVS` models such as `Atomic`, `Coupled` `Component`, or `Port`. + +* ### Rt.py + + * It has the adaptation of the `sim.py` components to the real-time simulation methodology developed. + +* ### Sim.py + + * It has the `DEVS` components to carry out a simulation based on the abstract simulator mechanism. + +## DEVS in a Nutshell + +Discrete Event System Specification (DEVS) is a mathematical formalism with modular and hierarchical characteristics. DEVS is based on discrete event simulation where events occur chronologically in discrete instants of time and result in a change of the system. + +DEVS is mainly based on atomic and coupled models. + +### Atomic Model + +An atomic model is the smallest representation of a system. It may remain in a state (S) for a certain time (ta); once the time has passed, it executes the internal transition function (`deltint`). This function will define what to do next for each state. However, if during that time something external occurs, the model reacts with its external transition function (`deltext`) that describes what to do in this case. If the external event occurs at the same time the `ta` has elapsed, the confluent transition function (`deltcon`) defines what to do next. Finally, the output function (`lambdaf`) defines for each state what should be done when transitioning between states. It is only executed after the internal transition function. + +### Coupled Model + +A coupled model defines the connections among atomic models and other coupled models. + +### Simulation Mechanism + +To simulate a system composed of atomic and coupled models, the abstract simulator mechanism is used. This methodology defines the simulators and coordinators. A simulator is attached to an atomic model, while the coordinator is attached to a coupled model. The coordinator will be in charge of carrying out the simulation. + +Refer to the xDEVS user’s manual for further and deeper understanding [here](https://iscar-ucm.github.io/xdevs/). + + +## xDEVS.py Wall-clock Simulation + +A real-time simulation intends to match the virtual time into a wall-clock time. The methodology followed in this repository to achieve the real-time behaviour is based on the arrival of external events. The system will remain waiting for external events between states, when an external event occurs the system will react according to its particular behaviour. + +In this repository, a `RealTimeManager` and a `RealTimeCoordinator` must be combined to achieve a wall-clock simulation. In addition, if the system requires the handling of input and output events, the `input_handler` and `output_handler` will be used. + +### System overview + +The next picture shows the system overview and how the different components interact with each other + +![System Overview](xdevs/images/sysoverview_small.png +) +1. The `input_handler` acts are the interface for any incoming event to the system sending it to the `RealTimeManager`. +2. The `RealTimeCoordinator` send the events collected from the `RealTimeManager` to the `DEVS` model. +3. The `DEVS` model may eject events out of the system, so they are routed to the `RealTimeManager`. +4. Finally, those outgoing events are forwarded to the `output_handler` which act as an interface to send the events. + +In order to execute real-time simulations examples go to `xdevs.examples.gpt.README` + + +## Deepening the repository + +In order to deepen the repository and understand the different functionalities, the following sections should be checked: + + +* What is a Factory? go to `xdevs.abc.README` + +* xDEVS.py simulations? go to `xdevs.examples.gpt.README` + +* JSON to xDEVS.py simulation? go to `xdevs.examples.json.README` + +* TCP examples? go to `xdevs.examples.gps.README` or `xdevs.examples.store.README` + +* MQTT examples? go to `xdevs.examples.store.README` + + + +___ + +Feel free to contribute to the project by opening issues or submitting pull requests. + + diff --git a/xdevs/examples/gpt/README.md b/xdevs/examples/gpt/README.md index 09fee53..8bcac30 100644 --- a/xdevs/examples/gpt/README.md +++ b/xdevs/examples/gpt/README.md @@ -6,6 +6,7 @@ This folder stores several examples in order to illustrate some of the possibili ## `xDEVS.py` Virtual Simulation A virtual simulation is carried out only taking into account the virtual environment. +In the context of this repository, a DEVS virtual simulation will be achieved by a Coordinator. Two possible options are provided based on two methods: `simulate` and `simulate_time`. The first one is based on the number of iterations and the second one on the desired virtual time to simulate. ### Example: @@ -27,7 +28,7 @@ This section aims to show a collection of examples based on the methodology foll $ cd xdevs/examples/gpt $ python3 gpt_rt_sim.py ``` -2. #### Input handler real-time simulation +2. #### (TCP) Input handler real-time simulation ```bash $ cd xdevs/examples/gpt @@ -49,7 +50,7 @@ c.connect((HOST,PORT)) c.sendall('ih_in,TCP'.encode()) # The data is 'port_name,msg' ``` -3. #### Output handler real-time simulation +3. #### (TCP) Output handler real-time simulation ```bash $ cd xdevs/examples/gpt @@ -83,7 +84,7 @@ while True: break ```` -4. #### Input and Output handlers real-time simulation +4. #### (TCP) Input and Output handlers real-time simulation ````bash $ cd xdevs/examples/gpt diff --git a/xdevs/examples/store/README.md b/xdevs/examples/store/README.md index 3550d00..e363a1a 100644 --- a/xdevs/examples/store/README.md +++ b/xdevs/examples/store/README.md @@ -43,4 +43,21 @@ The client can send messages to the server in the following format: ,? ``` -The model only has one input port, called `IP_NewClient`. \ No newline at end of file +The model only has one input port, called `IP_NewClient`. + +## MQTT Example + +An MQTT example is provided, in which the connection between two `DEVS` models is created. +The execution of both models should be carried out in parallel. + +_First model_ +```bash +$ cd xdevs/examples/store +$ python3 4_1_rt_simulation_mqtt_input_handler.py +``` + +_Second model_ +```bash +$ cd xdevs/examples/store +$ python3 4_2_rt_simulation_mqtt_output_handler.py +``` \ No newline at end of file diff --git a/xdevs/images/sysoverview_small.png b/xdevs/images/sysoverview_small.png new file mode 100644 index 0000000000000000000000000000000000000000..ab32e110256a23bbb08c55ba88f4a793a490e62d GIT binary patch literal 33436 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 literal 0 HcmV?d00001 diff --git a/xdevs/rt.py b/xdevs/rt.py index 925bd81..ae3998c 100644 --- a/xdevs/rt.py +++ b/xdevs/rt.py @@ -130,6 +130,12 @@ def propagate_output(self, port: Port): class RealTimeCoordinator(Coordinator): + """ + The RealTimeCoordinator is the adaptation of the already existing class Coordinator to the real-time simulations. + + :param Coupled model: A DEVS model to simulate in real-time. + :param RealTimeManager manager: A RealTimeManager to handle the external events. + """ def __init__(self, model: Coupled, manager: RealTimeManager): super().__init__(model) self.manager: RealTimeManager = manager @@ -147,10 +153,10 @@ def simulate_rt(self, time_interv: float = float("inf")): while self.clock.time < time_interv: if self.time_next == float("inf") and not self.manager.input_handlers: break - # SLEEP UNTIL NEXT STATE TRANSITION - t, msgs = self.manager.wait_until(min(time_interv, self.time_next)) + # WAIT UNTIL NEXT STATE TRANSITION + t, events = self.manager.wait_until(min(time_interv, self.time_next)) # INJECT EXTERNAL EVENTS (if any) - for port_id, msg in msgs: + for port_id, msg in events: port = self.model.get_in_port(port_id) if port is not None: try: From cc931d1fbb5f1dd7f3e0d3e72841933facebdd8d Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Fri, 28 Jun 2024 10:47:21 +0200 Subject: [PATCH 58/60] Doc clarification --- xdevs/examples/json/README.md | 20 ++++++++++---------- xdevs/examples/store/README.md | 4 ++-- xdevs/plugins/output_handlers/mqtt.py | 3 ++- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/xdevs/examples/json/README.md b/xdevs/examples/json/README.md index 8b94e12..fc9fe1e 100644 --- a/xdevs/examples/json/README.md +++ b/xdevs/examples/json/README.md @@ -11,7 +11,7 @@ The `from_json` method allows you to parse a `JSON` file into a `DEVS` model. Th **ATTENTION PLEASE** ❗❗ - Take into account that the `component_id` inside the `JSON` file must be identified as an `entry-point` of `Components` in `xdevs.factory `. (See the `Factory` section in `xdevs.abc` for more information). + Take into account that the `component_id` inside the `JSON` file must be identified as an `entry-point` of `Components` in `xdevs.factory`. (See the `Factory` section in `xdevs.abc.README` for more information). ## JSON Structure @@ -24,10 +24,10 @@ The top-level JSON object represents the master component. { "MasterComponentName": { "components": { - // Nested components + "Nested components" : {} }, "couplings": [ - // List of couplings + "List of couplings" ] } } @@ -45,21 +45,21 @@ Components can be either already registered in the `entry-points` or couple: "components": { "CoupledModel1": { "components": { - // Nested components + Nested components }, "couplings": [ - // List of connection dictionaries + List of connection dictionaries ] }, "Component2": { "component_id": "ID_from_factory", - "args": [/* positional arguments */], + "args": [ positional arguments ], "kwargs": { "a_parameter": "value", - // Other keyword arguments + Other keyword arguments } } - // Additional components + Additional components } ``` @@ -75,11 +75,11 @@ Couplings define connections between components: "couplings": [ { "componentFrom": "Model1", - "portFrom": "PortA", // Port name defined in Model1 + "portFrom": "PortA", Port name defined in Model1 "componentTo": "Model2", "portTo": "PortB" } - // Additional couplings + Additional couplings ] ``` diff --git a/xdevs/examples/store/README.md b/xdevs/examples/store/README.md index e363a1a..4321a98 100644 --- a/xdevs/examples/store/README.md +++ b/xdevs/examples/store/README.md @@ -50,13 +50,13 @@ The model only has one input port, called `IP_NewClient`. An MQTT example is provided, in which the connection between two `DEVS` models is created. The execution of both models should be carried out in parallel. -_First model_ +_First model, MQTT subscriber_ ```bash $ cd xdevs/examples/store $ python3 4_1_rt_simulation_mqtt_input_handler.py ``` -_Second model_ +_Second model, MQTT publisher_ ```bash $ cd xdevs/examples/store $ python3 4_2_rt_simulation_mqtt_output_handler.py diff --git a/xdevs/plugins/output_handlers/mqtt.py b/xdevs/plugins/output_handlers/mqtt.py index 1487578..fa6d088 100644 --- a/xdevs/plugins/output_handlers/mqtt.py +++ b/xdevs/plugins/output_handlers/mqtt.py @@ -15,7 +15,8 @@ class MQTTOutputHandler(OutputHandler): :param int port: port of the MQTT broker to be used. Default is 1883 :param int keepalive: keepalive time for the MQTT connection. Default is 60 :param str topic: desired topic to publish the events. Default is 'RTsys' and generate the topic as 'RTsys/output/' - :param Callable[[str, str], Tuple[str,str]] event_parser: function to obtain the topic and message payload to be published. + :param Callable[[str, str], Tuple[str,str]] event_parser: function to parser the port and the message to be + ejected into a MQTT topic and the payload of the MQTT message. """ def __init__(self, **kwargs): super().__init__(**kwargs) From 3d2949a2dce09823498b136171e95146e2c94da8 Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Wed, 10 Jul 2024 16:39:20 +0200 Subject: [PATCH 59/60] Add vsc to gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 12a6b9d..686ef89 100644 --- a/.gitignore +++ b/.gitignore @@ -137,3 +137,7 @@ dmypy.json # Log File *.log *.csv + +# visual studio code +.vscode/ + From ba35bcb5e0644414df16e80583bbeb9899e42c3a Mon Sep 17 00:00:00 2001 From: OscarFdezS Date: Wed, 10 Jul 2024 16:43:27 +0200 Subject: [PATCH 60/60] Remove vsc files --- .vscode/launch.json | 12 ------------ .vscode/settings.json | 10 ---------- 2 files changed, 22 deletions(-) delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/settings.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 8352304..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Python: Module", - "type": "python", - "request": "launch", - "module": "tests.test_devstone", - // "env": {"PYTHONPATH": "${workspaceFolder}/libs/"} - } - ] -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index bfaa2cd..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "python.autoComplete.extraPaths": ["${workspaceFolder}/xdevs"], - "python.testing.cwd": "${workspaceFolder/xdevs}", - "python.analysis.include": [ - "${workspaceFolder}/xdevs" - ], - "python.analysis.extraPaths": [ - "${workspaceFolder}" - ], -} \ No newline at end of file

    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 z

    bN!h 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%0AKzKr6`I;QC#_i>01weTWw{vFZ{l=mPk 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