From 8c5f98cc1534d7d9002120c02126df8c11fe4b41 Mon Sep 17 00:00:00 2001
From: angloth <angus.lothian@hotmail.com>
Date: Mon, 18 May 2020 15:12:50 +0200
Subject: [PATCH] Add temporary working printing of Schema without correct
 forwards and backwards slack

---
 b_asic/schema.py | 113 +++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 95 insertions(+), 18 deletions(-)

diff --git a/b_asic/schema.py b/b_asic/schema.py
index 157ed3b8..9c2a644f 100644
--- a/b_asic/schema.py
+++ b/b_asic/schema.py
@@ -3,7 +3,9 @@ This module contains the Schema class.
 TODO: More info
 """
 
-from typing import Dict, List
+from io import StringIO
+from typing import Dict, List, Set
+import math
 
 from b_asic.signal_flow_graph import SFG
 from b_asic.graph_component import GraphID
@@ -19,6 +21,10 @@ class Schema:
     _schedule_time: int
     _cyclic: bool
     _resolution: int
+    _max_end_op_id: GraphID
+    _max_end_time: int
+    _input_laps: Dict[GraphID, List[int]]
+    _non_schedulable_ops: Set[GraphID]
 
     def __init__(self, sfg: SFG, schedule_time: int = None, cyclic: bool = False, resolution: int = 1, scheduling_alg: str = "ASAP"):
 
@@ -31,44 +37,116 @@ class Schema:
         if scheduling_alg == "ASAP":
             self._schedule_asap()
         else:
-            raise NotImplementedError(f"No algorithm with name: {scheduling_alg} defined.")
+            raise ValueError(f"No algorithm with name: {scheduling_alg} defined.")
 
-        max_end_time = 0
+        # Set latest time of the schema.
+        self._latest_op_time = 0
         for op_id, op_start_time in self._start_times.items():
             op = self._sfg.find_by_id(op_id)
             for outport in op.outputs:
-                max_end_time = max(max_end_time, op_start_time + outport.latency_offset)
+                if op_start_time + outport.latency_offset > self._latest_op_time:
+                    self._max_end_op_id = op_id
+                    self._max_end_time = op_start_time + outport.latency_offset
 
         if not self._cyclic:
             if schedule_time is None:
-                self._schedule_time = max_end_time
-            elif schedule_time < max_end_time:
-                raise ValueError("Too short schedule time for non-cyclic Schedule entered.")
+                self.set_schedule_time(self._max_end_time)
             else:
+                self.set_schedule_time(schedule_time)
+        else:
+            raise NotImplementedError("Cyclic schedules not implemented yet.")
+
+    def __str__(self):
+        string_io = StringIO()
+        for op in self._sfg.get_operations_topological_order():
+            if op.graph_id in self._start_times:
+                string_io.write("name: " + op.name + ", \tid: " + op.graph_id + ", \t")
+                string_io.write("start_time: " + str(self._start_times[op.graph_id]) + ", \t")
+                string_io.write("backwards_slack: " + str(self.backwards_slack(op.graph_id)) + ", \t")
+                string_io.write("forwards_slack: " + str(self.forwards_slack(op.graph_id)) + "\n")
+        return string_io.getvalue()
+
+    @property
+    def schedule_time(self) -> int:
+        """Get the schedule time of the Schema."""
+        return self._schedule_time
+
+    def set_schedule_time(self, schedule_time: int) -> None:
+        """Set the schedule time to the specified value, if the specified value is shorter then 
+        what is required by the schedule then raises a ValueError."""
+        if not self._cyclic:
+            if schedule_time >= self._max_end_time:
                 self._schedule_time = schedule_time
+            else:
+                raise ValueError("Specified schedule time is shorter then the max length of the ")
+        else:
+            raise NotImplementedError("Cyclic schedules not implemented yet.")
 
-    def start_time_of_operation(self, op_id: GraphID):
+    def get_start_time_of_operation(self, op_id: GraphID) -> int:
         """Get the start time of the operation with the specified by the op_id."""
         assert op_id in self._start_times, "No operation with the specified op_id in this schema."
         return self._start_times[op_id]
 
-    def forward_slack(self, op_id):
-        raise NotImplementedError
+    def get_laps_of_operation_input_port(self, op_id: GraphID, input_index: int) -> int:
+        """Get the laps of input port with the entered index of the operation with the specified op_id."""
+        assert op_id in self._laps, "No operation with the specidied op_id in this schema."
+        assert input_index >= 0 and input_index < len(
+            self._laps[op_id]), "Input index for the specified operation out of bounds."
+        return self._laps[op_id][input_index]
+
+    def change_start_time_of_operation(self, op_id: GraphID, relative_change: int = None, new_start_time: int = None) -> None:
+        """Changes the start time of the operation with the specified op_id either by the 
+        relative_change if specified or to the new_start_time if that is specified. If both 
+        are specified then the new_start_time value is used.
+        A negative relative_change represents decreasing the operations start time.
+        If the change to the start time would break dependencies then the change is cancelled
+        and a message is printed to the user in the terminal.
+        """
+        assert op_id in self._start_times, "No operation with the specified op_id in this schema."
+        if new_start_time is not None:
+            relative_change = new_start_time - self._start_times[op_id]
+
+        if relative_change is not None:
+            if relative_change > 0:
+                forwards_slack = self.forwards_slack(op_id)
+                if relative_change > forwards_slack:
+                    print(f"The specified relative change of {relative_change} is larger than the forwards \
+                        slack of the operation of {forwards_slack}.")
+                    return
+            else:
+                backwards_slack = self.backwards_slack(op_id)
+                if abs(relative_change) > backwards_slack:
+                    print(f"The specified relative change of {abs(relative_change)} is larger than the forwards \
+                        slack of the operation of {forwards_slack}.")
+                    return
+
+            self._start_times[op_id] += relative_change
+
+        else:
+            raise ValueError("At least one of the parameters relative_change or new_start_time has to be specified.")
+
+    def backwards_slack(self, op_id: GraphID) -> int:
+        return 0
+        op_start_time = self.get_start_time_of_operation(op_id)
+        min_backwards_slack = math.inf
+        for i, inp in enumerate(self._sfg.find_by_id(op_id).inputs):
+            inp_time = op_start_time + inp.latency_offset
 
-    def backward_slack(self, op_id):
-        raise NotImplementedError
+            source_op_time = self.get_start_time_of_operation(inp.connected_source.operation.graph_id)
+            source_outp_time = source_op_time + inp.connected_source.latency_offset
 
-    def print_slacks(self):
-        raise NotImplementedError
+    def forwards_slack(self, op_id: GraphID) -> int:
+        return 0
+        assert op_id in self._start_times, "No operation with specified op_id in this schema."
 
-    def _schedule_asap(self):
+    def _schedule_asap(self) -> None:
         pl = self._sfg.get_precedence_list()
 
         if len(pl) < 2:
             print("Empty signal flow graph cannot be scheduled.")
             return
 
-        non_schedulable_ops = set((outp.operation.graph_id for outp in pl[0]))
+        self._non_schedulable_ops = set((outp.operation.graph_id for outp in pl[0]))
 
         for outport in pl[1]:
             op = outport.operation
@@ -83,13 +161,12 @@ class Schema:
                     # Schedule the operation if it doesn't have a start time yet.
                     op_start_time = 0
                     for inport in op.inputs:
-                        print(inport.operation.graph_id)
                         assert len(inport.signals) == 1, "Error in scheduling, dangling input port detected."
                         assert inport.signals[0].source is not None, "Error in scheduling, signal with no source detected."
                         source_port = inport.signals[0].source
 
                         source_end_time = None
-                        if source_port.operation.graph_id in non_schedulable_ops:
+                        if source_port.operation.graph_id in self._non_schedulable_ops:
                             source_end_time = 0
                         else:
                             source_op_time = self._start_times[source_port.operation.graph_id]
-- 
GitLab