Skip to content

Commit

Permalink
Merge pull request #6 from mitbailey/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
mitbailey authored Oct 14, 2022
2 parents 86c6889 + bf4ef4b commit daa6ea1
Show file tree
Hide file tree
Showing 12 changed files with 1,026 additions and 354 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
**/*.fls
**/*.log
**/*.synctex.gz
**/*.toc

# Compiled source #
###################
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,13 @@ Boot in debug mode, no real connections to hardware will be attempted:

## Compatible Hardware
- KST101 (Motor Controller)
- KM6485 (Picoammeter)
- KM6485 (Picoammeter)

## Hierarchy
GUI <-> Middleware <-> Drivers <-> Hardware
- The GUI calls Middleware functions and is the layer the user directly interacts with. The GUI simply knows that it exists above some Monochromator with some type of Motion Controller and Sampling device.
- The middleware allows the GUI to be agnostic to specific hardware and hardware implementations, providing the GUI layer with consistent functions to interface with across all forms of Motion Control and Sampling. The Middleware determines which drivers must be used.
- The drivers interact directly with the Motion Controller and Sampler and are specific to their model / type.

# Executable Compilation
`pyinstaller mmc.spec`
Binary file added documentation/manual.pdf
Binary file not shown.
151 changes: 151 additions & 0 deletions documentation/manual.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
\documentclass{article}
\usepackage[margin=1in]{geometry}
\usepackage{amsmath}
\usepackage{hyperref}
\usepackage{graphicx}
\usepackage[export]{adjustbox}
\usepackage{caption}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{color}
\usepackage{multirow}
\usepackage{lscape}
\usepackage{colortbl}
\usepackage{hanging}
\usepackage[most]{tcolorbox}
% \usepackage{titlesec}
% \usepackage{indentfirst}

\tcbset{
frame code={}
center title,
left=0pt,
right=0pt,
top=0pt,
bottom=0pt,
colback=gray!30,
colframe=white,
width=\dimexpr\textwidth\relax,
enlarge left by=0mm,
boxsep=5pt,
arc=0pt,outer arc=0pt,
}

\parindent 0pt
\parskip 2ex % paragraph spacing

\hypersetup{
colorlinks,
citecolor=red,
filecolor=green,
linkcolor=blue,
urlcolor=blue
}

\definecolor{dkgreen}{rgb}{0,0.6,0}
\definecolor{gray}{rgb}{0.5,0.5,0.5}
\definecolor{mauve}{rgb}{0.58,0,0.82}

\lstset{frame=tb,
language=C,
aboveskip=1mm,
belowskip=0mm,
backgroundcolor=\color{black!5},
showstringspaces=false,
columns=flexible,
basicstyle=\footnotesize,
numbers=none,
numberstyle=\tiny\color{gray},
keywordstyle=\color{blue},
commentstyle=\color{dkgreen},
stringstyle=\color{mauve},
breaklines=true,
breakatwhitespace=true,
tabsize=3
}

\renewcommand\lstlistingname{Code Block}

\title{%
McPherson Monochromator Controller Software \\
User Manual \\
\small Revision 1}
\author{Mit Bailey}
\date{18 September 2022}

\setcounter{tocdepth}{3}
\setcounter{secnumdepth}{3}

\newcommand*{\fullref}[1]{\hyperref[{#1}]{\ref*{#1} \nameref*{#1}}}

\begin{document}

\maketitle
\newpage

\tableofcontents
\newpage

\section{Getting Started} \label{section:gettingstarted}

\subsubsection{Download}
The latest version of the software will always be available at \url{https://github.com/mitbailey/MMC/releases}. Once on the webpage, extend the \textbf{Assets} drop-down menu and click the \verb|mmc.exe| file to begin downloading.

\subsubsection{Installation}
Currently there is no installation option.

\section{Starting the Program} \label{section:startingtheprogram}

\subsection{Startup} \label{subsection:startup}
To start the program simply double-click the previously downloaded \verb|mmc.exe| file. Please note that on startup the program will attempt to find the configuration file, \verb|config.ini|, in its current directory. If it does not find this file, one will be created with default settings.


\begin{tcolorbox}
{\color{red}\textbf{\emph{IMPORTANT!}}} \\
As of version \href{https://github.com/mitbailey/MMC/releases/tag/v0.3}{v0.3}, the McPherson Monochromator Controller Software (MMCS) requires that all hardware be connected when starting the program. In later implementations, a Device Manager window will allow selection of specific hardware and connection of hardware after MMCS boot-up.
\end{tcolorbox}

Please see \fullref{section:compatiblehardware} for details on compatibility.

\subsection{Debug Startup}
Starting the program with the command line argument \verb|1| will allow the MMCS to run in debug mode. In this mode the program will not attempt to connect to hardware and will simulate all functionality.

\subsection{Startup Behavior}

During initial startup, a loading window will be displayed while the MMCS searches for compatible hardware. Once the MMCS is started and has automatically detected and connected successfully to the hardware, it will begin by homing the device. The program will be mostly uninteractable until this is complete.

If the software is unable to find compatible hardware, the loading window will close and the program will stop running. In future versions a Device Manager window will be displayed allowing the user to select and connect hardware.

\section{Compatible Hardware} \label{section:compatiblehardware}

A list of currently supported hardware can be found in the \emph{Compatible Hardware} section of the README (\url{https://github.com/mitbailey/MMC#readme}).

\section{Graphical User Interface} \label{section:interface}

The Graphical User Interface (GUI) is split into three main areas: the left \emph{Interactable Controls} area, the right \emph{Data Graph} area containing a data display graph, and the bottom \emph{System Status} area. The GUI also has a \emph{Menu Bar} along the top.

\subsection{Interactable Controls Area}

This section is broken up into manual controls, scanning controls, and a data table.

The manual control section allows the user to enter a desired position in nanometers and then press \verb|Move to Position| to move the monochromator to the desired wavelength. Also present is a \verb|Home| button which manually homes the device.

The scanning control section allows the user to perform a scan with the device. Once \verb|Begin Scan| is pressed, the device will move to 0 nanometers and then to the desired start position. It begins by taking data at the start position, stepping by the distance specified in the \verb|Step| box collecting data at each step, and finishes by taking data at the end position.

Once a scan is completed, it will be displayed in the data table. By default it is also shown in the Data Graph to the right. The user can edit the Name and toggle visibility on the Data Graph by checking the \verb|Plot| checkbox. Selecting a scan and pressing \verb|Save Data| or \verb|Delete Data| will open a save-to-PC prompt or delete the saved data respectively.

The \verb|Machine Configuration| button opens a window allowing the user to manually specify details about the monochromator device they are using.

\subsection{Data Graph Area}

The graph shows all scans by default. Scans can be removed by unchecking the \verb|Plot| checkbox in the Data Table or by pressing the \verb|Clear Plots| button underneath the Data Graph.

\subsection{System Status Area}

This area shows, from left to right, the current monochromator position in nanometers, the current behavior of the system (system status), and the percent complete progress of any current scans.

\subsection{Menu Bar}

The menu bar contains a number of options and helpful links.

\end{document}
37 changes: 7 additions & 30 deletions picoammeter.py → drivers/picoammeter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,10 @@
import glob
import serial
from time import sleep

def serial_ports():
""" Lists serial port names
:raises EnvironmentError:
On unsupported or unknown platforms
:returns:
A list of the serial ports available on the system
"""
if sys.platform.startswith('win'):
ports = ['COM%s' % (i + 1) for i in range(256)]
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
# this excludes your current terminal "/dev/tty"
ports = glob.glob('/dev/tty[A-Za-z]*')
elif sys.platform.startswith('darwin'):
ports = glob.glob('/dev/tty.*')
else:
raise EnvironmentError('Unsupported platform')

result = []
for port in ports:
try:
s = serial.Serial(port)
s.close()
result.append(port)
except (OSError, serial.SerialException):
pass
return result
from utilities import ports_finder

class Picoammeter:
def __init__(self, samples: int):
def __init__(self, samples: int, man_port: str = None):
if samples < 2:
samples = 2
if samples > 20:
Expand All @@ -42,7 +15,11 @@ def __init__(self, samples: int):
self.s = None
self.found = False
self.port = -1
for port in serial_ports():
for port in ports_finder.find_serial_ports():
if man_port is not None:
if port != man_port:
continue

s = serial.Serial(port, 9600, timeout=1)
print('Beginning search for Keithley Model 6485...')
print('Trying port %s.'%(port))
Expand Down
136 changes: 136 additions & 0 deletions middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#
# @file middleware.py
# @author Mit Bailey ([email protected])
# @brief Provides a layer of abstraction between the MMC GUI and the underlying hardware device drivers.
# @version See Git tags for version information.
# @date 2022.09.23
#
# @copyright Copyright (c) 2022
#
#

# %% OS and SYS Imports
import os
import sys

try:
exeDir = sys._MEIPASS
except Exception:
exeDir = os.getcwd()

if getattr(sys, 'frozen', False):
appDir = os.path.dirname(sys.executable)
elif __file__:
appDir = os.path.dirname(__file__)

# %% More Standard Imports
import configparser as confp
from email.charset import QP
from time import sleep
from io import TextIOWrapper
import math as m
import numpy as np
import datetime as dt

import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT
from matplotlib.figure import Figure

# %% Custom Imports
from drivers import _thorlabs_kst_advanced as tlkt
from drivers import picoammeter as pico

from utilities import ports_finder

# Motion Controller Types
# 0 - KST101

# Data Sampler Types
# 0 - Picoammeter, Keithley

#%% MotionController
# Genericizes the type of motor controller.
class MotionController:
def __init__(self, dummy: bool = False, man_port: str = None):
self.controller_type = 0
self.mm_to_idx = 0
self._is_dummy = False

# Initializes our motor_ctrl stuff depending on what hardware we're using.
if self.controller_type == 0:
if dummy:
serials = tlkt.Thorlabs.KSTDummy._ListDevices()
self.motor_ctrl = tlkt.Thorlabs.KSTDummy(serials[0])
self.motor_ctrl.set_stage('ZST25')
self._is_dummy = True
else:
print("Trying...")
serials = tlkt.Thorlabs.ListDevicesAny()
print(serials)
if len(serials) == 0:
print("No KST101 controller found.")
raise RuntimeError('No KST101 controller found')
self.motor_ctrl = tlkt.Thorlabs.KST101(serials[0])
if (self.motor_ctrl._CheckConnection() == False):
print("Connection with motor controller failed.")
raise RuntimeError('Connection with motor controller failed.')
self.motor_ctrl.set_stage('ZST25')

elif self.controller_type == 1: # Example for adding future controller hardware.
print("Controller type 1 does not exist yet.")

self.mm_to_idx = self.motor_ctrl.mm_to_idx

def is_dummy(self):
return self.is_dummy

def home(self):
return self.motor_ctrl.home()

def get_position(self):
return self.motor_ctrl.get_position()

def is_homing(self):
return self.motor_ctrl.is_homing()

def is_moving(self):
return self.motor_ctrl.is_moving()

def move_to(self, position, block):
return self.motor_ctrl.move_to(position, block)

pass

#%% DataSampler
# Genericizes the type of data sampler.
class DataSampler:
def __init__(self, dummy: bool = False, man_port: str = None):
self.sampler_type = 0
self.pa = None
self._is_dummy = False

if self.sampler_type == 0:
if dummy:
self.pa = pico.Picodummy(3)
self._is_dummy = True
else:
if man_port is not None:
self.pa = pico.Picoammeter(3, man_port)
else:
self.pa = pico.Picoammeter(3)
elif self.sampler_type == 1: # Example for adding future controller hardware.
print("Sampler type 1 does not exist yet.")

# Only function used in mmc.py (.pa.sample_data())
def sample_data(self):
return self.pa.sample_data()

def is_dummy(self):
return self._is_dummy

pass

#%% ColorWheel
class ColorWheel:
pass
Loading

0 comments on commit daa6ea1

Please sign in to comment.