\chapter{Software Implementation}\label{chp:software}
\section{Program Structure}
\glsunset{psu}
\glsunset{irs}
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=\textwidth]{media/complete_software_architecture}
	\caption{Software file layout and architecture}
	\label{fig:softwarelayout}
\end{figure}

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 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 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{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{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}
\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\_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
\end{align}
Here $I_{max}$ is the maximum allowed current, as defined in the configuration page of the \gls{ui}.

\myparagraph{Value checking function \code{value\_in\_limits}}
This function checks whether a value is within the safe limits defined in \code{globals.py}.

\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.\\

\begin{figure}[h]
	\centering
	\includegraphics[width=\linewidth]{media/csv_mode_full}
	\caption{User interface (example state)}
	\label{fig:ui_overview}
\end{figure}

The general layout of the application window is shown in Figure \ref{fig:ui_overview}. At the bottom left is a status display, that is constantly updated with the current state of all connected devices. To its right is a text area that can be used to print out information to the user, similar to a basic console output. The top half is the main area, used to display the interactive elements. Its content changes depending on which mode the user has selected. At the very top of the window a drop-down menu is used to switch between the different modes. For more detailed usage instructions please refer to Section \ref{sec:software_guide}.\\
The interface is setup through a central object of class \code{HelmholtzGUI}, which inherits the \code{Tk} main application class from the \code{tkinter} library. The other \gls{ui} element classes (except the top menu) inherit from \code{tkinter.Frame} and are placed in the layout by the main object. This structure is based on a proposal by H. Kinsley \cite{Tkinter_tutorial}. Each class is briefly described below.

\myparagraph{Main Application Class \code{HelmholtzGUI}}
An instance of this class represents the application window. The program \code{mainloop} is running on this object. The major \gls{ui} elements are initialized here and placed at their appropriate positions. This class's  only method is \code{show\_frame}, which is used to switch between the different operating modes by displaying their respective frame in the main area.

\myparagraph{Menu Bar Class \code{TopMenu}}
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{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}.
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{Magnetometer Calibration Mode Class \code{CalibrateMagnetometer}}
This class constructs magnetometer calibration interface. 
It is placed in the application's main area, its layout is shown in Figure \ref{fig:magcalibrationpure}.
This class creates and manages a separate execution thread defined in \code{calibration.py} to preform the calibration without blocking the \gls{ui}.
Hardware control is acquired upon creation of the thread, or an exception is returned otherwise.
More details on the calibration thread is provided in Section \ref{sec:calibration_processes}.
Communication with the running thread is preformed by means of the \code{view\_mpi\_queue} queue variable, which is regularly polled in \code{update\_view}.
This is where different \gls{mpi} commands are delegated to their respective handlers.
For example, the live completion status and final calibration results are handled here.
It should be of note, that the calibration threads do not store any results or information, instead these are contained in variables defined near the top of \code{\_\_init\_\_}.

\myparagraph{Ambient Field Calibration Mode Class \code{CalibrateAmbientField}}
This class consists of an identical structure to the Magnetometer Calibration View Class.
The only major difference is that this view provides an interface for two calibration procedures; The ambient field and the coil constants.
All relevant calibration worker threads can also be found in \code{calibration.py}.

\myparagraph{Settings Page Class \code{Configuration}}
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 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.
\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 \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{Calibration Procedures \code{calibration.py}}
\label{sec:calibration_processes}
This file contains the worker thread objects \code{AmbientFieldCalibration}, \code{CoilConstantCalibration}, \code{MagnetometerCalibrationSimple}, and \code{MagnetometerCalibrationComplete}.
All of these threads start by checking for and acquiring hardware during instantiation, while still in the main thread.
Then upon running, the main code in \code{calibration\_procedure} is executed; This function may freely block and make use of sleep commands.
Typically, calibration procedures should save two data detail levels: Processed data that is displayed as a final result, and detailed, raw data points that can be exported by the user to apply custom algorithms or verify the applications function.

The calibration procedures communicate with the main thread via a \gls{mpi} queue.
Depending on whether the procedure completes successfully or not, a \code{finished} or \code{failed} status signal is sent.
In addition, a \code{calibration\_data} message is sent together with ``\code{finished}'' to convey the final results to the main thread.
Other signals may also be used, such as \code{progress} to indicate the current completion status to the main thread.
Data that should be passed to the calibration procedure from the main thread, is done so through the constructor.

\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.

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{TCP Remote Control \code{socket\_control.py}}
This class is responsible for enabling TCP control of the Helmholtz cage hardware.
In addition, it is the sole data path for magnetometer data to ingress into the control software.
As with all other software components, the command processing thread accesses the hardware using the global \code{g.CAGE\_DEVICE} and \code{g.MAGNETOMETER} instances, and uses the proxy model to prevent control collisions.

The \code{socket\_control.py} file defines the \code{SocketInterfaceThread} class, which provides a listener thread to accept incoming TCP connections.
The listener thread instance is defined at application start-up in \code{main.py}.
For each established connection, a \code{ClientConnectionThread} instance is created.
All the main logic and command parsing is handled the client connection/session threads.
The \gls{api} is documented in Chapter \ref{sec:tcp_api} and also in the application source code.

Note: Following an application crash, the application may present an error in acquiring the TCP port.
This may be fixed by waiting a few minutes, or restarting the computer.
Perhaps this special case can be fixed with a more rigorous exit handler in the future.

\subsection{Hardware Control Libraries}

\myparagraph{``\code{PSUDevice}'' Abstract Class and PSU Drivers \code{psu\_device.py} / \code{PS2000B.py}}

An abstract class \code{PSUDevice} describing any compatible power supply object is used to allow for quick and easy addition of new device support.
This improvement was implemented since power supplies may be replaced or upgraded on a moderately frequent basis.
The abstract class exposes functions to set currents and voltages, and also to query the current device status.
QL355TP devices are natively supported with the \code{PSUDeviceQL355TP} object, that is also defined in \code{psu\_device.py}.

Since the code was at some point migrated to require a consistent interface for PSU objects, the original PS2000B library had to be put in a wrapper class (\code{PSUDevicePS2000B}).
The external library provides the class \code{PS2000B} and some supporting functions, and was adapted from S. Sprößig \cite{PS2000B_lib} with only minor modifications.
More information can be found in the read me file provided by the original author.
The library objects provide effectively the same set of functions advertised by the abstract class, as such the wrapper is quite trivial.
In the future, it may make sense to migrate the library code directly into the \code{PSUDevicePS2000B} class.

The main modification to the PS2000B 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} / \code{arduino\_device.py}}
To control the Arduino microcontroller, an interface and wrapper class is used.
First, the online library from \cite{Arduino_lib} is integrated without major modifications to expose a hardware control \gls{api}.
It communicates with the provided \code{prototype.ino} Arduino program to allow toggling and control of its pins with methods that closely resemble the ones used in the standard Arduino programming language.
More details can be found in the read me file provided by the original author \cite{Arduino_lib}.

The \code{Arduino} class is then wrapped in the \code{ArduinoDevice} class defined in \code{arduino\_device.py}, to expose a more polarity-switcher-relevant \gls{api}.
This simplifies its use in the already complex \code{HelmholtzCageDevice} function.

\myparagraph{Magnetometer Device \code{magnetometer.py}}
The magnetometer object is hardware-agnostic, and merely mirrors the state of an external physical device.
To enable quick and optimal compatibility with new magnetometer devices, data is ingested by means of the TCP port and the application functions purely as a listener.
External magnetometer interface scripts will first push their data according to the \gls{api} defined in \code{socker\_control.py}, which will then be forwarded to the global \code{MagnetometerProxy} instance.
This means the magnetometer proxy object is purely passive, and functions like a simple data container.

The magnetometer object mirrors the state of the TCP connection, which is presented as a connected/disconnected state.

\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:
\begin{enumerate}
	\item \code{pip install auto-py-to-exe} in the project's virtual environment.
	\item Execute "auto-py-to-exe" in a console. To make sure the right version is executed, don't install auto-py-to-exe in your system environment.
	\item In the Auto Py to Exe \gls{ui}, select \code{main.py} file as "Script Location"
	\item Select options "One File" and "Window Based"
	\item Select file "Helmholtz.ico" as the "Icon"
	\item Select "Releases" in the development repository as the output folder
	\item Execute conversion
	\item Rename resulting "main.exe" file to "Helmholtz Cage Control.exe"
	\item Verify correct program operation from created executable
	\item Copy new executable to the release repository\footnotemark[\value{footnote}] and replace previous version
	\item Commit, merge and create a new release in the git web interface
	\item Record changes in release notes and changelog and update the documentation
\end{enumerate}
