From 628159e39a06da2a75b3b17f8dbf05cfc57de834 Mon Sep 17 00:00:00 2001
From: Blaise Thompson <blaise@untzag.com>
Date: Tue, 10 Apr 2018 09:55:59 -0500
Subject: 2018-04-10 09:55

---
 acquisition/chapter.tex | 172 ++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 139 insertions(+), 33 deletions(-)

(limited to 'acquisition/chapter.tex')

diff --git a/acquisition/chapter.tex b/acquisition/chapter.tex
index 4446c0f..5981e22 100644
--- a/acquisition/chapter.tex
+++ b/acquisition/chapter.tex
@@ -181,6 +181,8 @@ In this case, the displayed pixel (index 6, 40) took 2.448 seconds to acquire.
 \end{figure}
 \end{landscape}
 
+% TODO: queue figure
+
 \section{Internal structure}  % ===================================================================
 
 In this section I discuss the internal structure of PyCMDS.  %
@@ -221,7 +223,7 @@ Without any special protection, two threads have no reason not to simultaneously
 same location in memory.  %
 If a delay stage is writing its position to memory as a 64-bit double at the same time as the
 acquisition thread reads that memory address, the acquisition thread will read in nonsense (or
-worse), it will crash).  %
+worse, PyCMDS will crash).  %
 So some strategy is needed to ensure that threads respect each other.  %
 The Mutex design allows threads to ``lock'' an object such that it cannot be modified by a
 different thread.  %
@@ -255,7 +257,7 @@ The Qt signals and slots system massively simplifies programming within PyCMDS.
 
 Note that multithreading is very different from multiprocessing.  %
 
-\subsection{High level objects}  % ----------------------------------------------------------------
+\subsection{Abstraction and inheritance}  % -------------------------------------------------------
 
 Towards the goal of stability and extensability, PyCMDS makes heavy use of abstraction and
 inheritance.  %
@@ -302,14 +304,35 @@ Hardware  # implements basic thread control
     ├── Thorlabs
     └── Newport
 \end{codefragment}
-The powerful thing about this strategy is that the three driver-specific classes (...) need only
-implement minimal driver-specific code, typically start, set close...
-This means that code is more maintainable and less buggy...
-Easier to change high-level behavior without redoing low-level code...
-Only implemented once...
-
-At it's most basic PyCMDS defines the following simple data types (derived from
-\python{PyCMDS_object}):
+The powerful thing about this strategy is that the three driver-specific classes
+(\python{Homemade}, \python{Thorlabs}, and \python{Newport}) need only implement minimal
+driver-specific code, typically \python{start}, \python{set} and \python{close}.  % 
+This means that code is more maintainable and less repeated.  %
+For example, when I added the autonomic system to PyCMDS, I edited the parent \python{Delay} class
+to respect a new method \python{set_offset}.  %
+I did \python{not} need to modify any of the child classes, because nothing about communicating
+with the particular delay stages, in native units, had changed.  %
+This allowed me to implement the core of the autonomic system in just one weekend, something that
+probably would have taken weeks to do without inheritance.  %
+
+\subsection{Core classes of PyCMDS}  % ------------------------------------------------------------
+
+Now we can see that PyCMDS is going to use multi-threading, inheritance and abstraction as much as
+possible, so let's get into some details about the \emph{actual} internal structure of the
+software.  %
+
+For those that want to dig deeper, most of these top level classes are defined in
+\bash{PyCMDS/project/classes.py}.  %
+
+\subsubsection{Data types}  % ---------------------------------------------------------------------
+
+PyCMDS is made to be enhanced and extended by chemistry graduate students who may not have time or
+energy to learn about Mutexes, signals, slots and threads.  %
+They probably also don't have time to read Qt documentation and learn the details of GUI design and
+layout.  %
+PyCMDS does its very best to abstract these details away from developers by offering a set of
+basic \emph{data type} classes which work seamlessly with every part of the program.  %
+These are simple classes, each meant to represent one kind of data:
 \begin{ditemize}
   \item Bool
   \item Combo
@@ -317,19 +340,50 @@ At it's most basic PyCMDS defines the following simple data types (derived from
   \item Number
   \item String
 \end{ditemize}
-These classes do multiple things.  %
-First, they \emph{are} Mutexes, with thread-safe \python{read} and \python{write} methods.  %
-Secondly, they support ``implicit'' storage in ini files.  %
-Third, they know how to participate in the GUI.  %
-They can display their value, and if modified they will propagate that modification to the internal
-threads of outward...
-Finally, they have special properties like units and limits etc...
-
-Without getting into details, let's investigate the key ``signals and slots'' that hardware and
-sensors have.  %
-% TODO: elaborate
+All are children of the parent \python{PyCMDS_object} class, which defines much of their shared
+functionality.   %
 
-The following is the top-level hardware class, parent of all hardware and sensors.  %
+By using these classes to store and pass around bits of information within PyCMDS, users get the
+following advantages:
+\begin{ditemize}
+  \item Thread safety. These classes \emph{are} Mutexes.
+  \item Optional integration with the GUI (see section ...)
+  \item Optional storage of state within INI files, including through restart.  %
+  \item Special conveniences, like limits, units, and labels.
+\end{ditemize}
+In short, developers should use these classes whenever possible for a worry-free development
+experience.  %
+The only downside is that the value has to be accessed with \python{read} and \python{write}
+methods, rather than directly.  %
+This is typical behavior for Mutexes, however.  %
+
+\subsubsection{Hardware and driver}  % ------------------------------------------------------------
+
+Now that we have basic data types to work with, let's actually communicate with hardware.  %
+Every hardware and sensor have two classes: a driver class, which lives in the worker thread and
+handles direct communication, and a hardware class which lives in the main thread and
+``represents'' that device to the rest of PyCMDS.  %
+The idea is that all of PyCMDS communicates to that device \emph{only} via its hardware class, and
+that hardware class only talks to that driver class.  %
+Designing it in this simple way keeps everything clean and easy to understand.  %
+
+All hardware and driver classes are children of the same parent \python{Hardware} and
+\python{Driver} classes.  %
+These parent classes know how to communicate in a thread safe way, and they know the specific
+attributes (like \python{name}), and signals (like \python{update_ui}) that all hardware and
+sensors must have.  %
+\autoref{aqn:fig:parent_hardware_class} shows the parent hardware class, and
+\autoref{aqn:fig:parent_driver_class} shows the parent driver class.  %
+
+Communication between the hardware and the driver goes via a queue, as mentioned previously.  %
+The hardware class has an attribute \python{q}, which is an instance of the \python{Q} class.  %
+To enqueue an operation, use \python{q.push(<method>, <arguments>)} where \python{<method>} is a
+string corresponding to the name of the method you wish to run in the worker thread, and
+\python{<arguments>} is a list of arguments passed to that method.  %
+The \python{q} instance will hold the instruction until the \python{Driver} instance is ready, at
+which point \python{Driver.dequeue} will be called in the worker thread.  %
+There is no way for the driver to command hardware to do something in the main thread, but the
+driver can trigger signals like \python{update_ui} and modify Mutexes.  % 
 
 \begin{figure}
 	\includepython{"acquisition/parent_hardware.py"}
@@ -344,21 +398,73 @@ The following is the top-level hardware class, parent of all hardware and sensor
 \begin{figure}
 	\includepython{"acquisition/driver.py"}
   \caption[TODO]{
-    TODO
+    Parent class of all drivers.  %
   }
-  \label{aqn:fig:driver}
+  \label{aqn:fig:parent_driver_class}
 \end{figure}
 
-\subsection{Graphical user interface}  % ----------------------------------------------------------
-
-Made up of widgets...
-
-Table widget...
-
-Use of qt plots...
-
-pyqtgraph \cite{pyqtgraph}
+\subsubsection{GUI components}  % -----------------------------------------------------------------
+
+The PyCMDS GUI must change depending on which exact hardware, sensors, and acquisition modules are
+being used on a given instrument and given day.  %
+Internally, the GUI components are made to be modular and flexable to accommodate this
+requirement.  %
+
+[programmatically defined GUI]
+
+To keep things simple and easy to extend, PyCMDS is made up of only a few minimalist GUI
+elements.  %
+Probably the most important graphical element is the fixed-width vertical scroll area.  %
+As seen in [PYCMDS SCREENSHOTS], these vertical scroll areas contain almost all of the interactive
+elements within PyCMDS, with the only exceptions being the ``SHUT DOWN'' button and the interactive
+graphs.  %
+The left-hand scroll area is always present, and it contains the principle display and control for
+each hardware.  %
+There are also scroll areas inside the tabbed menus, typically only one per tab.  %
+Because the scroll areas can expand downwards infinitely, they are great at accommodating the
+changing contents of the PyCMDS GUI.  %
+
+Ignoring small decorative items, vertical scroll areas contain only two kinds of widgets: instances
+of \python{Button} and \python{InputTable}.  %
+Buttons are fairly self-explanatory.  %
+Internally they work through signals and slots (the \python{clicked} signal), and they can have
+different behaviors including changing their label and color when clicked and being disabled or
+enabled.  %
+
+Input tables are the two column GUI elements that are everywhere in PyCMDS.  %
+The great thing about input tables is that they accept PyCMDS ``data type'' objects directly.  %
+Given an instance of \python{Number} called \python{destination}, adding to the input table is as
+easy as \python{input_table.add('Destination', destination)}.  %
+Internally, PyCMDS will do all of the work to make sure that \python{destination} is displayed in
+the GUI.  %
+The \python{destination.updated} signal will fire whenever a user manually interacts with the
+display.  %
+Attributes \python{display} and \python{disabled} change the behavior of the GUI element.  %
+
+Vertical scroll areas contain the input tables, and (on the right hand side), tabs contain the
+vertical scroll areas.  %
+Like the input tables, this tabbed structure is designed to be extended as needed.  %
+For example, in the autonomic system, there needs to be a tab for each and every hardware currently
+loaded by PyCMDS.  %
+To accommodate this, the somatic system simply builds a tab for each hardware at PyCMDS startup.  %
+
+In addition to the parent \python{Hardware} and \python{Driver} classes mentioned in the previous
+section, each hardware type has a \python{GUI} class, itself a child of the parent \python{GUI}
+class.  %
+The \python{GUI} class defines the ``ADVANCED'' interface that is unique to each hardware (see
+HARDWARE SECTION).  %
+Like everywhere else, inheritance and abstraction are used to minimize unnecessary replication of
+code.  %
+To a first approximation, every delay stage needs the same ``ADVANCED'' settings as every other
+delay stage.  %
+
+PyCMDS uses pyqtgraph \cite{pyqtgraph} for interactive plotting.  %
+pyqtgraph is great because it is optimized for speed and interactivity.  %
+
+For those wanting to learn more, all graphical components are defined in
+\bash{PyCMDS/project/widgets.py}.  %
 
+\clearpage
 \section{Hardware} \label{aqn:sec:hardware}  % ====================================================
 
 Hardware are things that 1) have a position, 2) can be set to a destination.  %
-- 
cgit v1.2.3

