Execute a Method

Every operation of this application - matching, template creation and module management - is started the same way, and reports its outcome the same way.

The shape of an operation

An operation lives under its own address and always has three parts:

<operation>/execute                 write here to run it
<operation>/input/...               the values it works on
<operation>/output/status-code      what happened, as a number
<operation>/output/status-text      what happened, in words

Some operations have more under output/ - a match result, or the path of a written file - but execute, status-code and status-text are always there.

Running an operation

  1. Write the input nodes.

  2. Write to execute. The value does not matter; the write itself triggers the operation.

  3. Read output/status-code to find out whether it worked.

Writing to execute is a blocking call: it returns when the operation has finished, so there is no need to poll a "ready" flag or track state in your program. Inputs keep their values between runs, so a repeated operation only needs step 2.

Checking the outcome

output/status-code is a signed number, and the sign alone tells you whether it worked:

Value Meaning

greater than 0

The operation succeeded

0

The operation has not run since the application started

less than 0

The operation failed and produced nothing

So the check in a PLC is simply status-code < 0. You only need a specific code when you want to react differently to different failures - a missing template, say, versus a camera that is not answering.

output/status-text carries the same outcome as a sentence, and names the offending value when there is one:

ok: 3 match(es) using 'screw.png'
error: crop/startX + crop/width (100 + 900 = 1000) exceeds the image width 640

Read it when something goes wrong; do not build logic on it, because the wording can change between releases. The full list of codes is in Execution Status.

The reason arrives on output/status-text, not in the response to the execute write itself. A failed write answers with a status code only - over REST, a bare 400. The Data Layer discards any detail a provider attaches to a failed call, so reading output/status-text afterwards is the way to find out what was wrong. The result codes of a write on both Data Layers are listed in Results of a method call.

The same text is written to the application log, so a failure is also diagnosable without touching the Data Layer at all: on ctrlX OS in the ctrlX Logbook or with journalctl, on a PC in the application’s console output. When several inputs are wrong, each one gets its own warning line.

One operation at a time

A module runs one operation at a time. Starting a second one while the first is still running does not queue it - it returns immediately with status code -1 (busy) and does nothing. The write itself answers TooManyOperations (ctrlX: DL_TOO_MANY_OPERATIONS), see One operation at a time.

The lock is per module, so different modules match in parallel - a second camera is not held up by the first. system/create-object has its own lock, so creating a template does not block matching and two creations cannot race.

The outputs belong to the run that is starting

Every method clears its own output nodes as soon as it begins - find-object its output/result, create-object its output/object-path. They are cleared before anything that can fail, so a run that fails for any reason leaves them empty rather than holding what an earlier run produced.

That is what makes it safe to read an output without checking output/status-code first: what you see is either this run’s work or nothing, never a stale value from a previous image.

output/status-code and output/status-text are the exception - they always describe the latest attempt, which is the whole point of them.

Read an output before triggering the next run. The outputs describe the most recent run, and the next call empties them the moment it starts. Calling execute again and then reading the previous result gives you nothing.

The one case that does not clear anything is a call rejected as -1 (busy): it never ran, so it leaves the running operation’s outputs alone. So a second execute fired immediately after the first has two possible outcomes, and they differ:

  • the first run is still going - the second is refused with -1, and the outputs still hold the run before it;

  • the first run has just finished - the second is accepted, and it clears the outputs the first run had only just written.

Wait for execute to return, read the outputs, and only then trigger the next run.