-
Notifications
You must be signed in to change notification settings - Fork 9
large programs
If you are intending on making a full-on control program for a set-up, it is often helpful to stick to some guide-lines in terms of what it should contains and how it's laid-out.
On this page we shall cover these concepts along with an example program which we will write in Kotlin.
The first thing to understand is the idea of extending the existing GUI classes to create our own, custom, GUI elements. In-particular by using the container classes like Grid
and Tabs
. In essence this will let you create a window/tab and have it completely defined within its own class thus breaking up your code into manageable chunks.
For example, if we wanted a window that contains a set of elements in a Grid
, then we could extend the Grid
class like so:
class MyGrid : Grid("My Grid", 2) {
}
In this example, the class MyGrid
will be a Grid
titled "My Grid" with 2 columns. That is if we were now to create a MyGrid
object and call show()
on it:
val myGrid = MyGrid()
myGrid.show()
we would get a blank window titled "My Grid", just as if we have created a grid with that name and shown it:
val myGrid = Grid("My Grid", 2)
myGrid.show()
You can then store elements as class variables and then add them to the grid when it is created inside an init { }
block like so:
class MyGrid : Grid("My Grid", 2) {
val plot = Plot("Plot of Results", "X", "Y")
val table = Table("Table of Results")
init {
addAll(plot, table)
}
}
Now, MyGrid
objects will be a Grid
containing a Table
and Plot
. Basically we have created a new GUI element by combining the standard ones.
Showing it as before would give us:
The first tab to create is normally a ConnectorGrid
to let the user connect to instruments.
class Connections : ConnectorGrid("Connections") {
}
Now, we want to add InstrumentConfig
elements to this Grid
to represent each instrument connection that we want to form. For instance, if we want two SMU
s, a TC
, and a LockIn
:
class Connections : Grid("Connections", 2) {
// Create each element that we want to add to the grid
val smu1 = addSMU("SMU 1")
val smu2 = addSMU("SMU 2")
val tc = addTC("Temperature Controller")
val lockIn = addLockIn("Lock-In Amplifier")
}
If we were to create a Connections
element now and show()
it:
val connections = Connections()
connections.show()
we would get:
The next element you will likely want to create is one to configure which instrument and which channel on which instrument is used for what purpose. As described on the Configurators
page, this is done using the various Configurator
objects.
We will want to arrange all our Configurator
objects in a Grid
, so let's create a class that extends Grid
and have it add the Configurator
objects to itself in its init {...}
block. Additionally, each Configurator
will need to be supplied with our ConnectorGrid
, so we should add that as a constructor argument for our Config
element like so:
class Configs(connections: ConnectorGrid) : Grid("Configuration", 2) {
val sourceDrain = Configurator.SMU("Source-Drain", connections)
val sourceGate = Configurator.SMU("Source-Gate", connections)
val temperature = Configurator.TC("Temperature Control", connections)
init {
addAll(sourceDrain, sourceGate, temperature)
}
}
Creating and showing:
val connections = Connections()
val configs = Configs(connections)
configs.show()
You will normally want to monitor the various parameters in your experiment when running. Therefore you will want to have a logger running in the background, periodically logging important quantities and live-plotting them in a convenient dashboard type tab.
To do this, we will need to not just extend Grid
by adding elements but also by adding an extra two methods to start and stop the logger: startLog()
and stopLog()
.
class Dashboard : Grid("Dashboard", 3) {
val sdVPlot = Plot("Source-Drain Voltage")
val sdIPlot = Plot("Source-Drain Current")
val sgVPlot = Plot("Source-Gate Voltage")
val sgIPlot = Plot("Source-Gate Current")
val tPlot = Plot("Temperature")
val fPlot = Plot("Frequency")
val plots = arrayOf(sdVPlot, sdIPlot, sgVPlot, sgIPlot, tPlot, fPlot)
var rTask = RTask(2000) { t -> }
var log: ResultTable = ResultList("")
init {
addAll(sdVPlot, sdIPlot, sgVPlot, sgIPlot, tPlot, fPlot)
for (plot in plots) {
plot.showLegend(false)
}
}
// We will call this function when we want to start a log
fun startLog(file: String, sdSMU: SMU, sgSMU: SMU, tc: TC, lock: LockIn) {
// Create a new log file
log = ResultStream(
file,
Col("Time", "s"),
Col("SDV", "V"),
Col("SDI", "A"),
Col("SGV", "V"),
Col("SGI", "A"),
Col("T", "K"),
Col("f", "Hz")
)
// Clear all the plots of any old data
for (plot in plots) {
plot.clear()
}
// Tell the plots to watch the log file
sdVPlot.watchList(log, 0, 1)
.showMarkers(false)
.setColour(Colour.ORANGE)
sdIPlot.watchList(log, 0, 2)
.showMarkers(false)
.setColour(Colour.TEAL)
sgVPlot.watchList(log, 0, 3)
.showMarkers(false)
.setColour(Colour.PURPLE)
sgIPlot.watchList(log, 0, 4)
.showMarkers(false)
.setColour(Colour.RED)
tPlot.watchList(log, 0, 5)
.showMarkers(false)
.setColour(Colour.BLUE)
fPlot.watchList(log, 0, 6)
.showMarkers(false)
.setColour(Colour.CORNFLOWERBLUE)
// Create a new repeat-task to take log measurements every 2 seconds
rTask = RTask(2000) { t ->
log.addData(
t.getSecFromStart(),
sdSMU.getVoltage(),
sdSMU.getCurrent(),
sgSMU.getVoltage(),
sgSMU.getCurrent(),
tc.getTemperature(),
lock.getFrequency()
)
}
// Start the logger!
rTask.start()
}
fun stopLog() {
rTask.stop()
log.finalise()
}
}
In JISA
there is a standard "template" for creating measurement code. This is the form of the Measurement
class, specifically you can extend Measurement
to define your own measurements. When properly defined, a Measurement
object can be used like our Conductivity
example below:
val smu = K2450(GPIBAddress(0,25))
val measurement = Conductivity(smu)
val results = measurement.newResults("outputFile.csv")
measurement.setVoltages(0, 60, 61)
measurement.performMeasurement()
- Getting Started
- Object Orientation
- Choosing a Language
- Using JISA in Java
- Using JISA in Python
- Using JISA in Kotlin
- Exceptions
- Functions as Objects
- Instrument Basics
- SMUs
- Thermometers (and old TCs)
- PID and Temperature Controllers
- Lock-Ins
- Power Supplies
- Pre-Amplifiers
- Writing New Drivers