Skip to content

Logging

William Wood edited this page Feb 18, 2024 · 16 revisions

Logging

Often you will want to create some sort of logging feature for your experiment. For example, if you have a cryostat you may wish to keep a running log of temperature, heater power, flow rate etc. To do this, JISA provides multiple tools.

Contents

  1. Streaming Results
  2. Repeated Tasks

Streaming Results

Top ↑

The first problem you might imagine when taking a large number of data-points is that we could run out of memory. When using a ResultList object, each "row" is stored in memory. To counter this, we use instead a ResultStream object which directly writes the data to a file instead of holding it in memory.

Using a ResultStream is easy as it is almost identical to a ResultList, the only difference is you need to specify where to save the file when you create it:


Java

Column<Double> TIME   = Column.ofDoubles("Time", "s");
Column<Double> TEMP   = Column.ofDoubles("Temperature", "K");
Column<Double> HEAT   = Column.ofDoubles("Heater", "%");
ResultTable    stream = new ResultStream("file.csv", TIME, TEMP, HEAT);

Python

TIME   = Column.ofDoubles("Time", "s")
TEMP   = Column.ofDoubles("Temperature", "K")
HEAT   = Column.ofDoubles("Heater", "%")
stream = ResultStream("file.csv", TIME, TEMP, HEAT)

Now you will only be limited by how much space you have on your hard-drive, which is likely to be vastly more than you have available in RAM.

When you are finished making additions and changes to the ResultStream you can release any resources it's holding open (ie the file) by using:

stream.close();

Calling close() on a ResultStream object causes it to become read-only and cannot be undone.

Repeated Tasks

Top ↑

To take a measurement periodically, what we essentially want is a piece of code to be run periodically independently of the rest of the code. To achieve this, JISA provides the RTask class. RTask works very similarly to Thread in that you provide it code to run and then start it, but differs in the fact that you also need to specify how often you want it to run.

To create an RTask you do the following:


Java

RTask task = new RTask(interval, t -> {
    // Code to run periodically goes here
});

Kotlin

val task = RTask(interval) { t -> 
    // Code to run periodically goes here
}

Python

def onTask(t: RTask):
    # Code to run periodically goes here
    ...

task = RTask(interval, onTask)

where interval is the interval in milliseconds.

After creating the RTask you can set it going by use of:

task.start();

and stop it with:

task.stop();

You can check whether it is currently running or not:

boolean running = task.isRunning();

and get how much time has passed since it was started in milliseconds or seconds:

int    count        = task.getCount();
long   milliSeconds = task.getMSecFromStart();
double seconds      = task.getSecFromStart();

These are all also available from inside the repeating task lambda itself via the t argument provided (i.e., in our above examples, t is of type RTask).

Now let's look at a complete example:


Java

public class Main {

    private final static Column<Double> TIME = Column.ofDoubles("Time", "s");
    private final static Column<Double> TEMP = Column.ofDoubles("Temperature", "K");
    private final static Column<Double> HEAT = Column.ofDoubles("Heater", "%");

    private static RTask logger;

    public static void main(String[] args) throws Exception {

        // Connect to temperature controller
        TC        tc     = new ITC503(new GPIBAddress(0, 20));
        TC.TMeter sensor = tc.getThermometers().get(0);
        TC.Heater heater = tc.getHeaters().get(0);

        // Create results storage
        stream = new ResultStream("file.csv", TIME, TEMP, HEAT);

        // Create the repeat-task, but don't start it yet
        logger = new RTask(5000, t -> {

            stream.addRow(row -> {
                row.set(TIME, t.getSecFromStart());
                row.set(TEMP, sensor.getTemperature());
                row.set(HEAT, heater.getPower());
            });

        });

    }

    public static void startLog() {
        logger.start();
    }

    public static void stopLog() {
        logger.stop();
    }

}

Kotlin

val TIME = Column.ofDoubles("Time", "s");
val TEMP = Column.ofDoubles("Temperature", "K");
val HEAT = Column.ofDoubles("Heater", "%");

val stream = ResultStream("file.csv", TIME, TEMP, HEAT);
val tc     = ITC503(new GPIBAddress(0, 20))
val sensor = loop.thermometers[0]
val heater = loop.heaters[0]

val logger = RTask(5000) { t -> 

    stream.addRow {
        row[TIME] = t.secFromStart
        row[TEMP] = sensor.temperature
        row[HEAT] = heater.power
    }

}

fun startLog() {
    logger.start();
}

fun stopLog() {
    logger.stop();
}

fun main() {
    ...
}

Python

TIME = Column.ofDoubles("Time", "s");
TEMP = Column.ofDoubles("Temperature", "K");
HEAT = Column.ofDoubles("Heater", "%");

stream = ResultStream("file.csv", TIME, TEMP, HEAT);
tc     = ITC503(new GPIBAddress(0, 20))
sensor = loop.getThermometers()[0]
heater = loop.getHeaters()[0]

def log(t: RTask):

    stream.mapRow({
        TIME: t.getSecFromStart(),
        TEMP: sensor.getTemperature(),
        HEAT: heater.getPower()
    })


logger = RTask(5000, log)

def startLog():
    logger.start()


def stopLog():
    logger.stop()

In the above example, when Main.startLog() is called, the code inside the RTask will be run every 5 seconds (5000 milliseconds). Each time this happens we log the temperature (tc.getTemperature()) and heater power (tc.getHeaterPower()) being reported by the temperature controller tc along with the number of seconds since the log was started by use of logger.getSecFromStart().

Clone this wiki locally