From 7f1a3577377f138247808d4eae9313c6e6232a51 Mon Sep 17 00:00:00 2001
From: Thomas Juerges <4-jurges@users.noreply.git.astron.nl>
Date: Wed, 20 May 2020 18:21:55 +0200
Subject: [PATCH] Display visibilities as 2d real and imaginary plots

Also remove all the locking that somehow blocked the entire Python program.
---
 StatsCrosslet-DS/StatsCrosslet.py  | 154 ++++++++++++-----------------
 StatsCrosslet-DS/StatsCrosslet.xmi |  22 ++---
 2 files changed, 75 insertions(+), 101 deletions(-)

diff --git a/StatsCrosslet-DS/StatsCrosslet.py b/StatsCrosslet-DS/StatsCrosslet.py
index 335c3783e..23db5fe49 100644
--- a/StatsCrosslet-DS/StatsCrosslet.py
+++ b/StatsCrosslet-DS/StatsCrosslet.py
@@ -16,7 +16,7 @@ import tango
 from tango import DebugIt
 from tango.server import run
 from tango.server import Device
-from tango.server import attribute, command, pipe
+from tango.server import attribute, command
 from tango.server import device_property
 from tango import AttrQuality, DispLevel, DevState
 from tango import AttrWriteType, PipeWriteType
@@ -26,7 +26,7 @@ import opcua
 import numpy
 import traceback
 import threading
-import time
+from datetime import datetime
 # PROTECTED REGION END #    //  StatsCrosslet.additionnal_import
 
 __all__ = ["StatsCrosslet", "main"]
@@ -42,8 +42,8 @@ class StatsCrosslet(Device):
             - Type:'DevString'
         OPC_Server_Port
             - Type:'DevULong'
-        OPC_time_out
-            - Type:'DevULong'
+        OPC_Time_out
+            - Type:'DevDouble'
         Default_pause_time
             - Type:'DevDouble'
         Default_subband
@@ -56,32 +56,36 @@ class StatsCrosslet(Device):
     ns = 0
     obj = 0
     record_cross = 0
-    data = []
-    data_lock = threading.Lock()
-    stop_data_read_loop = threading.Event()
-    data_acquisition_is_active = threading.Event()
+    stop_data_read_loop = False
+    data_acquisition_is_active = False
     data_read_loop = threading.Thread()
 
     def read_data(self):
         # This is the the thread that continuously reads the crosslet
         # statistics from the station and feeds it into the Tango
         # property system.
-        while self.stop_data_read_loop.is_set() is False:
-            time.sleep(self._pause_time)
-            if self.data_acquisition_is_active.is_set() is True:
-                data = []
+        self.debug_stream("Entering read_data loop.")
+        while self.stop_data_read_loop is False:
+            self.debug_stream("read_data is running...")
+            threading.Event().wait(self._pause_time)
+            if self.data_acquisition_is_active is True:
                 try:
-                    timestamp, visibilities, rcu_modes = self.obj.call_method(self.record_cross, self._subband, self._integration_time)
-                    self.data_lock.acquire()
-                    self._time_stamp = timestamp
-                    self._raw_visibilities = visibilities
-                    self._visibilities = numpy.array(visibilities)[0] + 1j * numpy.array(visibilities[1])
+                    self.debug_stream("fetching data:  subband = %d, integration_time = %d", self._subband, self._integration_time)
+                    t, visibilities_list, rcu_modes = self.obj.call_method(self.record_cross, self._subband, self._integration_time)
+                    self.debug_stream("fetching data done")
+                    time_stamp = datetime(t[0], t[1], t[2], t[3], t[4], t[5])
+                    self._time_stamp = time_stamp.isoformat()
+                    visibilities = numpy.array(visibilities_list)[0] + 1j * numpy.array(visibilities_list[1])
+                    self._visibilities_real = visibilities.real
+                    self._visibilities_imag = visibilities.imag
                     self._rcu_modes = rcu_modes
-                    self.data_lock.release()
-
+                    self.debug_stream("timestamp = %s, visibilities_real [0][0] = %f", self._time_stamp, self._visibilities_real[0][0])
+                except KeyboardInterrupt:
+                    self.debug_stream("KBD interrupt caught, leaving data read loop.")
+                    self.stop_data_read_loop = True
                 except Exception as e:
-                    print("Cannot call the method %s in the OPC-UA server %s.  Trace: %s" % (self.record_cross, self.OPC_Server_Name, traceback.format_exc
-()))
+                    self.error_stream("Exception: %s.  Cannot call the method %s in the OPC-UA server %s.  Trace: %s", e, self.record_cross, self.OPC_Server_Name, traceback.format_exc())
+
     # PROTECTED REGION END #    //  StatsCrosslet.class_variable
 
     # -----------------
@@ -98,14 +102,14 @@ class StatsCrosslet(Device):
         default_value=55556
     )
 
-    OPC_time_out = device_property(
-        dtype='DevULong',
-        default_value=1000
+    OPC_Time_out = device_property(
+        dtype='DevDouble',
+        default_value=1.0
     )
 
     Default_pause_time = device_property(
         dtype='DevDouble',
-        default_value=60.0
+        default_value=1.0
     )
 
     Default_subband = device_property(
@@ -132,7 +136,7 @@ class StatsCrosslet(Device):
     )
 
     time_stamp = attribute(
-        dtype='DevDouble',
+        dtype='DevString',
     )
 
     pause_time = attribute(
@@ -140,28 +144,19 @@ class StatsCrosslet(Device):
         access=AttrWriteType.READ_WRITE,
     )
 
-    visibilities = attribute(
-        dtype=('DevDouble',),
-        max_dim_x=96,
-    )
-
     rcu_modes = attribute(
         dtype=('DevString',),
         max_dim_x=96,
     )
 
-    raw_visibilities = attribute(
+    visibilities_imag = attribute(
         dtype=(('DevDouble',),),
         max_dim_x=96, max_dim_y=96,
     )
 
-    # -----
-    # Pipes
-    # -----
-
-    pipe_visibilities = pipe(
-    )
-    pipe_raw_visibilities = pipe(
+    visibilities_real = attribute(
+        dtype=(('DevDouble',),),
+        max_dim_x=96, max_dim_y=96,
     )
 
     # ---------------
@@ -172,25 +167,26 @@ class StatsCrosslet(Device):
         """Initialises the attributes and properties of the StatsCrosslet."""
         Device.init_device(self)
         # PROTECTED REGION ID(StatsCrosslet.init_device) ENABLED START #
-        self._visibilities = (0.0,)
         try:
-            self.client = opcua.Client("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), self.OPC_time_out)
+            self.debug_stream("Connecting to OPC-UA server %s:%d...", self.OPC_Server_Name, self.OPC_Server_Port)
+            self.client = opcua.Client("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), self.OPC_Time_out)
             self.client.connect()
             ns = self.client.get_namespace_index("http://lofar.eu")
             self.obj = self.client.get_root_node().get_child(["0:Objects", "{}:StationMetrics".format(ns),                                            "{}:RCU".format(ns)])
             self.record_cross = "{}:record_cross".format(ns)
-
-            # Set default values in the read/write attributes
+            self.debug_stream("Connecting to OPC-UA server %s:%d done.", self.OPC_Server_Name, self.OPC_Server_Port)
+            # Set default values in the read/write attributes.
             self._pause_time = self.Default_pause_time
             self._integration_time = self.Default_integration_time
             self._subband = self.Default_subband
 
             self.data_read_loop = threading.Thread(target = self.read_data)
-            self.data_acquisition_is_active.set()
-            self.stop_data_read_loop.clear()
+            self.data_acquisition_is_active = False
+            self.stop_data_read_loop = False
             self.data_read_loop.start()
         except Exception as e:
-            print("Cannot connect to the OPC-UA server %s.  Trace: %s" % (self.OPC_Server_Name, traceback.format_exc()))
+            self.error_stream("Cannot connect to the OPC-UA server %s.  Trace: %s" % (self.OPC_Server_Name, traceback.format_exc()))
+            raise e
         # PROTECTED REGION END #    //  StatsCrosslet.init_device
 
     def always_executed_hook(self):
@@ -206,12 +202,16 @@ class StatsCrosslet(Device):
         destructor and by the device Init command.
         """
         # PROTECTED REGION ID(StatsCrosslet.delete_device) ENABLED START #
-        self.data_acquisition_is_active.clear()
-        self.stop_data_read_loop.set()
+        self.debug_stream("Shutting down...")
+        self.data_acquisition_is_active = False
+        self.stop_data_read_loop = True
+        self.debug_stream("Waiting for data acquisition thread to shut down...")
         self.data_read_loop.join()
+        self.debug_stream("Waiting for data acquisition thread to shut down, done")
         if self.client is not Null:
             self.client.close_session()
             self.client_secure_channel()
+        self.debug_stream("Shutdown done.")
         # PROTECTED REGION END #    //  StatsCrosslet.delete_device
     # ------------------
     # Attributes methods
@@ -238,10 +238,7 @@ class StatsCrosslet(Device):
     def read_time_stamp(self):
         # PROTECTED REGION ID(StatsCrosslet.time_stamp_read) ENABLED START #
         """Return the time_stamp attribute."""
-        self.data_lock.acquire()
-        time_stamp = self._time_stamp
-        self.data_lock.release()
-        return time_stamp
+        return self._time_stamp
         # PROTECTED REGION END #    //  StatsCrosslet.time_stamp_read
 
     def read_pause_time(self):
@@ -256,46 +253,23 @@ class StatsCrosslet(Device):
         self._pause_time = value
         # PROTECTED REGION END #    //  StatsCrosslet.pause_time_write
 
-    def read_visibilities(self):
-        # PROTECTED REGION ID(StatsCrosslet.visibilities_read) ENABLED START #
-        """Return the visibilities attribute."""
-        self.data_lock.acquire()
-        visibilities = self._visibilities
-        self.data_lock.release()
-        return visibilities
-        # PROTECTED REGION END #    //  StatsCrosslet.visibilities_read
-
     def read_rcu_modes(self):
         # PROTECTED REGION ID(StatsCrosslet.rcu_modes_read) ENABLED START #
         """Return the rcu_modes attribute."""
-        self.data_lock.acquire()
-        rcu_modes = self._rcu_modes
-        self.data_lock.release()
-        return rcu_modes
+        return self._rcu_modes
         # PROTECTED REGION END #    //  StatsCrosslet.rcu_modes_read
 
-    def read_raw_visibilities(self):
-        # PROTECTED REGION ID(StatsCrosslet.raw_visibilities_read) ENABLED START #
-        """Return the raw_visibilities attribute."""
-        self.data_lock.acquire()
-        raw_visibilities = self._raw_visibilities
-        self.data_lock.release()
-        return raw_visibilities
-        # PROTECTED REGION END #    //  StatsCrosslet.raw_visibilities_read
-
-    # -------------
-    # Pipes methods
-    # -------------
-
-    def read_pipe_visibilities(self):
-        # PROTECTED REGION ID(StatsCrosslet.pipe_visibilities_read) ENABLED START #
-        return dict(x=0, y=0)
-        # PROTECTED REGION END #    //  StatsCrosslet.pipe_visibilities_read
-
-    def read_pipe_raw_visibilities(self):
-        # PROTECTED REGION ID(StatsCrosslet.pipe_raw_visibilities_read) ENABLED START #
-        return dict(x=0, y=0)
-        # PROTECTED REGION END #    //  StatsCrosslet.pipe_raw_visibilities_read
+    def read_visibilities_imag(self):
+        # PROTECTED REGION ID(StatsCrosslet.visibilities_imag_read) ENABLED START #
+        """Return the visibilities_imag attribute."""
+        return self._visibilities_imag
+        # PROTECTED REGION END #    //  StatsCrosslet.visibilities_imag_read
+
+    def read_visibilities_real(self):
+        # PROTECTED REGION ID(StatsCrosslet.visibilities_real_read) ENABLED START #
+        """Return the visibilities_real attribute."""
+        return self._visibilities_real
+        # PROTECTED REGION END #    //  StatsCrosslet.visibilities_real_read
 
     # --------
     # Commands
@@ -313,7 +287,8 @@ class StatsCrosslet(Device):
 
         :return:None
         """
-        self.data_acquisition_is_active.set()
+#        self.data_acquisition_is_active.set()
+        self.data_acquisition_is_active = True
         # PROTECTED REGION END #    //  StatsCrosslet.start_acquisition
 
     @command(
@@ -326,7 +301,8 @@ class StatsCrosslet(Device):
 
         :return:None
         """
-        self.data_acquisition_is_active.clear()
+#        self.data_acquisition_is_active.clear()
+        self.data_acquisition_is_active = False
         # PROTECTED REGION END #    //  StatsCrosslet.stop_acquisition
 
 # ----------
diff --git a/StatsCrosslet-DS/StatsCrosslet.xmi b/StatsCrosslet-DS/StatsCrosslet.xmi
index 6f8c58792..7ce474dbf 100644
--- a/StatsCrosslet-DS/StatsCrosslet.xmi
+++ b/StatsCrosslet-DS/StatsCrosslet.xmi
@@ -15,15 +15,15 @@
       <status abstract="false" inherited="false" concrete="true" concreteHere="true"/>
       <DefaultPropValue>55556</DefaultPropValue>
     </deviceProperties>
-    <deviceProperties name="OPC_time_out" description="">
-      <type xsi:type="pogoDsl:UIntType"/>
+    <deviceProperties name="OPC_Time_out" description="">
+      <type xsi:type="pogoDsl:DoubleType"/>
       <status abstract="false" inherited="false" concrete="true" concreteHere="true"/>
-      <DefaultPropValue>1000</DefaultPropValue>
+      <DefaultPropValue>1.0</DefaultPropValue>
     </deviceProperties>
     <deviceProperties name="Default_pause_time" description="">
       <type xsi:type="pogoDsl:DoubleType"/>
       <status abstract="false" inherited="false" concrete="true" concreteHere="true"/>
-      <DefaultPropValue>60.0</DefaultPropValue>
+      <DefaultPropValue>1.0</DefaultPropValue>
     </deviceProperties>
     <deviceProperties name="Default_subband" description="">
       <type xsi:type="pogoDsl:UIntType"/>
@@ -88,7 +88,7 @@
       <properties description="" label="" unit="" standardUnit="" displayUnit="" format="" maxValue="" minValue="" maxAlarm="" minAlarm="" maxWarning="" minWarning="" deltaTime="" deltaValue=""/>
     </attributes>
     <attributes name="time_stamp" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true" isDynamic="false">
-      <dataType xsi:type="pogoDsl:DoubleType"/>
+      <dataType xsi:type="pogoDsl:StringType"/>
       <changeEvent fire="false" libCheckCriteria="false"/>
       <archiveEvent fire="false" libCheckCriteria="false"/>
       <dataReadyEvent fire="false" libCheckCriteria="true"/>
@@ -103,23 +103,23 @@
       <status abstract="false" inherited="false" concrete="true" concreteHere="true"/>
       <properties description="" label="" unit="" standardUnit="" displayUnit="" format="" maxValue="" minValue="" maxAlarm="" minAlarm="" maxWarning="" minWarning="" deltaTime="" deltaValue=""/>
     </attributes>
-    <attributes name="visibilities" attType="Spectrum" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="96" maxY="" allocReadMember="true" isDynamic="false">
-      <dataType xsi:type="pogoDsl:DoubleType"/>
+    <attributes name="rcu_modes" attType="Spectrum" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="96" maxY="" allocReadMember="true" isDynamic="false">
+      <dataType xsi:type="pogoDsl:StringType"/>
       <changeEvent fire="false" libCheckCriteria="false"/>
       <archiveEvent fire="false" libCheckCriteria="false"/>
       <dataReadyEvent fire="false" libCheckCriteria="true"/>
       <status abstract="false" inherited="false" concrete="true" concreteHere="true"/>
       <properties description="" label="" unit="" standardUnit="" displayUnit="" format="" maxValue="" minValue="" maxAlarm="" minAlarm="" maxWarning="" minWarning="" deltaTime="" deltaValue=""/>
     </attributes>
-    <attributes name="rcu_modes" attType="Spectrum" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="96" maxY="" allocReadMember="true" isDynamic="false">
-      <dataType xsi:type="pogoDsl:StringType"/>
+    <attributes name="visibilities_imag" attType="Image" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="96" maxY="96" allocReadMember="true" isDynamic="false">
+      <dataType xsi:type="pogoDsl:DoubleType"/>
       <changeEvent fire="false" libCheckCriteria="false"/>
       <archiveEvent fire="false" libCheckCriteria="false"/>
       <dataReadyEvent fire="false" libCheckCriteria="true"/>
       <status abstract="false" inherited="false" concrete="true" concreteHere="true"/>
       <properties description="" label="" unit="" standardUnit="" displayUnit="" format="" maxValue="" minValue="" maxAlarm="" minAlarm="" maxWarning="" minWarning="" deltaTime="" deltaValue=""/>
     </attributes>
-    <attributes name="raw_visibilities" attType="Image" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="96" maxY="96" allocReadMember="true" isDynamic="false">
+    <attributes name="visibilities_real" attType="Image" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="96" maxY="96" allocReadMember="true" isDynamic="false">
       <dataType xsi:type="pogoDsl:DoubleType"/>
       <changeEvent fire="false" libCheckCriteria="false"/>
       <archiveEvent fire="false" libCheckCriteria="false"/>
@@ -127,8 +127,6 @@
       <status abstract="false" inherited="false" concrete="true" concreteHere="true"/>
       <properties description="" label="" unit="" standardUnit="" displayUnit="" format="" maxValue="" minValue="" maxAlarm="" minAlarm="" maxWarning="" minWarning="" deltaTime="" deltaValue=""/>
     </attributes>
-    <pipes name="pipe_visibilities" description="" label="" rwType="READ" displayLevel="OPERATOR"/>
-    <pipes name="pipe_raw_visibilities" description="" label="" rwType="READ" displayLevel="OPERATOR"/>
     <preferences docHome="./doc_html" makefileHome="/usr/local/share/pogo/preferences"/>
   </classes>
 </pogoDsl:PogoSystem>
-- 
GitLab