diff --git a/.env b/.env new file mode 100644 index 0000000000000000000000000000000000000000..3479d9155be18a317eac88a61c1c0f92b73352b7 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +PYTHONPATH="/libs" \ No newline at end of file diff --git a/.ipynb_checkpoints/requirements-checkpoint.txt b/.ipynb_checkpoints/requirements-checkpoint.txt new file mode 100644 index 0000000000000000000000000000000000000000..e76e091f268ad6d943051645e639b13f08ec2ceb --- /dev/null +++ b/.ipynb_checkpoints/requirements-checkpoint.txt @@ -0,0 +1,2 @@ +voila +voila-gridstack diff --git a/Balance_Binary.ipynb b/Balance_Binary.ipynb index 4c87cd94f48c10dc9eb62a7b671caa5675a83b0c..9b71568adcd5637a68605acf12291558d69289ab 100644 --- a/Balance_Binary.ipynb +++ b/Balance_Binary.ipynb @@ -132,7 +132,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "ab70ee95-ce3c-4c4c-a7bc-2658a1a5d41a", "metadata": { "extensions": { @@ -152,15 +152,15 @@ }, "outputs": [], "source": [ - "import nowo_base_1 as no_ba\n", - "import nowo_sim_binary as no_bi\n", - "import nowo_gui_base as no_gui\n", - "import nowo_log_base as no_log" + "import nowo1_base as no_ba\n", + "import nowo1_sim_binary as no_bi\n", + "import nowo1_gui_base as no_gui\n", + "import nowo1_log_base as no_log" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "eec838fd-a5a9-49c8-a873-07c0e3653622", "metadata": { "extensions": { @@ -180,15 +180,15 @@ }, "outputs": [], "source": [ - "test_obj = no_bi.binaer_node('test_obj') # Umgebungs\n", - "step_1 = no_ba.step_single('step_1')\n", + "bilanz_1 = no_bi.binary_node('bilanz_1') # Umgebungs\n", + "step = no_ba.step_single('step')\n", "gui_data = no_gui.gui_ipysheet('gui_data')\n", - "log_1 = no_log.log_sheet('log_1')" + "log = no_log.log_sheet('log')" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "5e7dc37c-8fe1-4569-a233-e3e45f450ae2", "metadata": { "extensions": { @@ -208,13 +208,13 @@ }, "outputs": [], "source": [ - "step_1.Init(work_objs=[test_obj.Calc_A_B])\n", - "log_1.Init(Values=[test_obj.log_all], Gui_For_Data = gui_data)" + "step.Init(work_objs=[bilanz_1.Calc_A_B])\n", + "log.Init(Values=[bilanz_1.log_all], Gui_For_Data = gui_data)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "9a853d13-b21f-475a-8262-b9616bad6a60", "metadata": { "extensions": { @@ -233,14 +233,29 @@ }, "tags": [] }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3b61942308af4a80ae7925cd8d0f83e0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Sheet(cells=(Cell(column_end=0, column_start=0, row_end=7, row_start=0, squeeze_row=False, type…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "gui_data.Show_GUI()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "155a052c-b1b6-4098-8614-19388211296a", "metadata": { "extensions": { @@ -258,14 +273,29 @@ } } }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6be30af1c3954d52ba1a8a26b2a93196", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(VBox(children=(Label(value='extensive Größe:'), HBox(children=(Dropdown(layout=Layout(width='10…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "test_obj.Show_GUI()" + "bilanz_1.Show_GUI()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "aa1fe3ad-55bb-491d-a677-ea20354ba5c1", "metadata": { "extensions": { @@ -283,10 +313,63 @@ } } }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a7b9403b09c842cd849ebe6827a4801b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Button(description='Calc', style=ButtonStyle()),))" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ed555c63520a4dd7a0a83be748fcfb44", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "step_1.Show_GUI()" + "step.Show_GUI()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e725524f-503a-4133-8445-2702b729abcf", + "metadata": { + "extensions": { + "jupyter_dashboards": { + "activeView": "grid_default", + "views": { + "grid_default": { + "col": null, + "height": 2, + "hidden": true, + "row": null, + "width": 2 + } + } + } + }, + "tags": [] + }, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/README.md b/README.md index 476b50cb6350d6ec2d8ca30e1b59ffa59ac2be54..22af31aa0eb40598eb048f074d2318c2866b63ff 100644 --- a/README.md +++ b/README.md @@ -5,5 +5,4 @@ -[](https://mybinder.org/v2/gh/was-ist-immer/Binary_Balance/HEAD?urlpath=voila/render/Balance_Binary.ipynb) - +[](https://mybinder.org/v2/gh/was-ist-immer/Binary_Balance/HEAD?urlpath=voila%2Frender%2FBalance_Binary.ipynb) diff --git a/__pycache__/nowo1_base.cpython-39.pyc b/__pycache__/nowo1_base.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a2d8537bfdba8749adffb5c8d4fb534ecbee302 Binary files /dev/null and b/__pycache__/nowo1_base.cpython-39.pyc differ diff --git a/__pycache__/nowo1_gui_base.cpython-39.pyc b/__pycache__/nowo1_gui_base.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eedef653f6714a41958a39925eccf904fb940d62 Binary files /dev/null and b/__pycache__/nowo1_gui_base.cpython-39.pyc differ diff --git a/__pycache__/nowo1_log_base.cpython-39.pyc b/__pycache__/nowo1_log_base.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a454f371b8823692aa4b1f24492237b7ff8d62d Binary files /dev/null and b/__pycache__/nowo1_log_base.cpython-39.pyc differ diff --git a/__pycache__/nowo1_sim_binary.cpython-39.pyc b/__pycache__/nowo1_sim_binary.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bcc80afd87a78bb2f2485fba89fdead4adb99d0c Binary files /dev/null and b/__pycache__/nowo1_sim_binary.cpython-39.pyc differ diff --git a/__pycache__/sim_base.cpython-39.pyc b/__pycache__/sim_base.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0a283222cd5cdad6d62aca686191908ea88c219 Binary files /dev/null and b/__pycache__/sim_base.cpython-39.pyc differ diff --git a/nowo1_base.py b/nowo1_base.py new file mode 100644 index 0000000000000000000000000000000000000000..3fdac910808d4185a2fee51beb6ef0b9a939a0cf --- /dev/null +++ b/nowo1_base.py @@ -0,0 +1,1527 @@ +import sys +import sim_base as sim_g +import pandas as pd +import datetime as dt +from dateutil.relativedelta import relativedelta +from typing import Any, Callable, List, Type +import math +import numpy as np +import ipywidgets as widgets +from IPython.display import display + + + +Main_Modul = sys.modules['__main__'] +Self_Modulname = globals()['__name__'] +Self_Modul = sys.modules[Self_Modulname] + + +def get_attr_rek(names, start_obj=Main_Modul): + """get attr / obj by list or str. + +Keyword arguments: +names -- as str or list (example: 'obj.attr1.attr2' or ['obj', 'attr1', 'attr2']) +start_obj -- obj where to search +""" + if isinstance(names, str): + name_list = names.split('.') + elif isinstance(names, list): + name_list = names + else: + return None + + obj = start_obj + for name in name_list: + if hasattr(obj, name): + obj = getattr(obj, name) + else: + return None + return obj + + +def analyse_func_old(func_to_analyse, sim_obj): + if isinstance(func_to_analyse, str): + func_name = func_to_analyse + func = get_attr_rek(func_to_analyse) + if not func: return None + sim_obj = func.__self__ + + elif callable(func_to_analyse): + func = func_to_analyse + func_name = func.__name__ + if hasattr(func, '__self__'): + sim_obj = func.__self__ + func_name = sim_obj.name + '.' + func_name + else: + setattr(sim_obj, func_name, func) + func_name = sim_obj.name + '.' + func_name + else: return None + + return [sim_obj, func, func_name] + + +def analyse_func(func): + if isinstance(func, str): + func_name = func + func = get_attr_rek(func) + if not func: return None + + if callable(func): + func_name = func.__name__ + sim_obj = None + if hasattr(func, '__self__'): + sim_obj = func.__self__ + func_name = sim_obj.name + '.' + func_name + + else: return None + + return [sim_obj, func, func_name] + + + +class sim_value(): + def __init__(self, name: str, parent, value_type ): + self.name = name + self._parent = parent + self._simparent = getattr(self._parent, 'sim_parent', self._parent) + if not self._simparent: self._simparent = self._parent + value_type_list = list(value_type) + value_type_list.append(type(None)) + self.value_type = tuple(value_type_list) + self._value = None + self._default_value = None + self.unit = '' + + self.set = self._set # Überschreibbar durch callback funktion + self.call_extern = None + self.call_intern = None + self._fullname = self._get_fullname() + self._loggerlist : set = set() + + + def _get_fullname(self): + namelist = [] + if not self._simparent == self._parent: + namelist.append(self._simparent.name) + namelist.append(self._parent.name) + namelist.append(self.name) + return '.'.join(namelist) + + #TODO typen überprüfen + def _set(self, value): + if not isinstance(value, self.value_type): raise Exception('ungültiger typ') + self._value = value + return self._value + + # Wird geändert über value erfolgt kein callback zugriff es wird nur + #_value geändert oder zurückgegeben + @property + def value(self): + return self._value + + + @value.setter + def value(self, value): + if not isinstance(value, self.value_type): raise Exception('ungültiger typ') + self._value = value + + + @property + def default_value(self): + return self._default_value + + + @default_value.setter + def default_value(self, value): + self._default_value = value + self._value = self._default_value + + + def get_set(self, stepper=None, value=None): + if self.call_extern: + self._value = self.call_extern(self, value, stepper) + elif self.call_intern: + self._value = self.call_intern(value, stepper) + return self._value + + + + #TODO Warum Any würde hier nicht auch ein None genügen? + def Init(self, value = Any, default = Any, unit = ''): + #self._loggerlist : set = set() + self.unit = unit + self._default_value = default + if isinstance(value, str): + value = get_attr_rek(value) + if isinstance(value, Callable): + if hasattr(value, '__self__') and (value.__self__ == self._simparent): + self.call_intern = value + else: self.call_extern = value + self._value = self._default_value + return + elif isinstance(value, type(None)): + self._value = value + self.set = self._set + elif isinstance(value, self.value_type): + self._value = self.value_type[0](value) + self.set = self._set + return + + + def _add_logger(self, logger): + self._simparent._add_logger(logger) + self._loggerlist.add(logger) + return self._fullname + + + def _del_logger(self, logger): + self._simparent._del_logger(logger) + + + def _node_success(self, stepper): + for logger in self._loggerlist: + logger._add_value(self._fullname, self._value) + + +#------------------------------------------------------------------- +class gate_base: + def __init__(self, name, sim_parent): + self.name = name + self.sim_parent = sim_parent + self._success_flag : bool = True + + def set(self, value: Any, value_name: str = ''):pass + + def get(self, value_name: str = ''):pass + + def Init(self, values):pass + + def _reset(self): + self._success_flag : bool = True + + def _node_success(self, stepper): pass + + def log_all(self, logger): + name_list = [] + for key, value in self.__dict__.items(): + if issubclass(type(value), sim_value): + name_list.append(value._add_logger(logger)) + return name_list + + +#------------------------------------------------------------------- +class gate_time(gate_base): + def __init__(self, name, sim_parent): + super().__init__(name, sim_parent) + self.time = sim_value('time', self, (dt.datetime,)) + + def _node_success(self, timer): + self.time._node_success(timer) + + def _reset(self): + super()._reset() + self.time.set(dt.datetime(1, 1, 1, 0, 0, 0)) + + def set(self, value): + if isinstance(value, dt.datetime): + self.time.value = value + + def get(self, value_name: str = ''): + if not value_name: + return self.time.value + elif value_name == 'time': + return self.time.value + + +#------------------------------------------------------------------- +class gate_general(gate_base): + def __init__(self, name, sim_parent): + super().__init__(name, sim_parent) + self.general = sim_value('general', self, (float, int,)) + + # self.time_factor = 0.0 + + + def _reset(self): + super()._reset() + #self.general.set(np.nan) + + + def set(self, value): + if isinstance(value, (float, int)): + self.general.set(float(value)) + elif isinstance(value, type(self)): + self.general.set(value.general.value) + + + def get(self, value_name: str = ''): + if not value_name: + return self.general.value + elif value_name == 'general': + return self.general.value + + + def _node_success(self, stepper): + if not self._success_flag: return + self.general._node_success(stepper) + self._success_flag = False + + +#------------------------------------------------------------------- +class gate_dynamic(gate_base): + def __init__(self, name, sim_parent): + super().__init__(name, sim_parent) + self.Dyn_Values : dict = dict() + + + def Init(self, values): + super().Init(values) + if not isinstance(values, dict): raise Exception('das ist kein Dictionary') + for key, value in values.items(): + base_val = sim_value(key, self, value['type']) + base_val.Init(value['value'], value['default'], value['unit']) + self.Dyn_Values.update({key : base_val}) + setattr(self,key,base_val) + + + def _reset(self): + super()._reset() + for val in self.Dyn_Values.values(): + val._value = val._default_value + + + def set(self, value, value_name): + if not isinstance(value_name, str): + raise Exception('Name nich korrekt') + if not value_name in self.Dyn_Values.keys(): + raise Exception('Name nich korrekt') + + val = self.Dyn_Values[value_name] + + if not isinstance(value, val.value_type): + raise Exception('Falscher typ') + val.set(value) + + + + def get(self, value_name: str = ''): + if not isinstance(value_name, str): + raise Exception('Name nich korrekt') + if not value_name in self.Dyn_Values.keys(): + raise Exception('Name nich korrekt') + + return self.Dyn_Values[value_name]._value + + + def _node_success(self, timer): + if not self._success_flag: return + for val in self.Dyn_Values.values(): + val._value = val._node_success(timer) + self._success_flag = False + + + def _actual_values(self, timer): + for val in self.Dyn_Values.values(): + val.get_set(timer) + + +class gate_dataframe(gate_base): + def __init__(self, name, sim_parent): + super().__init__(name, sim_parent) + self.dataframe = sim_value('dataframe', self, (pd.DataFrame,)) + + + def _reset(self): + super()._reset() + #self.dataframe.set(0.0) + + + def _add_logger(self, logger): + super()._add_logger(logger) + + + def set(self, value): + if isinstance(value, (float, int)): + self.dataframe.set(float(value)) + elif isinstance(value, type(self)): + self.dataframe.set(value.dataframe.value) + + + def get(self, value_name: str = ''): + if not value_name: + return self.dataframe.value + elif value_name == 'dataframe': + return self.dataframe.value + + + def _node_success(self, stepper): + if not self._success_flag: return + self.dataframe._node_success(stepper) + self._success_flag = False + + + +#***************************************************************** +class step_base(): + def __init__(self, name : str, GUI : bool = False): + self.name = name + self.step_nr: int = 0 + self.total_steps: int = 0 + self.work_objs = [ ] # Diese Objekte sind mit dem Timer verknüpft + self.sim_funcs : dict = {}# Ersetzt self.work_objs und self.work_funcs + self._loggerlist : set = set() + + self._GUI_visible = GUI + self._GUI_itemlist : dict = dict() + self.GUI_Item = self._create_gui() + self._GUI_is_visible = False + + self.info = widgets.Output() + + + + def on_start_clicked(self, args): + parent = args.parent + parent.reset() + #with self.info: + # print('Hier') + + # for obj in parent.work_objs: + # if obj._GUI_is_visible: + # obj.Init_Over_GUI() + parent.work() + + + def Show_GUI(self): + for obj in self.work_objs: + if obj._GUI_visible: + obj.Show_GUI() + if self.GUI_Item: + self._GUI_is_visible = True + display(self.GUI_Item, self.info) + #with self.info: + # print('Na dann wollen wir mal') + + + def _create_gui(self): + for widget in self._GUI_itemlist.values(): + setattr(widget, 'parent', self) + return None + + + def reset(self): + self.step_nr = 0 + + + + def Init(self, work_objs : list = []): + self.step_nr = 0 + if not work_objs: raise Exception(f'obj : {self.name} |wenigstens eine start Methode muss angeben werden!') + self.work_objs = [] # Diese Objekte sind mit dem Timer verknüpft + + for func_to_analyse in work_objs: + func_info = analyse_func(func_to_analyse) + if not func_info:raise Exception(f'obj : {self.name} Startfunktion nicht korrekt') + if not isinstance(func_info[0], work_base):raise Exception(f'obj : {self.name} |node ist kein Type von work_base') + self.work_objs.append(func_info[0]) + if not callable(func_info[1]): raise Exception(f'obj : {self.name} |node hat keine func_work methode') + gate = func_info[0].ch_type('Timer_Summe', self) + gate.set(0.0) + self.sim_funcs.update( + {func_info[1] : { + 'obj' : func_info[0], + 'gate' : gate}}) + + + def work(self): + for obj in self.work_objs: + if obj._GUI_is_visible: + obj.Init_Over_GUI() + + for obj in self.work_objs: + obj.ready_for_start(self) + for logger in self._loggerlist: + logger.ready_for_start(self) + + if self._GUI_visible: pass + while self.step() == 1: + self._node_success() + + for obj in self.work_objs: + obj.ready_for_end() + + for logger in self._loggerlist: + logger.ready_for_end() + + + def step(self): + for obj in self.work_objs: + obj._reset() #Todo ist das immer erforderlich? + + for func, values in self.sim_funcs.items(): + func(stepper = self, Gate_In = values['gate']) + values['obj']._node_success(stepper = self) + + self.step_nr = self.step_nr + 1 + + + def _add_logger(self, logger): + if isinstance(logger, set): + self._loggerlist.update(logger) + elif isinstance(logger, port_base): + self._loggerlist.add(logger) + + + def _del_logger(self, logger): + self._loggerlist.remove(logger) + + + def _node_success(self): pass + + +class step_range(step_base): + def __init__(self, name : str, GUI : bool = False): + super().__init__(name, GUI) + self.total_steps = sim_value('total_steps', self, (float, int,)) + self.Gate_Step: gate_general = gate_general( 'Gate_Step', self) + + + def Init(self, + work_objs : list = [], + total_steps = 1 + ): + + super().Init(work_objs=work_objs) + self.total_steps.Init(total_steps, 1) + self.step_nr = 0 + + + def _create_gui(self): + + start_button = widgets.Button(description='Calc') + start_button.on_click(self.on_start_clicked) + total_box = widgets.VBox([start_button]) + + self._GUI_itemlist.update({ + 'start_button' : start_button + }) + super()._create_gui()# Attention! --must-- because the widget get a parent attr. + return total_box + + + + + # ergebnis 1 ein step + # 0 kein step + def step(self): + if self.step_nr <= self.total_steps.get_set(self): + self.Gate_Step.set(self.step_nr) + super().step() + return 1 + else: return 0 + + + def _node_success(self): + super()._node_success() + self.Gate_Step._node_success(self) + + + +class step_single(step_base): + def __init__(self, name : str, GUI : bool = False): + super().__init__(name, GUI) + + + def Init(self, work_objs : list = []): + super().Init(work_objs=work_objs) + self.step_nr = 0 + + + # Später im Parent + def _create_gui(self): + start_button = widgets.Button(description='Calc') + start_button.on_click(self.on_start_clicked) + total_box = widgets.VBox([start_button]) + + self._GUI_itemlist.update({ + 'start_button' : start_button + }) + + super()._create_gui()# Attention! --must-- because the widget get a parent attr. + return total_box + + + def step(self): + if self.step_nr < 1: + super().step() + return 1 + else: return 0 + + +# ------------------------------------------------------------------ +class step_timer(step_base): + def __init__(self, name : str, GUI : bool = False): + super().__init__(name, GUI) + + self.abs_start = dt.datetime(1, 1, 1, 0, 0, 0) + self.start: dt.datetime = dt.datetime(1, 1, 1, 0, 0, 0) + self.ende: dt.datetime = dt.datetime(1, 1, 1, 1, 0, 0) + self.actual: dt.datetime = self.start + self.step_width: str = 's' + self.step_count: int = 0 + self.total_sec: int = 0 + self._max_sec: int = 0 + self._step_sec : int = 0 + self.step_props = dict(s=1, m=60, h=3600, d=86400, m15=900) + + self.Gate_Time: gate_time = gate_time( 'Gate_Time', self) + + self.factor : float = self.step_props[self.step_width] / 3600.0 + + + def Init(self, + work_objs : list = [], + start = '0001-01-01 00:00:00', + ende = '0001-01-01 01:00:00', + stepwidth = 's' + ): + + super().Init(work_objs=work_objs) + + self.start = dt.datetime.strptime(start, sim_g.timepattern) + self.ende = dt.datetime.strptime(ende, sim_g.timepattern) + if not self.start < self.ende: raise Exception('ende vor start') + + self.step_width = stepwidth + if not self.step_width in self.step_props.keys():raise Exception('step_width nicht bekannt (s,m,h,d, m15)') + + self.actual = self.start + self._step_sec = self.step_props[self.step_width] + self.Gate_Time.set(self.actual) + self.total_sec = 0 + self._max_sec = int((self.start - self.abs_start).total_seconds()) + + self.total_steps = int(self._max_sec /self.step_props[self.step_width]) + self.factor : float = self.step_props[self.step_width] / 3600.0 + + # ergebnis 1 ein step + # 0 kein step act_time < self.start + # -1 kein step act_time > self.ende + def step(self): + relation_time = self.actual + if relation_time < self.start: return 0 + + if (relation_time >= self.start) and (relation_time < self.ende): + # Mache einen step + self.total_sec = self.total_sec + self.step_props[self.step_width] + self.actual = self.actual + \ + dt.timedelta(seconds=self.step_props[self.step_width]) + self.Gate_Time.time.set(self.actual) + super().step() + return 1 + + if self.actual >= self.ende: return -1 + + + + def _node_success(self): + super()._node_success() + self.Gate_Time._node_success(self) + + + +#***************************************************************** +class work_base(): + def __init__(self, name: str, GUI : bool = False, **kwargs): + + self.name = name + self._ctrl_ok : bool = False + + self._GUI_visible = GUI + self._GUI_itemlist : dict = dict() + self.GUI_Item = None + self._GUI_is_visible = False + + self.info = widgets.Output() + + def Init(self): pass + + def ready_for_start(self, timer: step_timer): pass + + def ready_for_end(self):pass + + + def _create_gui(self): + for widget in self._GUI_itemlist.values(): + setattr(self, 'parent', widget) + return None + + + def Show_GUI(self): + #display(self.info) + if not self.GUI_Item: + self.GUI_Item = self._create_gui() + self._GUI_is_visible = True + display(self.GUI_Item) + + +class gui_base(work_base): + def __init__(self, name: str, GUI : bool = False, **kwargs): + super().__init__(name, GUI, **kwargs) + + + def ready_for_start(self, stepper: step_base):pass + + def ready_for_end(self):pass + + def Init(self): super().Init() + + def Init_by_dataframe(self, dataframe): pass + + + +# ------------------------------------------------------------------ +class port_base(work_base): + def __init__(self, name: str, GUI : bool = False, **kwargs): + super().__init__(name, GUI, **kwargs) + self.buffer_data: dict = dict() + self.col_name : list = list() + self.Gui_For_Data : gui_base = None + + + def clear_buffer(self): + for key in self.buffer_data.keys(): + self.buffer_data[key] = [] + + + def ready_for_start(self, stepper: step_base): + self.clear_buffer() + + + def ready_for_end(self):pass + + #with self.info: + # print('ready_for_end') + + + def reset_ports(self, port_parentname: str): + if port_parentname in self.ports.keys(): + self.ports[port_parentname] = dict() + + + def Init(self, + Values = [], + Gui_For_Data = None, + ): + super().Init() + self.Gui_For_Data = Gui_For_Data + self.buffer_data: dict = dict() + for value in Values: + if isinstance(value, sim_value): + name = value._add_logger(self) + self.buffer_data.update({name: []}) + elif isinstance(value, Callable): + for item in value(self): + self.buffer_data.update({item: []}) + self.col_name = list(self.buffer_data.keys()) + + + def _add_value(self, name, value): + if name in self.buffer_data.keys(): + self.buffer_data[name].append(value) + + +# ------------------------------------------------------------------- +class sim_base(work_base): + + def __init__(self, name: str, GUI : bool = False, **kwargs): + super().__init__(name, GUI, **kwargs) + self.time_factor: float = 0.0 + self.ch_type: gate_base = None + + if 'ch_type' in kwargs.keys(): + self.ch_type = kwargs['ch_type'] + if isinstance(self.ch_type, str): + self.ch_type = Main_Modul.sim_obj_factory.get_classtype_by_name( + self.ch_type) + + if not issubclass(self.ch_type, gate_base): raise Exception('ist nicht vom Type gate_base abgeleitet') + + self._loggerlist : set = set() + self._funcgate : dict = {} + + + + def _getgate_func(self, func : Callable): + if func in self._funcgate.keys(): + return self._funcgate[func] + return None + + def _node_success(self, timer: step_timer):pass + + + def _reset(self):pass + + + def _add_logger(self, logger): + self._loggerlist.add(logger) + + + def _del_logger(self, logger): + self._loggerlist.remove(logger) + + + def ready_for_start(self, stepper: step_base): + super().ready_for_start(stepper) + self._reset() + if isinstance(stepper, step_timer): + self.time_factor = stepper.factor + stepper._add_logger(self._loggerlist) + + + def log_all(self, logger): + name_list = [] + for key, value in self.__dict__.items(): + if issubclass(type(value), sim_value): + name_list.append(value._add_logger(logger)) + elif issubclass(type(value), gate_base): + name_list = name_list + value.log_all(logger) + return name_list + + + + def Init_Over_GUI(self): pass + + + + +class ctrl_base(): + def __init__(self, name : str, gate_type : gate_base): + super().__init__() + self._parent = None + self.name = name + self.Gate_Ctrl = gate_type('Gate_Ctrl', self) + + + def Do(self, timer: step_timer, Summe: gate_general): + pass + + def _init(self, parent): + self._parent = parent + return self.Do + + def ready_for_start(self, timer: step_timer): pass + + def ready_for_end(self):pass + + def _reset(self): + self.Gate_Ctrl.reset() + +#*****************************************************************#-------------------------------------------------------------------- +class ctrl_general_factor(ctrl_base): + def __init__(self, name : str = 'ctrl_general_factor', gate_type : gate_base = gate_general, Factor = 1.0): + super().__init__(name, gate_type) + self.Factor = sim_value('Factor', self, (float, int,)) + self.Factor.Init(Factor) + + + def Do(self, timer: step_timer, Gate_In: gate_general): + self.Gate_Ctrl.general._value = Gate_In.general._value * self.Factor.get_set(stepper=timer) + return self.Gate_Ctrl + + +class ctrl_general_rest(ctrl_base): + def __init__(self, name : str = 'ctrl_general_rest', gate_type : gate_base = gate_general): + super().__init__(name, gate_type) + + + def Do(self, timer: step_timer, Gate_In: gate_general): + sum = self._parent.Gate_In.general._value + for gate in self._parent._sequence_work[self._parent.Sequence_Key]['gates']: + sum = sum + gate.general._value + self.Gate_Ctrl.set(sum) + #self._parent.Gate_Node.set(sum) + return self.Gate_Ctrl + + +class ctrl_general_set(ctrl_base): + def __init__(self, name : str = 'ctrl_general_rest', gate_type : gate_base = gate_general, Value = 0.0): + super().__init__(name, gate_type) + self.Value = sim_value('Factor', self, (float, int,)) + self.Value.Init(Value) + + + def Do(self, timer: step_timer, Gate_In: gate_general): + self.Gate_Ctrl.general._value = self.Value.get_set(timer) + return self.Gate_Ctrl + + +class ctrl_general_zero(ctrl_general_rest): + def __init__(self, name : str = 'ctrl_general_zero', gate_type : gate_base = gate_general): + super().__init__(name, gate_type) + + + def Do(self, timer: step_timer, Gate_In: gate_general): + super().Do(timer = timer, Gate_In = Gate_In) + if (abs(self.Gate_Ctrl.general._value) < 0.0000001): + self._parent._ctrl_ok = True + return self.Gate_Ctrl + + +class ctrl_general_step(ctrl_base): + def __init__(self, name : str = 'ctrl_general_step', gate_type : gate_base = gate_general): + super().__init__(name, gate_type) + self.Gate_In = gate_general('Gate_In', self) + self._Gate_Step = gate_general('_Gate_Step', self) + self._sequence_work : dict = None + self.Sequence_Key : str = '' + + + def _init(self, parent): + super()._init(parent) + self._sequence_work = parent._sequence_work + self.Sequence_Key = parent.Sequence_Key + + def Do(self, timer: step_timer, Gate_In: gate_general): + self._ctrl_ok = False + self.Gate_In.general._value = Gate_In.general._value + self._Gate_Step.general._value = Gate_In.general._value + for gate_func in self._sequence_work[self.Sequence_Key]['funcs']: + gate = gate_func(timer, self.Summe) + self._Gate_Step.general._value = gate.general._value + if self._ctrl_ok: + self.Gate_Ctrl.set(0.0) + return self.Gate_Ctrl + + sum = 0 + for gate in self._sequence_work[self.Sequence_Key]['gates']: + sum = sum + gate.general._value + self.Gate_Ctrl.set(sum) + return self.Gate_Ctrl + + +#***************************************************************** +class sim_dynamic(sim_base): + def __init__(self, name: str, GUI : bool = False, **kwargs): + kwargs.update({'ch_type' : gate_general}) + super().__init__(name, GUI, **kwargs) + + self.Gate_Dynamic = gate_dynamic('Gate_Dynamic', self) + self.Feature : dict = None + self._funcgate.update({ + self.Do : self.Gate_Dynamic}) + + + def _reset(self): + self.Gate_Dynamic._reset() + super()._reset() + + + def _node_success(self, timer: step_timer): + self.Gate_Dynamic._node_success(timer) + + + def Init(self, Feature : None): + super().Init() + self.Feature = Feature + self.Gate_Dynamic.Init(self.Feature) + + + def Do(self, timer: step_timer, Gate_In: gate_general): + self.Gate_Dynamic._actual_values(timer) + return self.Gate_Dynamic + + +#-------------------------------------------------------------------- +class sim_general_node(sim_base): + def __init__(self, name: str, GUI : bool = False, **kwargs): + kwargs.update({'ch_type' : gate_general}) + super().__init__(name, GUI, **kwargs) + + self._iterat_counter : int = 0 + self.Max_Iterat = sim_value('Max_Iterat', self, (int,)) + self.Gate_Node = gate_general('Gate_Node', self) + self.Summe = gate_general('Summe', self) + self.Gate_In = gate_general('Gate_In', self) + + #Controll Strukturen + self._ctrl_nodeok : bool = False + + self.Sequence: dict = dict() # beinhaltet den vollen Namen und die Instance + self.Sequence_Key: str = '' + + self._sequence_work : dict = dict() # beinhaltet verschiedene Informationen für verschiedene Aufgaben wird gebildet + self._sequence_sims : set = set() # speichert alle verlinken sims + self._sims_gates : dict = dict() + self.Joker : sim_general_joker = None + + self._funcgate.update({ + self.Step : self.Gate_Node, + self.StepRec : self.Gate_Node, + self.SumEqual : self.Gate_Node}) + + + def Init(self, + Sequence = None, + Sequence_Key = '', + Joker = False, + Max_Iterat = -1 + ): + + super().Init() + self.Max_Iterat.Init(Max_Iterat) + + if Joker: self.Joker = sim_general_joker(self.name + '.joker') + + if not isinstance(Sequence, dict): + raise Exception(f'Sequence ist kein dict') + self.Sequence = Sequence + + + for sequence_name, sequence_list in self.Sequence.items(): + self._sequence_work.update({sequence_name : { + 'func_names' : [], + 'funcs' : [], + 'sims' : set(), + 'gates' : set() + }}) + + for seq_item in sequence_list: + if isinstance(seq_item, ctrl_base): + seq_item = seq_item._init(self) + + func_info = analyse_func(seq_item) + if not func_info: + raise Exception(f'obj : {self.name} Startfunktion nicht korrekt') + if not isinstance(func_info[0], (work_base, ctrl_base,)): + + raise Exception(f'obj : {self.name} | {func_info[0].name}|node ist kein Type von work_base') + if not callable(func_info[1]): + raise Exception(f'obj : {self.name} |node hat keine func_work methode') + + self._sequence_work[sequence_name]['func_names'].append(func_info[2]) + self._sequence_work[sequence_name]['funcs'].append(func_info[1]) + if isinstance(func_info[0], work_base): + self._sequence_work[sequence_name]['sims'].add(func_info[0]) + self._sequence_sims.add(func_info[0]) + + if not Sequence_Key : + self.Sequence_Key = next(iter(self._sequence_work)) # Der erste wird gewählt + elif Sequence_Key in self._sequence_str.keys(): + self.Sequence_Key = Sequence_Key + else: raise Exception(f'Sequence_Key {Sequence_Key} nicht vorhanden!') + + + def ready_for_start(self, timer: step_timer): + super().ready_for_start(timer) + for sim_obj in self._sequence_sims: + if sim_obj == self: + continue + sim_obj.ready_for_start(timer) + + for func in self._sequence_work[self.Sequence_Key]['funcs']: + for sim_obj in self._sequence_work[self.Sequence_Key]['sims']: + gate = sim_obj._getgate_func(func) + if isinstance(gate, gate_base): + self._sequence_work[self.Sequence_Key]['gates'].add(gate) + break + + #raise Exception('gate to Funktion not found') + + + #----------------------- Simulations Steps --------------------------------- + def _reset(self): + super()._reset() + self.Gate_Node._reset() + self.Gate_In._reset() + self.Summe._reset() + self._iterat_counter = 0 + for sim_obj in self._sequence_work[self.Sequence_Key]['sims']: + if sim_obj == self: + continue + sim_obj._reset() + if not self.Joker == None: + self.Joker._reset() + + + def _calc_node(self): + sum = 0.0 + for gate in self._sequence_work[self.Sequence_Key]['gates']: + sum = sum + gate.get() + + sum = sum - self.Gate_Node.get() + self.Summe.set(sum) + if (abs(sum) < 0.0000001): + return True + return False + + + def _node_success(self, timer: step_timer): + for sim_obj in self._sequence_work[self.Sequence_Key]['sims']: + if sim_obj == self: continue + sim_obj._node_success(timer) + + if not self.Joker == None: + self.Joker._node_success(timer) + + super()._node_success(timer) + self.Gate_Node._node_success(timer) + self.Gate_In._node_success(timer) + + + + def sum_master(self, timer: step_timer, Summe: gate_base = None): + master_gate : gate_general = gate_general('master_gate', self) + master_sum : gate_general = gate_general('master_sum', self) + + if isinstance(Summe, self.ch_type): + self.Summe.set(Summe) + self.Gate_Node.set(Summe) + else: + self.Summe.set(0.0) + + old_master = 0.0 + new_master = 0.0 + master_sum.set(old_master) + while True: + master_func = self._sequence_work[self.Sequence_Key]['funcs'][0] + value = master_func(timer, master_sum).general.value + master_gate.set(-value) + for gate_func in self._sequence_work[self.Sequence_Key]['funcs'][1:]: + gate_func(timer, master_gate) + + if self._calc_node(): + self._node_success(timer) + return self.Gate_Node + + new_master = master_gate.general.value + self.Summe.general.value + master_sum.general.value = new_master + if abs(new_master - old_master) < 0.0000001: + break + + old_master = new_master + + if self.Joker: + self.Joker.Var(timer, self.Summe) + #if self._calc_node(): + self._node_success(timer) + return self.Gate_Node + + + def SumEqual(self, timer: step_timer, Summe: gate_base = None): + self.Summe.general.value = Summe.general.value + # Hier eine Zählschleife um weitere Informationen über den Ablauf zu erhalten + + for gate_func in self._sequence_work[self.Sequence_Key]['funcs']: + gate_func(timer, self.Summe) + if self._ctrl_nodeok: + return self._ctrl_nodeok + + + if self.Joker: + self.Joker.Var(timer, self.Summe) + return self.Gate_Node + + + + def Step(self, timer: step_timer, Gate_In: gate_base = None): + self._ctrl_ok = False + self.Gate_In.general._value = Gate_In.general._value + self.Summe.general._value = Gate_In.general._value + for gate_func in self._sequence_work[self.Sequence_Key]['funcs']: + if gate_func == self.Step: # Verhindern von rekursiven Aufrufs + continue + gate = gate_func(timer, self.Summe) + self.Summe.general._value = gate.general._value + if self._ctrl_ok: + self.Gate_Node.set(0.0) + return self.Gate_Node + + #sum = self.In.general._value + sum = 0 + for gate in self._sequence_work[self.Sequence_Key]['gates']: + sum = sum + gate.general._value + self.Gate_Node.set(sum) + return self.Gate_Node + + + #Rekursiver aktiver Chanal von Step + # ruft sich selber auf wenn kein 0 erreicht ist am Ende + def StepRec(self, timer: step_timer, Gate_In: gate_base = None, counter : int = 0): + max_iterat = self.Max_Iterat.get_set(timer) + + self._ctrl_ok = False + self.Gate_In.general._value = Gate_In.general._value + self.Summe.general._value = Gate_In.general._value + for gate_func in self._sequence_work[self.Sequence_Key]['funcs']: + gate = gate_func(timer, self.Summe) + if gate_func == self.Step: # Verhindern von rekursiven Aufrufs + continue + + self.Summe.general._value = gate.general._value + if self._ctrl_ok: # rekursion beenden + self.Gate_Node.set(0.0) + return self.Gate_Node + + counter = counter + 1 + if (max_iterat >= 0) and (counter < max_iterat): + for sim_obj in self._sequence_work[self.Sequence_Key]['sims']: + if sim_obj == self: continue + sim_obj._reset() + self.StepRec(timer=timer, Gate_In=Gate_In, counter = counter) + + + sum = 0 + for gate in self._sequence_work[self.Sequence_Key]['gates']: + sum = sum + gate.general._value + self.Gate_Node.set(sum) + return self.Gate_Node + + + + def ready_for_end(self): + super().ready_for_end() + for sim_obj in self._sequence_sims: + if sim_obj == self:continue + sim_obj.ready_for_end() + + +class sim_general_chanal(sim_base): + def __init__(self, name: str, GUI : bool = False, **kwargs): + kwargs.update({'ch_type' : gate_general}) + super().__init__(name, GUI, **kwargs) + + self.Gate_Set = gate_general('Gate_Set', self) + self.Gate_Get = gate_general('Gate_Get', self) + self.Gate_Chanal = gate_general('Gate_Chanal', self) + + self._funcgate.update({ + self.Set : self.Gate_Set, + self.Get : self.Gate_Get}) + + + def Init(self, Gate_Set = None, Gate_Get = None): + super().Init() + self.Gate_Set.Init(Gate_Set) + self.Gate_Get.Init(Gate_Get) + self.Gate_Chanal.Init(None) + + + + def Set(self, timer: step_timer, Gate_In: gate_general): + self.Gate_Set.general._value = -Gate_In.general._value + self.Gate_Chanal.general._value = Gate_In.general._value + self.Gate_Get.general._value = 0 + return self.Gate_Set + + + def Get(self, timer: step_timer, Gate_In: gate_general): + self.Gate_Get.general._value = -self.Gate_Set.general._value + self.Gate_Set.general._value = 0 + return self.Gate_Get + + + def _reset(self): + self.Gate_Set._reset() + self.Gate_Get._reset() + self.Gate_Chanal._reset() + super()._reset() + + + def _node_success(self, timer: step_timer): + self.Gate_Set._node_success(timer) + self.Gate_Get._node_success(timer) + self.Gate_Chanal._node_success(timer) + + + +class sim_general_user(sim_base): + def __init__(self, name: str, **kwargs): + + kwargs.update({'ch_type' : gate_general}) + super().__init__(name, **kwargs) + self.Gate_User = gate_general('Gate_User', self) + self.User_Func = sim_value('UserFunc', self, (float, int,)) + self._funcgate.update({ + self.User : self.Gate_User}) + + + def Init(self, + User_Func = None, + Gate_User = None + ): + + super().Init() + self.Gate_User.Init(Gate_User) + self.User_Func.Init(User_Func) + + + def User(self, timer: step_timer, Gate_In: gate_general): + gate = self.User_Func.get_set(stepper = timer, value = Gate_In) + self.Gate_User.general._value = gate.general._value + return self.Gate_User + + + def _reset(self): + self.Gate_User._reset() + super()._reset() + + + def _node_success(self, timer: step_timer): + self.Gate_User._node_success(timer) + + + +#TODO ergänzen um pulswidth von 0..1 +#TODO absolute zeit bezogen auf s einführen +#TODO verschiedene Formen auswählen +class sim_general_signal(sim_base): + def __init__(self, name: str, GUI : bool = False, **kwargs): + kwargs.update({'ch_type' : gate_general}) + super().__init__(name, GUI, **kwargs) + + self.Gate_Signal = gate_general('Gate_Signal', self) + + self.Frequency = sim_value('Frequency', self, (float, int,)) + self.Amplitude = sim_value('Amplitude', self, (float, int,)) + self.Offset = sim_value('Offset', self, (float, int,)) + + self._max_val: float = 0 + self._min_val: float = 0 + self._funcgate.update({ + self.Fix : self.Gate_Signal}) + + + def Init(self, + Amplitude = None, + Frequency = None, + Offset = None, + Gate_Signal = None + ): + + super().Init() + self.Gate_Signal.Init(Gate_Signal) + self.Amplitude.Init(Amplitude) + self.Frequency.Init(Frequency) + self.Offset.Init(Offset) + + self._max_val = self.Amplitude.value - self.Offset.value + self._min_val = self.Offset.value + + + def Fix(self, timer: step_timer, Gate_In: gate_general): + value = int(timer.step_nr / self.Frequency.value) + if value % 2: # ungrade + self.Gate_Signal.set(self._min_val) + else: # grade + self.Gate_Signal.set(self._max_val) + return self.Gate_Signal + + + def _reset(self): + self.Gate_Signal._reset() + super()._reset() + + + def _node_success(self, stepper: step_base): + self.Gate_Signal._node_success(stepper) + + +#-------------------------------------------------------------------- +class sim_general_joker(sim_base): + def __init__(self, name: str, GUI : bool = False, **kwargs): + kwargs.update({'ch_type' : gate_general}) + super().__init__(name, GUI, **kwargs) + + + def Init(self, + Gate = None): + super().Init() + self.Gate.Init(Gate) + + + def _reset(self): + self.Gate._reset() + super()._reset() + + + def _node_success(self, timer: step_timer): + self.Gate._node_success(timer) + + + def Var(self, timer: step_timer, Summe: gate_general): + self.Gate.general.set(-Summe.general.value) + return self.Gate + +#TODO ch_type raus +#-------------------------------------------------------------------- +class sim_general_store(sim_base): + def __init__(self, name: str, GUI : bool = False, **kwargs): + kwargs.update({'ch_type' : gate_general}) + super().__init__(name, GUI, **kwargs) + + self.Gate_Store = gate_general('Gate_Store', self) + self.Gate_Leak = gate_general('Gate_Leak', self) + self.Capacity_Actual = sim_value('Capacity_Actual', self, (float, int,)) + self.Leak_Capacity_Actual = sim_value('Leak_Capacity_Actual', self, (float, int,)) + + self.Capacity_Max = sim_value('Capacity_Max', self, (float, int,)) # Wenn None dann unendlich + + self.Capacity_Min = sim_value('Capacity_Min', self, (float, int,)) # Wenn None dann unendlich - + self.Load_Max = sim_value('Load_Max', self, (float, int,)) # Wenn None dann unendlich schnelles beladen + self.Unload_Max = sim_value('Unload_Max', self, (float, int,)) # Wenn None dann unendlich schnelles entladen + self.Leak_Rate = sim_value('Leak_Rate', self, (float, int,)) + self.Factor = sim_value('Factor', self, (float, int,)) + self.Invert = sim_value('invert', self, (bool,)) + + self.func_leak : dict = {'leak_rate' : 0.0} + + self._funcgate.update({self.Var : self.Gate_Store}) + + + + def Init(self, + Capacity_Max = None, + Capacity_Min = None, + Load_Max = None, + Unload_Max = None, + Leak_Rate = 0.0, + Gate_Store = 0.0, + Gate_Leak = 0.0, + Capacity_Actual = 0.0, + Factor = 1.0, + Invert = False + ): + """ Initialisiert den Speicher + + Keyword Arguments: + Capacity_Max {float oder funktion} -- maximale Kapazität des Speichers (default: {None}) + Kein Wert (None) Speichergröße im positiven Bereich unbegrenzt + Capacity_Min {[type]} -- [description] (default: {None}) + Load_Max {[type]} -- [description] (default: {None}) + Unload_Max {[type]} -- [description] (default: {None}) + Gate_Store {[type]} -- [description] (default: {None}) + Gate_Leak {[type]} -- [description] (default: {None}) + Capacity_Actual {[type]} -- [description] (default: {None}) + Factor{[float, int]} Bereich üblicherweise zwischen 0.0 und 1.0 + """ + + super().Init() + + self.Gate_Store.Init(Gate_Store) + self.Capacity_Actual.Init(Capacity_Actual, default = 0.0) + self.Gate_Leak.Init(Gate_Leak) + self.Capacity_Max.Init(Capacity_Max) + self.Capacity_Min.Init(Capacity_Min) + self.Load_Max.Init(Load_Max) + self.Unload_Max.Init(Unload_Max) + + self.Factor.Init(Factor, default = 1.0) + self.Invert.Init(Invert, default = False) + if isinstance(Leak_Rate, (float, int,)): + self.func_leak['leak_rate'] = Leak_Rate + Leak_Rate = self._leak + self.Leak_Rate.Init(Leak_Rate, default = 0.0) + + + + def _reset(self): + self.Gate_Store._reset() + super()._reset() + + # Es wird die Differenz berechnet + def _leak(self, value = None, timer : step_timer = None): + rate = self.func_leak['leak_rate'] + akt_cap = self.Capacity_Actual.value + if rate == 0.0: + return 0.0 + new_cap = akt_cap * math.exp(-rate * timer.total_sec) + return akt_cap - new_cap + + + + def calc_loadcapacity(self, value: float): + if not isinstance(self.Capacity_Max._value, (float, int,)): + return value + full_diff = (self.Capacity_Max._value - + self.Capacity_Actual._value) / self.time_factor + if full_diff > value: + return value + elif full_diff == 0.0: + return 0.0 + else: + return full_diff + + + + def calc_unloadcapacity(self, value: float): + if not isinstance(self.Capacity_Min._value, float): + return value + empty_diff = -(self.Capacity_Min._value - + self.Capacity_Actual._value) / self.time_factor + if empty_diff >= -value: + return value + elif empty_diff == 0: + return 0.0 + else: + return -empty_diff + + #TODO es sollte noch eine variante zu Var geben, um z.B. einfach Verluste Widerstände zu simulieren + def Var(self, timer: step_timer, Gate_In: gate_general): + aim_value = -Gate_In.general.value * self.Factor.get_set(timer) + if self.Invert.value : aim_value = -aim_value + if aim_value >= 0: # Beladen + max_value = self.Load_Max.get_set(stepper=timer) + if max_value == None: # Keine Begrenzung + real_value = aim_value + elif max_value >= 0 and max_value > aim_value: + real_value = aim_value + elif max_value >= 0 and max_value <= aim_value: + real_value = max_value + elif max_value < 0: + real_value = max_value + else: + max_value = self.Unload_Max.get_set(stepper=timer) + if max_value == None: # Keine Begrenzung + real_value = aim_value + elif max_value <= 0 and max_value < aim_value: + real_value = aim_value + elif max_value <= 0 and max_value >= aim_value: + real_value = max_value + elif max_value > 0: + real_value = max_value + + if real_value >= 0: # Beladen + real_value = self.calc_loadcapacity(real_value) + else: # Entladen + real_value = self.calc_unloadcapacity(real_value) + + self.Gate_Store.set(real_value) #Todo Set verbessern damit auch ein Callback erfolgt + return self.Gate_Store + + + # Immer den gleichen Wert speichern + def Fix_Load(self, timer: step_timer, Summe: gate_general): + self.Gate_Store.set(self.Load_Max) + return self.Gate_Store, True + + + # Immer den gleichen Wert entladen + def Fix_Unload(self, timer: step_timer, Summe: gate_general): + self.Gate_Store.set(self.Unload_Max) + return self.Gate_Store, True + + + def _node_success(self, timer: step_timer): + super()._node_success(timer) + + corr_value = self.Gate_Store.get() * self.time_factor + value = self.Capacity_Actual._value + corr_value + self.Capacity_Actual._value = value + + diff = self.Leak_Rate.get_set(stepper=timer) + self.Capacity_Actual._value = self.Capacity_Actual._value - diff + #self.Leak_Capacity_Actual._value = self.Leak_Capacity_Actual._value + diff + + self.Gate_Store._node_success(timer) + self.Capacity_Actual._node_success(timer) + self.Gate_Leak._node_success(timer) diff --git a/nowo1_gui_base.py b/nowo1_gui_base.py new file mode 100644 index 0000000000000000000000000000000000000000..5cb83109a02d7ca9560953e47ef9a2f7aa4f7a93 --- /dev/null +++ b/nowo1_gui_base.py @@ -0,0 +1,47 @@ +import sim_base as sim_g +import nowo1_base as nowo +import datetime as dt +import pandas as pd +import numpy as np + +import ipywidgets as widgets +import ipysheet +from ipysheet import from_dataframe +from IPython.display import display + + + +class gui_ipysheet(nowo.gui_base): + def __init__(self, name: str, GUI : bool = False, **kwargs): + super().__init__(name, GUI, **kwargs) + self.sheet : ipysheet.sheet = None + self.sheet_colnames : list = [] + self.sheet_rownames : list = [] + self.sheet_data: pd.DataFrame = None + self.dataframe_out: pd.DataFrame = None + + + def Init_by_dataframe(self, dataframe): + self.sheet_data = dataframe + self.sheet_colnames = list(self.sheet_data.head()) + self.sheet_rownames = list(self.sheet_data.index.tolist()) + + + def ready_for_end(self): + buffer = self.sheet_data.values.T.tolist() + ipysheet.cell_range(buffer[0], column_start=1) + # self.dataframe_out = ipysheet.to_dataframe(self.sheet) + #self.sheet_data.to_clipboard() + + def _create_gui(self): + # sheet = ipysheet.sheet(rows = len(self.sheet_rownames) + 1, columns= len(self.sheet_colnames) + 1) + self.sheet = ipysheet.sheet(rows = len(self.sheet_rownames), + columns= len(self.sheet_colnames) + 1, + column_headers = ['variable'] + self.sheet_colnames) + + cols_1 = ipysheet.column(0, self.sheet_rownames, row_start = 0) + + total_box = widgets.VBox([self.sheet]) + self._GUI_itemlist.update({'sheet' : self.sheet}) + super()._create_gui()# Attention! --must-- because the widget get a parent attr. + return total_box \ No newline at end of file diff --git a/nowo1_log_base.py b/nowo1_log_base.py new file mode 100644 index 0000000000000000000000000000000000000000..c65b4f7f11bd681416f3ec2dda75d39aee645d5c --- /dev/null +++ b/nowo1_log_base.py @@ -0,0 +1,85 @@ +import sim_base as sim_g +import nowo1_base as nowo +import datetime as dt +import pandas as pd +import numpy as np + +class logger(nowo.port_base): + def __init__(self, name: str, GUI : bool = False, **kwargs): + super().__init__(name, GUI, **kwargs) + self.log_data: pd.DataFrame = pd.DataFrame() + + def clear_buffer(self): + super().clear_buffer() + self.log_data = pd.DataFrame() + + def ready_for_end(self): + super().ready_for_end() + self.log_data = pd.DataFrame(dict([ (k,pd.Series(v)) for k,v in self.buffer_data.items() ])) + + + +# Nimmt nur den ersten Wert und bildet eine Tabelle +# wird verwendet bei gleichen sim_obj die nur einmal berechnet wurden +class log_sheet(nowo.port_base): + def __init__(self, name: str, GUI : bool = False, **kwargs): + self.log_data: pd.DataFrame = pd.DataFrame() + super().__init__(name, GUI, **kwargs) + self.sheet_colnames : list = [] + self.sheet_rownames : list = [] + self.sheet_data: pd.DataFrame = None + + + def clear_buffer(self): + super().clear_buffer() + self.log_data = pd.DataFrame() + + + def Init(self, Values = [], Gui_For_Data = None, ): + super().Init(Values, Gui_For_Data) + for name in self.buffer_data.keys(): + split_name = name.split('.', 1) + if not split_name[0] in self.sheet_colnames: + self.sheet_colnames.append(split_name[0]) + if not split_name[1] in self.sheet_rownames: + self.sheet_rownames.append(split_name[1]) + self.sheet_data = pd.DataFrame(columns = self.sheet_colnames, index = self.sheet_rownames) + if self.Gui_For_Data: + self.Gui_For_Data.Init_by_dataframe(self.sheet_data) + + + def ready_for_end(self): + super().ready_for_end() + # self.log_data = pd.DataFrame(dict([ (k,pd.Series(v)) for k,v in self.buffer_data.items() ])) + + for col_name in self.sheet_colnames: + for row_name in self.sheet_rownames: + key_name = col_name + '.' + row_name + value = self.buffer_data[key_name] + self.sheet_data.at[row_name, col_name] = value + self.sheet_data.to_clipboard() + if self.Gui_For_Data: + self.Gui_For_Data.ready_for_end() + + + +class log_dataframe(nowo.port_base): + def __init__(self, name: str, GUI : bool = False, **kwargs): + super().__init__(name, GUI, **kwargs) + self.log_data: pd.DataFrame = pd.DataFrame() + + def clear_buffer(self): + super().clear_buffer() + self.log_data = pd.DataFrame() + + + def ready_for_end(self): + super().ready_for_end() + new = pd.DataFrame() + for name, valuelist in self.buffer_data.items(): + for value in valuelist: + if isinstance(value, pd.DataFrame): + new = pd.concat([new,value], ignore_index=True) + + self.log_data = new + self.col_name = self.log_data.columns.tolist() \ No newline at end of file diff --git a/nowo1_sim_binary.py b/nowo1_sim_binary.py new file mode 100644 index 0000000000000000000000000000000000000000..953e140f559f1fa781a17afc07c4cfb287132dd8 --- /dev/null +++ b/nowo1_sim_binary.py @@ -0,0 +1,406 @@ + +import sim_base as sim_g +import nowo1_base as nowo +import datetime as dt +import pandas as pd +import numpy as np +import ipywidgets as widgets +from IPython.display import display + + +class binary_node(nowo.sim_base): + def __init__(self, name: str, **kwargs): + kwargs.update({'ch_type' : nowo.gate_general}) + super().__init__(name, **kwargs) + self.Gate_A = nowo.gate_general('Gate_A', self) + self.Gate_B = nowo.gate_general('Gate_B', self) + self.Gate_A_B = nowo.gate_general('Gate_A_B', self) + + self.A_ratio = nowo.sim_value('A_ratio', self, (float, int,)) + self.B_ratio = nowo.sim_value('B_ratio', self, (float, int,)) + self.A_portion = nowo.sim_value('A_portion', self, (float, int,)) + self.B_portion = nowo.sim_value('B_portion', self, (float, int,)) + + self.Info = nowo.sim_value('Info', self, (str,)) + + self.calcflag : bool = True + self._value_key : np.byte = 0b00000000 + + + self._checkstruct : dict = dict( + {'one_val' : [0,1,2,4,8,16,32,64]} + ) + + self._value_keys : dict = { + 'A' : 0b01000000, + 'B' : 0b00100000, + 'A_B': 0b00010000, + 'A_ratio': 0b00001000, + 'B_ratio': 0b00000100, + 'A_portion':0b00000010, + 'B_portion':0b00000001 + } + + self._funcgate.update({ + self.Calc_A : self.Gate_A, + self.Calc_B : self.Gate_B, + self.Calc_A_B : self.Gate_A_B + }) + + + def _create_gui(self): + options =[('Strom A', 'A'), + ('Strom B', 'B'), + ('Summe A und B', 'A_B'), + ('Verhältnis A', 'A_ratio'), + ('Verhältnis B', 'B_ratio'), + ('Anteil A', 'A_portion'), + ('Anteil B', 'B_portion') + ] + + layout_select = widgets.Layout(width='100%') + layout_valbox = widgets.Layout(padding = '1em 0 0 0', background_color='red') + + val_label_1 = widgets.Label(value='extensive Größe:') + val_select_1 = widgets.Dropdown(options = options[:3], + value='A', + #description='Extensive Größe:', + layout = layout_select + ) + value_1 = widgets.FloatText(value = 0.0, layout=widgets.Layout(width='15%')) + + val_label_2 = widgets.Label(value='extensive oder intensive Größe') + val_select_2 = widgets.Dropdown(options = options, + value='B', + #description='extensive oder intensive Größe:', + layout = layout_select) + value_2 = widgets.FloatText(value = 0.0, layout=widgets.Layout(width='15%')) + + val1_box = widgets.VBox([val_label_1, widgets.HBox([val_select_1, value_1])], + background_color='red') + val2_box = widgets.VBox([val_label_2, widgets.HBox([val_select_2, value_2])], layout = layout_valbox) + + + total_box = widgets.VBox([val1_box, val2_box], background_color='red') + + self._GUI_itemlist.update({ + 'val_select_1' : val_select_1, + 'value_1' : value_1, + 'val_select_2' : val_select_2, + 'value_2' : value_2, + }) + super()._create_gui()# Attention! --must-- because the widget get a parent attr. + return total_box + + + def Init_Over_GUI(self): + para = { + self._GUI_itemlist['val_select_1'].value : self._GUI_itemlist['value_1'].value, + self._GUI_itemlist['val_select_2'].value : self._GUI_itemlist['value_2'].value + } + self.Init(**para) + + + def Init(self, + A = np.nan, + B = np.nan, + A_B = np.nan, + A_ratio = np.nan, + B_ratio = np.nan, + A_portion = np.nan, + B_portion = np.nan, + ): + + super().Init() + self._value_key = 0b00000000 + self.Gate_A.general.Init(A) + self.Gate_B.general.Init(B) + self.Gate_A_B.general.Init(A_B) + self.A_ratio.Init(A_ratio) + self.B_ratio.Init(B_ratio) + self.A_portion.Init(A_portion) + self.B_portion.Init(B_portion) + self.Info.Init('kein Kommentar') + self.Info._value = 'Oh mann' + + + def _set_values(self): + if not self._korr_key & 0b01000000: self.A = np.nan + if not self._korr_key & 0b00100000: self.B = np.nan + if not self._korr_key & 0b00010000: self.A_B = np.nan + if not self._korr_key & 0b00001000: self.A_ratio = np.nan + if not self._korr_key & 0b00000100: self.B_ratio = np.nan + if not self._korr_key & 0b00000010: self.A_portion = np.nan + if not self._korr_key & 0b00000001: self.B_portion = np.nan + + + # es muss mindestens ein A oder B oder A_B vorhanden sein aber nicht mehr wie zwei + def _check_values(self, stepper : nowo.step_base): + # Build zahl bei values + count : int = 0 + if not np.isnan(self.Gate_A.general.get_set(stepper)): + self._value_key = self._value_key |self._value_keys['A'] + count = count + 1 + if not np.isnan(self.Gate_B.general.get_set(stepper)): + self._value_key = self._value_key |self._value_keys['B'] + count = count + 1 + if not np.isnan(self.Gate_A_B.general.get_set(stepper)): + self._value_key = self._value_key |self._value_keys['A_B'] + count = count + 1 + if not np.isnan(self.A_ratio.get_set(stepper)): + self._value_key = self._value_key |self._value_keys['A_ratio'] + count = count + 1 + if not np.isnan(self.B_ratio.get_set(stepper)): + self._value_key = self._value_key |self._value_keys['B_ratio'] + count = count + 1 + if not np.isnan(self.A_portion.get_set(stepper)): + self._value_key = self._value_key |self._value_keys['A_portion'] + count = count + 1 + if not np.isnan(self.B_portion.get_set(stepper)): + self._value_key = self._value_key |self._value_keys['B_portion'] + count = count + 1 + + if count < 2: + self.Info._value = 'Es müssen mind. 2 unterschiedliche Werte angeben werden' + return False + elif count > 2: + self.Info._value = 'Es dürfen nicht mehr als 2 unterschiedliche Werte angeben werden' + return False + + # Abfrage ob wenigstens eine Masse vorhanden ist + if self._value_key < 16: + self.Info._value = 'Es muss min. ein Strom (A, B oder A_B) angegeben werden' + return False + + return True + + + def Calc_A(self, stepper : nowo.step_base, Gate_In: nowo.gate_general): + self._calc(stepper, Gate_In) + return self.Gate_A + + + def Calc_B(self, stepper : nowo.step_base, Gate_In: nowo.gate_general): + self._calc(stepper, Gate_In) + return self.Gate_B + + + def Calc_A_B(self, stepper : nowo.step_base, Gate_In: nowo.gate_general): + self._calc(stepper, Gate_In) + return self.Gate_A_B + + + def _calc(self, stepper : nowo.step_base, Gate_In: nowo.gate_general): + if not self._check_values(stepper): return + self.calcflag = True + while self.calcflag: + self.calcflag = False + self._calc_A_ratio() + self._calc_B_ratio() + self._calc_A_portion() + self._calc_B_portion() + self._calc_A() + self._calc_B() + self._calc_A_B() + + + def _set_calcflag(self, flag): + if not self.calcflag: + self.calcflag = flag + + + + def _calc_A_ratio(self): + if self._value_keys['A_ratio'] & self._value_key: + self._set_calcflag(False) + return + + try: + if self._value_keys['A_portion'] & self._value_key: + self.A_ratio._value = 1/(1/self.A_portion._value -1) + + elif self._value_keys['B_ratio'] & self._value_key: + self.A_ratio._value = 1/self.B_ratio._value + + elif self._value_keys['A'] & self._value_key and self._value_keys['B'] & self._value_key: + self.A_ratio._value = self.Gate_A.general._value / self.Gate_B.general._value + + elif self._value_keys['A_B'] & self._value_key and self._value_keys['A'] & self._value_key: + self.A_ratio._value = self.Gate_A.general._value / (self.Gate_A_B.general._value - self.Gate_A.general._value) + + elif self._value_keys['A_B'] & self._value_key and self._value_keys['B'] & self._value_key: + self.A_ratio._value = (self.Gate_A_B.general._value - self.Gate_B.general._value) / self.B + + else: + self._set_calcflag(False) + return + self._set_calcflag(True) + self._value_key = self._value_key | self._value_keys['A_ratio'] + except ZeroDivisionError as error: + pass + except Exception as exception: + pass + + + + def _calc_A_portion(self): + if self._value_keys['A_portion'] & self._value_key: + self._set_calcflag(False) + return + try: + if self._value_keys['A_ratio'] & self._value_key: + self.A_portion._value = 1/(1/self.A_ratio._value +1) + else: + self._set_calcflag(False) + return + self._set_calcflag(True) + self._value_key = self._value_key | self._value_keys['A_portion'] + + except ZeroDivisionError as error: + pass + except Exception as exception: + pass + + + + def _calc_B_portion(self): + if self._value_keys['B_portion'] & self._value_key: + self._set_calcflag(False) + return + + try: + if self._value_keys['B_ratio'] & self._value_key: + self.B_portion._value = 1/(1/self.B_ratio._value +1) + + else: + self._set_calcflag(False) + return + self._set_calcflag(True) + self._value_key = self._value_key | self._value_keys['B_portion'] + + except ZeroDivisionError as error: + pass + except Exception as exception: + pass + + + + def _calc_B_ratio(self): + if self._value_keys['B_ratio'] & self._value_key: + self._set_calcflag(False) + return + try: + if self._value_keys['A_ratio'] & self._value_key: + self.B_ratio._value = 1/self.A_ratio._value + + elif self._value_keys['B_portion'] & self._value_key: + self.B_ratio._value = 1/(1/self.B_portion._value -1) + + elif self._value_keys['A'] & self._value_key and self._value_keys['B'] & self._value_key: + self.B_ratio._value = self.Gate_B.general._value / self.Gate_A.general._value + + elif self._value_keys['A_B'] & self._value_key and self._value_keys['B'] & self._value_key: + self.B_ratio._value = self.Gate_B.general._value / (self.Gate_A_B.general._value - self.Gate_B.general._value) + + elif self._value_keys['A_B'] & self._value_key and self._value_keys['A'] & self._value_key: + self.B_ratio._value = (self.Gate_A_B.general._value - self.Gate_A.general._value) / self.Gate_A.general._value + else: + self._set_calcflag(False) + return + self._set_calcflag(True) + self._value_key = self._value_key | self._value_keys['B_ratio'] + + except ZeroDivisionError as error: + pass + except Exception as exception: + pass + + + + def _calc_A(self): + if self._value_keys['A'] & self._value_key: + self._set_calcflag(False) + return + + try: + if self._value_keys['A_B'] & self._value_key and self._value_keys['B'] & self._value_key: + self.Gate_A.general._value = (self.Gate_A_B.general._value - self.B) + + elif self._value_keys['A_B'] & self._value_key and self._value_keys['A_portion'] & self._value_key: + self.Gate_A.general._value = (self.Gate_A_B.general._value * self.A_portion._value) + + elif self._value_keys['A_B'] & self._value_key and self._value_keys['A_ratio'] & self._value_key: + self.Gate_A.general._value = (self.Gate_A_B.general._value * self.A_portion._value) + + elif self._value_keys['B'] & self._value_key and self._value_keys['A_ratio'] & self._value_key: + self.Gate_A.general._value = (self.Gate_B.general._value * self.A_ratio._value) + + else: + self._set_calcflag(False) + return + self._set_calcflag(True) + self._value_key = self._value_key | self._value_keys['A'] + + except ZeroDivisionError as error: + pass + except Exception as exception: + pass + + + + def _calc_B(self): + if self._value_keys['B'] & self._value_key:# + self._set_calcflag(False) + return + + try: + if self._value_keys['A_B'] & self._value_key and self._value_keys['A'] & self._value_key: + self.Gate_B.general._value = (self.Gate_A_B.general._value - self.Gate_A.general._value) + elif self._value_keys['A_B'] & self._value_key and self._value_keys['B_portion'] & self._value_key: + self.Gate_B.general._value = (self.Gate_A_B.general._value * self.B_portion._value) + elif self._value_keys['A'] & self._value_key and self._value_keys['B_ratio'] & self._value_key: + self.Gate_B.general._value = (self.Gate_A.general._value * self.B_ratio._value) + + else: + self._set_calcflag(False) + return + self._set_calcflag(True) + self._value_key = self._value_key | self._value_keys['B'] + + except ZeroDivisionError as error: + pass + except Exception as exception: + pass + + + + + def _calc_A_B(self): + if self._value_keys['A_B'] & self._value_key: + self._set_calcflag(False) + return + + if self._value_keys['A'] & self._value_key and self._value_keys['B'] & self._value_key: + self.Gate_A_B.general._value = (self.Gate_A.general._value + self.Gate_B.general._value) + self._set_calcflag(True) + self._value_key = self._value_key | self._value_keys['A_B'] + return + self._set_calcflag(False) + + + def _reset(self): + self.Gate_A._reset() + self.Gate_B._reset() + self.Gate_A_B._reset() + super()._reset() + + + def _node_success(self, stepper: nowo.step_base): + self.Gate_A._node_success(stepper) + self.Gate_B._node_success(stepper) + self.Gate_A_B._node_success(stepper) + self.A_ratio._node_success(stepper) + self.B_ratio._node_success(stepper) + self.A_portion._node_success(stepper) + self.B_portion._node_success(stepper) + self.Info._node_success(stepper) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e76e091f268ad6d943051645e639b13f08ec2ceb..a02b5803fc417385ed06045d798c016ac268c78f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ voila voila-gridstack +ipysheet diff --git a/sim_base.py b/sim_base.py new file mode 100644 index 0000000000000000000000000000000000000000..865172dd2ee94c8f8ab96436e8a816f684059117 --- /dev/null +++ b/sim_base.py @@ -0,0 +1,11 @@ +import pathlib +import sys + +# Globale Festlegungen +Main_Modul = sys.modules['__main__'] +Self_Modulname = globals()['__name__'] +Self_Modul = sys.modules[Self_Modulname] + +#sim_obj_factory = Main_Modul.sim_obj_factory + +timepattern = '%Y-%m-%d %H:%M:%S' \ No newline at end of file