aboutsummaryrefslogtreecommitdiff
path: root/acquisition/chapter.tex
diff options
context:
space:
mode:
authorBlaise Thompson <blaise@untzag.com>2018-04-10 09:55:59 -0500
committerBlaise Thompson <blaise@untzag.com>2018-04-10 09:55:59 -0500
commit628159e39a06da2a75b3b17f8dbf05cfc57de834 (patch)
tree3a3fdf9825f4da89679e0a1807ba6cb7f08f56dd /acquisition/chapter.tex
parent3dc46ef66670d4e465d7d662e5dd7175fb8fae2c (diff)
2018-04-10 09:55
Diffstat (limited to 'acquisition/chapter.tex')
-rw-r--r--acquisition/chapter.tex172
1 files changed, 139 insertions, 33 deletions
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. %