Explicitly call the functions of RF controller.

We separate the plugin API to two layers: device layer and
RF controller layer. Originally, we called the function of the
RF controller by __getattr__, which means that if the device doesn't
have the function, then we find the function from the active
controller.

But it causes bug that both layers have "Initialize" and "Terminate"
functions. The functions of the RF controller will not be called and
it is confusing to find which function is called.

Now we explicitly call the RF controller function by:
device.active_controller.Function() instead of using __getattr__, and
change "_Initialize", "_Terminate" functions optional.

BUG=chromium:713589
TEST=none

Change-Id: Ie34a71358d621b8b84b40d516aa1cde27e116bb0
Reviewed-on: https://chromium-review.googlesource.com/483260
Commit-Ready: Chih-Yu Huang <[email protected]>
Tested-by: Chih-Yu Huang <[email protected]>
Reviewed-by: Joel Kitching <[email protected]>
diff --git a/graphyte/device.py b/graphyte/device.py
index 9e26eed..b444d47 100644
--- a/graphyte/device.py
+++ b/graphyte/device.py
@@ -36,30 +36,48 @@
     self.controllers = {}
     self.link = link.Create(kwargs['link_options']) if self.need_link else None
 
-  def __getattr__(self, name):
-    """Delegates to the active controller, that is recorded at rf_type."""
-    try:
-      return getattr(self.controllers[self.rf_type], name)
-    except:
-      logger.exception('Failed in delegating RF method: %s', name)
-      raise NotImplementedError
+  @property
+  def active_controller(self):
+    if self.rf_type not in self.controllers:
+      raise KeyError('rf_type "%s" is not supported.' % self.rf_type)
+    return self.controllers[self.rf_type]
+
+  @active_controller.setter
+  def active_controller(self, value):
+    del value
+    raise ValueError(
+        'Update `rf_type` value instead of updating `active_controller`.')
 
   def Initialize(self):
     """Initializes the device."""
-    raise NotImplementedError
+    logger.debug('device.Initialize')
+    if self.need_link:
+      self.link.CheckReady()
+    return self._Initialize()
+
+  def _Initialize(self):
+    pass
 
   def Terminate(self):
     """Terminates the device."""
+    logger.debug('device.Terminate')
+    if self.rf_type is not None:
+      self.controllers[self.rf_type].Terminate()
+      self.rf_type = None
+    self._Terminate()
     if self.link is not None:
       self.link.Close()
       self.link = None
 
+  def _Terminate(self):
+    pass
+
   def SetRF(self, rf_type):
     """Set the RF type."""
     if self.rf_type is not None:
-      self.controllers[self.rf_type].Terminate()
+      self.active_controller.Terminate()
     self.rf_type = rf_type
-    self.controllers[self.rf_type].Initialize()
+    self.active_controller.Initialize()
 
 
 class ControllerBase(object):
@@ -81,6 +99,20 @@
   # The RF type of the controller. Child class should override this.
   RF_TYPE = ''
 
+  def Initialize(self):
+    return self._Initialize()
+
+  def _Initialize(self):
+    """Initializes the controller."""
+    pass
+
+  def Terminate(self):
+    return self._Terminate()
+
+  def _Terminate(self):
+    """Disconnects/unconfigures the interface that was being used."""
+    pass
+
   def _CheckTestArgument(self, test_case, required_test_type):
     """Checks the test case has correct test type and RF type.
 
diff --git a/graphyte/dut/__init__.py b/graphyte/dut/__init__.py
index d462b86..c4e9a50 100644
--- a/graphyte/dut/__init__.py
+++ b/graphyte/dut/__init__.py
@@ -32,20 +32,6 @@
     def __init__(self, dut=None):
       self.dut = dut
 
-    def Initialize(self):
-      return self._Initialize()
-
-    def _Initialize(self):
-      """Initializes the controller."""
-      raise NotImplementedError
-
-    def Terminate(self):
-      return self._Terminate()
-
-    def _Terminate(self):
-      """Disconnects/unconfigures the interface that was being used."""
-      raise NotImplementedError
-
     def TxStart(self, test):
       test_case = self._CheckTestArgument(test.ToDict(), 'TX')
       return self._TxStart(**test_case)
diff --git a/graphyte/dut/sample/dummy_dut.py b/graphyte/dut/sample/dummy_dut.py
index 4c27966..afcc18a 100644
--- a/graphyte/dut/sample/dummy_dut.py
+++ b/graphyte/dut/sample/dummy_dut.py
@@ -21,10 +21,10 @@
         'BLUETOOTH': self.BluetoothController(self),
         '802_15_4': self.ZigbeeController(self)}
 
-  def Initialize(self):
+  def _Initialize(self):
     logger.info('DUT Initialize')
 
-  def Terminate(self):
+  def _Terminate(self):
     logger.info('DUT Terminate')
 
   class WlanController(DUTBase.WlanControllerBase):
diff --git a/graphyte/graphyte.py b/graphyte/graphyte.py
index 8111ff4..e661937 100644
--- a/graphyte/graphyte.py
+++ b/graphyte/graphyte.py
@@ -221,15 +221,15 @@
     self.result_writer.WriteResult(test_case, result)
 
   def RunTxTest(self, test_case):
-    self.dut.TxStart(test_case)
-    self.inst.TxMeasure(test_case)
-    self.dut.TxStop(test_case)
-    result = self.inst.TxGetResult(test_case)
+    self.dut.active_controller.TxStart(test_case)
+    self.inst.active_controller.TxMeasure(test_case)
+    self.dut.active_controller.TxStop(test_case)
+    result = self.inst.active_controller.TxGetResult(test_case)
     return result
 
   def RunRxTest(self, test_case):
-    self.dut.RxClearResult(test_case)
-    self.inst.RxGenerate(test_case)
-    result = self.dut.RxGetResult(test_case)
-    self.inst.RxStop(test_case)
+    self.dut.active_controller.RxClearResult(test_case)
+    self.inst.active_controller.RxGenerate(test_case)
+    result = self.dut.active_controller.RxGetResult(test_case)
+    self.inst.active_controller.RxStop(test_case)
     return result
diff --git a/graphyte/inst/__init__.py b/graphyte/inst/__init__.py
index 2f21f3c..3b1cb48 100644
--- a/graphyte/inst/__init__.py
+++ b/graphyte/inst/__init__.py
@@ -95,20 +95,6 @@
     def __init__(self, inst=None):
       self.inst = inst
 
-    def Initialize(self):
-      return self._Initialize()
-
-    def _Initialize(self):
-      """Initializes the RF interface."""
-      raise NotImplementedError
-
-    def Terminate(self):
-      return self._Terminate()
-
-    def _Terminate(self):
-      """Disconnects/unconfigures the interface that was being used."""
-      raise NotImplementedError
-
     def TxMeasure(self, test):
       test_case = self._CheckTestArgument(test.ToDict(), 'TX')
       return self._TxMeasure(**test_case)
diff --git a/graphyte/inst/anritsu/ml2437a.py b/graphyte/inst/anritsu/ml2437a.py
index b5eb3ef..53369f6 100644
--- a/graphyte/inst/anritsu/ml2437a.py
+++ b/graphyte/inst/anritsu/ml2437a.py
@@ -16,22 +16,14 @@
 from ...testplan import ChainMaskToList
 from ...utils.graphyte_utils import CalculateAverage
 
-class InstrumentNotReady(Exception):
-  pass
-
 
 class RFController(InstBase.PowerMeterControllerBase):
   def __init__(self, inst=None, duty_cycle=None):
     super(RFController, self).__init__(inst)
     self.result = None
     self.duty_cycle = duty_cycle
-
-  def _Initialize(self):
     self.inst.SetDutyCycle(self.duty_cycle)
 
-  def _Terminate(self):
-    pass
-
   def _TxMeasure(self, center_freq, **kwargs):
     self.inst.SetFrequency(center_freq)
     time.sleep(2)  # Wait for power meter stable
@@ -71,13 +63,11 @@
         'BLUETOOTH': RFController(self, bluetooth_duty_cycle),
         '802_15_4': RFController(self, zigbee_duty_cycle)}
 
-  def Initialize(self):
+  def _Initialize(self):
     logger.info('Inst Initialize')
-    if not self.link.IsReady():
-      raise InstrumentNotReady
     self.link.CheckCall('*RST')
 
-  def Terminate(self):
+  def _Terminate(self):
     pass
 
   def _SetPortConfig(self, port_mapping, pathloss):
diff --git a/graphyte/inst/keysight/n1914a.py b/graphyte/inst/keysight/n1914a.py
index b31366a..4a19ba6 100644
--- a/graphyte/inst/keysight/n1914a.py
+++ b/graphyte/inst/keysight/n1914a.py
@@ -59,9 +59,8 @@
     self.controllers = {rf_type: RFController(self)
                         for rf_type in ['WLAN', 'BLUETOOTH', '802_15_4']}
 
-  def Initialize(self):
+  def _Initialize(self):
     logger.info('Inst Initialize')
-    self.link.CheckReady()
     # Choose SCPI language
     self.SendCommand('SYSTem:LANGuage SCPI')
     self.SendCommand('*RST')
@@ -75,7 +74,7 @@
       self._SetContinuousTrigger(self.continuous_trigger, port_idx)
       self._SetTriggerMode(self.trigger_source, port_idx)
 
-  def Terminate(self):
+  def _Terminate(self):
     pass
 
   def _SetPortConfig(self, port_mapping, pathloss):
@@ -188,12 +187,6 @@
     super(RFController, self).__init__(inst)
     self.result = None
 
-  def _Initialize(self):
-    pass
-
-  def _Terminate(self):
-    pass
-
   def _TxMeasure(self, center_freq, **kwargs):
     self.inst.SendCommand('ABORt')
     for port_idx in PORT_INDEX_MAPPING.values():
diff --git a/graphyte/inst/sample/dummy_inst.py b/graphyte/inst/sample/dummy_inst.py
index d8b1ffd..f129b4c 100644
--- a/graphyte/inst/sample/dummy_inst.py
+++ b/graphyte/inst/sample/dummy_inst.py
@@ -23,10 +23,10 @@
         'BLUETOOTH': self.BluetoothController(self),
         '802_15_4': self.ZigbeeController(self)}
 
-  def Initialize(self):
+  def _Initialize(self):
     logger.info('Inst Initialize')
 
-  def Terminate(self):
+  def _Terminate(self):
     logger.info('Inst Terminate')
 
   def _SetPortConfig(self, port_mapping, pathloss):
diff --git a/graphyte/inst/sample/wait_inst.py b/graphyte/inst/sample/wait_inst.py
index 4c8e6c6..86147f6 100644
--- a/graphyte/inst/sample/wait_inst.py
+++ b/graphyte/inst/sample/wait_inst.py
@@ -15,12 +15,6 @@
 
 
 class WaitController(InstBase.ControllerBase):
-  def _Initialize(self):
-    pass
-
-  def _Terminate(self):
-    pass
-
   def _TxMeasure(self, **kwargs):
     logger.info('TxMeasure')
     self.inst.wait_func()
@@ -66,12 +60,6 @@
     logger.info('Wait %s seconds', self.wait_secs)
     time.sleep(self.wait_secs)
 
-  def Initialize(self):
-    pass
-
-  def Terminate(self):
-    pass
-
   def _SetPortConfig(self, port_mapping, pathloss):
     pass
 
OSZAR »