Updated software architecture description. Still missing some new files.

This commit is contained in:
2021-11-03 18:08:41 +01:00
parent ac609b242f
commit e1e7db29a9
6 changed files with 215 additions and 271 deletions
+212 -104
View File
@@ -2,96 +2,135 @@
\section{Program Structure}
\glsunset{psu}
\glsunset{irs}
To operate the test bench, a Python software with graphical \gls{ui} was developed. It controls the used PS2000B Power Supply Units (\gls{psu}) as well as the Arduino microcontroller inside the switch box. This chapter focuses on the overall implementation. More detailed information is provided in the form of comments in the source code, which is available on the \gls{irs} git server.\footnote{\url{https://egit.irs.uni-stuttgart.de/zietzm/Helmholtz_Test_Bench.git}} A users guide can be found in Section \ref{sec:software_guide}.\\
Software development and testing were done in Windows 10 and Python 3.7. Some aspects may need to be adapted to use the software on a different operating system or Python version. The code was tested with a \gls{psu} or the switch box individually. However, integrated verification with both \gls{psu}s and the switch box Arduino connected simultaneously was not possible up to this point, as some of the equipment was located inside the \gls{irs} cleanroom.\\
The program file architecture is shown in Figure \ref{fig:softwarelayout}. This is meant to give an overview of the structure, therefore it does not show all interactions between the files.
\begin{figure}[hb]
To operate the test bench, a Python software with a graphical \gls{ui} was developed.
It manages the test bench hardware, such as the PS2000B and QL355TP Power Supply Units (\gls{psu}), the polarity switch box, and magnetometers.
This chapter focuses on the overall software implementation.
Detailed information is provided in the form of comments in the source code, which is available on the \gls{irs} git server\footnote{\url{https://egit.irs.uni-stuttgart.de/eive/Helmholtz_Test_Bench.git}}.
For a user manual, please refer to Section \ref{sec:software_guide}.
Software development and testing were done in Windows 10 and Python 3.9.
Some aspects may need to be adapted to use the software on a different operating system or Python version.
The software in its current state (26. October 2021) has been fully tested with the complete hardware suite.
For the complete system test, a FGM3D magnetometer, QL355TP \gls{psu}, and the polarity switcher were used and are thus recommended as a reference configuration.
\begin{figure}[h]
\centering
\includegraphics[width=0.8\textwidth]{media/Software layout}
\caption{Software file architecture}
\includegraphics[width=\textwidth]{media/complete_software_architecture}
\caption{Software file layout and architecture}
\label{fig:softwarelayout}
\end{figure}
Upon execution of \code{main.py}, the test bench devices (\gls{psu}s and switch box Arduino), program objects and variables are initialized. Next, the \gls{ui} (controlled by \code{User\_Interface.py}) is set up and displayed. Program elements needed for more complex functionalities are coded in \code{csv\_logging.py}, \code{config\_handling.py} and \code{csv\_threading.py}.\\
All program elements use the classes in the \code{cage\_func.py} file to control and read data from the test bench. Communication with the actual hardware (power supply units and switch box Arduino) is achieved via the libraries in \code{PS2000B.py} and \code{Arduino.py}, which were taken from online sources \cite{PS2000B_lib, Arduino_lib} with only minor modifications.\\
\code{globals.py} is used to easily pass frequently used variables between the different program files. It contains mainly variable initializations, that can be accessed and changed by the other program elements after importing it.\\
The application is able to operate with one or all test bench devices disconnected. For example, it is possible to generate a magnetic field in only one test bench axis with just a single \gls{psu}. The program also provides error handling for a variety of device disconnects and user mistakes. This includes protection against the commanding of excessive currents and voltages, as well as the display of warnings when attempting to enter a potentially dangerous value in the settings.
The program file architecture is shown in Figure \ref{fig:softwarelayout}.
This is meant to give an overview of the structure, therefore it does not show all interactions between the files.
Upon execution of \code{main.py}, a test bench proxy object is initialized that acts as a wrapper around the \gls{psu} and Arduino interfaces and calls.
Next, the \gls{ui} (controlled by \code{user\_interface.py}) is set up and displayed.
Advanced software functionality is split into separate files and components that are called from the \gls{ui} code.
This includes the CSV and calibration functionality for example.
All program elements use the global \code{HelmholtzCageDevice} object instance defined in the \code{helmholtz\_cage\_device.py} file to control and read data from the test bench.
Communication with some of the hardware is achieved via modified third-party libraries such as \code{PS2000B.py} and \code{Arduino.py}, which were taken from online sources \cite{PS2000B_lib, Arduino_lib}.
\code{globals.py} is used to easily pass frequently used variables between the different program files.
It contains mainly constants and defaults as well as some global instances, that can be accessed and changed by the other program elements after importing it.
The application is able to operate with one or all test bench devices disconnected.
For example, if one \gls{psu} were to be disconnected, commands and fields could still be applied to the axes accessible on the other \gls{psu}.
The program also provides error handling for a variety of device disconnects and user mistakes.
This includes protection against the commanding of excessive currents and voltages, as well as the display of warnings when attempting to enter a potentially dangerous value in the settings.
\section{Program Files}
\subsection{Main Executable \code{main.py}}
The main executable file contains the framework code for start and end of the program.\\
During initialization the information from the configuration file is read out to set the necessary constants. Then the test bench is prepared using the \code{setup\_all} function (see Section \ref{sec:cage_func}), the \gls{ui} is initialized and its \code{mainloop} started.\\
The \code{program\_end} function in this file ensures a safe shutdown of all equipment at the end of the program. It is immediately called when the user closes the window. The function stops any other operational threads (see Section \ref{sec:csv_exec}) and powers down the \gls{psu}s and Arduino. It also asks the user to store possible unsaved log data and then destroys the application window.\\
For exceptions that were not caught by lower level error handling, the main file also includes a global error protection. In such a case an error message is displayed and the \code{program\_end} function called. This is to ensure a safe shutdown, regardless of the current program state.
The main executable file contains the code to start (\code{program\_start}) and stop (\code{program\_end}) the program.
During program initialization the information from the default configuration file is read out to set the necessary constants.
The startup defaults may be modified according to the currently used hardware setup.
Afterwards, the global Helmholtz cage and magnetometer proxy device instances are created.
These instances are requisite for the following \gls{ui} initialization, but do not yet need to be connected to hardware devices.
Afterwards, the graphical and TCP interfaces are created, and the hardware initialization is triggered in a non-blocking, asynchronous function call.
Finally, the UI's \code{mainloop} is started.
The \code{program\_end} function in this file ensures a safe shutdown of all equipment at the end of the program.
It is immediately called when the user closes the window.
The function stops any other operational threads (see Section \ref{sec:csv_exec}) and powers down the \gls{psu}s and Arduino.
It also asks the user to store possible unsaved log data and then destroys the application window.
For exceptions that were not caught by lower levels, the main file also includes application-level error handling.
In such a case an error message is displayed and the \code{program\_end} function called to ensure a safe shutdown, regardless of the current program state.
\subsection{Global Variables File \code{globals.py}}
This file holds global variables and constants that are used often and by more than one module.\\
Examples of these are \code{PS2000B} and \code{ArduinoCtrl} objects that represent the test bench devices, \code{Axis} objects for each spatial axis but also status indicators like \code{exitFlag}, which signals the end of the program to all modules.\\
The file also contains the default values for all constants used to run the test bench, like coil constants, resistances and maximum currents. These are stored in a hard-coded dictionary, which can be used to generate a default configuration. The dictionary also includes the minimum and maximum safe value for each constant. These limits are used to warn users if they attempt to set a value that may damage equipment. In the future, it may be advantageous to store this information in a separate configuration file to allow easier modification.
This file holds global variables and constants that are shared between modules.
Examples of these are the \code{CAGE\_DEVICE} and \code{MAGNETOMETER} objects that represent hardware devices, but also status indicators like \code{exit\_flag}, which signals the end of the program to all components.
The file also contains the default values for all constants used to run the test bench, like coil constants, resistances, and maximum currents.
These are stored in a hard-coded dictionary, which is also used to regenerate the default configuration.
The dictionary also includes the minimum and maximum safe value for each constant, which are used to warn users if they attempt to set a value that may damage equipment.
In the future, it may be advantageous to store this information in a separate configuration file to allow easier modification.
\subsection{Test Bench Control File \code{cage\_func.py}}\label{sec:cage_func}
This file contains all classes and functions directly related to the operation of the test bench. It includes the two main control classes \code{Axis} and \code{ArduinoCtrl}, functions for initialization and shutdown as well as for controlling more than one axis at a time.
\subsection{Test Bench Control File \code{helmholtz\_cage\_device.py}}
\label{sec:cage_func}
This file contains all classes directly related to the operation of the test bench.
It includes the main control class \code{HelmholtzCageDevice}, its proxy \code{HelmholtzCageProxy}, as well as the \code{Axis} adapter object used by both.
Magnetometer interfacing is handled in the separate \code{magnetometer.py} file.
The application contains a single global \code{HelmholtzCageDevice} instance, contained in the \code{CAGE\_DEVICE} variable.
\myparagraph{Axis Control Class \code{Axis}}
This is the main class representing an axis (x,y,z) of the test bench. It contains static and dynamic status information about the axis in its attributes and the means to command the axis in its methods. During program initialization an object of this class is created for each of the three axes and stored in \code{globals.py} \\
Apart from methods to get status information from the appropriate \gls{psu}, the class contains the primary way to command the test bench in form of the \code{set\_signed\_current} method. Its parameter is a positive or negative current value in Ampere. The method first checks if this value exceeds the safe limits defined in the program configuration. If not, the appropriate \gls{psu} channel and the switch box Arduino are commanded to supply the desired current and to actuate the polarity change relay as needed.\\
To generate a specific magnetic field, the \code{set\_field} method can be called. It takes a given field value $B$ in Tesla and calculates the needed current:
\myparagraph{Main Control Class \code{HelmholtzCageDevice}}
This class provides the following important functionality:
\begin{itemize}
\item Asynchronous connection and disconnection of hardware devices (\code{connect\_hardware\_async} and \code{shutdown}).
These functions may be called at any time during program execution, and are used as the primary means of reloading configuration variables and connecting new hardware at runtime.
\item Set currents, raw fields, and compensated fields in the test bench coils.
Uses \code{\_set\_signed\_currents}, \code{\_set\_field\_raw}, and \code{\_set\_field\_compensated}.
These functions are called internally when the corresponding command is received in the \code{\_cmd\_exec\_thread}.
\item Hardware status and control is mediated by an array of \code{Axis} adapter objects.
Each axis stores its own specific configuration data and related status information.
\item Components may provide callbacks to the \code{subscribe\_status\_updates} function, to receive periodic status information about the test bench.
This is more sensible than polling, since this operation is slow.
\end{itemize}
Device access is limited to one application component at once by means of a proxy pattern.
With the \code{request\_proxy} and \code{release\_proxy} functions, up to one \code{HelmholtzCageProxy} instance may be obtained if none are currently active.
This proxy must be released with the latter function, to allow for another component to request a proxy instance.
All commands to the test bench are set by means of the proxy object, except for startup and shutdown commands, which must always be available to the application.
The hardware connection sequence goes as follows:
\begin{itemize}
\item Read the current config variables (see Section \ref{sec:config_handling}).
\item Attempt to connect to Arduino on specified com port.
\item Select \gls{psu} object type based on configuration variable.
\item Attempt to create \gls{psu} object instances.
\item Axis specific config variables are reloaded.
\item All \gls{psu} channels are zeroed and activated. The Arduino relay pins are set to their default ``low'' state.
\end{itemize}
The disconnection sequence:
\begin{itemize}
\item Zero and disable all \gls{psu} output channels. The \gls{psu} serial device is disconnected.
\item The Arduino relay pins are set to low, and the serial device is closed.
\item Information regarding the shutdown process is presented to the user.
\end{itemize}
\myparagraph{Axis Adapter Class \code{Axis}}
This class is an adapter object representing a single axis (X, Y, Z) of the test bench.
It contains static and dynamic status information about the axis in its attributes and the means to command the axis in its methods.
During program initialization an object of this class is created for each of the three axes and stored in the global \code{HelmholtzCageDevice} instance.
The class contains the primary way to command the test bench hardware in form of the \code{set\_signed\_current} method.
Its parameter is a positive or negative current value in Ampere.
The method first checks if this value exceeds the safe limits defined in the program configuration.
If not, the appropriate \gls{psu} channel and the switch box Arduino are commanded to supply the desired current and to actuate the polarity change relay as needed.
To generate a specific magnetic field, the \code{set\_field\_compensated} method can be called.
It takes a given field value $B$ in Tesla and calculates the needed current:
\begin{align}
I=\frac{B-B_0}{K}
\label{eq:field_current}
I=\frac{B-B_0}{K}
\label{eq:field_current}
\end{align}
$B_0$ is the background magnetic field in the measurement area for this axis in Tesla and $K$ is the coil constant in [\si{\tesla\per\ampere}]. The calculated value $I$ is passed to the \code{set\_signed\_current} method to command the test bench. The \code{set\_field\_simple} method works the same way, but without subtracting the ambient field.\\
$B_0$ is the background magnetic field in the measurement area for this axis in Tesla and $K$ is the coil constant in [\si{\tesla\per\ampere}].
The calculated value $I$ is passed to the \code{set\_signed\_current} method to command the test bench.
The \code{set\_field\_raw} method works the same way, but without subtracting the ambient field.
Similarly the minimum and maximum achievable field values $B_{min}$ and $B_{max}$ are calculated in the class initialization, mainly in order to provide this information to the user:
\begin{align}
B_{max} &= B_0 + I_{max}*K\\
B_{min} &= B_0 - I_{max}*K
B_{max} &= B_0 + I_{max}*K\\
B_{min} &= B_0 - I_{max}*K
\end{align}
Here $I_{max}$ is the maximum allowed current, as defined in the configuration page of the \gls{ui}.
\myparagraph{Arduino Control Class \code{ArduinoCtrl}}
This is the main class used to control the Arduino microcontroller inside the switch box. It inherits the \code{Arduino} class from the Arduino command \gls{api} \cite{Arduino_lib}. This provides a way to pass commands directly through the class object, which is created during program initialization and stored in \code{globals.py}. \\
Most commands to the switch box are passed directly through the inherited \code{Arduino} class. For example, the \code{digitalWrite()} method is called to energize a pin that actuates a specific relay. The purpose of the \code{ArduinoCtrl} class is mainly to provide supporting functionality directly related to the specific use case on the test bench, including:
\begin{itemize}
\item{Configuration of output pins used to trigger polarity switch relays}
\item{Checking Arduino connection and output pin status}
\item{\code{safe} method to set all used output pins to "LOW" for safe shutdown}
\end{itemize}
\myparagraph{Value checking function \code{value\_in\_limits}}
This function checks whether a value is within the safe limits defined in \code{globals.py}.
\myparagraph{Setup Function \code{setup\_all}}
This is the main initialization function. It is used at application start-up and during the runtime whenever the user updates settings or (re)connects a device. Its main tasks are:
\begin{itemize}
\item Read the latest information from the configuration object (see Section \ref{sec:config_handling})
\item Initiate communication with switch box Arduino and create object of \code{ArduinoCtrl} class
\item Initiate communication with \gls{psu}s
\item Create object of class \code{Axis} for each axis (x,y,z)
\end{itemize}
During these tasks it also handles different error cases, like disconnected devices or wrong settings in the configuration object.
\myparagraph{Shutdown Function \code{shut\_down\_all}}
This function is used to safely shut down all devices at the end of the program or if an error occurs. It is called in the \code{program\_end} function of \code{main.py}. Its primary tasks are:
\begin{itemize}
\item Secure all connected \gls{psu}s
\begin{itemize}
\item Set voltages and currents on both channels to \SI{0}{\volt} and \SI{0}{\ampere}
\item Disable power outputs on both channels
\end{itemize}
\item Secure Arduino
\begin{itemize}
\item Set all used output pins to "LOW"
\item Close serial connection
\end{itemize}
\item Show message to user with shutdown status of all devices and any errors encountered
\end{itemize}
During this process different error cases, like disconnected devices, are handled and the user is informed.
\myparagraph{Other Functions}
In addition to those discussed above, the file also contains functions to:
\begin{itemize}
\item Command currents or magnetic fields on all axes, based on a given vector
\item Check if all devices are still connected
\item Check if a given value is within the safe limits defined in \code{globals.py}
\end{itemize}
\newpage
\subsection{Graphical User Interface \code{User\_Interface.py}}
This file is used to construct the \gls{ui}, that the user interacts with to control the test bench. It contains several classes, each with code to build a specific section of the \gls{ui} as well as methods to perform the actions that are triggered by different user inputs in that section.\\
@@ -112,57 +151,126 @@ An instance of this class represents the application window. The program \code{m
The menu bar at the very top of the application window is constructed by this class. It contains methods to implement each option of the menu. At this time, it is only used to switch between modes, but more functionalities could be added in the future if needed.
\myparagraph{Static Manual Input Mode Class \code{ManualMode}}
The manual input mode interface is used to manually command static values of magnetic fields or currents on the test bench. It is placed in the main area of the application window, the layout can be seen in Figure \ref{fig:manualmodepure}. The methods provided in this class interface mainly with the \code{cage\_func.py} file to command the devices.
The manual input mode interface is used to manually command static values of magnetic fields or currents on the test bench.
It is placed in the main area of the application window, the layout can be seen in Figure \ref{fig:manualmodepure}.
The methods provided in this class interface mainly with the \code{globals.CAGE\_DEVICE} instance to command the devices.
\myparagraph{CSV Sequence Execution Mode Class \code{ExecuteCSVMode}}
This class constructs and operates the interface for executing magnetic field sequences from a \gls{csv} file. It is placed in the application's main area, its layout is shown in Figure \ref{fig:csvmodepure}.\\
Apart from a method to load \gls{csv} files, the class manages the separate thread that needs to be created every time a sequence is run. For this purpose, it interfaces mainly with the \code{csv\_threading.py} file to initialize, start and (if needed) stop the thread object. More details on the multithreading functionality is provided in Section \ref{sec:csv_exec}.
This class constructs and operates the interface for executing magnetic field sequences from a \gls{csv} file.
It is placed in the application's main area, its layout is shown in Figure \ref{fig:csvmodepure}.
Buttons are provided to load and run \gls{csv} sequences.
To execute the sequence without blocking the \gls{ui}, a separate execution thread defined in \code{csv\_threading.py} is created and managed by this class.
Hardware control is acquired upon creation of the thread, or an exception is returned otherwise.
More details on the execution thread is provided in Section \ref{sec:csv_exec}.
\myparagraph{Settings Page Class \code{Configuration}}
The program settings page is constructed in this class. It also controls the reading and writing of configuration files. Like the actual test bench control modes, the settings page is placed in the application's main area. Its layout can be seen in Figure \ref{fig:settingspure}.\\
The class generates and reads out all entry fields the user can use to set program constants like used serial ports, resistances and current limits. It interfaces with the \code{config\_handling.py} file to store this information in a \code{ConfigParser} object and to read and write it from and to configuration files (see Section \ref{sec:config_handling}).\\
Individual entry fields can be highlighted to point out values that exceed the safe limits defined in \code{globals.py} to the user.
The program settings page is constructed in this class.
It provides a \gls{ui} to edit application settings and also controls the reading and writing of configuration files.
Like the test bench control modes, the settings page is placed in the application's main area.
Its layout can be seen in Figure \ref{fig:settingspure}.
It interfaces with the \code{config\_handling.py} file to store this information in a \code{ConfigParser} object.
The \code{ConfigParser} object can then also be read and written to and from configuration files (see Section \ref{sec:config_handling}).
Individual entry fields are automatically highlighted when values exceed the safe limits defined in \code{globals.py} to notify the user.
Usage instructions and additional information can be found in Section \ref{sec:settings_guide}.
\myparagraph{Data Logging Configuration Page Class \code{ConfigureLogging}}
This class generates the page for configuring the logging of test bench data to a \gls{csv} file. Similarly to the settings page it is placed in the main area, its layout is shown in Figure \ref{fig:loggingpure}.\\
The page provides a number of checkboxes used to select what specific data is to be logged. This information is stored in a list of string keys. Whenever a row of data needs to be logged, a method in the class calls the \code{log\_datapoint} function in \code{csv\_logging.py}. To indicate what data to log, the string key list is passed as the function parameter (see Section \ref{sec:logging}). \\
There are two options to control when and how often data is logged: in regular intervals and on event. These can also be used at the same time. The information, which of these is enabled, is stored in two boolean attributes of the class object. For logging in regular intervals a method in the \code{ConfigureLogging} class object periodically calls itself. \\
For logging on event, a data point is generated whenever a significant change on the test bench is commanded. As of now, this is simply done by inserting a piece of code in every function where it was seen as appropriate. However, this is somewhat inconsistent and carries the risk of missing some state changes. A better balance between logging consistency and excessive data generation should be devised in a future update.
This class generates the page for configuring and controlling the logging of test bench data to a \gls{csv} file.
Similarly to the settings page it is placed in the main area, its layout is shown in Figure \ref{fig:loggingpure}.
The page provides a number of checkboxes used to select what specific data is to be logged.
This information is stored in a dictionary of log options.
Whenever a row of data needs to be logged, a method in the class calls the \code{log\_datapoint} function in \code{csv\_logging.py}.
To indicate what data to log, the config dictionary is passed as the function parameter (see Section \ref{sec:logging}).
There are two options to control when and how often data is logged: in regular intervals and on event.
These can also be used at the same time.
The information, which of these is enabled, is stored in two boolean attributes of the class object.
For logging in regular intervals a method in the \code{ConfigureLogging} class object periodically calls itself.
For logging on event, a data point is generated whenever a significant change on the test bench is commanded.
As of now, this is simply done by inserting a piece of code in every function where it was seen as appropriate.
However, this is somewhat inconsistent and carries the risk of missing some state changes.
A better balance between logging consistency and excessive data generation should be devised in a future update.
\myparagraph{Status Display Class \code{StatusDisplay}}
The status display (bottom left in Figure \ref{fig:ui_overview}) is used to monitor the state of the test bench devices. The individual values are displayed and updated through labels tied to variables of type \code{tkinter.StringVar()}, which are stored in a dictionary. The \code{update\_labels} method then polls the current values from the \code{Axis} and \code{ArduinoCtrl} objects described in Section \ref{sec:cage_func} and updates the label variables. This is done both periodically and on major test bench commands.
The status display (bottom left in Figure \ref{fig:ui_overview}) is used to monitor the state of the test bench devices.
The individual values are displayed and updated through labels tied to variables of type \code{tkinter.StringVar()}, which are stored in a dictionary.
\code{StatusDisplay} subscribes to status updates from \code{HelmholtzCageDevice}, by registering a callback that queues the new information.
The \code{update\_labels} method is called with the new status information and updates the label variables.
A shared queue and regular polling for new status information in the main thread is used to avoid calling Tkinter code outside the main thread.
Updates are received from the cage object at a polling rate of \SI{1}{\second}.
\myparagraph{Output Console Class \code{OutputConsole}}
The output console in the bottom right of the \gls{ui} (see Figure \ref{fig:ui_overview}) is generated in this class. The main body is a \code{tkinter.Text} widget. Printing of information to it is done via the custom \code{ui\_print} function, which can be used exactly like the built-in \code{print}.
The output console in the bottom right of the \gls{ui} (see Figure \ref{fig:ui_overview}) is generated in this class.
The main body is a \code{tkinter.Text} widget.
Printing of information to it is done via the \code{ui\_print} function defined in \code{utility.py}, which can be used exactly like the built-in \code{print}.
\subsection{Sequence Execution File \code{csv\_threading.py}}
\label{sec:csv_exec}
This file contains code for executing a timed sequence of magnetic field vectors from a \gls{csv} file.
To do this without interfering with the \gls{ui}, it must run in a separate thread.
The main class for this is \code{ExecCSVThread}.
It inherits the \code{Thread} class from the \code{threading} library, so each of its instances represents a unique thread.
Its main method, apart from those needed to start and stop it, is \code{execute\_sequence}.
It takes the desired sequence in the form of a \code{numpy} array and commands the test bench at the appropriate times.
The method also continuously polls that all devices are still connected and that the run has not been aborted by user input or closing of the main application window.
Apart from the main class this file contains functions to read data from a \gls{csv} file to a \code{numpy} array and check that no values in it exceed the test bench limits.
There is also a function to generate a plot of the data for display in the \gls{ui}.
The line plot is modified to visually reflect the discrete and nearly instantaneous change of fields in the test bench (see result in Figure \ref{fig:ui_overview}).
\subsection{Configuration Handling File \code{config\_handling.py}}
\label{sec:config_handling}
This file contains functions and variables for reading and writing configuration files.
The processing is done using the \code{configparser} library.
The current program configuration is stored in \code{CONFIG\_OBJECT}, an instance of \code{ConfigParser}.
It contains all configurable information and can be stored in its entirety in a .ini file.
The \code{write\_config\_to\_file} and \code{get\_config\_from\_file} functions are used to read/write the entire \code{CONFIG\_OBJECT} from/to such a file.
An example is provided in Appendix \ref{app:example_files}.
To access or change specific values the \code{read\_from\_config} and \code{edit\_config} functions are provided.
The latter also checks if a value is within the safe limits defined in \code{globals.py}.
If it is not, the user is warned and asked to confirm, before the value is written to \code{CONFIG\_OBJECT}.
The \code{check\_config} function does this check for all values of a provided configuration object.
If excessive values are found, a warning message is displayed and the configuration \gls{ui} page opened, where they are highlighted.
\subsection{Sequence Execution File \code{csv\_threading.py}}\label{sec:csv_exec}
This file contains code for executing a timed sequence of magnetic field vectors from a \gls{csv} file. To do this without interfering with the \gls{ui}, it must run in a separate thread.\\
The main class for this is \code{ExecCSVThread}. It inherits the \code{Thread} class from the \code{threading} library, so each of its instances represents a unique thread. Its main method, apart from those needed to start and stop it, is \code{execute\_sequence}. It takes the desired sequence in the form of a \code{numpy} array and commands the test bench at the appropriate times. When a new field vector is set, all related actions need to be performed before the scheduler returns to the main thread. A \code{threading.Lock} object is used to ensure this. The method also continuously checks that all devices are still connected and that the run has not been aborted by user input or closing of the main application window.\\
Apart from the main class this file contains functions to read data from a \gls{csv} file to a \code{numpy} array and check that no values in it exceed the test bench limits. There is also a function to generate a plot of the data for display in the \gls{ui}. The line plot is modified to reflect the discrete and nearly instantaneous change of fields in the test bench (see result in Figure \ref{fig:ui_overview}).
\newpage
\subsection{Configuration Handling File \code{config\_handling.py}}\label{sec:config_handling}
This file contains functions and variables for reading and writing configuration files. The processing is done using the \code{configparser} library. \\
The current program configuration is stored in \code{CONFIG\_OBJECT}, an instance of \code{ConfigParser}. It contains all configurable information and can be stored in its entirety in a .ini file. The \code{write\_config\_to\_file} and \code{get\_config\_from\_file} functions are used to read/write the entire \code{CONFIG\_OBJECT} from/to such a file. An example is provided in Appendix \ref{app:example_files}.\\
To access or change specific values the \code{read\_from\_config} and \code{edit\_config} functions are provided. The latter also checks if a value is within the safe limits defined in \code{globals.py}. If it is not, the user is warned and asked to confirm, before the value is written to \code{CONFIG\_OBJECT}.\\
The \code{check\_config} function does this check for all values of a provided configuration object. If excessive values are found, a warning message is displayed and the configuration \gls{ui} page opened, where they are highlighted.\\
The last function of the file is \code{reset\_config\_to\_default}, which overwrites the entire \code{CONFIG\_OBJECT} with the default values defined in \code{globals.py}.
\subsection{Data Logging Handling File \code{csv\_logging.py}}\label{sec:logging}
This file handles the logging of test bench status data to a \gls{csv} file, using the \code{pandas} library. Within the program the logged data is stored in a \code{pandas.DataFrame} object, which represents a table with column headers and data rows.\\
The information about what data can be logged and how to access the specific values is stored in a central dictionary. Its keys are the names of the values as they are displayed in the \gls{ui}, e.g. "Voltage Setpoint" or "Actual Current". The keys are used as handles for accessing the dictionary elements and as labels for the checkboxes in the logging configuration \gls{ui} page (see Figure \ref{fig:loggingpure}). The \code{ConfigureLogging} class of the \gls{ui} generates a list containing the keys belonging to the ticked checkboxes, which is then passed to the logging functions to indicate what data should be logged.
The keys are intentionally identical to the labels used for the status display. This makes it easier for the user to know what each value means. It may also allow unifying some of the code for these two functionalities in the future.\\
The dictionary elements themselves are a string representation of the respective attribute names in the \code{Axis} class. They are used with the \code{getattr} command to get the values of these attributes from the individual axis objects.\\
To log a data point, the mentioned list of keys is passed to the \code{log\_datapoint} function. For each of its elements, the data from all three axes is read out using \code{getattr} as described above. To form the correct number of column headers, the key list is expanded to include each item three times, for example \code{["Target Field"]} becomes \code{["Target Field X", "Target Field Y", "Target Field Z"]}. Together with the values this forms a new \code{DataFrame}, which is then appended to the previously logged data.\\
When the logging is finished, the entire \code{DataFrame} can then be saved to a \gls{csv} file using the \code{write\_to\_file} function. The file also provides functions to allow the user to choose a filename and clear the logged data.
\newpage
\subsection{Data Logging Handling File \code{csv\_logging.py}}
\label{sec:logging}
This file handles the logging of test bench status data to a \gls{csv} file, using the \code{pandas} library.
Within the program the logged data is stored in a \code{pandas.DataFrame} object, which represents a table with column headers and data rows.
A \code{logging\_selection\_options} dict, contains a key-value list of logging options and their display labels, which are used to generate the \gls{ui}.
The \code{ConfigureLogging} class of the \gls{ui} then later generates a list containing the keys belonging to the ticked checkboxes, which is then passed to the logging functions to indicate what data should be logged.
To log a data point, this option dict is passed to the \code{log\_datapoint} function.
According to the options set, data is aggregated from the \code{CAGE\_DEVICE} and \code{MAGNETOMETER} components and assembled into a key-value dictionary.
Since no unified set of keys covering all of the logged parameters exist, new, understandable keys were selected.
The dict of log values is appended to the end of a Python list that collects every data point.
When the logging is finished, a \gls{csv} file is assembled and saved using the \code{write\_to\_file} function.
Assembly consists of first prepending two headers to the data rows, one containing the dictionary keys, and one containing the user-readable long names.
The long names can be obtained from the \code{get\_long\_column\_header} function.
The file also provides functions to allow the user to choose a filename and clear the logged data.
\subsection{Hardware Control Libraries}
\myparagraph{PS2000B \gls{psu} Control Library \code{PS2000B.py}}
The code necessary to control the power supply units was adapted from S. Spr{\"o}ßig \cite{PS2000B_lib} with only minor modifications. The library provides the class \code{PS2000B} and some supporting functions. Each object of the class represents a physical \gls{psu}. These objects can be used to access status information and command the device, for example to set currents and voltages. More information can be found in the readme file provided by the original author.\\
The main modification to this library done as part of this thesis, was to implement independent commanding of both \gls{psu} channels. For this an additional parameter \code{channel} (integer type, 0 or 1) was added to all methods of the \code{PS2000B} class that interact with the device.
The code necessary to control the power supply units was adapted from S. Sprößig \cite{PS2000B_lib} with only minor modifications.
The library provides the class \code{PS2000B} and some supporting functions.
Each instance of the class represents a physical \gls{psu}.
These objects can be used to access status information and command the device, for example to set currents and voltages.
More information can be found in the readme file provided by the original author.
The main modification to this library done as part of this thesis, was to implement independent commanding of both \gls{psu} channels.
For this an additional parameter \code{channel} (integer type, 0 or 1) was added to all methods of the \code{PS2000B} class that interact with the device.
Additionally the properties \code{PS2000B.output1}, \code{PS2000B.voltage1} and \code{PS2000B.current1} were duplicated to support the second channel (e.g. \code{PS2000B.output2}).
\myparagraph{Arduino Command \gls{api} \code{arduino.py}}
To control the Arduino microcontroller the online library from \cite{Arduino_lib} was integrated without major modifications. To use it, the provided \code{prototype.ino} file needs to be transferred to the Arduino. An object of class \code{Arduino} can then be used to control the board (connected via \gls{usb}) with methods that closely resemble the ones used in the standard Arduino programming language. More details can be found in the readme file provided by the original author \cite{Arduino_lib}.
To control the Arduino microcontroller the online library from \cite{Arduino_lib} was integrated without major modifications.
To use it, the provided \code{prototype.ino} file needs to be transferred to the Arduino.
An object of class \code{Arduino} can then be used to control the board (connected via \gls{usb}) with methods that closely resemble the ones used in the standard Arduino programming language.
More details can be found in the readme file provided by the original author \cite{Arduino_lib}.
\section{Conversion to Executable}
The program is compiled to a .exe executable file to enable simple use without a Python installation. For this, the "Auto Py to Exe" tool developed by B. Vollebregt \cite{auto_py_to_exe} is used. The resulting files are distributed in a separate repository on the \gls{irs} git server.\footnote{\url{https://egit.irs.uni-stuttgart.de/zietzm/Helmholtz_Test_Bench_Releases}} When a new software version is ready for publication, the following procedure should be used to release it:
@@ -129,7 +129,7 @@
\setlength{\parindent}{0mm}
%\setlength{\unitlength}{1mm}
%\sloppy
\sloppy
% ********************************************************************
% Begin of document
@@ -170,6 +170,7 @@
\glsunset{pc}
\glsunset{tbd}
\glsunset{com}
\glsunset{api}
\include{Chapters/03_Software}
\include{Chapters/04_Users Guide}
+1 -1
View File
@@ -5,7 +5,7 @@
$B$ & Magnetic flux density & \si{\tesla} \\[0.6em]
$B_0$ & Ambient magnetic flux density & \si{\tesla} \\[0.6em]
$B_{max}$ & Maximum achievable magnetic flux density & \si{\tesla} \\[0.6em]
$B_{max}$ & Minimum achievable magnetic flux density & \si{\tesla} \\[0.6em]
$B_{min}$ & Minimum achievable magnetic flux density & \si{\tesla} \\[0.6em]
$I$ & Current & \si{\ampere} \\[0.6em]
$I_{max}$ & Maximum current & \si{\ampere} \\[0.6em]
$K$ & Coil constant & \si{\tesla\per\ampere} \\[0.6em]
-165
View File
@@ -1,165 +0,0 @@
%PDF-1.4
%âãÏÓ
1 0 obj
<<
/Title ()
/Author ()
/Subject ()
/Keywords ()
/Creator (yExport 1.5)
/Producer (org.freehep.graphicsio.pdf.YPDFGraphics2D 1.5)
/CreationDate (D:20210314231237+01'00')
/ModDate (D:20210314231237+01'00')
/Trapped /False
>>
endobj
2 0 obj
<<
/Type /Catalog
/Pages 3 0 R
/ViewerPreferences 4 0 R
/OpenAction [5 0 R /Fit]
>>
endobj
4 0 obj
<<
/FitWindow true
/CenterWindow false
>>
endobj
5 0 obj
<<
/Parent 3 0 R
/Type /Page
/Contents 6 0 R
>>
endobj
6 0 obj
<<
/Length 7 0 R
/Filter [/ASCII85Decode /FlateDecode]
>>
stream
Gb"/*D3*J\&cSq?_,!?9F;;NG'_Tjnj1%M[f^Yg:&-R`*:Ud5gX1`1jrUTD=Uc?jr]6>W+;U,Od"'Ii5
^'m"4LGL4#@LE,IgA>$X*gAF6^QA+HqE=*[gdW_/EJH'_Y"BH%S#E'nkW_oRm<nZ&]"cE+g$Ar<\t<SU
=oS#R&\B='-j5OerdfJ,k@Q^IiS4k)Y+;&mAiN?S`sOV:43UCq[d_CdZ@5A0T,6TCT.AOGb-e[;A4fW$
'abRBg[S[:1I5+(0"V#!N'(>'i@lT\iF7MI!I%RP1lZWE&glYgfRPanCQ5jp3"[a^njAAtc2:N2:XD=g
d)/YCN%qq^milXkZX*3G$Y=6pN"<!DoUUpe<Y(>2#rhSSrb=.LX./=b=,\hN't_&:rc2$*%qL12lPoEW
h(SZ?P,nUP5?raMHY<DQP<4=O4B8XcSQujXj%OXfN#:r4jbJ'ecHNKHo5KG"@J0C^cKmc]L\2[uhL/Rl
3DS70&#rs#)kRYY&Xe@g+_&^34IIdX\FMV!fN1$G@#[1gZ\Wo0rWG6A(hg_$`%#AF,RHBZmQ.QFnbqXr
nkXA6ii*k#2?%]&5Hf:]%;@mK+7c;QYH),cRgN^8K+L:@,/BJuodg6*\O'PH/0)\#6]C,>h4J<2D$!eK
fjmu)369W@)m8d,nc&5aY=$sZd^1#`&uY!8NaD/=6aNi7U%;*XgI&NP+6#t4m(X1p7VR@$_k(`iY%'a8
0#0&J_0LNj&J&LKf9<(,Qk3nfVOI.,J-d%RL'c5"jKO_6/5cXd3!%WCdYo*#7Ee&%Ql<d@ak2:Pp7"M:
88&oS.:Sq^<.a6#Yg>u=e[FNN_n$VY,#p"PZ.-S.[Oo,N;Zgb2[>!qfl%/u3\5a7II\:P!K5n)MiHLf3
-<&@mZ1E3+IQEOhRn'Z23Z*ooXla9i>i;ZQknIaa#nA0#f4W5CF9UCim-t61rOVhqI41O&4RgPMG&(./
C\V7:esiQXETf7'ju!+Q<,eg!)-N!W,u#`u.'u^"(R-Dp.,kT=6L;i*YqJ?-/igO];^*]t:IkO];C4kN
?GGibk.P&7\,!T@g+%eNr'V<u//d-qO%!>2q4Sq?%;_K6!Z(Gf!?H2:<t^Z#-:`5]DXXW8>LWmuKRo;l
^-<YS#imrVT7\#X&K/od5U*':<uu.4Z2>/Cq,rp$fl`bsa<c[K*t#[&kWj%ciI/,gCr#Xtn<9=AQ.q+4
FNjOSr/_TD(HLj'bBNtmJ1OB`3T(S\!]/"WilYc$E%F5lF:#7sY1jYrJ;99FcV.$J`fan7hj0c+G:li#
+q-G+UH:CUD.IiT(\u>5aF1C!(]-AkL4!g[6P2T2eFo74S:e0N4G"B'r.<^iM^50i1)!Z$gt%-N9miUR
RrXl&1j`GULjf)%.6>(YP:2m=>`^LIb;K:cE7Y)264$8l*=3/tkRX;Cp!HZKG7/49WZ/5sIQnV@qNNp7
G+;^=bl`1t48d\):\(4'anAgW8jDGA%T5-Zm>A!(;'DY=;D?b1U_d!:+AmYhc31l(XE!5T$;P"^\2eN2
.p+C`j0rl3ej2$"QF(-5!UJbnk_-=U2<Sl<(&59N.:4QMQB;*,8bk!\_'4siBos%g/_^(<nARFU>^;i"
N9,gs/TCDA^MZuU$Edi0D2L;6NdC:,+oqIf#Z\6T@gG,@gMW'p\Sh/YO5OG9hC"#?L!*BsCnh+qU#Ol2
,=VcCq&s'r/G&0cJLoB1M(l6rc-<MpG<n<a9%l!n,Kl"Rk7N*(PX1>Zs"So3,R9ea'_E%M\[jIuRe(<S
\FMV!(s<FEQCj?ooBZ*4Q\\4rg!^EPO@js@QZY<WAVmsL(H+l-:$.dL&tXlIE"XsPS7LGiJ7N@I]18s9
[G17>]YmC\r45$-1R2B\clpH=0r,!5R0`o))/71F#+iNaQiiAPQ^M!W"^#+up9,'0L`%::H[pP?"dh<F
DU66p-"AAHK3Xq7/:a.lqIuohGe5S!F0*-Zk8fMe=@);9MDigb0bn=pTqLrX6+rqnE[L9UAprUBN.M_;
_%jn$MS]jlM=CfUDjoLZagYbC<2c:;&OM<-^*Ra),U)^?.&]1XoE&+U9Rp,T2WGDW5**TaR>YDUMEpen
RK5(%T?=1XS'O"cs14d,Y*bW)o1h)]ehIJ,b#(uXJcl6Zj\12)k,$BJ$9f,OQoX`e@4qfAgjS$HRTGV5
a=KR<c%s94.:H!0;8S3Y6\!eNlO5[FfSk7XN>V8.?f=\_`+d(4l:oqubdkE$9rI"hnCebOi7"cWQ.Jae
PRmVROmfREKd>:;HS"dT+jl)fI=Kf5_-E<ufU(>O)-b&YXk[FX`A2dgTeI1'Qmqc%"KuSQImI<$,)i,M
k4(*CMc";Kp@2X04oHRboj>Y>b\2?YjB`C2'0L1,7^4UQe5I9\"U1LM3\9ftT$3-sB3:.u\1='-MJTPI
`B?XCOYCkBE/)EsKk4+7LI^STV"O&6=\opo"EW)@0b&&iI0mul%E<0G!OL.$YP`T<\VsJ*UT1I5hI::G
PJ(;m]/XNbZ15:6-^8O4Z)LO.nh:O>9$e@Nr^F!pISWK,O0)>s4dGa#ZWgj_(#"qo/B4GHOfD^sr(\@!
s6ZHW+/QZ)5Q:fghd`J~>
endstream
endobj
7 0 obj
2645
endobj
8 0 obj
<<
/Type /Font
/Subtype /Type1
/Name /F1
/BaseFont /Helvetica
/Encoding 9 0 R
>>
endobj
9 0 obj
<<
/Type /Encoding
/Differences [0 /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /grave /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /bullet /dagger /daggerdbl /ellipsis /emdash /endash /florin /fraction /guilsinglleft /guilsinglright /minus /perthousand /quotedblbase /quotedblleft /quotedblright /quoteleft /quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron /Ydieresis /Zcaron /dotlessi /lslash /oe /scaron /zcaron /.notdef /Euro /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /.notdef /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis]
>>
endobj
3 0 obj
<<
/Parent null
/Type /Pages
/MediaBox [0.0000 0.0000 723.00 557.00]
/Resources 10 0 R
/Kids [5 0 R]
/Count 1
>>
endobj
11 0 obj
[/PDF /Text /ImageC]
endobj
12 0 obj
<<
/F1 8 0 R
>>
endobj
13 0 obj
<<
/S /Transparency
/CS /DeviceRGB
/I true
/K false
>>
endobj
14 0 obj
<<
/Alpha1
<<
/ca 1.0000
/CA 1.0000
/BM /Normal
/AIS false
>>
>>
endobj
10 0 obj
<<
/ProcSet 11 0 R
/Font 12 0 R
/ExtGState 14 0 R
>>
endobj
xref
0 15
0000000000 65535 f
0000000015 00000 n
0000000315 00000 n
0000005659 00000 n
0000000445 00000 n
0000000521 00000 n
0000000609 00000 n
0000003364 00000 n
0000003387 00000 n
0000003520 00000 n
0000006160 00000 n
0000005830 00000 n
0000005870 00000 n
0000005915 00000 n
0000006017 00000 n
trailer
<<
/Size 15
/Root 2 0 R
/Info 1 0 R
>>
startxref
6254
%%EOF