From 91ed6490d806abd1d61c5e06eccd9d4809696fe8 Mon Sep 17 00:00:00 2001
From: mehtank <mehtank@ucla.edu>
Date: Mon, 19 Sep 2022 03:39:35 +0000
Subject: [PATCH] Overrides

---
 rocolib/api/Parameterized.py          | 38 +++++++++++++++++++++++----
 rocolib/builders/ServoMountBuilder.py |  4 ++-
 rocolib/library/Cutout.py             | 24 ++++++++---------
 rocolib/library/MountedServo.yaml     |  9 ++-----
 rocolib/library/RectBeam.py           |  2 +-
 rocolib/library/ServoMount.yaml       |  9 ++-----
 rocolib/library/Stool.py              | 10 +++----
 rocolib/library/UChannel.py           | 10 +++----
 rocolib/library/Wheel.yaml            |  9 ++-----
 9 files changed, 62 insertions(+), 53 deletions(-)

diff --git a/rocolib/api/Parameterized.py b/rocolib/api/Parameterized.py
index 1fc3fbc..b3d3632 100644
--- a/rocolib/api/Parameterized.py
+++ b/rocolib/api/Parameterized.py
@@ -44,8 +44,8 @@ class Parameter:
 
         self.name = name
 
-        if defaultValue is None and not kwargs.get("optional", False):
-            raise ValueError(f"Must specify either defaultValue or optional=True for parameter {name}")
+        if defaultValue is None and not kwargs.get("overrides", False):
+            raise ValueError(f"Must specify either defaultValue or overrides (implies optional) for parameter {name}")
 
         self.defaultValue = defaultValue
         self.spec = {}
@@ -60,7 +60,7 @@ class Parameter:
         self.assertValid(defaultValue)
 
         vt = self.spec.get("valueType", "")
-        no = not(self.spec.get("optional", False))
+        no = not(self.spec.get("overrides", False))
 
         if no and ("int" in vt or "float" in vt):
             integer=None
@@ -94,11 +94,11 @@ class Parameter:
 
     def assertValid(self, value):
         if value is None:
-            if self.spec.get("optional", False):
+            if self.spec.get("overrides", False):
                 self.value = value
                 return
             else:
-                raise ValueError(f"Parameter {self.name} is not optional and cannot be set to None")
+                raise ValueError(f"Parameter {self.name} is not optional (via overrides) and cannot be set to None")
 
         def check(spec, test, error):
             if (self.spec.get(spec, None) is not None and not test(self.spec[spec])):
@@ -116,6 +116,32 @@ class Parameter:
         else:
             return self.symbol
 
+    def doOverrides(self, c):
+        if self.value is None:
+            return
+        overrides = self.spec.get("overrides", [])
+        if isinstance(overrides, dict):
+            for k, fn in overrides.items():
+                try:
+                    import rocolib.utils.numsym as np
+                    value = eval(fn, locals())
+                    c.setParameter(k, value)
+                except ValueError as e:
+                    raise ValueError(str(e) + f" (via setting overriding parameter {self.name} to {self.value})")
+                except KeyError as e:
+                    logging.debug(str(e) + f" (via setting overriding parameter {self.name} to {self.value}) -- ignoring")
+        elif isinstance(overrides, (list, tuple)):
+            for k in overrides:
+                try:
+                    c.setParameter(k, self.value)
+                except KeyError as e:
+                    logging.debug(str(e) + f" (via setting overriding parameter {self.name} to {self.value}) -- ignoring")
+        else:
+            try:
+                c.setParameter(overrides, self.value)
+            except KeyError as e:
+                logging.debug(str(e) + f" (via setting overriding parameter {self.name} to {self.value}) -- ignoring")
+
     def getInfo(self, keys=None):
         if isinstance(keys, str):
             return getattr(self, keys)
@@ -158,6 +184,8 @@ class Parameterized(object):
         """
         for n, p in self.parameters.items():
             p.setDefault(force)
+        for n, p in self.parameters.items():
+            p.doOverrides(self)
 
 
     def setParameter(self, n, v, validate=True):
diff --git a/rocolib/builders/ServoMountBuilder.py b/rocolib/builders/ServoMountBuilder.py
index 53ba903..900cde2 100644
--- a/rocolib/builders/ServoMountBuilder.py
+++ b/rocolib/builders/ServoMountBuilder.py
@@ -4,11 +4,13 @@ from rocolib.api.Function import Function
 c = Component()
 
 c.addParameter("servo", "fs90r", paramType="dimension")
+
 c.addParameter("flip", False, valueType="bool")
 c.addParameter("center", True, valueType="bool")
 c.addParameter("shift", 0, paramType="length")
+
 # XXX TODO: Define type: tuple of two numbers
-c.addParameter("offset", optional=True, overrides=("flip", "center", "shift"))
+c.addParameter("offset", 0)
 
 c.addSubcomponent("beam", "SimpleRectBeam", inherit=("length", "addTabs"), prefix=None)
 c.addSubcomponent("mount", "Cutout")
diff --git a/rocolib/library/Cutout.py b/rocolib/library/Cutout.py
index 77cb2bb..feb51f7 100644
--- a/rocolib/library/Cutout.py
+++ b/rocolib/library/Cutout.py
@@ -2,20 +2,18 @@ from rocolib.api.components import DecorationComponent
 from rocolib.api.composables.graph.Face import Rectangle
 
 class Cutout(DecorationComponent):
-  def define(self):
-      self.addParameter("dx", 10, paramType="length")
-      self.addParameter("dy", 20, paramType="length")
-      self.addParameter("d", optional=True, overrides=("dx", "dy"))
+    def define(self):
+        self.addParameter("dx", 10, paramType="length")
+        self.addParameter("dy", 20, paramType="length")
+        self.addParameter("d", overrides={
+            "dx": lambda c : c.getParameter("d"),
+            "dy": lambda c : c.getParameter("d"),
+        })
 
-  def modifyParameters(self):
-    if self.getParameter("d") is not None:
-      self.setParameter("dx", self.getParameter("d"))
-      self.setParameter("dy", self.getParameter("d"))
-
-  def assemble(self):
-    dx = self.getParameter("dx")
-    dy = self.getParameter("dy")
-    self.addFace(Rectangle("r0", dx, dy), prefix="r0")
+    def assemble(self):
+        dx = self.getParameter("dx")
+        dy = self.getParameter("dy")
+        self.addFace(Rectangle("r0", dx, dy), prefix="r0")
 
 if __name__ == "__main__":
     Cutout.test()
diff --git a/rocolib/library/MountedServo.yaml b/rocolib/library/MountedServo.yaml
index f6353e8..2bcbbe8 100644
--- a/rocolib/library/MountedServo.yaml
+++ b/rocolib/library/MountedServo.yaml
@@ -133,13 +133,8 @@ parameters:
       units: mm
       valueType: (float, int)
   offset:
-    defaultValue: null
-    spec:
-      optional: true
-      overrides:
-      - flip
-      - center
-      - shift
+    defaultValue: 0
+    spec: {}
   servo:
     defaultValue: fs90r
     spec:
diff --git a/rocolib/library/RectBeam.py b/rocolib/library/RectBeam.py
index 60abf3d..12a323d 100644
--- a/rocolib/library/RectBeam.py
+++ b/rocolib/library/RectBeam.py
@@ -14,7 +14,7 @@ class RectBeam(FoldedComponent):
     self.addParameter("tangle", 80, paramType="angle", maxValue=180)
     self.addParameter("bangle", 135, paramType="angle", maxValue=180)
 
-    self.addParameter("root", optional=True, valueType="int")
+    self.addParameter("root", 0, valueType="int", minValue=0)
     self.addParameter("addTabs", True, valueType="bool")
 
     for i in range(4):
diff --git a/rocolib/library/ServoMount.yaml b/rocolib/library/ServoMount.yaml
index 829101a..9aa85d0 100644
--- a/rocolib/library/ServoMount.yaml
+++ b/rocolib/library/ServoMount.yaml
@@ -73,13 +73,8 @@ parameters:
       units: mm
       valueType: (float, int)
   offset:
-    defaultValue: null
-    spec:
-      optional: true
-      overrides:
-      - flip
-      - center
-      - shift
+    defaultValue: 0
+    spec: {}
   servo:
     defaultValue: fs90r
     spec:
diff --git a/rocolib/library/Stool.py b/rocolib/library/Stool.py
index 3bf5b6e..a7d4165 100644
--- a/rocolib/library/Stool.py
+++ b/rocolib/library/Stool.py
@@ -8,14 +8,12 @@ class Stool(FoldedComponent):
     def define(self):
         self.addParameter("height", 60, paramType="length", minValue=10)
         self.addParameter("legs", 3, paramType="count", minValue=1)
-        self.addParameter("radius", optional=True, overrides=("legwidth",))
-        self.addParameter("legwidth", 20, paramType="length", minValue=10)
+        self.addParameter("radius", minValue=10, overrides={
+            "legwidth": lambda c: r2l.r2l(c.getParameter("radius"), c.getParameter("legs")*2)
+        })
+        self.addParameter("legwidth", 20, paramType="length", minValue=9)
         self.addParameter("angle", 80, paramType="angle")
 
-    def modifyParameters(self):
-        if self.getParameter("radius") is not None:
-          self.setParameter("legwidth", r2l.r2l(self.getParameter("radius"), self.getParameter("legs")*2))
-
     def assemble(self):
         h = self.getParameter("height")
         lp = self.getParameter("legs")
diff --git a/rocolib/library/UChannel.py b/rocolib/library/UChannel.py
index 010014d..088b9ac 100644
--- a/rocolib/library/UChannel.py
+++ b/rocolib/library/UChannel.py
@@ -9,9 +9,12 @@ class UChannel(FoldedComponent):
     self.addParameter("depth", 20, paramType="length")
 
     # Minimum of 45deg to make sure the geometry stays convex
-    self.addParameter("angle", optional=True, overrides=("tangle", "bangle"))
     self.addParameter("tangle", 80, paramType="angle", minValue=45)
     self.addParameter("bangle", 135, paramType="angle", minValue=45)
+    self.addParameter("angle", paramType="angle", minValue=45, overrides={
+        "bangle": lambda c : c.getParameter("angle"),
+        "tangle": lambda c : c.getParameter("angle"),
+    })
 
     for i in range(3):
       self.addEdgeInterface("topedge%d" % i, "r%d.e0" % i, ["depth", "width"][i % 2])
@@ -23,11 +26,6 @@ class UChannel(FoldedComponent):
     self.addEdgeInterface("ledge", "r0.e3", "length")
     self.addEdgeInterface("redge", "r2.e1", "length")
 
-  def modifyParameters(self):
-    if self.getParameter("angle") is not None:
-      self.setParameter("bangle", self.getParameter("angle"))
-      self.setParameter("tangle", self.getParameter("angle"))
-
   def assemble(self):
     bangle = 90 - self.getParameter("bangle")
     tangle = 90 - self.getParameter("tangle")
diff --git a/rocolib/library/Wheel.yaml b/rocolib/library/Wheel.yaml
index 7d22b01..f902aa7 100644
--- a/rocolib/library/Wheel.yaml
+++ b/rocolib/library/Wheel.yaml
@@ -133,13 +133,8 @@ parameters:
       units: mm
       valueType: (float, int)
   offset:
-    defaultValue: null
-    spec:
-      optional: true
-      overrides:
-      - flip
-      - center
-      - shift
+    defaultValue: 0
+    spec: {}
   radius:
     defaultValue: 25
     spec:
-- 
GitLab