Files
xod/docs/guide/cpp-time/README.md
2017-12-14 19:53:40 +03:00

4.5 KiB
Raw Blame History

title
title
Dealing with Time in C++

Dealing with Time in C++

Many nodes respond to input changes immediately, but some nodes control processes lasting for long time spans. They force themselves to be re-evaluated after some delay to repeat a task or complete the job.

XOD C++ node API provides scheduling functions to deal with these cases. In this article, well learn them by example.

The task

Were a going to implement a tick node which when triggered by SET pin starts sending pulses at equal time intervals T. A user should be able to cancel the series in progress by sending a pulse on RST input.

Although you could trivially express such node with a combination of flip-flop and clock without touching C++ at all, lets ignore it for now.

Prepare the node

As always, when you make a C++ node, start with a new patch, add required terminals, and the not-implemented-in-xod node.

Patch outline

Dont forget to provide a resonable default value for T. 1 second is fine.

Double-click on not-implemented-in-xod node to open the code editor.

Set timeout

First, we should handle pulses on SET input. When set, well use setTimeout function to ask XOD engine to call evaluate again after given timeout:

struct State { };

\{{ GENERATED_CODE }}

void evaluate(Context ctx) {
    if (isInputDirty<input_SET>(ctx)) {
        // Get T-input value. Conventionally it should be expressed in seconds
        Number t = getValue<input_T>(ctx);

        // However, XOD API works with millisecond values, so convert
        TimeMs milliseconds = t * 1000;

        // Schedule re-evaluation after calculated number of milliseconds
        setTimeout(ctx, milliseconds);
    }
}

Handle timeout

Good. We scheduled ourselves. Now we need to react. Use isTimedOut function for this. We need an explicit check of whether the current evaluation caused by the timeout because the reasons for evaluate calls differ. It could be an input value update before the time interval elapsed.

struct State { };

\{{ GENERATED_CODE }}

// Note, we extracted a function to read `T` input and set timeout
// with that value. The function helps us to avoid code duplication
// in `evaluate` since we need the code twice.
void charge(Context ctx) {
    Number t = getValue<input_T>(ctx);
    TimeMs milliseconds = t * 1000;
    setTimeout(ctx, milliseconds);
}

void evaluate(Context ctx) {
    if (isInputDirty<input_SET>(ctx)) {
        charge(ctx);
    }

    if (isTimedOut(ctx)) {
        // Timeout has been elapsed, emit an output pulse
        emitValue<output_OUT>(ctx, true);
        // To be re-evaluated next time we need to set timeout again
        charge(ctx);
    }
}

Cancel timeout

The only thing left to be done is reset handling. When a pulse is sent to RST well use clearTimeout function to stop counting.

struct State { };

\{{ GENERATED_CODE }}

void charge(Context ctx) {
    Number t = getValue<input_T>(ctx);
    TimeMs milliseconds = t * 1000;
    setTimeout(ctx, milliseconds);
}

void evaluate(Context ctx) {
    if (isInputDirty<input_RST>(ctx)) {
        // When pulsed on `RST` we cancel the timeout countdown regardless
        // whether it was set or not
        clearTimeout(ctx);
        // Return from `evaluate` early giving priority to `RST` so that
        // pulse on `SET` and timeout will not be even checked at this
        // evaluation pass
        return;
    }

    if (isInputDirty<input_SET>(ctx)) {
        charge(ctx);
    }

    if (isTimedOut(ctx)) {
        emitValue<output_OUT>(ctx, true);
        charge(ctx);
    }
}

Test

Thats all. Our node is ready. Test it with two buttons connected to SET and RST and a flip-flop with LED on another side

Test patch

Conclusion

XOD provides quite a basic API to manage time. Although it is simple, you get all tools you need to control lengthy processes. Main principles are:

  • Use setTimeout to schedule re-evaluation of self. Remember, the timeout is expressed in milliseconds.
  • Always use isTimedOut to be sure youre evaluating because the time has passed.
  • If you want to run a task periodically, call setTimeout again manually when isTimedOut.
  • Use clearTimeout to ensure no timeout countdown is in progress.