The TaskUtil class offers the isValidName method to check a task name and getDefaultName to generate a default name.
> >
The TaskUtil class offers the isValidName method to check a task name and getDefaultName to generate a default name.
Naming style: given a new functionality for a task, there are 2 options:
* Role naming: This is how most old task are named, it is object-oriented but is ill suited for functions. (Ex fitsReader, asciiTableReader)
Changed:
< <
* action-target naming: This is how we recommend to name new tasks as the action comes first and then (optionally) the target. (Ex readFits, readTable, openFile, sortTable, save, restore, pause ...). This is better suited to the functional style that tasks support.
> >
Action-target naming: This is how we recommend to name new tasks as the action comes first and then (optionally) the target. (Ex readFits, readTable, openFile, sortTable, save, restore, pause ...). This is better suited to the functional style that tasks support.
Task parameters
Changed:
< <
Every task has parameters, defined by the TaskParameter class. Task parameters have the following features:
> >
Every task has parameters, defined by the TaskParameter class. Task parameters have the following features:
They can be of three kinds: input (IN), output (OUT) or input-output (IO). Use an input-output parameter if your task needs to modify (or replace) the passed object. To set a parameter type:
Note that in Jython you can pass most configuration with the call to create the parameter. In Java, you can use TaskUtil.buildParameter to set most of them in just one step. From a usability POV, the configuration (most members except value) of a task parameter should be constants: a user will not be able to make sense of the system if, under some circumstances, a parameter changes from being mandatory to optional, for example. The way to properly understand this is that we are configuring by code (and data) extended functions definitions.
> >
Note that in Jython you can pass most configuration with the call to create the parameter. In Java, you can use TaskUtil.buildParameter to set most of them in just one step. From a usability POV, the configuration (most members except value) of a task parameter should be constant: a user will not be able to make sense of the system if, under some circumstances, a parameter changes from being mandatory to optional, for example. The way to properly understand this is that we are configuring by code (and data) extended functions definitions.
Do not try to reuse reserved words for parameter names (for , if , print ...), otherwise Jython will not be able to understand your task call.
Line: 173 to 173
The task will not be registered.
The task will not even appear in the All folder of the Tasks view.
Changed:
< <
This means that users can only invoke the task via the command line, either in a script or at the prompt in the Console view. (The root cause is that the Task View (and ToolRegistry) require a primary input).
> >
This means that users can only invoke the task via the command line, either in a script or at the prompt in the Console view. (The root cause is that the Task View, and ToolRegistry, require a primary input).
There were two such tasks in the core HIPE software: pause and resume, both in the ia.toolbox.util package, but they are now defined as plain functions.
Line: 185 to 185
For example, this is a task whose primary input accepts String, Integer and Product:
private static final String TASKNAME = "combined";
private static final String PRIME = "input";
/** Jython doc string */
Line: 245 to 245
The framework cleans the data from parameters after executing a task.
CHANGES FOR JYTHON CALLS TO TASKS THAT HAVE MULTIPLE OUTPUTS
Changed:
< <
The way client code retrieves the outputs for tasks than have multiple output is changing. The current way was not Python-like and could bound to generate memory leaks (and even dependencies from previous executions in the case of IO (INOUT) parameters).
> >
The way client code retrieves the outputs for tasks that have multiple outputs is changing. The current way was not Python-like and could generate memory leaks (and even dependencies from previous executions in the case of IO (INOUT) parameters).
This will only affect tasks with multiple outputs. When the transition is complete, in the case of multiple outputs, all outputs will be returned as a list (and thus the signature will be cleaned just after execution in all cases). var1, var2 = myTask().
The migration to this new style has been staged as follows:
HIPE 11
To enable the new syntax you have to add __list__=True to the task call: var1, var2 = myTask(arg=1, ..., __list__=True). If you do not use this form the system will issue a warning every time the task is called. Note that this means that if you just put a variable in the left side of the assignment (var= myTask(arg=1, ..., __list__=True)), var will be a list with all outputs if there is more than one defined (remember that INOUTs are outputs too!).
HIPE 12
The default syntax will be the new one and you no longer need to add __list__=True. var1, var2 = myTask() will work as is (given that myTask has two outputs defined, of course).
Added:
> >
HIPE 13 or later
__list__ is no longer a valid optional parameter in HIPE 13 or later.
Changed:
< <
Note that tasks with multiple outputs will have more restrictions regarding changes in their outputs (order is fixed, adding or removing outputs may result in syntax errors ...). If you want to keep the flexibility to evolve your task it is recommended that you evaluate moving to a task with just one compound output. The best time to do it is now (HIPE 11 development), as users will have to modify their calls, anyway. Also note that it is impossible for the task framework to know how the task has been called wrt the left side of an assignment (the framework just 'sees' the right hand side, i.e. see INFO Task perform: [Full Call] log messages).
> >
Note that tasks with multiple outputs will have more restrictions regarding changes in their outputs (order is fixed, adding or removing outputs may result in syntax errors ...). If you want to keep the flexibility to evolve your task it is recommended that you evaluate moving to a task with just one compound output. The best time to do it was during HIPE 11 development but it is never too late. Also note that it is impossible for the task framework to know how the task has been called with respect to the left side of an assignment (the framework just 'sees' the right hand side, i.e. see INFO Task perform: [Full Call] log messages).
Changed:
< <
This is why most parameters are cleaned (reset to their default values) after a task is executed in Jython:
> >
This is why most parameters are cleared (reset to their default values) after a task is executed in Jython:
Changed:
< <
The first output parameter is cleaned after execution of the task. You should store the value of this parameter into a variable when you execute the task: var = myTask().
IN DEPRECATION: The second and additional output (or input-output) parameters are kept in memory until they are accessed, then they are cleaned: var2 = myTask.output2.
Input parameters are cleaned after the task is executed.
> >
The first output parameter is cleared after execution of the task. You should store the value of this parameter into a variable when you execute the task: var = myTask().
IN DEPRECATION: The second and additional output (or input-output) parameters are kept in memory until they are accessed, then they are cleared: var2 = myTask.output2.
Input parameters are cleared after the task is executed.
Changed:
< <
Try to use a single output parameter per task, so that there are no leftover values after a task execution. If you need to return multiple values you can use collections or define an appropriate type that contains all outputs (check herschel.ia.dataset.Metadata, a general Dictionary-like (map or hash) class).
> >
Try to use a single output parameter per task, so that there are no leftover values after a task execution. If you need to return multiple values you can use collections or define an appropriate type that contains all outputs (check herschel.ia.dataset.Metadata, a general Dictionary-like class with map and hash implementations).
Changed:
< <
If you execute tasks from Java code, there is no automatic cleanup. You need to clean the outputs manually like this:
> >
If you execute tasks from Java code, there is no automatic cleanup. You need to reset the outputs manually like this:
myTask.perform();
Line: 271 to 273
myTask.reset();
Changed:
< <
Of course, if myTask is going to be garbage collected there is no need to clean it up. Note that the task instances already available in HIPE are not going to be garbage collected.
> >
Of course, if myTask is going to be garbage collected there is no need to clear it up. Note that the task instances already available in HIPE are not going to be garbage collected.
Running a task in the HIPE Console view
Changed:
< <
The Task framework offers different ways to execute the task as well: Calling the execute method using "positional parameters" and "named parameters". Suppose that a task expects three input parameters: One image parameter called 'img', one float indicating a rotation angle 'angle', and one boolean (true or false) called 'takeNeg' indicating whether the negative of the image shall be products.
> >
The Task framework offers different ways to execute the task as well: Calling the execute method using "positional parameters" and "named parameters". Suppose that a task expects three input parameters: One image parameter called 'img', one float indicating a rotation angle 'angle', and one boolean (true or false) called 'takeNeg' indicating whether the negative of the image shall be produced.
If you call the execute methods using positional parameters, then the simple order of the parameters tells the Task which is the image, which is the angle and which is the boolean. If you use named parameters, then you are allowed to use any order for the parameters, but every parameter you add after the first named one must be a named parameter too (If you were allowed to put positional parameters after a named one their positions would not be evident).
Line: 288 to 290
Tasks should be included in HIPE such that they can be accessed directly from its user interface, in particular the Tasks View.
In short, including a task in HIPE comprises:
Changed:
< <
Adding the task to HIPE registry. It is also possible to specify a specific category for your task, such that the task will be available for a specific type of variable.
> >
Adding the task to HIPE registry. It is also possible to specify a specific category for your task, such that the task will be available for a specific type of variable.
Optionally setting validation requirements for a specific parameter, using a ParameterValidator. See Validating prime input.
Added:
> >
Optionally provide a custom task dialog. HIPE automatically provides a default Task dialog panel for the registered tasks.
Changed:
< <
Optionally provide a custom task dialog. HIPE automatically provides a default Task dialog panel for the registered tasks.
If an error occurs during execution of the task, and the task cannot recover from it, an exception should be raised. This exception should be derived from TaskException (a RuntimeException ). The Task API does not declare any exception (Task.perform), so we can't use checked exceptions. If you must raise a checked exception, use the initCause() method of a TaskException (or subclass thereof).
> >
If an error occurs during execution of the task, and the task cannot recover from it, an exception should be raised. This exception should be derived from TaskException (a RuntimeException ). The Task API does not declare any exception (Task.perform), so we can't use checked exceptions. If you must raise a checked exception, use the initCause() method of a TaskException (or subclass thereof).
Interrupting the execution of a task
Line: 363 to 361
Now, when the task perform method is called, its Status is automatically changed to the RUNNING value and it will keep this value until its successful conclusion ( SUCCESS ), an error is found ( FAILED ) or it happens to be interrupted before finish (INTERRUPTED).
Changed:
< <
This transitions between states are automatically registered into the Status field by the default implementation. The developer can use this information as a quick feedback to the user about the final result of the task. Also, the developer can update the StatusMessage field during the execution of the task as a way to notify the user of current stage into a time consuming task.
> >
These transitions between states are automatically registered into the Status field by the default implementation. The developer can use this information as a quick feedback to the user about the final result of the task. Also, the developer can update the StatusMessage field during the execution of the task as a way to notify the user of current stage into a time consuming task.
Showing task execution progress
Changed:
< <
The Progress field marks the advance in the execution of a given task as a integer value between 0 and 100 . This provide a way to show the user how a single task is performing and how much time is expected this task to last until its conclusion. The developer can update this value at any time during the execution process and the The Task package includes a bean to easily display this value on the screen as the typical progress bar.
> >
The Progress field marks the advance in the execution of a given task as a integer value between 0 and 100 . This provide a way to show the user how a single task is performing and how much time is expected this task to last until its conclusion. The developer can update this value at any time during the execution process and the Task package includes a bean to easily display this value on the screen as the typical progress bar.
The Progress parameter is accessible via the name Signature.PROGRESS and can be used as in the following example.
Line: 421 to 419
Task compliance checklist
Changed:
< <
Developing a task that looks native involves quite a few steps. We have provided a list with the qualities a task should hold: ChecklistTask
> >
Developing a task that looks native involves quite a few steps. We have provided a list with the qualities a task should hold: ChecklistTask (this is a TWiki page only for internal developers).
The default syntax will be the new one and you no longer need to add __list__=True. var1, var2 = myTask() will work as is (given that myTask has two outputs defined, of course).
Changed:
< <
Note that tasks with multiple outputs will have more restrictions regarding changes in their outputs (order is fixed, adding or removing outputs may result in syntax errors ...). If you want to keep the flexibility to evolve your task it is recommended that you evaluate moving to a task with just one compound output. The best time to do it is now (HIPE 11 development), as users will have to modify their calls, anyway. Also note that it is impossible for the task framework to know how the task has been called wrt the left side of an assignment (the framework just 'sees" the right hand side, i.e. see INFO Task perform: [Full Call] log messages).
> >
Note that tasks with multiple outputs will have more restrictions regarding changes in their outputs (order is fixed, adding or removing outputs may result in syntax errors ...). If you want to keep the flexibility to evolve your task it is recommended that you evaluate moving to a task with just one compound output. The best time to do it is now (HIPE 11 development), as users will have to modify their calls, anyway. Also note that it is impossible for the task framework to know how the task has been called wrt the left side of an assignment (the framework just 'sees' the right hand side, i.e. see INFO Task perform: [Full Call] log messages).
This is why most parameters are cleaned (reset to their default values) after a task is executed in Jython:
CHANGES FOR JYTHON CALLS TO TASKS THAT HAVE MULTIPLE OUTPUTS
The way client code retrieves the outputs for tasks than have multiple output is changing. The current way was not Python-like and could bound to generate memory leaks (and even dependencies from previous executions in the case of IO (INOUT) parameters).
Changed:
< <
This will only affect tasks with multiple outputs. When the transition is complete, in the case of multiple outputs, all outputs will be returned as a list (and thus the signature will be cleaned just after execution in all cases). var1, var2 = myTask()
> >
This will only affect tasks with multiple outputs. When the transition is complete, in the case of multiple outputs, all outputs will be returned as a list (and thus the signature will be cleaned just after execution in all cases). var1, var2 = myTask().
The migration to this new style has been staged as follows:
HIPE 11
To enable the new syntax you have to add __list__=True to the task call: var1, var2 = myTask(arg=1, ..., __list__=True). If you do not use this form the system will issue a warning every time the task is called. Note that this means that if you just put a variable in the left side of the assignment (var= myTask(arg=1, ..., __list__=True)), var will be a list with all outputs if there is more than one defined (remember that INOUTs are outputs too!).
HIPE 12
The default syntax will be the new one and you no longer need to add __list__=True. var1, var2 = myTask() will work as is (given that myTask has two outputs defined, of course).
Note that tasks with multiple outputs will have more restrictions regarding changes in their outputs (order is fixed, adding or removing outputs may result in syntax errors ...). If you want to keep the flexibility to evolve your task it is recommended that you evaluate moving to a task with just one compound output. The best time to do it is now (HIPE 11 development), as users will have to modify their calls, anyway. Also note that it is impossible for the task framework to know how the task has been called wrt the left side of an assignment (the framework just 'sees" the right hand side, i.e. see INFO Task perform: [Full Call] log messages).
This is why most parameters are cleaned (reset to their default values) after a task is executed in Jython:
You should not define a task without input parameters, because it will have very limited support in HIPE:
Line: 172 to 175
This means that users can only invoke the task via the command line, either in a script or at the prompt in the Console view. (The root cause is that the Task View (and ToolRegistry) require a primary input).
Changed:
< <
There are two such tasks in the core HIPE software: pause and resume, both in the ia.toolbox.util package. They are meant only to be used from the Jython editor/Console so, in this case, the limitations are fitting.
> >
There were two such tasks in the core HIPE software: pause and resume, both in the ia.toolbox.util package, but they are now defined as plain functions.
Multiple data types for a parameter
You cannot set multiple data types for a parameter, but you can achieve the same effect by using one of the following workarounds:
Use a more general data type, then define a validator doing additional checks.
Changed:
< <
Register your task several times with different primary inputs.
> >
Register your task several times with different primary inputs. CURRENTLY BROKEN
Use multiple optional parameters.
Cleaning of parameters after execution
Changed:
< <
The execution of a task should not depend on any previous executions. This is why most parameters are cleaned (reset to their default values) after a task is executed in Jython:
> >
The execution of a task should not depend on any previous executions. Thus, your task class should not have member or class variables (everything should be done through parameters).
The framework cleans the data from parameters after executing a task.
CHANGES FOR JYTHON CALLS TO TASKS THAT HAVE MULTIPLE OUTPUTS
The way client code retrieves the outputs for tasks than have multiple output is changing. The current way was not Python-like and could bound to generate memory leaks (and even dependencies from previous executions in the case of IO (INOUT) parameters).
This will only affect tasks with multiple outputs. When the transition is complete, in the case of multiple outputs, all outputs will be returned as a list (and thus the signature will be cleaned just after execution in all cases). var1, var2 = myTask()
This is why most parameters are cleaned (reset to their default values) after a task is executed in Jython:
The first output parameter is cleaned after execution of the task. You should store the value of this parameter into a variable when you execute the task: var = myTask().
Changed:
< <
The second and additional output (or input-output) parameters are kept in memory until they are accessed, then they are cleaned: var2 = myTask.output2.
> >
IN DEPRECATION: The second and additional output (or input-output) parameters are kept in memory until they are accessed, then they are cleaned: var2 = myTask.output2.
Input parameters are cleaned after the task is executed.
Changed:
< <
Try to use a single output parameter per task, so that there are no leftover values after a task execution. If you need to return multiple values you can use collections or define an appropriate type that contains all outputs.
> >
Try to use a single output parameter per task, so that there are no leftover values after a task execution. If you need to return multiple values you can use collections or define an appropriate type that contains all outputs (check herschel.ia.dataset.Metadata, a general Dictionary-like (map or hash) class).
If you execute tasks from Java code, there is no automatic cleanup. You need to clean the outputs manually like this:
Line: 203 to 213
Of course, if myTask is going to be garbage collected there is no need to clean it up. Note that the task instances already available in HIPE are not going to be garbage collected.
Deleted:
< <
CHANGES FOR JYTHON CALLS TO TASKS THAT HAVE MULTIPLE OUTPUTS
The way client code retrieves the outputs for tasks than have multiple output is going to change. The current way is not Python-like and is bound to generate memory leaks (and even dependencies from previous executions in the case of IO (INOUT) parameters).
The execution of a task should depend as little as possible on any previous executions. This is why most parameters are cleaned (reset to their default values) after a task is executed in Jython:
> >
The execution of a task should not depend on any previous executions. This is why most parameters are cleaned (reset to their default values) after a task is executed in Jython:
The first output parameter is cleaned after execution of the task. You should store the value of this parameter into a variable when you execute the task: var = myTask().
The second and additional output (or input-output) parameters are kept in memory until they are accessed, then they are cleaned: var2 = myTask.output2.
Line: 202 to 202
Of course, if myTask is going to be garbage collected there is no need to clean it up. Note that the task instances already available in HIPE are not going to be garbage collected.
Added:
> >
CHANGES FOR JYTHON CALLS TO TASKS THAT HAVE MULTIPLE OUTPUTS
The way client code retrieves the outputs for tasks than have multiple output is going to change. The current way is not Python-like and is bound to generate memory leaks (and even dependencies from previous executions in the case of IO (INOUT) parameters).
> >
If you call the execute methods using positional parameters, then the simple order of the parameters tells the Task which is the image, which is the angle and which is the boolean. If you use named parameters, then you are allowed to use any order for the parameters, but every parameter you add after the first named one must be a named parameter too (If you were allowed to put positional parameters after a named one their positions would not be evident).
Line: 230 to 234
Handling exceptions in tasks
Changed:
< <
If an error occurs during execution of the task, and the task cannot recover from it, an exception should be raised. This exception should be derived from TaskException (a RuntimeException ). The Task API does not declare any exception (Task.perform), so we can't use checked exceptions. If you must raise a checked exception, use the initCause() method of a RuntimeException (or subclass thereof).
> >
If an error occurs during execution of the task, and the task cannot recover from it, an exception should be raised. This exception should be derived from TaskException (a RuntimeException ). The Task API does not declare any exception (Task.perform), so we can't use checked exceptions. If you must raise a checked exception, use the initCause() method of a TaskException (or subclass thereof).
Line: 547 to 551
setValue(Signature.PROGRESS, new Integer(((j*100)/_iterations)));
// Also, this shortcut is valid for any subclass of Task:
// setProgress((j*100)/_iterations)
Added:
> >
// Additionally, the following code updates both progress and status message (preferred)
advance((j*100)/_iterations, "iteration " + j+1 + " of " + _iterations);
def __init__(self, name = 'Vector Transformer'):
p = TaskParameter(name = 'input', valueType = array(Integer), mandatory = 1)
Line: 230 to 230
Handling exceptions in tasks
Changed:
< <
If an error occurs during execution of the task, and the task cannot recover from it, an exception should be raised. This exception should be derived from TaskException (a RuntimeException ). The JTask API does not declare any exception, so we can't use checked exceptions. If you must raise a checked exception, use the initCause() method of the RuntimeException.
> >
If an error occurs during execution of the task, and the task cannot recover from it, an exception should be raised. This exception should be derived from TaskException (a RuntimeException ). The Task API does not declare any exception (Task.perform), so we can't use checked exceptions. If you must raise a checked exception, use the initCause() method of a RuntimeException (or subclass thereof).
Line: 373 to 373
Jython example
Changed:
< <
class MyTask(JTask, java.awt.event.ActionListener, java.beans.PropertyChangeListener):
> >
class MyTask(Task, java.awt.event.ActionListener, java.beans.PropertyChangeListener):
Adding the task to HIPE registry. It is also possible to specify a specific category for your task, such that the task will be available for a specific type of variable.
Changed:
< <
Optionally setting validation requirements for a specific parameter, using a ParameterValidator. See Prime input validation.
> >
Optionally setting validation requirements for a specific parameter, using a ParameterValidator. See Validating prime input.
Optionally provide a custom task dialog. HIPE automatically provides a default Task dialog panel for the registered tasks.
The preamble allows us to do some preprocessing on our data. For example, our average function calculate the averages of the columns of a table. But what if we wanted our task to calculate the average of two one-dimensional arrays of numbers? We can write a Task to takes two of these vectors as input and use the preamble to create a table, that has these two vectors as columns.
The below example illustrates this. Note that we first invoke the preamble from our parent task (JTask) to guarantee that our needed parameters have a value before putting them into the table.
class AverageArray(JTask):
# Creation method
def __init__(self,name = "averageArray"):
p = TaskParameter("vector1",valueType = PyList, mandatory = 1)
self.addTaskParameter(p)
p = TaskParameter("vector2",valueType = PyList, mandatory = 1)
self.addTaskParameter(p)
p = TaskParameter("result",valueType = Double1d, type = OUT)
self.addTaskParameter(p)
# Create an internal variable table which is our TableDataset
self.__dict__['table'] = TableDataset()
def preamble(self):
# In the preamble we do the store the two vectors in the table
JTask.preamble(self) # the first statement must always be a call to the parent preamble (preamble chain)
self.table["0"] = Column(Double1d(self.vector1))
self.table["1"] = Column(Double1d(self.vector2))
def execute(self):
self.result = average(self.table)
Note that the execute method is identical to the original Task. The following can be used to try this Task:
If you use tasks from Jython code, when you access the outputs they are cleaned up automatically. On the other hand, when you call a task from Java code, you need to clean the outputs manually like this:
t.perform();
t. ... // get t results
t.reset();
Of course, if t is going to be garbage collected there is no need to clean it up. Note that the task instances already available in HIPE are not going to be garbage collected.
Tasks and tools involving products and datasets
For tasks that deal with products and datasets, the users (astronomers and calibration scientists) should not be required to perform type conversions between products and datasets. If such conversions are required, they should be dealt with by the software. This in part stems from the fact that manipulation of datasets loses any associated history. It is recommended that all tasks (and tools) should work with, and save outputs as, products.
Did you spot something wrong or missing on this page? If you have an account on this TWiki, you can fix it yourself by editing the page. If you don't have an account, you can send a message to the Editorial Board to tell us about it. Thank you in advance!
Writing tasks
Tasks are data processors. You can use a task to manipulate a data product, such as an image, or to convert data from one kind into another, for instance extracting a spectrum from a cube.
Line: 946 to 954
Developing a task that looks native involves quite a few steps. We have provided a list with the qualities a task should hold: ChecklistTask
If you have an algorithm that does data processing, you can turn it into a task whether it is written in Jython or in Java. This topic explains how this is done.
Added:
> >
If you are an internal developer, please look at the task checklist and task FAQ as well.
This section provides some general guidelines, that are expected to be useful also for simple tasks.
> >
This section provides some general guidelines, that are expected to be useful also for simple tasks. Make sure you also check the javadoc of TaskUtil for helper functions.
Parameters that are facultative must have been given a default value before the task is executed, failure to do so will provoke an exception when parameters values are checked in the preamble. Usually, the default value for a parameter is provided by the task implementer, not the end user. Parameters requiring a value from the user should be tagged as mandatory .
Deleted:
< <
Task documentation
How to get help about a task from the Console:
print taskName long, 1 paragraph per parameter task autodocumentation
print bg.__doc__ if here doc defined it shows it, else if jtags documentation present shows it, else show a short 1 line per parameter autodocumentation
How to get help about a task from the GUI:
Open the task GUI: parameter labels describe the parameters, modifier editors signal if current data is valid for the parameters
Right click on the Task View node, open URM for the task documentation
Task Compliance Checklist
Developing a task that looks native involves quite a few steps. We have provided a list with the qualities a task should hold: ChecklistTask
Parameters that are facultative must have been given a default value before the task is executed, failure to do so will provoke an exception when parameters values are checked in the preamble. Usually, the default value for a parameter is provided by the task implementer, not the end user. Parameters requiring a value from the user should be tagged as mandatory .
Deleted:
< <
The TaskUtil utility class
This class ia a one place stop for auxiliary functions to help you in building tasks. As of 23 Aug 2010, it contains the following functions:
Auxiliary functions for constructing the signature:
Any task that should be included for other to use should be properly documented. there are multiple documentation requirements for tasks. We try to help the task implementor, but we cannot automate many things. The different documentation requirements are:
Provide a proper name to your task This will be used in Task View, by the interpreter to execute your tasks, by documentation in general to refer to your task ...
Document your task parameters This is used to generate default documentation for print _taskname_ and print _taskname.paramname_ , and in the GUI for parameter labels' tooltips.
Use a doc static member This is used as output for print _taskname.__doc__
How to get help about a task from the Console:
Added:
> >
print taskName long, 1 paragraph per parameter task autodocumentation
print bg.__doc__ if here doc defined it shows it, else if jtags documentation present shows it, else show a short 1 line per parameter autodocumentation
Line: 971 to 966
Right click on the Task View node, open URM for the task documentation
Task Compliance Checklist
Deleted:
< <
Developing a task that looks "native" takes time and involves quite a few steps. To help you comply with all requirements, we offer a (high level) list of common deficiencies and mistakes to be taken into account. This is not a MUST comply list (yet):
Open the task GUI: parameter labels describe the parameters, modifier editors signal if current data is valid for the parameters
Right click on the Task View node, open URM for the task documentation
Added:
> >
Task Compliance Checklist
Developing a task that looks "native" takes time and involves quite a few steps. To help you comply with all requirements, we offer a (high level) list of common deficiencies and mistakes to be taken into account. This is not a MUST comply list (yet):
ChecklistTask
Document your task parameters This is used to generate default documentation for print _taskname_ and print _taskname.paramname_ , and in the GUI for parameter labels' tooltips.
Use a doc static member This is used as output for print _taskname.__doc__
Changed:
< <
> >
How to get help about a task from the Console:
print taskName long, 1 paragraph per parameter task autodocumentation
print bg.__doc__ if here doc defined it shows it, else if jtags documentation present shows it, else show a short 1 line per parameter autodocumentation
How to get help about a task from the GUI:
Open the task GUI: parameter labels describe the parameters, modifier editors signal if current data is valid for the parameters
Right click on the Task View node, open URM for the task documentation
Any task that should be included for other to use should be properly documented. there are multiple documentation requirements for tasks. We try to help the task implementor, but we cannot automate many things. The different documentation requirements are:
Provide a proper name to your task This will be used in Task View, by the interpreter to execute your tasks, by documentation in general to refer to your task ...
Document your task parameters This is used to generate default documentation for print _taskname_ and print _taskname.paramname_ , and in the GUI for parameter labels' tooltips.
Use a doc static member This is used as output for print _taskname.__doc__
Document your task parameters This is used to generate default documentation for print _taskname_ and print _taskname.paramname_ , and in the GUI for parameter labels' tooltips.
Changed:
< <
Use a doc static member This is used in the Task View to generate tooltips, as output for print _taskname.__doc__, will be used in the future for code completion
> >
Use a doc static member This is used as output for print _taskname.__doc__
Deleted:
< <
How to fill the __doc__ static member
From Dive Into Python:
Many Python IDEs use the doc string to provide context-sensitive documentation, so that when you type a function name, its doc string appears as a tooltip. This can be incredibly helpful, but it's only as good as the doc strings you write.
OBSOLETE: rewrite
Task provides a default signature string (Task#getSignatureString()) that is either extracted from your __doc__ string or generated from the current signature of the task. This string is better than nothing, but the task framework is not rich enough to be able to provide the best string possible, only the task creator can.
For Python functions, it is enough with a one line explanation and futher into after a blank line (see http://docs.python.org/tutorial/controlflow.html#SECTION006750000000000000000), the format we autogenerate for tasks differs:
1st block signature string (automated)
2nd block text description of task
3rd block parameter description (automated)
An implementation of the __doc__ string in your task:
public static PyString __doc__ ;
public MyTask() {
super(TASKNAME);
makeSignature();
__doc__ = TaskUtil.makeDoc(this, null,
"Where all parameters are strings, except overwrite, archive is the source file and dirout a (possibly not existing) directory",
null);
}
}
Here, you are using the default string, but if it does not generate the signature string that you want, you should write it by hand:
public MyTask() {
super(TASKNAME);
makeSignature();
__doc__ = TaskUtil.makeDoc(this, "<v>=radialVelocity((<storage> | <context> | <pointing>, <orbit>), <time> [, <ephemerides>, <corrections>]); <ra> = radialVelocity.ra; <dec> = radialVelocity.dec",
"Where all parameters are strings, except overwrite, archive is the source file and dirout a (possibly not existing) directory",
null);
}
}
The format of the doc string of a task is TENTATIVELY as follows: (TO BE REVIEWED)
each symbolic name (arguments, return variables) that the user can change is surrounded by < and > and uses the corresponding parameter name :
<v> = t( <arg>)
each task argument appears in the signature order (in or input output), separated by commas:
t( <arg1>, <arg2>, <arg3>)
each optional argument is surrounded by [ and ] (but they may be grouped together):
This means one of storage, context or pointing & orbit must be used.
for addittional output parameters, sentences can be added, of the form:
; <out2> = t.out2
where out2 is the name of the (in)out parameter
A sample __doc__ string follows:
<v>=radialVelocity((<storage> | <context> | <pointing>, <orbit>), <time> [, <ephemerides>, <corrections>]); <ra> = radialVelocity.ra; <dec> = radialVelocity.dec
Returns S/C radial velocity (signed) projection in the pointing direction (in km/s) at t, (also ra and dec of the direction)
To sum this up, a stateless function can be used both in static and dynamic environment. It is advantageous, when possible, to write stateless functions.
Added:
> >
Doing incremental calculations
If you want to support incremental calculations then client code has to hold all memory between calls to the auxiliary functional elements (wether they are pure functions or tasks). An example follows.
Lets suppose you want to use an average task (or function) that supports incremental calculations. You do not "know" beforehand how many items you are going to average and you don't want to repeat already done calculations every time you get a new value to add to the average. To calculate incremental averages you can use the following recurrence relation:
Usage will require that client code keeps track of auxiliary aggregates (average and count):
mean = 0.0
count = 1
mean = inc_avg(5, mean, count)
count += 1
mean = inc_avg(3, mean, count)
...
count += 1
mean = inc_avg(2, mean, count)
A way of transforming inc_avg into a proper task is sketched as follows:
avg = inc_avg(element, oldavg, count)
where all parameters are float (Double in Java):
* element : (IN mandatory) the next item to add
* oldavg : (IN mandatory) the current average
* count : (IN mandatory) the current item order
* avg: (OUT) the new average
Added:
> >
And usage would be exactly the same as with the Jython function.
From Dive Into Python:
Many Python IDEs use the doc string to provide context-sensitive documentation, so that when you type a function name, its doc string appears as a tooltip. This can be incredibly helpful, but it's only as good as the doc strings you write.
Added:
> >
OBSOLETE: rewrite
Task provides a default signature string (Task#getSignatureString()) that is either extracted from your __doc__ string or generated from the current signature of the task. This string is better than nothing, but the task framework is not rich enough to be able to provide the best string possible, only the task creator can.
For Python functions, it is enough with a one line explanation and futher into after a blank line (see http://docs.python.org/tutorial/controlflow.html#SECTION006750000000000000000), the format we autogenerate for tasks differs:
We describe here some features that may not of interest to simple tasks.
> >
We describe here some features that may not be of interest to simple tasks.
User level feedback
Line: 899 to 899
Parameters that are facultative must have been given a default value before the task is executed, failure to do so will provoke an exception when parameters values are checked in the preamble. Usually, the default value for a parameter is provided by the task implementer, not the end user. Parameters requiring a value from the user should be tagged as mandatory .
Added:
> >
The TaskUtil utility class
This class ia a one place stop for auxiliary functions to help you in building tasks. As of 23 Aug 2010, it contains the following functions:
Auxiliary functions for constructing the signature:
Any task that should be included for other to use should be properly documented. there are multiple documentation requirements for tasks. We try to help the task implementor, but we cannot automate many things. The different documentation requirements are:
Provide a proper name to your task This will be used in Task View, by the interpreter to execute your tasks, by documentation in general to refer to your task ...
Line: 908 to 923
Document your task parameters This is used to generate default documentation for print _taskname_ and print _taskname.paramname_ , and in the GUI for parameter labels' tooltips.
Use a doc static member This is used in the Task View to generate tooltips, as output for print _taskname.__doc__, will be used in the future for code completion
Deleted:
< <
How to fill the __doc__ static member
Added:
> >
How to fill the __doc__ static member
From Dive Into Python:
Many Python IDEs use the doc string to provide context-sensitive documentation, so that when you type a function name, its doc string appears as a tooltip. This can be incredibly helpful, but it's only as good as the doc strings you write.
Task provides a default signature string (Task#getSignatureString()) that is either extracted from your __doc__ string or generated from the current signature of the task. This string is better than nothing, but the task framework is not rich enough to be able to provide the best string possible, only the task creator can.
<v>=radialVelocity((<storage> | <context> | <pointing>, <orbit>), <time> [, <ephemerides>, <corrections>]); <ra> = radialVelocity.ra; <dec> = radialVelocity.dec
Returns S/C radial velocity (signed) projection in the pointing direction (in km/s) at t, (also ra and dec of the direction)
A first implementation of the __doc__ string in your task:
> >
An implementation of the __doc__ string in your task:
=
public static PyString __doc__ ;
Line: 940 to 942
public MyTask() {
super(TASKNAME);
makeSignature();
Changed:
< <
if (doc == null) { //no need to sync
doc = new PyString( getSignatureString(false) + "\n" +
"\nmy short description of the task";
> >
doc = TaskUtil.makeDoc(this, null,
"Where all parameters are strings, except overwrite, archive is the source file and dirout a (possibly not existing) directory",
null);
}
}
=
Line: 950 to 952
Here, you are using the default string, but if it does not generate the signature string that you want, you should write it by hand:
=
Deleted:
< <
public static PyStringdoc = new PyString("=radialVelocity(( | | , ),
public MyTask() {
super(TASKNAME);
makeSignature();
Note: the __doc__ string requires a single line with the signature template because we cannot easily support enter in the Console prompt, nor can the user edit easily multiline blocks (this hard requirement may change in the future).
> >
The format of the doc string of a task is TENTATIVELY as follows: (TO BE REVIEWED)
each symbolic name (arguments, return variables) that the user can change is surrounded by < and > and uses the corresponding parameter name :
<v> = t( <arg>)
each task argument appears in the signature order (in or input output), separated by commas:
t( <arg1>, <arg2>, <arg3>)
each optional argument is surrounded by [ and ] (but they may be grouped together):
This means one of storage, context or pointing & orbit must be used.
for addittional output parameters, sentences can be added, of the form:
; <out2> = t.out2
where out2 is the name of the (in)out parameter
A sample __doc__ string follows:
<v>=radialVelocity((<storage> | <context> | <pointing>, <orbit>), <time> [, <ephemerides>, <corrections>]); <ra> = radialVelocity.ra; <dec> = radialVelocity.dec
Returns S/C radial velocity (signed) projection in the pointing direction (in km/s) at t, (also ra and dec of the direction)
If you have an algorithm that does data processing, you can turn it into a task whether it is written in Jython or in Java. This topic explains how this is done.
Document your task parameters This is used to generate default documentation for print _taskname_ and print _taskname.paramname_ , and in the GUI for parameter labels' tooltips.
Use a doc static member This is used in the Task View to generate tooltips, as output for print _taskname.__doc__, will be used in the future for code completion
Changed:
< <
How to fill the doc static member
> >
How to fill the __doc__ static member
From Dive Into Python:
Changed:
< <
Many Python IDEs use the doc string to provide context-sensitive documentation, so that when you type a function name, its doc string appears as a tooltip. This can be incredibly helpful, but it's only as good as the doc strings you write._
> >
Many Python IDEs use the doc string to provide context-sensitive documentation, so that when you type a function name, its doc string appears as a tooltip. This can be incredibly helpful, but it's only as good as the doc strings you write.
Changed:
< <
Task provides a default signature string (Task#getSignatureString()) that is either extracted from your doc string or generated from the current signature of the task. This string is better than nothing, but the task framework is not rich enough to be able to provide the best string possible, only the task creator can.
> >
Task provides a default signature string (Task#getSignatureString()) that is either extracted from your __doc__ string or generated from the current signature of the task. This string is better than nothing, but the task framework is not rich enough to be able to provide the best string possible, only the task creator can.
While for Python functions, it is enough with a one line explanation and futher into after a blank line (see http://docs.python.org/tutorial/controlflow.html#SECTION006750000000000000000), the format we use for tasks differs:
* 1st line signature string
* 2nd line typically blank
* 3rd and following explanation and further info.
Changed:
< <
A sample doc string follows
> >
The format of the doc string of a task is as follows:
each symbolic name (arguments, return variables) that the user can change is surrounded by < and > and uses the corresponding parameter name :
<v> = t( <arg>)
each task argument appears in the signature order (in or input output), separated by commas:
t( <arg1>, <arg2>, <arg3>)
each optional argument is surrounded by [ and ] (but they may be grouped together):
This means one of storage, context or pointing & orbit must be used.
for addittional output parameters, sentences can be added, of the form:
; <out2> = t.out2
where out2 is the name of the (in)out parameter
A sample __doc__ string follows:
<v>=radialVelocity((<storage> | <context> | <pointing>, <orbit>), <time> [, <ephemerides>, <corrections>]); <ra> = radialVelocity.ra; <dec> = radialVelocity.dec
Returns S/C radial velocity (signed) projection in the pointing direction (in km/s) at t, (also ra and dec of the direction)
Added:
> >
A first implementation of the __doc__ string in your task:
public static PyString __doc__ ;
public MyTask() {
super(TASKNAME);
makeSignature();
if (__doc__ == null) { //no need to sync
__doc__ = new PyString( getSignatureString(false) + "\n" +
"\nmy short description of the task";
}
}
Here, you are using the default string, but if it does not generate the signature string that you want, you should write it by hand:
public static PyString __doc__ = new PyString("<v>=radialVelocity((<storage> | <context> | <pointing>, <orbit>), <time> [, <ephemerides>, <corrections>]); <ra> = radialVelocity.ra; <dec> = radialVelocity.dec\n" +
"\nmy short description of the task";
public MyTask() {
super(TASKNAME);
makeSignature();
}
Note: the __doc__ string requires a single line with the signature template because we cannot easily support enter in the Console prompt, nor can the user edit easily multiline blocks (this hard requirement may change in the future).
Parameters that are facultative must have been given a default value before the task is executed, failure to do so will provoke an exception when parameters values are checked in the preamble. Usually, the default value for a parameter is provided by the task implementer, not the end user. Parameters requiring a value from the user should be tagged as mandatory .
Added:
> >
Task documentation
Any task that should be included for other to use should be properly documented. there are multiple documentation requirements for tasks. We try to help the task implementor, but we cannot automate many things. The different documentation requirements are:
Provide a proper name to your task This will be used in Task View, by the interpreter to execute your tasks, by documentation in general to refer to your task ...
Document your task parameters This is used to generate default documentation for print _taskname_ and print _taskname.paramname_ , and in the GUI for parameter labels' tooltips.
Use a doc static member This is used in the Task View to generate tooltips, as output for print _taskname.__doc__, will be used in the future for code completion
How to fill the doc static member
From Dive Into Python:
Many Python IDEs use the doc string to provide context-sensitive documentation, so that when you type a function name, its doc string appears as a tooltip. This can be incredibly helpful, but it's only as good as the doc strings you write._
Task provides a default signature string (Task#getSignatureString()) that is either extracted from your doc string or generated from the current signature of the task. This string is better than nothing, but the task framework is not rich enough to be able to provide the best string possible, only the task creator can.
While for Python functions, it is enough with a one line explanation and futher into after a blank line (see http://docs.python.org/tutorial/controlflow.html#SECTION006750000000000000000), the format we use for tasks differs:
* 1st line signature string
* 2nd line typically blank
* 3rd and following explanation and further info.
A sample doc string follows
To support the re-use of tasks as well as the related views, I give the following example: We have a AccessHk task as well as a DisplayParam task which we want to combine in a BrowseHk task, where we re-use the view from the AccessHk task to setup the query, and the view from the DisplayParam which allows to plot select a parameter. The BrowseHk will therefor only contain the glue to combine those views into a higher level task. We define the BrowseHk as a task in order to adopt to the task API, to
> >
To support the re-use of tasks as well as the related views, I give the following example: We have a AccessHk task as well as a DisplayParam task which we want to combine in a BrowseHk task, where we re-use the view from the AccessHk task to setup the query, and the view from the DisplayParam which allows to plot select a parameter. The BrowseHk will therefor only contain the glue to combine those views into a higher level task. We define the BrowseHk as a task in order to adopt to the task API, to achieve the following:
Allow the user to pass: a) an "obsid" as default entry for the task's view, and b) allow the user to retrieve back data into its jide session as one is used to within the task concept
Changed:
< <
To support the developer in providing easy syntax to handle data via task parameters.
> >
Support the developer in providing easy syntax to handle data via task parameters.
Parameters that are facultative must have been given a default value before the task is executed, failure to do so will provoke an exception when parameters values are checked in the preamble. Usually, the default value for a parameter is provided by the task implementer, not the end user. Parameters requiring a value from the user should be tagged as mandatory .
# average function
# Takes a TableDataSet as input
> >
# Average function
# Takes a TableDataset as input
# Returns a Double1d (1D array of real numbers)
# in which each row is the average of the values
# in the input table columns
#--------------------------------------------------
You can use the following functions to try it out:
Changed:
< <
# Function for creating a table with some test data
> >
# Function to create a table for testing the average function.
# The table will be like this:
# 0 1 2 3 4 5
# 1 2 3 4 5 6
Line: 68 to 63
# 3 4 5 6 7 8
# 4 5 6 7 8 9
def createTable():
Changed:
< <
#create array x (0.0, 1.0, 2.0, 3.0, 4.0)
> >
# Create array x (0.0, 1.0, 2.0, 3.0, 4.0)
x = Double1d.range(5)
columns = 5
Changed:
< <
#create an empty table with a name
table = TableDataset(description = "a test table")
#iterate for the the number of columns to fill up the table
> >
# Create an empty table with a name
table = TableDataset(description = "A test table")
# Iterate for the the number of columns to fill up the table
for column in range(0,columns):
table["%i" % column] = Column(x)
x = x + 1
Changed:
< <
#return the result, a table called 'table'
> >
# Return the result, a table called 'table'
return table
Added:
> >
Finally, this function creates the table and executes the average function:
# Function for executing the method
def testAverage():
Changed:
< <
# create the table
> >
# Create the table
table = createTable()
Changed:
< <
# get the average and put it into an array called 'result'
> >
# Get the average and put it into an array called 'result'
result = average(table)
Changed:
< <
# print the result
> >
# Print the result
print 'result', result
Changed:
< <
The result of typing testAverage() on the Console view of HIPE is:
> >
The result of typing testAverage() in the Console view of HIPE is:
result [2.0,3.0,4.0,5.0,6.0]
Added:
> >
Creating the Average task
Changed:
< <
Creating the Task: Average
To turn our average algorithm into a task we need to wrap the algorithm into a piece of code which adapts it to form a task. Our current function has no input parameters (it is a parameterless execute method). We need to wrap it so that we have the appropriate input parameters (so it becomes a parameterized method).
> >
We will name the task itself Average. Every task is a class, and by convention class names are capitalized nouns. Also, every task extends the JTask class.
Changed:
< <
We will name the task itself Average (a task is a class, it is callable from the command line, and by convention class names are capitalized nouns). In our Average class we have no needs other than setting up a signature and calling the average function as part of its execution.
Every Task must extend the JTask, so the first step in creating our task class is to subclass JTask.
If you check the chapter about writing a Task in Java, you will see that the class in Jython looks slightly different. This is due to differences in implementation details between Jython and Java, and cannot be avoided. To extend JTask, only the following is required:
> >
Here is the code of the Average task:
# Import task framework classes.
Line: 153 to 143
The second and additional output (or input-output) parameters will be kept in memory until they are accessed, then they will be cleaned: var2 = task.output2 .
Input parameters are cleaned after execution of the task.
Changed:
< <
We recommend to use only one output paramater per task, so that no state is kept between task invocations. If you need to return multiple values you can use collections or define an appropiate type that contains all outputs.
> >
We recommend to use only one output paramater per task, so that no state is kept between task invocations. If you need to return multiple values you can use collections or define an appropriate type that contains all outputs.
Changed:
< <
Running the Task from Jython
> >
Running the task in the HIPE Console view
Changed:
< <
The following commands can be used to run the Average Task from the HIPE Console. We shows three ways of running the task:
> >
The following commands show how to run the Average Task from the HIPE Console view. We use the createTable function defined previously:
Deleted:
< <
from herschel.ia.dataset.all import *
from herschel.ia.numeric.all import *
from Average import Average
x = Double1d.range(5)
columns = 5
data = TableDataset(description = "a test table")
for column in range(columns):
data["%i" % column] = Column(x)
x = x + 1
# Create an instance of the Task
avg = Average()
Changed:
< <
# Print the usage information of the task (the "Signature")
> >
# Print the usage information of the task
info('avg')
# Set the input parameter 'table'
Changed:
< <
avg.table = data
> >
avg.table = createTable()
# Execute the task and get the result
avg.execute()
This section describes how to turn a Java algorithm into a task. We assume here that you have read the preceding section about writing a Task in Jython.
If you have an algorithm that does data processing, you can turn it into a task whether it is written in Jython or in Java. This topic explains how this is done.
Changed:
< <
How does the Task Framework help?
The Task framework provides functionality in a Task superclass, which can be reused by the user's tasks. The Task superclass provides, for example:
Command line help, exposing available algorithms and how they should be called
Integration in HIPE: Tasks can be accessed and run using the HIPE GUI
Input parameter checking
History recording
A common look and feel or the user (fixed methods to execute, to get the output, etc.)
Re-usability: Tasks are the building blocks of pipelines, and complex tasks can be composed of other tasks
Modifiability: Because Task defines how to execute the algorithm and how to get the output, etc, the user can modify the algorithm, or change one algorithm for another, as long as the established Task rules are still followed
This has some similarities to the Interactive Reduction and Analysis Facility (IRAF) where tasks/programs have a set of their own parameters and help. In IRAF, the history is placed in the header information of the files worked on.
A function is therefore placed within a task and can be adjusted and changed and replaced within the task so long as the parameters for input and output remain the same. This makes the task concept ideal for users who are creating scripts for other users. You get the consistency checking, help and history recording in a consistent fashion for a little bit of extra packaging.
Tasks can be used anywhere in the DP environment: From the Jython command line in HIPE, the Quick-Look Analysis (QLA) environment and in the pipelines (Standard Product Generation).
> >
How do tasks help?
Added:
> >
Any task provides the following functionality:
Added:
> >
Command line help, exposing available algorithms and how they should be called.
Integration in HIPE: you can register a task to be listed in HIPE, where it gets a standard graphical interface.
Checking of input parameters.
Recording of history.
A common look-and-feel for the graphical interface.
Re-usability: tasks are the building blocks of pipelines, and complex tasks can be composed of other tasks.
Modifiability: a task defines how to execute the internal algorithm, how to provide inputs and to get outputs. You can modify the internal algorithm or even change it altogether, as long as input and output parameters remain the same.
Added:
> >
This has some similarities to the Interactive Reduction and Analysis Facility (IRAF) where tasks/programs have a set of their own parameters and help. In IRAF, the history is placed in the header information of the files worked on.
Added:
> >
A task is ideal if you want to create routines for other users. You get the consistency checking, help and history recording in a consistent fashion for a little bit of extra packaging.
Changed:
< <
How To Write a Task in Jython
> >
You can execute tasks via the HIPE graphical interface, call them from the Console view or from your Jython scripts.
Changed:
< <
This section describes how to turn a Jython algorithm into a Task. This is done with an example of a Jython "algorithm" that calculates an average. This "algorithm" is coded in a Jython function. It will be shown how to turn this function into a task.
> >
Writing a task in Jython
Changed:
< <
We start by giving the example function.
> >
This section describes how to turn a Jython function into a task.
Changed:
< <
A Jython function: average(table)
> >
The Jython function: average(table)
Changed:
< <
The following is a Jython function that takes a table and calculates the average of each of the columns.
> >
The following Jython function takes a table dataset and calculates the average of each column.
Tasks are data processors. You can use a task to manipulate a data product, such as an image, or to convert data from one kind into another, for instance extracting a spectrum from a cube.
If you have an algorithm that does data processing, you can turn it into a task whether it is written in Jython or in Java. This topic explains how this is done.
How does the Task Framework help?
The Task framework provides functionality in a Task superclass, which can be reused by the user's tasks. The Task superclass provides, for example:
Command line help, exposing available algorithms and how they should be called
Integration in HIPE: Tasks can be accessed and run using the HIPE GUI
Input parameter checking
History recording
A common look and feel or the user (fixed methods to execute, to get the output, etc.)
Re-usability: Tasks are the building blocks of pipelines, and complex tasks can be composed of other tasks
Modifiability: Because Task defines how to execute the algorithm and how to get the output, etc, the user can modify the algorithm, or change one algorithm for another, as long as the established Task rules are still followed
This has some similarities to the Interactive Reduction and Analysis Facility (IRAF) where tasks/programs have a set of their own parameters and help. In IRAF, the history is placed in the header information of the files worked on.
A function is therefore placed within a task and can be adjusted and changed and replaced within the task so long as the parameters for input and output remain the same. This makes the task concept ideal for users who are creating scripts for other users. You get the consistency checking, help and history recording in a consistent fashion for a little bit of extra packaging.
Tasks can be used anywhere in the DP environment: From the Jython command line in HIPE, the Quick-Look Analysis (QLA) environment and in the pipelines (Standard Product Generation).
How To Write a Task in Jython
This section describes how to turn a Jython algorithm into a Task. This is done with an example of a Jython "algorithm" that calculates an average. This "algorithm" is coded in a Jython function. It will be shown how to turn this function into a task.
We start by giving the example function.
A Jython function: average(table)
The following is a Jython function that takes a table and calculates the average of each of the columns.
# Import data structures from java classes
from herschel.ia.numeric.all import *
from herschel.ia.dataset.all import *
#--------------------------------------------------
# average function
# Takes a TableDataSet as input
# Returns a Double1d (1D array of real numbers)
# in which each row is the average of the values
# in the input table columns
#--------------------------------------------------
#routine for calculating the average
def average(table):
columns = table.columnCount
divider = 1.0 / columns
result = Double1d(table.rowCount)
for column in range(columns):
result.add(table.getColumn(column).data)
return result.multiply(divider)
You can use the following functions to try it out:
# Function for creating a table with some test data
# The table will be like this:
# 0 1 2 3 4 5
# 1 2 3 4 5 6
# 2 3 4 5 6 7
# 3 4 5 6 7 8
# 4 5 6 7 8 9
def createTable():
#create array x (0.0, 1.0, 2.0, 3.0, 4.0)
x = Double1d.range(5)
columns = 5
#create an empty table with a name
table = TableDataset(description = "a test table")
#iterate for the the number of columns to fill up the table
for column in range(0,columns):
table["%i" % column] = Column(x)
x = x + 1
#return the result, a table called 'table'
return table
# Function for executing the method
def testAverage():
# create the table
table = createTable()
# get the average and put it into an array called 'result'
result = average(table)
# print the result
print 'result', result
The result of typing testAverage() on the Console view of HIPE is:
result [2.0,3.0,4.0,5.0,6.0]
Creating the Task: Average
To turn our average algorithm into a task we need to wrap the algorithm into a piece of code which adapts it to form a task. Our current function has no input parameters (it is a parameterless execute method). We need to wrap it so that we have the appropriate input parameters (so it becomes a parameterized method).
We will name the task itself Average (a task is a class, it is callable from the command line, and by convention class names are capitalized nouns). In our Average class we have no needs other than setting up a signature and calling the average function as part of its execution.
Every Task must extend the JTask, so the first step in creating our task class is to subclass JTask.
If you check the chapter about writing a Task in Java, you will see that the class in Jython looks slightly different. This is due to differences in implementation details between Jython and Java, and cannot be avoided. To extend JTask, only the following is required:
# Import task framework classes.
from herschel.ia.task.all import *
# Import data structures from java classes needed
from herschel.ia.dataset.all import *
from herschel.ia.numeric.all import *
# Import our algorithm
from average import average
class Average(JTask):
def __init__(self):
# This is the constructor of the Task. Initialize the superclass:
JTask.__init__(self, "averageTask")
# Define the *Signature* using calls to the addTaskParameter method
# The following lines define one TableDataset input parameter, called table
# And one output parameter of type Double1d.
p = TaskParameter("table",valueType = TableDataset, mandatory = 1)
self.addTaskParameter(p)
p = TaskParameter("result",valueType = Double1d, type = OUT)
self.addTaskParameter(p)
# Here we define the execute method:
def execute(self):
# Define the code to be *executed*: Call our average method here
# The input parameter 'table' is passed as the argument of the function
self.result = average(self.table)
These steps are explained in greater detail in the next sections.
About the import statements in the above example, the following: The task framework classes are the "standard" definitions allowing import of a task definition. Task and TaskParameter classes will be automatically imported with the 'all' import statement. The set of import statements after the 'all' import statements are specific to this example task. The last import statement simply gives us access to the definition of our average function. For this import statement to work as is, the file with the Jython average function needs to be in your CLASSPATH. You can also simply copy the average function into the Jython code of the Task. In that case you don't need the import statement for the 'average' function.
A note about input parameters
As you can see in the above examples, the average function takes one parameter and returns the result. The execute method of the task, on the other hand, has no parameters and returns nothing. The Task defines one input and one output parameter in the constructor, which in Jython is called __init__.
A note about output parameters
One feature of Task is that it tries to minimize its hidden state between calls. This helps if tasks are being executed multiple times: This way, one execution depends as little as possible on the previous executions.
From one execution of the Task to another, most parameters are cleaned (reset to their default values), after invocation:
The first output parameter will be cleaned after execution of the task. This parameter will be available as the return value of the task and should be stored into a variable for later use: var = task() .
The second and additional output (or input-output) parameters will be kept in memory until they are accessed, then they will be cleaned: var2 = task.output2 .
Input parameters are cleaned after execution of the task.
We recommend to use only one output paramater per task, so that no state is kept between task invocations. If you need to return multiple values you can use collections or define an appropiate type that contains all outputs.
Running the Task from Jython
The following commands can be used to run the Average Task from the HIPE Console. We shows three ways of running the task:
from herschel.ia.dataset.all import *
from herschel.ia.numeric.all import *
from Average import Average
x = Double1d.range(5)
columns = 5
data = TableDataset(description = "a test table")
for column in range(columns):
data["%i" % column] = Column(x)
x = x + 1
# Create an instance of the Task
avg = Average()
# Print the usage information of the task (the "Signature")
info('avg')
# Set the input parameter 'table'
avg.table = data
calcResult = avg.result
print 'result', calcResult
The Task framework offers different ways to execute the task as well: Calling the execute method using "positional parameters" and "named parameters". Suppose that a task expects three input parameters: One image parameter called 'img', one float indicating a rotation angle 'angle', and one boolean (true or false) called 'takeNeg' indicating whether the negative of the image shall be products.
If you call the execute methods using positional parameters, then the simple order of the parameters tells the Task which is the image, which is the angle and which is the boolean. If you use named parameters, then you are allowed to use any order for the parameters, but every parameters must preceded by its name. The following example illustrates this using our Average Task:
# Invocation using positional parameter
avg(data)
# Invocation using named parameter
avg(table = data)
How To Write a Java Task
This section describes how to turn a Java algorithm into a task. We assume here that you have read the preceding section about writing a Task in Jython.
Again we use a function that calculates an average. This function is called average and is defined in the Java class Function. This is the full Function class:
import herschel.ia.dataset.TableDataset;
import herschel.ia.dataset.Column;
import herschel.ia.numeric.Double1D;
public class Function {
public static Double1D average(TableDataset table) {
if (table == null) {
throw (new NullPointerException("missing table value"));
}
int columns = table.getColumnCount();
double divider = 1.0 / ((double) columns);
Double1D result = new Double1D(table.getRowCount());
for (int column = 0; column < columns; column++) {
result.mAdd((Double1D) table.getColumn(column).getData());
}
return result.mMultiply(divider);
}
}
The following Java class can be used to test the above code:
import herschel.ia.dataset.TableDataset;
import herschel.ia.dataset.Column;
import herschel.ia.numeric.Double1D;
import java.text.DecimalFormat;
public class TestFunction {
public static void main(String[] arguments) {
Double1D result = Function.average(generator());
show(result);
}
/**
* Generate some test data.
*/
public static TableDataset generator() {
TableDataset result = new TableDataset("A test table");
int columns = 5;
int rows = 5;
Double1D x = Double1D.range(rows);
for (int column = 0;column < columns;column++) {
result.addColumn(("" + column), new Column(x.copy()));
x.mAdd(1);
}
return result;
}
public static void show(Double1D data) {
DecimalFormat twoDigits = new DecimalFormat("0.0");
int size = data.length();
System.out.print("result [");
for (int row = 0;row < size;row++) {
System.out.print(twoDigits.format(data.get(row)));
if (row < (size - 1)) {
System.out.print(",");
} else {
System.out.print("] ");
}
}
System.out.println();
}
}
The result of running TestFunction is:
result [2.0,3.0,4.0,5.0,6.0]
Task Average
To turn the average algorithim in the Function class into a Task, we will, as before for the Jython function, create a Task that calls the right function. The following is an example of how this can be done:
// Import Task class definitions
import herschel.ia.task.Task;
import herschel.ia.task.TaskParameter;
// Import data types
import herschel.ia.dataset.TableDataset;
import herschel.ia.numeric.Double1D;
public class Average extends Task {
public Average() {
super("Averaging of table data set");
// Add one input parameter: the TableDataset
TaskParameter parameter = new TaskParameter("table", TableDataset.class);
parameter.setMandatory(true);
addTaskParameter(parameter);
// And declare the output parameter
parameter = new TaskParameter("result", Double1D.class);
parameter.setType(TaskParameter.OUT);
addTaskParameter(parameter);
}
public void execute() {
try {
setValue("result", Function.average((TableDataset) getValue("table")));
} catch (Exception e) {
e.printStackTrace();
}
}
}
The following code can be used to test the above Java Task:
import herschel.ia.dataset.TableDataset;
import herschel.ia.numeric.Double1D;
import java.text.DecimalFormat;
public class TestTask {
public static void main(String[] arguments) {
Average task = new Average();
TableDataset data = generate()
task.setValue("table", data);
task.execute();
Double1D result = (Double1D) task.getValue("result");
show(result);
}
public static TableDataset generate() {
TableDataset result = new TableDataset("A test table");
int columns = 5;
int rows = 5;
Double1D x = Double1D.range(rows);
for (int column = 0;column < columns;column++) {
result.addColumn(("" + column), new Column(x.copy()));
x.mAdd(1);
}
return result;
}
public static void show(Double1D data) {
DecimalFormat twoDigits = new DecimalFormat("0.0");
int size = data.length();
System.out.print("result [");
for (int row = 0;row < size;row++) {
System.out.print(twoDigits.format(data.get(row)));
if (row < (size - 1)) {
System.out.print(",");
} else {
System.out.print("]");
}
}
System.out.println();
}
}
Calling a Java task from a Jython script
The script below shows the only difference between calling a task coded in Java (e.g. Average.java) and a task coded in Jython (e.g. Average.py). The import of Average statement ( import JavaAverage ) is different in that we do not need to specify a module name. Note also that the name of the class is changed to JavaAverage in order to avoid a clash of names with our Jython Average class. The test function is identical (albeit shortened).
from herschel.ia.numeric.all import *
from herschel.ia.dataset.all import *
# Import our java task
import JavaAverage
def test():
x = Double1d.range(5)
columns = 5
table = TableDataset(description = "a test table")
for column in range(0,columns):
table["%i" % column] = Column(x)
x = x + 1
avg = JavaAverage()
# Invocation using positional parameter
print 'result', avg(table = table)
Executing the above code in HIPE will define the test() method. Type test() on the HIPE Console to execute it.
Examples
Here we provide some examples on how to use a preprocessor, to perform some actions on your data before starting, and how to write your task if your input consists of arrays. The examples are given in Jython, but can be applied to Java Tasks as well.
Example: Using a preprocessor: Preamble
The preamble allows us to do some preprocessing on our data. For example, our average function calculate the averages of the columns of a table. But what if we wanted our task to calculate the average of two one-dimensional arrays of numbers? We can write a Task to takes two of these vectors as input and use the preamble to create a table, that has these two vectors as columns.
The below example illustrates this. Note that we first invoke the preamble from our parent task (JTask) to guarantee that our needed parameters have a value before putting them into the table.
class AverageArray(JTask):
# Creation method
def __init__(self,name = "averageArray"):
p = TaskParameter("vector1",valueType = PyList, mandatory = 1)
self.addTaskParameter(p)
p = TaskParameter("vector2",valueType = PyList, mandatory = 1)
self.addTaskParameter(p)
p = TaskParameter("result",valueType = Double1d, type = OUT)
self.addTaskParameter(p)
# Create an internal variable table which is our TableDataset
self.__dict__['table'] = TableDataset()
def preamble(self):
# In the preamble we do the store the two vectors in the table
JTask.preamble(self)
self.table["0"] = Column(Double1d(self.vector1))
self.table["1"] = Column(Double1d(self.vector2))
def execute(self):
self.result = average(self.table)
Note that the execute method is identical to the original Task. The following can be used to try this Task:
Task Transformer illustrates the use of a task that handles arrays. This example transforms arrays into TableDatasets.
from herschel.ia.task.all import *
from herschel.ia.numeric.all import *
from herschel.ia.dataset.all import *
from java.lang import Integer
class Transformer(JTask):
def __init__(self, name = 'Vector Transformer'):
p = TaskParameter(name = 'input', valueType = array(Integer), mandat\
ory = 1)
self.addTaskParameter(p)
p = TaskParameter( name = "result", valueType = TableDataset, type = OUT)
self.addTaskParameter(p)
def execute(self):
self.result = TableDataset(description = 'Integrated vector as colum\
n zero')
r = Double1d(len(self.input))
index = 0
for data in (self.input):
r[index] = data
index = index + 1
self.result['0'] = Column(r)
The following can be used to try this Task:
# Test data
sample = [10, 20, 30, 40]
# Create the Task:
transform = Transformer()
# Execute the Task:
transform(sample)
# Get the output:
table = transform.result
# Print:
print table
print table['0']
Including Tasks in HIPE
Tasks should be included in HIPE such that they can be accessed directly from its user interface, in particular the Tasks View.
In short, including a task in HIPE comprises:
Adding the task to HIPE registry. It is also possible to specify a specific category for your task, such that the task will be available for a specific type of variable.
Optionally setting validation requirements for a specific parameter, using a ParameterValidator. See Prime input validation.
Optionally provide a custom task dialog. HIPE automatically provides a default Task dialog panel for the registered tasks.
This section provides some general guidelines, that are expected to be useful also for simple tasks.
Exceptions
If an error occurs during execution of the task, and the task cannot recover from it, an exception should be raised. This exception must derived from RuntimeException. The JTask API does not declare any exception, so we can't use checked exceptions. If you must raise a checked exception, use the initCause() method of the RuntimeException.
Interrupting Task execution
The Stop button in the HIPE user interface generates an interruption event and sends it to the separate thread where Jython code is running.
This signal is read between consecutive commands of the Jython script. For example, pressing the Stop button interrupts immediately this infinite loop:
while 1:
print 1
However, this may not be so when the interpreter releases control to a time-consuming Task. If the Task does not periodically check for the interruption signal, pressing the Stop button will have no effect.
You can check for the interruption signal within your code by using the checkInterrupted method of the Task class. If an interruption request is detected, the method will throw an InterruptedException that would be caught by the interpreter, thus cancelling the task. You may pass a String representing the message to issue when the Task is interrupted:
myTask.checkInterrupted(); # Uses default message
myTask.checkInterrupted("Interruption signal received");
Your code should check for the interruption signal with sufficient frequency to allow a timely interruption. For example:
// Within the execute method of your task
for (int i = 0; i < nSteps; i++) {
Task.checkInterrupted(getName() + " interrupted in step " + i);
doStep(i);
}
Cleaning Task output
If you use tasks from Jython code, when you access the outputs they are cleaned up automatically. On the other hand, when you call a task from Java code, you need to clean the outputs manually like this:
t.perform();
t. ... // get t results
t.reset();
Of course, if t is going to be garbage collected there is no need to clean it up. Note that the task instances already available in HIPE are not going to be garbage collected.
Tasks and tools Involving Products and Datasets
For tasks that deal with products and datasets, the users (astronomers and calibration scientists) should not be required to perform type conversions between products and datasets. If such conversions are required, they should be dealt with by the software. This in part stems from the fact that manipulation of datasets loses any associated history. It is recommended that all tasks (and tools) should work with, and save outputs as, products.
Stateless and stateful functions
The function, as designed above, is what is called (with respect to its mode of operation) a state-full function . There are two types of functions that exist in the Herschel IA software:
Stateless function : this function is called with a set of actual parameters, perform some computation and deliver a result based solely on the current inputs.
Stateful function : this function performs some computation based on a set of actual parameters and on its previous internal state . In other words a stateful function is a function with a memory (or a history).
On the command line interactive analysis, data is usually static and therefore the functions used to manipulate them can be either stateful or stateless. To clarify the difference we take a simple averaging of vectors.
The static version of averaging two vectors is simply (call model):
result = average(vector1, vector2)
This method would work well so long as vector1 and vector2 are not themselves the results of averaging (implicitly the state is the weight for the vectors and is equal to 1). In a process where the average is a running average, we could turn this function into a dynamic function by introducing the weights of both input vectors as follows:
result = average(avg1, weight1, avg2, weight2)
This new function can be used in both a dynamic and in a static way. The static call used for instance within the IA (under Jconsole ) could be:
result = average(avg1, 1, avg2, 1)
In the more dynamic QLA environment where the second vector is the data read from a stream, the function would be called as:
To sum this up, a stateless function can be used both in static and dynamic environment. It is advantageous, when possible, to write stateless functions.
How to use GUIs with your tasks
Tasks can have an extended set of parameters and/or include results which need to be visualized (a plot or an image). An additional API is provided, which allows the user to interact with the task using a GUI. The GUI and its components are set up such that it can be re-used within other tasks.
To include objects which represents a certain view which is related to the task (most often this is a javax.swing.JComponent), the TaskSignature contains a dedicated parameter called "views". This parameter contains a map object: Map[String, Object]. In this way the developer is capable to include any type of view. Below we show a Java as well as a Jython examples demonstrating the creation and use of view components.
Notes that a task is a DP component, which should be usable in the entire DP environment. This includes the operational pipelines, where no display is available and no GUI should be initialized. The examples below show how to make sure that the GUI is only created when needed.
Java example
import herschel.ia.task.Task;
import herschel.ia.task.TaskParameter;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Map;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MyAccessTask extends Task implements PropertyChangeListener, ActionListener {
private JComponent _myTaskDialog;
public MyAccessTask(){
super("MyTask");
makeSignature();
// instantiate a JComponent including (1) a StringField
// for the "db" parameter and (2) an "accept" button
_myTaskDialog = MyTaskDialog(this, getValue("db"));
}
private void makeSignature() {
TaskParameter param = new TaskParameter("db");
param.setValueType(String.class);
param.setMandatory(false);
addTaskParameter(param);
getParameter("db").addPropertyChangeListener(this);
// create boolean for the dialog component
// via this parameter the user is able to launch it
param = new TaskParameter("gui");
param.setValueType(Boolean.class);
param.setDefaultValue(Boolean.FALSE);
param.setType(TaskParameter.IN);
addTaskParameter(param);
getParameter("gui").addPropertyChangeListener(this);
// add view component to the "views" parameter:
((Map<Object, Object>) getValue("views")).put("dialog", _myTaskDialog);
}
public void propertyChange(PropertyChangeEvent event) {
if (event.getPropertyName().equals("dialog")) {
if ((Boolean) getValue("dialog") && getViews().get("GUI") == null) {
JPanel jpan = new JPanel();
jpan.add((JPanel) getViews().get("dialog"));
JFrame jframe = new JFrame("MyAccessTask Dialog");
jframe.add(jpan);
getViews().put("GUI", jframe);
}
((JFrame) getViews().get("GUI")).setVisible((Boolean) getValue("dialog"));
}
}
public void execute() {
if ((Boolean) getValue("dialog")) {
while ((Boolean) getValue("dialog")) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// execute algorithm
}
public void actionPerformed(ActionEvent e){
if ( ((JButton) e.getSource()).getText().equals("accept") ) {
setValue("db", _myTaskDialog.getDbValue());
setValue("dialog", Boolean.FALSE) ;
}
}
}
To support the re-use of tasks as well as the related views, I give the following example: We have a AccessHk task as well as a DisplayParam task which we want to combine in a BrowseHk task, where we re-use the view from the AccessHk task to setup the query, and the view from the DisplayParam which allows to plot select a parameter. The BrowseHk will therefor only contain the glue to combine those views into a higher level task. We define the BrowseHk as a task in order to adopt to the task API, to
Allow the user to pass: a) an "obsid" as default entry for the task's view, and b) allow the user to retrieve back data into its jide session as one is used to within the task concept
To support the developer in providing easy syntax to handle data via task parameters.
class BrowseHk(JTask, PropertyChangeListener):
def __init__(self, name = 'browser'):
JTask.__init__(self, name, developerAccessMode = 1)
self.__dict__['apk'] = AccessHk()
self.__dict__['dpk'] = DisplayParam()
self.apk.getParameter('result').addPropertyChangeListener(self)
self.dpk.getParameter('result').addPropertyChangeListener(self)
self.addTaskParameter(TaskParameter('query',valueType = \
array(TmSourcePacket), type = TaskParameter.OUT))
self.addTaskParameter(TaskParameter('selected',valueType = \
TableDataset, type = TaskParameter.OUT))
self.addTaskParameter(TaskParameter('gui',valueType = \
Boolean, defaultValue = Boolean(Boolean.FALSE)))
self.getParameter('gui').addPropertyChangeListener(self)
panel = JPanel()
panel.add(self.apk.views['apk-panel']
panel.add(self.dpk.views['dpk-panel']
self.views['main-panel'] = panel
def propertyChange(self, event):
if ( String("result").equals(event.getPropertyName()) ):
if ( String(event.getSource().getBaseType().getName()).equals
("herschel.ccm.api.TmSourcePacket") ):
// event comes from AccessHk
ar = event.getNewValue()
self.dpk.hkarray = ar
self.query = ar
else:
// event comes from DisplayParam
self.selected = self.dpk.result
elif ( String('gui').equals(event.getPropertyName()) ):
if (self.getValue("gui")):
if (self.views["Gui"] == None):
frame = JFrame('House Keeping Packet Browser v1.0')
frame.getContentPane().add(self.views['panel'])
frame.setSize(625,680)
self.views['Gui'] = frame
self.views['Gui'].visible = 1
else:
if (self.views["Gui"] != None):
self.views['Gui'].visible = 0
#
def execute(self):
if (self.gui == Boolean.FALSE):
self.gui = Boolean.TRUE
Furthermore the developer is able to extract the set of views from a task into its IA session, simply by:
HIPE> views = task.views
Conventions for task GUI labels
The conventions for Task parameter labels in Task dialogs are:
The primary input parameter should be the first one. Its label will be bold.
All mandatory parameters (including primary input) will have an asterisk mark in their label. Of course, this does not change the parameter name.
The other parameters have plain labels.
Label tooltips should be formatted as follows:
(optional | mandatory) type
-------------------------------
parameter description
The utility JTaskSignatureComponent#getDecoratedLabel(...) can be used to create a label that follows these conventions (including the tooltips).
Example:
Advanced Features
We describe here some features that may not of interest to simple tasks.
User level feedback
Suppose a task can not create the expected outputs because it found an error in the input data. This can not be considered a program failure and has more to do with the notifications an interactive system should give to its operators/final users. Even if the task terminates normally, it should be desirable to provide a way to know if a task was already executed or it is still pending or was interrupted during the execution. Another common situation is that where a time consumer task needs to give some feedback to the user during it execution.
For these situations, all tasks include the following fields in the Signature class: Status, StatusMessage and the Progress.
The Status parameter stores a numeric code without restrictions but with a few, standardized, values:
Status Status Meaning/
Value "Name" Status Message
------ ----------- ---------------------------------------------------------
100 UNKNOWN Signature is created but not assigned to a given task yet
200 READY Task is ready to be executed
400 RUNNING During the execution of the perform method of the task
000 SUCCESS Task has finished without problems
500 INTERRUPTED Task has been interrupted during its execution
900 FAILED Error found during the task execution
Once a task is created the READY value is assigned to it. UNKNOWN value is only used for those cases where a Signature is created before its assignment to a Task or even a pipeline.
Now, when the task perform method is called, its Status is automatically changed to the RUNNING value and it will keep this value until its successful conclusion ( SUCCESS ), an error is found ( FAILED ) or it happens to be interrupted before finish (INTERRUPTED).
This transitions between states are automatically registered into the Status field by the default implementation. The developer can use this information as a quick feedback to the user about the final result of the task. Also, the developer can update the StatusMessage field during the execution of the task as a way to notify the user of current stage into a time consuming task.
The Progress parameter
The Progress field marks the advance in the execution of a given task as a integer value between 0 and 100 . This provide a way to show the user how a single task is performing and how much time is expected this task to last until its conclusion. The developer can update this value at any time during the execution process and the The Task package includes a bean to easily display this value on the screen as the typical progress bar.
The Progress parameter is accessible via the name Signature.PROGRESS and can be used as in the following example.
Java:
public void execute(){
// assuming to iterate for a number of _iterations
for (int j=0; j<=_iterations ; j++) {
// DO SOMETHING DURING THE ITERATION
//xxxxx
// LOG PROGRESS INFORMATION
setValue(Signature.PROGRESS, new Integer(((j*100)/_iterations)));
// Also, this shortcut is valid for any subclass of Task:
// setProgress((j*100)/_iterations)
}
}
Values must fall into the [0-100] range to be properly interpreted and displayed.
Jython:
def execute(self):
#assuming to iterate for a number of _iterations
for j in range(_iterations):
## DO SOMETHING DURING THE ITERATION
##xxxxx
## LOG PROGRESS INFORMATION
self.progress = j*100/_iterations
The FAILED status
The developer must clearly separate the situation where the Task is suppose to return a failed result from any other problem derived from an implementation error or an unexpected situation not well managed by the code. In the latter cases, a RuntimeException shall be raised and the calling method (maybe the graphical environment where the task is running) take care of it.
By definition, a task should be marked as FAILED when, even assuming an error free algorithm and giving the valid inputs, it fails to produce the expected output. A simple example is a task that implements an algorithm to identify the starts into a image. This task should fail when the provided image doesn't contains any start or the algorithm can not distinguish them due to a noisy background.
Optional parameters
Parameters that are facultative must have been given a default value before the task is executed, failure to do so will provoke an exception when parameters values are checked in the preamble. Usually, the default value for a parameter is provided by the task implementer, not the end user. Parameters requiring a value from the user should be tagged as mandatory .