Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task support for ij macro language #303

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions src/main/java/net/imagej/legacy/task/TaskHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
package net.imagej.legacy.task;

import net.imagej.legacy.LegacyService;
import org.scijava.Context;
import org.scijava.log.LogService;
import org.scijava.object.ObjectService;
import org.scijava.task.Task;
import org.scijava.task.TaskService;

import java.util.List;
import java.util.stream.Collectors;

/**
* Static methods enabling the use of {@link Task} in ImageJ macro language through the call method:
* output = call('net.imagej.legacy.task.Taskhelper.METHODNAME', args, ...);
* where METHODNAME belong to the {@link Task} interface
* <p>
* The ObjectService is used to store tasks created using this static class (within {@link TaskHelper#createTask(String)})
* Tasks are removed from the ObjectService when {@link TaskHelper#finish(String)} is called.
* <p>
* Using this class, it is possible to create two tasks with the same name. Name uniqueness is not guaranteed.
* If two tasks with the same name exist in the ObjectService, a warning message will be displayed via the
* {@link LogService}. It is possible to clear all tasks present in the ObjectService with a specific name
* using {@link TaskHelper#removeAll(String)}. This may be necessary to remove dangling tasks existing after
* a macro has not not been completed successfully, or in the case where calling task.finish was forgotten.
* <p>
* The {@link LogService} and {@link ObjectService} are accessed
* statically via the {@link LegacyService}, where a single instance can exist anyway.
* <p>
* @author Nicolas Chiaruttini, EPFL, 2023
*/
@SuppressWarnings("unused")
public class TaskHelper {
// Since the LegacyService exists only once, the LogService and the ObjectService can be accessed statically
static {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make services load 'more lazily'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check double checked locking pattern

Context ctx = LegacyService.getInstance().context();
logService = ctx.getService(LogService.class);
objectService = ctx.getService(ObjectService.class);
taskService = ctx.getService(TaskService.class);
}
final private static LogService logService;
final private static ObjectService objectService;
final private static TaskService taskService;

private static Task getTaskNamed(String name) throws IllegalArgumentException {

List<Task> tasks = objectService.getObjects(Task.class)
.stream()
.filter(task -> task.getName().equals(name)).collect(Collectors.toList());

if (tasks.size() == 0) {
throw new IllegalArgumentException("No task named "+name+" was found in the object service.");
}

if (tasks.size() > 1) {
logService.warn("Multiple tasks named "+name+" found! Taking the first one.");
}

return tasks.get(0);
}

private static List<Task> getTasksNamed(String name) {
return objectService.getObjects(Task.class)
.stream()
.filter(task -> task.getName().equals(name)).collect(Collectors.toList());
}

/**
* Returns true as a String if the (first) task named taskName found in the {@link ObjectService} has been cancelled
* Returns false as a String if the (first) task named taskName found in the {@link ObjectService} has not been cancelled
* @param taskName name of the task to act on
* @return true or false as String depending on whether the task named taskName has been cancelled
*/
public static String isCanceled(String taskName) {
try {
Task task = getTaskNamed(taskName);
return task.isCanceled()?"true":"false";
} catch (IllegalArgumentException e) {
logService.error("Error: task not found. ("+e.getMessage()+")");
return "Error: task not found. ("+e.getMessage()+")";
}
}

/**
* Returns {@link Task#getCancelReason()} on the (first) task named taskName found in the {@link ObjectService}
* @param taskName name of the task to act on
* @return the cancellation reason of the task named taskName
*/
public static String getCancelReason(String taskName) {
try {
Task task = getTaskNamed(taskName);
return task.getCancelReason();
} catch (IllegalArgumentException e) {
logService.error("Error: task not found. ("+e.getMessage()+")");
return "Error: task not found. ("+e.getMessage()+")";
}
}

/**
* Creates a new {@link Task} named taskNamer and stores it into {@link ObjectService}
* @param taskName name of the task to act on
*/
public static void createTask(String taskName) {
Task task = taskService.createTask(taskName);
objectService.addObject(task);
}

/**
* Sets the maximal progression of the first task {@link Task} named taskName found in the {@link ObjectService}
* @param taskName name of the task to act on
* @param max the progression maximum (when the task is done)
*/
public static void setProgressMaximum(String taskName, String max) {
try {
Task task = getTaskNamed(taskName);
task.setProgressMaximum(Long.parseLong(max));
} catch (IllegalArgumentException e) {
logService.error("Error: task not found. ("+e.getMessage()+")");
}
}

/**
* Returns {@link Task#getProgressMaximum()} on the (first) task named taskName found in the {@link ObjectService}
* @param taskName name of the task to act on
* @return the current maximal progression of the task named taskName
*/
public static String getProgressMaximum(String taskName) {
try {
Task task = getTaskNamed(taskName);
return Long.toString(task.getProgressMaximum());
} catch (Exception e) {
logService.error("Error: task not found. ("+e.getMessage()+")");
return "task "+taskName+" not found.";
}
}

/**
* Sets the status message of the first task {@link Task} named taskName found in the {@link ObjectService}
* @param taskName name of the task to act on
* @param status the status message to set
*/
public static void setStatusMessage(String taskName, String status) {
try {
Task task = getTaskNamed(taskName);
task.setStatusMessage(status);
} catch (Exception e) {
logService.error("Error: task not found. ("+e.getMessage()+")");
}
}

/**
* Returns {@link Task#getStatusMessage()} on the (first) task named taskName found in the {@link ObjectService}
* @param taskName name of the task to act on
* @return the current status message of the task named taskName
*/
public static String getStatusMessage(String taskName) {
try {
Task task = getTaskNamed(taskName);
return task.getStatusMessage();
} catch (Exception e) {
logService.error("Error: task not found. ("+e.getMessage()+")");
return "task "+taskName+" not found.";
}
}

/**
* Calls {@link Task#start()} on the (first) task named taskName found in the {@link ObjectService}
* @param taskName name of the task to act on
*/
public static void start(String taskName) {
try {
Task task = getTaskNamed(taskName);
task.start();
} catch (Exception e) {
logService.error("Error: task not found. ("+e.getMessage()+")");
}
}

/**
* Calls {@link Task#setProgressValue(long)} on the (first) task named taskName found in the {@link ObjectService}
* @param taskName name of the task to act on
* @param step the current task progression value
*/
public static void setProgressValue(String taskName, String step) {
try {
Task task = getTaskNamed(taskName);
task.setProgressValue(Long.parseLong(step));
} catch (Exception e) {
logService.error("Error: task not found. ("+e.getMessage()+")");
}
}

/**
* Calls {@link Task#finish()} on the (first) task named taskName found in the {@link ObjectService}
* @param taskName name of the task to act on
*/
public static void finish(String taskName) {
try {
Task task = getTaskNamed(taskName);
task.finish();
objectService.removeObject(task);
} catch (Exception e) {
logService.error("Error: task not found. ("+e.getMessage()+")");
}
}

/**
* Clean up method to removes tasks dangling after a macro has crashed.
* If several tasks exist with the same name, they will all be removed
* @param taskName name of the tasks to remove
*/
public static void removeAll(String taskName) {
for (Task task: getTasksNamed(taskName)) {
task.finish();
objectService.removeObject(task);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#@ TaskService taskService
#@ Double (label = "Waiting time per step (s)", style = "0.000", value = "0.5") wait_s
#@ Integer(label = "Number of steps", value = "20") n_steps

def task = taskService.createTask("A task");
task.setProgressMaximum(n_steps)

try {

task.start()
//... do stuff
for (int i = 0; i<n_steps; i++) {
task.setProgressValue(i); // Updates the progress bar with the current progression
Thread.sleep((int) (wait_s*1000))
}

} finally {
// Block always executed, irrespective of a previous potential crash
task.finish()
}

import org.scijava.task.TaskService
39 changes: 39 additions & 0 deletions src/main/resources/script_templates/ImageJ_1.x/Task/SimpleTask.ijm
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#@ Double (label = "Waiting time per step (s)", style = "0.000", value = "0.5") wait_s
#@ Integer(label = "Number of steps", value = "20") n_steps

taskName = "A task";

taskCreate(taskName, n_steps);

for (i = 0; i<n_steps; i++) {
taskSetProgressValue(taskName, i);
wait(wait_s*1000.0);
}

taskFinish(taskName);

// --------------------------- FUNCTIONS

function taskCreate(name, maxSteps) {
call("net.imagej.legacy.task.TaskHelper.removeAll", name); // Removes dangling tasks named identically generated during a macro crash for instance
call("net.imagej.legacy.task.TaskHelper.createTask", name);
call("net.imagej.legacy.task.TaskHelper.setProgressMaximum", name, maxSteps);
call("net.imagej.legacy.task.TaskHelper.start", name);
}

function taskSetProgressValue(name, step) {
call("net.imagej.legacy.task.TaskHelper.setProgressValue", taskName, step);
}

function taskFinish(name) {
call("net.imagej.legacy.task.TaskHelper.finish", name);
}

function taskIsCanceled(name) {
ans = call("net.imagej.legacy.task.TaskHelper.isCanceled", name);
if (ans == "true") {
return 1;
} else {
return 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#@ Double (label = "Waiting time per step (s)", style = "0.000", value = "0.5") wait_s
#@ Integer(label = "Number of steps", value = "20") n_steps

taskName = "A task"

call("net.imagej.legacy.task.TaskHelper.removeAll", taskName); // Removes dangling tasks named identically generated during a macro crash for instance

call("net.imagej.legacy.task.TaskHelper.createTask", taskName);
call("net.imagej.legacy.task.TaskHelper.setProgressMaximum", taskName, n_steps);

call("net.imagej.legacy.task.TaskHelper.start", taskName);

for (i = 0; i<n_steps; i++) {
call("net.imagej.legacy.task.TaskHelper.setProgressValue", taskName, i);
wait(wait_s*1000.0);
}

call("net.imagej.legacy.task.TaskHelper.finish", taskName);
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#@ TaskService taskService
#@ Double (label = "Waiting time per step (s)", style = "0.000", value = "0.5") wait_s
#@ Integer(label = "Number of steps", value = "20") n_steps

Task task = taskService.createTask("A task");
task.setProgressMaximum(n_steps)

try {

task.start()
//... do stuff
for (int i = 0; i<n_steps; i++) {
task.setProgressValue(i); // Updates the progress bar with the current progression
Thread.sleep((int) (wait_s*1000))
if (task.isCanceled()) {
IJ.log(task.getName()+" has been cancelled");
return ; // the finally statement will be executed anyway, no need to finish the task
}
}

} finally {
// Block always executed, irrespective of a previous potential crash
task.finish()
}


import ij.IJ
import org.scijava.task.Task
import org.scijava.task.TaskService
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#@ Double (label = "Waiting time per step (s)", style = "0.000", value = "0.5") wait_s
#@ Integer(label = "Number of steps", value = "20") n_steps

taskName = "A task";

taskCreate(taskName, n_steps);

for (i = 0; i<n_steps; i++) {
taskSetProgressValue(taskName, i);
wait(wait_s*1000.0);
if (taskIsCanceled(taskName)) {
print(taskName+" has been cancelled");
taskFinish(taskName);
return;
}
}

taskFinish(taskName);

// --------------------------- FUNCTIONS

function taskCreate(name, maxSteps) {
call("net.imagej.legacy.task.TaskHelper.removeAll", name); // Removes dangling tasks named identically generated during a macro crash for instance
call("net.imagej.legacy.task.TaskHelper.createTask", name);
call("net.imagej.legacy.task.TaskHelper.setProgressMaximum", name, maxSteps);
call("net.imagej.legacy.task.TaskHelper.start", name);
}

function taskSetProgressValue(name, step) {
call("net.imagej.legacy.task.TaskHelper.setProgressValue", taskName, step);
}

function taskFinish(name) {
call("net.imagej.legacy.task.TaskHelper.finish", name);
}

function taskIsCanceled(name) {
ans = call("net.imagej.legacy.task.TaskHelper.isCanceled", name);
if (ans == "true") {
return 1;
} else {
return 0;
}
}
Comment on lines +22 to +44
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Find a way to 'autorun' or auto import these functions -

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check the #@import that was worked on by @ctrueden and @imagejan

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loading
Loading