From 7c94e73be7e825b1e5bc98f283efdcc10dd021de Mon Sep 17 00:00:00 2001
From: Olle Hansson <olle.hansson@liu.se>
Date: Wed, 15 Feb 2023 08:58:32 +0100
Subject: [PATCH] Closes #128, #134, #138. Working on #136

---
 b_asic/GUI/main_window.py         |  26 +++++-
 b_asic/GUI/simulate_sfg_window.py | 149 +++++++++++++++++++++++++-----
 2 files changed, 151 insertions(+), 24 deletions(-)

diff --git a/b_asic/GUI/main_window.py b/b_asic/GUI/main_window.py
index 34cd0d34..3a4633af 100644
--- a/b_asic/GUI/main_window.py
+++ b/b_asic/GUI/main_window.py
@@ -65,6 +65,8 @@ class MainWindow(QMainWindow):
         self.operationDragDict = {}
         self.operationItemSceneList = []
         self.signalList = []
+        self.mouse_pressed = False
+        self.mouse_draggin = False
         self.pressed_operations = []
         self.portDict = {}
         self.signalPortDict = {}
@@ -191,6 +193,7 @@ class MainWindow(QMainWindow):
             operation_positions[op_drag.operation.graph_id] = (
                 int(op_scene.x()),
                 int(op_scene.y()),
+                op_drag.is_flipped(),
             )
 
         try:
@@ -251,10 +254,17 @@ class MainWindow(QMainWindow):
         if positions is None:
             positions = {}
 
+        # print(sfg)
         for op in sfg.split():
+            # print(op)
             self.create_operation(
                 op,
-                positions[op.graph_id] if op.graph_id in positions else None,
+                positions[op.graph_id][0:2]
+                if op.graph_id in positions
+                else None,
+                positions[op.graph_id][-1]
+                if op.graph_id in positions
+                else None,
             )
 
         def connect_ports(ports):
@@ -484,8 +494,14 @@ class MainWindow(QMainWindow):
             namespace, self.ui.custom_operations_list
         )
 
-    def create_operation(self, op, position=None):
+    def create_operation(self, op, position=None, is_flipped=False):
         try:
+            if op in self.operationDragDict:
+                self.logger.warning(
+                    "Multiple instances of operation with same name"
+                )
+                return
+
             attr_button = DragButton(op.graph_id, op, True, window=self)
             if position is None:
                 attr_button.move(GRID * 3, GRID * 2)
@@ -537,8 +553,14 @@ class MainWindow(QMainWindow):
             )
             operation_label.moveBy(10, -20)
             attr_button.add_label(operation_label)
+
+            if isinstance(is_flipped, bool):
+                if is_flipped:
+                    attr_button._flip()
+
             self.operationDragDict[op] = attr_button
             self.dragOperationSceneDict[attr_button] = attr_button_scene
+
         except Exception as e:
             self.logger.error(
                 "Unexpected error occurred while creating operation: " + str(e)
diff --git a/b_asic/GUI/simulate_sfg_window.py b/b_asic/GUI/simulate_sfg_window.py
index 8989d75d..9a7429de 100644
--- a/b_asic/GUI/simulate_sfg_window.py
+++ b/b_asic/GUI/simulate_sfg_window.py
@@ -1,6 +1,7 @@
 """
 B-ASIC window to simulate an SFG.
 """
+import numpy as np
 from matplotlib.backends.backend_qt5agg import (
     FigureCanvasQTAgg as FigureCanvas,
 )
@@ -9,6 +10,7 @@ from qtpy.QtCore import Qt, Signal
 from qtpy.QtGui import QKeySequence
 from qtpy.QtWidgets import (
     QCheckBox,
+    QComboBox,
     QDialog,
     QFileDialog,
     QFormLayout,
@@ -24,6 +26,8 @@ from qtpy.QtWidgets import (
     QVBoxLayout,
 )
 
+from b_asic.signal_generator import Impulse, Step, ZeroPad
+
 
 class SimulateSFGWindow(QDialog):
     simulate = Signal()
@@ -42,6 +46,8 @@ class SimulateSFGWindow(QDialog):
         self.simulate_btn.clicked.connect(self.save_properties)
         self.dialog_layout.addWidget(self.simulate_btn)
         self.setLayout(self.dialog_layout)
+        self.input_grid = QGridLayout()
+        self.input_files = {}
 
     def add_sfg_to_dialog(self, sfg):
         sfg_layout = QVBoxLayout()
@@ -66,36 +72,37 @@ class SimulateSFGWindow(QDialog):
             "iteration_count": spin_box,
             "show_plot": check_box_plot,
             "all_results": check_box_all,
-            "input_values": [],
         }
 
         if sfg.input_count > 0:
             input_label = QLabel("Input values:")
             options_layout.addRow(input_label)
 
-            input_grid = QGridLayout()
             x, y = 0, 0
             for i in range(sfg.input_count):
-                input_layout = QHBoxLayout()
-                input_layout.addStretch()
                 if i % 2 == 0 and i > 0:
                     x += 1
                     y = 0
 
                 input_label = QLabel(f"in{i}")
-                input_layout.addWidget(input_label)
-                input_value = QLineEdit()
-                input_value.setPlaceholderText("e.g. 0, 0, 0")
-                input_value.setFixedWidth(100)
-                input_layout.addWidget(input_value)
-                input_layout.addStretch()
-                input_layout.setSpacing(10)
-                input_grid.addLayout(input_layout, x, y)
-
-                self.input_fields[sfg]["input_values"].append(input_value)
+                self.input_grid.addWidget(input_label, i, 0)
+
+                input_dropdown = QComboBox()
+                input_dropdown.insertItems(
+                    0, ["Impulse", "Step", "Input", "File"]
+                )
+                input_dropdown.currentTextChanged.connect(
+                    lambda text, i=i: self.change_input_format(i, text)
+                )
+                self.input_grid.addWidget(
+                    input_dropdown, i, 1, alignment=Qt.AlignLeft
+                )
+
+                self.change_input_format(i, "Impulse")
+
                 y += 1
 
-            sfg_layout.addLayout(input_grid)
+            sfg_layout.addLayout(self.input_grid)
 
         frame = QFrame()
         frame.setFrameShape(QFrame.HLine)
@@ -105,6 +112,59 @@ class SimulateSFGWindow(QDialog):
         self.sfg_to_layout[sfg] = sfg_layout
         self.dialog_layout.addLayout(sfg_layout)
 
+    def change_input_format(self, i, text):
+        grid = self.input_grid.itemAtPosition(i, 2)
+        if grid:
+            for j in reversed(range(grid.count())):
+                item = grid.itemAt(j)
+                widget = item.widget()
+                if widget:
+                    widget.hide()
+            self.input_grid.removeItem(grid)
+
+        param_grid = QGridLayout()
+
+        if text == "Impulse":
+            delay_label = QLabel("Delay")
+            param_grid.addWidget(delay_label, 0, 0)
+            delay_spin_box = QSpinBox()
+            delay_spin_box.setRange(0, 2147483647)
+            param_grid.addWidget(delay_spin_box, 0, 1)
+        elif text == "Step":
+            delay_label = QLabel("Delay")
+            param_grid.addWidget(delay_label, 0, 0)
+            delay_spin_box = QSpinBox()
+            delay_spin_box.setRange(0, 2147483647)
+            param_grid.addWidget(delay_spin_box, 0, 1)
+        elif text == "Input":
+            input_label = QLabel("Input")
+            param_grid.addWidget(input_label, 0, 0)
+            input_sequence = QLineEdit()
+            param_grid.addWidget(input_sequence, 0, 1)
+            zpad_label = QLabel("Zpad")
+            param_grid.addWidget(zpad_label, 1, 0)
+            zpad_button = QCheckBox()
+            param_grid.addWidget(zpad_button, 1, 1)
+        elif text == "File":
+            file_label = QLabel("Browse")
+            param_grid.addWidget(file_label, 0, 0)
+            file_browser = QPushButton("No file selected")
+            file_browser.clicked.connect(
+                lambda i=i: self.get_input_file(i, file_browser)
+            )
+            param_grid.addWidget(file_browser, 0, 1)
+        else:
+            raise Exception("Input selection is not implemented")
+
+        self.input_grid.addLayout(param_grid, i, 2)
+
+        return
+
+    def get_input_file(self, i, file_browser):
+        module, accepted = QFileDialog().getOpenFileName()
+        file_browser.setText(module)
+        return
+
     def parse_input_values(self, input_values):
         _input_values = []
         for _list in input_values:
@@ -128,15 +188,60 @@ class SimulateSFGWindow(QDialog):
 
     def save_properties(self):
         for sfg, _properties in self.input_fields.items():
-            input_values = self.parse_input_values(
-                [
-                    widget.text().split(",") if widget.text() else [0]
-                    for widget in self.input_fields[sfg]["input_values"]
-                ]
-            )
+            ic_value = self.input_fields[sfg]["iteration_count"].value()
+            if ic_value == 0:
+                self._window.logger.error("Iteration count is set to zero.")
+
+            tmp = []
+
+            for i in range(self.input_grid.rowCount()):
+                in_format = (
+                    self.input_grid.itemAtPosition(i, 1).widget().currentText()
+                )
+                in_param = self.input_grid.itemAtPosition(i, 2)
+
+                tmp2 = []
+
+                if in_format == "Impulse":
+                    g = Impulse(in_param.itemAtPosition(0, 1).widget().value())
+                    for j in range(ic_value):
+                        tmp2.append(str(g(j)))
+
+                elif in_format == "Step":
+                    g = Step(in_param.itemAtPosition(0, 1).widget().value())
+                    for j in range(ic_value):
+                        tmp2.append(str(g(j)))
+
+                elif in_format == "Input":
+                    widget = in_param.itemAtPosition(0, 1).widget()
+                    tmp3 = widget.text().split(",")
+                    if in_param.itemAtPosition(1, 1).widget().isChecked():
+                        g = ZeroPad(tmp3)
+                        for j in range(ic_value):
+                            tmp2.append(str(g(j)))
+                    else:
+                        tmp2 = tmp3
+
+                elif in_format == "File":
+                    widget = in_param.itemAtPosition(0, 1).widget()
+                    path = widget.text()
+                    try:
+                        tmp2 = np.loadtxt(path, dtype=str).tolist()
+                    except FileNotFoundError:
+                        self._window.logger.error(
+                            f"Selected input file not found."
+                        )
+                        continue
+                else:
+                    raise Exception("Input selection is not implemented")
+
+                tmp.append(tmp2)
+
+            input_values = self.parse_input_values(tmp)
+
             max_len = max(len(list_) for list_ in input_values)
             min_len = min(len(list_) for list_ in input_values)
-            ic_value = self.input_fields[sfg]["iteration_count"].value()
+
             if max_len != min_len:
                 self._window.logger.error(
                     "Minimum length of input lists are not equal to maximum "
-- 
GitLab