-
Notifications
You must be signed in to change notification settings - Fork 9
Using JISA in Java
Using JISA
in Java makes sense, since JISA
is written in Java. If you're after a more traditional programming language similar to C/C++ then this is probably the one for you.
This page will cover the basics of creating simple JISA
programs in Java as well as the basics of Java that you may be unfimiliar with if you are coming from a language like Python or Kotlin.
- Creating a Project
- Including JISA
- Main Class, Main Method
- Variables
- Public Static What Now?
- Functions/Methods
- Looping
- Objects and Imports
- Input and Output Streams
- Using GUI Elements
- Example Program
First you need to bring up the new project wizard. You can do this either by going to File
> New
> Project...
or by pressing Create New Project
on the welcome screen.
Then select Java
in the list on the left-hand-side and press Next
. On the next screen, select Create project from template
and select Command Line App
then press Next
:
Decide what to call your project and put that in Project name
. The Base package
is essentially the name of the project used inside Java, so keep it all one word.
Now click Finish
and your new project should appear before your very eyes!
Before we can do anything towards creating a JISA
application, you will need to include the JISA.jar
library file into your project.
First, download the jar file:
https://github.com/OE-FET/JISA/raw/master/JISA.jar
Next, move it into your project folder by dragging it into the project tree on the left:
Now you can add it as a library by right clicking on it and selectin Add as Library...
then just click OK
:
After creating a new Java project you will notice that you get a class called Main
with a public static
method inside it called main(...)
. This is what's known as the "entry point" of your program, that is to say it is the code that will run first when you run the program.
The public
part means it's accessible to code written outside Main
, static
means it belongs to the class Main
itself rather than each individual instance of Main
and void
means it returns no value.
The first thing to do is to let Java know that your program might throw an exception. To do this, add throws Exception
to the end of the main()
method declaration like so:
public class Main {
public static void main(String[] args) throws Exception {
}
}
This isn't ideal, as really you should be catching any and all exceptions in your code so that you can deal with them appropriately. This way, any exception that is thrown by main()
will cause the program to crash. However, it will do to start with.
Test out that Java is working by creating a simple Hello World!
application like so:
public class Main {
public static void main(String[] args) throws Exception {
System.out.println("Hello World!");
}
}
Run it by clicking the green play button on the top toolbar. You should see the output at the bottom reading something like:
/usr/lib/jvm/java-8-oracle/bin/java...
Hello World!
Java is what's known as a "statically typed" language. As a result, before using a variable in Java, it needs to be "declared". This is where you state its type and name. For example, if we wanted to store an integer, in a variable called myInteger
, then we would have to declare our variable as an int
like so:
int myInteger;
// Now we can use myInteger, for instance:
myInteger = 5 + 3;
In the example above we decalred myInteger
and assigned it its first value on separate lines. Essentially we have told the compiler that we want an integer-sized space in memory to be reserved and that it shall be referred to in-code as myInteger
. We then tell it to put the result of 5 + 3
(ie 8
) into this space.
However, we can actually do this all on one line like so:
int myInteger = 5 + 3;
Our integer type, int
, is one of 8 basic variable types in Java. These basic types are called "primitive" types and are built-in to the language.
The primitive variable types are as follows:
Name | Description |
---|---|
int |
Integer (whole numbers) |
long |
Integer but can hold more digits (at the expense of more memory) |
short |
Integer with fewer digits (uses less memory) |
byte |
Represents 8 bit signed integer (ie very short integer) |
float |
Floating-point number (basically a non-integer number) |
double |
Double-precision floating-point (same as float but higher precision) |
boolean |
A simple true or false value |
char |
A single character (eg a or K ) |
int integer = 312;
long longInteger = 3029387637483746384;
short shortInteger = 5;
float floatVar = 3.5;
double doubleVar = 1.23453484e-12;
boolean boolVar = false;
char charVar = 'W';
In this context of JISA
you will likely only ever come across int
, double
and boolean
out of these.
You can then use all the usual comparison operators on these primitive variables:
int a = 15;
int b = 17;
if (b == a) { ... } // Equal to each other
if (b != a) { ... } // Not equal
if (b > a) { ... } // Greater than
if (b < a) { ... } // Less than
if (b >= a) { ... } // Greater than or equal
if (b <= a) { ... } // Less than or equal
Anything more complex than these primitive types are represented as objects. The most notable example of this is String
which is used to represent text (ie a "string" of characters). Object cannot be compared using the in-built comparison operators shown above. As a result, if you did the following:
String line1 = "This is some text!";
String line2 = "This is some text!";
if (line1 == line2) {
}
Then line1 == line2
would never return true
despite their contents being identical. This is because when you use these comparitors on objects, you are not comparing their contents but their memory addresses, so it will only return true if they are literally the same object.
Therefore, you need to use the equals(...)
method on one of the String
objects like so:
String line1 = "This is some text!";
String line2 = "This is some text!";
if (line1.equals(line2)) {
}
If you want to create an array of something, then you simply append []
to the end of the type like so:
int[] myIntegers;
You can create an empty array using the new
keyword like so:
// Array of 8 integers:
int[] myIntegers = new int[8];
Each individual element can then be accessed using square brackets (indices start from 0):
// Set fourth element to equal 5
myIntegers[3] = 5;
You might notice some extra words next to some variables such as public
, static
, final
etc. Let's go over what they mean:
Modifier | Meaning |
---|---|
public |
Can be accessed by code outside this class. |
private |
Can only be accessed by code inside this class. |
static |
Belongs directly to this class, not to individual instances of it |
final |
Cannot be re-assigned, the value stored in this variable cannot be changed once set |
To better understand what static
means let's take an example. Here I define a class called "MyClass":
public class MyClass {
public int number = 15;
}
By writing public int number
within the class, I have defined that objects of class MyClass
shall each have their own variable called number
that shall initially have the value of 15
:
// object1 and object2 both have number = 15
MyClass object1 = new MyClass();
MyClass object2 = new MyClass();
object1.number = 12;
object2.number = 333;
// object1 now has number = 12 and object2 has number = 333
That is, each instance of MyClass
will have its own copy of number
. However, if we declare it as static:
public class MyClass {
public static int number = 15;
}
Then each instance of MyClass
will no-longer have a variable called number
. Instead there is a single number
variable "inside" the class itself:
MyClass object = new MyClass();
// object.number no-longer exists
// Instead it belongs to the class itself:
MyClass.number = 125;
// Doing this will cause an error because object.number does not exist:
object.number = 12;
This is known as the access level. If a variable (or function) is defined as being public
, then it means it can be accessed from code written outside this class. For instance, the following would be okay:
class Main {
public static int integer = 3;
}
class OtherClass {
public static void something() {
Main.integer = 5;
}
}
However, this would not work:
class Main {
private static int integer = 3;
}
class OtherClass {
public static void something() {
Main.integer = 5;
}
}
Because in OtherClass
we try and access integer
in Main
which is declared as private
thus meaning we can only access it from inside Main
itself.
The final
keyword simply indicates that once a value has been assigned to this variable, it cannot be re-assigned (ie it cannot be changed). It essentially means "constant".
final int number = 12;
number = 15; // This will cause an error
Like with variables, functions (or "methods") in Java need to have the type of variable they return declared and often have extra modifier keywords attached. For example, the main()
method declaration:
public static void main(String[] args) { ... }
We have the modifiers first public static
which mean the same as they do for variables. Next comes the returns type. Since main()
does not return anything, we specify the return type as void
. Then we have the name of the method: main
and inside the brackets we have any arguments that the method takes. In this case, the main method takes an array of String
s representing any arguments supplied from the command-line when the program was launched.
For example, if we have a class Numberoonie
, and we want all Numberoonie
objects the have the method getNumber()
which returns an integer:
public class Numberoonie {
public int getNumber() {
return 5;
}
}
This means that we can do:
Numberoonie object = new Numberoonie();
// number will now == 5
int number = object.getNumber();
Just like with vcariables, if we declared getNumber()
to be static
then we no-longer reference it from individual instances:
public class Numberoonie {
public static int getNumber() {
return 5;
}
}
int number = Numberoonie.getNumber();
Java offers a few different loop structures. The first is the "traditional" for
loop:
for (int i = 0; i < 3; i++) {
System.out.println(i);
}
The above example will cause the varible i
to start at 0, keep going until it is no-longer less than 3, and increase by 1 each time. On each iteration it will print the value of i
out to the terminal. The result being the following output to the terminal:
0
1
2
Using this is the "traditional" way of looping over an array. For example:
int[] myValues = {5, 8, 3};
for (int i = 0; i < myValues.length; i++) {
System.out.println(myValues[i]);
}
would give:
5
8
3
However, Java offers a for-each type loop like so:
int[] myValues = {5, 8, 3};
for (int value : myValues) {
System.out.println(value);
}
which does the same. You can read it as saying "for each integer (which we shall call value
) in myValues
, do the following...".
Java also supports while
and do...while
loops like so:
int a = 0;
// Condition checked before each loop
while (a != 15) {
a += 3;
}
// Condition checked after each loop
// (ie it will always run at least once)
do {
a += -3;
} while (a > 0);
Much like almost any language (except MATLAB because it's bad), if you want to use a class of object you need to import it first. For example, if I wanted to use K2600B
and GPIBAddress
objects, I will need to add the following import statements to the top of the file:
import jisa.devices.K2600B;
import jisa.addresses.GPIBAddress;
If you are using IDEA to write your program, then it will normally handle these automatically for you as you write your code.
In Java, when instantiating an object, you need to use the new
keyword. For example, let's say we wanted to create a Plot
object. First we need to declare the variable that we are storing it in:
Plot myPlot;
Then we create the object to put in it:
myPlot = new Plot("Plot Title", "X", "Y");
Or (much better) all on one line:
Plot myPlot = new Plot("Plot Title", "X", "Y");
This is why you often see the class name twice when creating an object (once to declare the type of the variable and another time for actually creating the object to put into the variable).
Reading from and writing to the terminal in Java is slightly more round-about than you may be used to from languages such as Kotlin and Python. This is because Java takes after C++ in presenting the user with the raw standard input and output streams rather than wrapping them with nice functions like print()
or input()
.
In Java, the input and output streams are represented as statically (globally) accessible objects: System.in
and System.out
. Thus to send text to the terminal, you need to print to the System.out
stream like so:
System.out.print("Some stuff without a newline");
System.out.println(" But with a newline now");
System.out.printf("Something fancy! %d\n", 2);
That's simple enough. However, input is slightly more tricky. Dealing with the raw input stream System.in
is non-trivial. However, thankfully, Java gives us a structure called a Scanner
that simplifies things massively. What a Scanner
does, is it takes an input stream of some kind and lets you "scan" the contents for what you're looking for.
First we need to create a new Scanner
that wraps around System.in
like so:
Scanner in = new Scanner(System.in);
Then we can ask it for things from the input stream. For example, we can ask it for the next whole line of text that was entered into the terminal by the user:
String line = in.nextLine();
Alternatively, we can ask it specifically for a certain type of data. For instance, we could ask it for the next integer value it can find in the input stream:
int integer = in.nextInt();
If you plan on using terminal input/output in your program it might be worth storing System.out
and your scanner as static variables in the Main
class like so:
public class Main {
public static PrintStream out = System.out;
public static Scanner in = new Scanner(System.in);
public static void main(String[] args) throws Exception {
out.println("Hello! What's you name?");
String name = in.nextLine();
out.printf("Hello %s! Nice to meet you!\n", name);
}
}
This is a common thread when using any language, but when you first use any GUI-related stuff in from JISA
you will set the GUI thread off running in the background. This is the thread that takes care of drawing the GUI and updating it to the screen etc. For this reason, after using a GUI element, you must make sure that your program terminates properly.
For example, if we made the following:
import jisa.gui.GUI;
public class Main {
public static void main(String[] args) {
GUI.infoAlert("Hello!");
}
}
Then we would get a pop-up dialogue box saying "Hello!", but after clicking "OK" and thus closing it, the program wouldn't terminate because the GUI thread is still running. Therefore, we need to manually make it close using System.exit(0)
:
import jisa.gui.GUI;
public class Main {
public static void main(String[] args) {
GUI.infoAlert("Hello!");
System.exit(0);
}
}
The reason why it does this is easy to understand if we consider a slightly different example:
import jisa.gui.Plot;
public class Main {
public static void main(String[] args) {
Plot plot = new Plot("Plot Title", "X", "Y");
plot.show();
}
}
In the above case, we create a Plot
and show it in its own window. However, after that has been executed our main()
method will have come to an end. If the program were to terminate because of this, then the plot would show up then immediately close again afterwards since the program would have terminated.
Therefore, instead we can specify the plot window to cause the program to terminate when it is close like so:
import jisa.gui.Plot;
public class Main {
public static void main(String[] args) {
Plot plot = new Plot("Plot Title", "X", "Y");
// Tells the plot to terminate the program when closed
plot.setExitOnClose(true);
plot.show();
}
}
This should be set on whichever window you consider to be your main window in your program to ensure that the program ends when it is closed. Alternatively, you can specify your own custom action to perform when closing a window instead:
import jisa.gui.Plot;
import jisa.gui.GUI;
public class Main {
public static void main(String[] args) {
Plot plot = new Plot("Plot Title", "X", "Y");
plot.setOnClose(() -> {
GUI.infoAlert("Goodbye!");
System.exit(0);
});
plot.show();
}
}
In the above example, trying to close the window will cause a dialogue box to pop-up saying "Goodbye!" before the program exits, like so:
import jisa.devices.*;
import jisa.addresses.*;
import jisa.gui.*;
import jisa.experiment.*;
import jisa.Util;
public class Main {
public static SMU smu;
public static Column<Double> VOLT = Column.ofDecimals("Voltage", "V");
public static Column<Double> CURR = Column.ofDecimals("Current", "A")
public static ResultTable results = new ResultList(VOLT, CURR);
public static void main(String[] args) throws Exception {
// Try to connect to SMU
try {
smu = new K2450(new GPIBAddress(0, 24));
} catch (Exception e) {
GUI.errorAlert("Connection Error", e.getMessage());
System.exit(0);
}
// Create GUI elements
Table table = new Table("Table of Results", results);
Plot plot = new Plot("Plot of Results", results);
Grid grid = new Grid("Conductivity", table, plot);
// Add start button to toolbar, make it run measure() when clicked
grid.addToolbarButton("Start", Main::measure);
// Want program to terminate when this window is closed
grid.setExitOnClose(true);
grid.show();
}
public static void measure() throws Exception {
// Clear out any previous results
results.clear();
smu.turnOn();
for (double voltage : Range.step(0, 60, 1)) {
// Set each voltage, wait 0.5 s then take readings
smu.setVoltage(voltage);
Util.sleep(500);
results.addRow(row -> {
row.set(VOLT, smu.getVoltage());
row.set(CURR, smu.getCurrent());
});
}
smu.turnOff();
}
}
The result is the following:
And after pressing "Start":
- 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