Source code for openclsim.core.events_container

"""EventsContainer provide a basic class for managing information which has to be stored in an object."""
import simpy


[docs]class EventsContainer(simpy.FilterStore): """ EventsContainer provide a basic class for managing information which has to be stored in an object. It is a generic container, which has a default behavior, but can be used for storing arbitrary objects. Parameters ---------- store_capacity Number of stores that can be contained by the multicontainer """ def __init__(self, env, store_capacity: int = 1, *args, **kwargs): super().__init__(env, capacity=store_capacity) self._env = env self._get_available_events = {} self._put_available_events = {}
[docs] def initialize(self, init=0, capacity=0): """Initialize method is a convenience method for backwards compatibility reasons.""" self.put(init, capacity)
[docs] def initialize_container(self, initials): """Initialize method used for MultiContainers.""" for item in initials: assert "id" in item assert "capacity" in item assert "level" in item super().put(item)
[docs] def get_available(self, amount, id_="default"): if self.get_level(id_) >= amount: return self._env.event().succeed() if id_ in self._get_available_events: if amount in self._get_available_events[id_]: return self._get_available_events[id_][amount] # else case: id_ is not in self._get_available_events new_event = self._env.event() self._get_available_events[id_] = {} self._get_available_events[id_][amount] = new_event return new_event
[docs] def get_capacity(self, id_="default"): if self.items is None: return 0 res = [item["capacity"] for item in self.items if item["id"] == id_] if isinstance(res, list) and len(res) > 0: return res[0] return 0
[docs] def get_level(self, id_="default"): if self.items is None: return 0 res = [item["level"] for item in self.items if item["id"] == id_] if isinstance(res, list) and len(res) > 0: return res[0] return 0
[docs] def put_available(self, amount, id_="default"): if self.get_capacity(id_) - self.get_level(id_) >= amount: return self._env.event().succeed() if id_ in self._put_available_events: if amount in self._put_available_events: return self._put_available_events[amount] new_event = self._env.event() self._put_available_events[id_] = {} self._put_available_events[id_][amount] = new_event return new_event
[docs] def get_empty_event(self, start_event=False, id_="default"): if not start_event: return self.put_available(self.get_capacity(id_), id_) elif start_event.processed: return self.put_available(self.get_capacity(id_), id_) else: return self._env.event()
[docs] def get_full_event(self, start_event=False, id_="default"): if not start_event: return self.get_available(self.get_capacity(id_), id_) elif start_event.processed: return self.get_available(self.get_capacity(id_), id_) else: return self._env.event()
@property def empty_event(self): """Properties that are kept for backwards compatibility. mThey are NOT applicable for MultiContainers.""" return self.put_available(self.get_capacity()) @property def full_event(self): """Properties that are kept for backwards compatibility. mThey are NOT applicable for MultiContainers.""" return self.get_available(self.get_capacity())
[docs] def put(self, amount, capacity=0, id_="default"): current_amount = 0 if len(self.items) > 0: status = super().get(lambda status: status["id"] == id_) # if status.ok: if status.triggered: status = status.value if "capacity" in status: capacity = status["capacity"] if "level" in status: current_amount = status["level"] else: raise Exception( f"Failed to derive the previous version of container {id_}" ) # this is a fall back in case the container is used with default put_event = super().put( {"id": id_, "level": current_amount + amount, "capacity": capacity} ) put_event.callbacks.append(self.put_callback) return put_event
[docs] def put_callback(self, event, id_="default"): if isinstance(event, simpy.resources.store.StorePut): if "id" in event.item: id_ = event.item["id"] if id_ in self._get_available_events: for amount in sorted(self._get_available_events[id_]): if self.get_level(id_) >= amount: if id_ in self._get_available_events: self._get_available_events[id_][amount].succeed() del self._get_available_events[id_][amount] else: return
[docs] def get(self, amount, id_="default"): store_status = super().get(lambda state: state["id"] == id_).value store_status["level"] = store_status["level"] - amount get_event = super().put(store_status) get_event.callbacks.append(self.get_callback) return get_event
[docs] def get_callback(self, event, id_="default"): # it is confusing that this is checking for storeput while doing a get # the reason is that subtracting from a container requires to get the complete # content of a container and then add the remaining content of the container # which creates a storeput if isinstance(event, simpy.resources.store.StorePut): if "id" in event.item: id_ = event.item["id"] if id_ in self._put_available_events: for amount in sorted(self._put_available_events[id_]): # if isinstance(self, ReservationContainer): # if self.get_capacity(id_) - self.get_expected_level(id_) >= amount: # self._put_available_events[amount].succeed() # del self._put_available_events[amount] # el if self.get_capacity(id_) - self.get_level(id_) >= amount: if id_ in self._put_available_events: self._put_available_events[id_][amount].succeed() del self._put_available_events[id_][amount] else: return
@property def container_list(self): container_ids = [] if len(self.items) > 0: container_ids = [item["id"] for item in self.items] return container_ids