Skip to content

Using JISA in Java

William Wood edited this page Apr 12, 2023 · 10 revisions

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.

Contents

  1. Creating a Project
  2. Including JISA
  3. Main Class, Main Method
  4. Variables
  5. Public Static What Now?
  6. Functions/Methods
  7. Looping
  8. Objects and Imports
  9. Input and Output Streams
  10. Using GUI Elements
  11. Example Program

Creating a Project

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!

Including JISA

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:

Main Class, Main Method

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!

Variables

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;

Public static what now?

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

Static

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;

Public and Private

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.

Final

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

Functions/Methods

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 Strings 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();

Looping

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);

Objects and Import

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

Input and Output Streams

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);

  }

}

Using GUI Elements

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:

Example Program

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":

Clone this wiki locally