Skip to content

File syn_sequencer.c

File List > sched > syn_sequencer.c

Go to the documentation of this file

#if __has_include("syn_config.h")
#include "syn_config.h"
#endif

#if !defined(SYN_USE_SEQUENCER) || SYN_USE_SEQUENCER

#include "../util/syn_assert.h"
#include "syn_sequencer.h"

#include <string.h>

void syn_seq_init(SYN_Sequencer *seq, const SYN_SeqStep *steps, uint16_t count)
{
    SYN_ASSERT(seq != NULL);
    SYN_ASSERT(steps != NULL);
    SYN_ASSERT(count > 0);

    if (seq == NULL || steps == NULL || count == 0) {
        return;
    }

    memset(seq, 0, sizeof(*seq));
    seq->steps = steps;
    seq->step_count = count;
    seq->state = SYN_SEQ_IDLE;
}

void syn_seq_on_complete(SYN_Sequencer *seq, SYN_SeqCompleteCallback cb, void *ctx)
{
    SYN_ASSERT(seq != NULL);

    if (seq == NULL) {
        return;
    }
    seq->on_complete = cb;
    seq->on_complete_ctx = ctx;
}

void syn_seq_set_loop(SYN_Sequencer *seq, bool loop)
{
    SYN_ASSERT(seq != NULL);

    if (seq == NULL) {
        return;
    }
    seq->loop = loop;
}

void syn_seq_start(SYN_Sequencer *seq)
{
    SYN_ASSERT(seq != NULL);

    if (seq == NULL) {
        return;
    }
    seq->current = 0;
    seq->state = SYN_SEQ_RUNNING;
}

void syn_seq_stop(SYN_Sequencer *seq)
{
    SYN_ASSERT(seq != NULL);

    if (seq == NULL) {
        return;
    }
    seq->state = SYN_SEQ_IDLE;
}

static void execute_step(SYN_Sequencer *seq)
{
    const SYN_SeqStep *step = &seq->steps[seq->current];

    /* Execute action if present */
    if (step->action != NULL) {
        step->action(step->ctx);
    }

    /* Start delay if present */
    if (step->delay_ms > 0) {
        seq->wait_start = syn_port_get_tick_ms();
        seq->state = SYN_SEQ_WAITING;
    } else {
        /* No delay — advance immediately */
        seq->current++;
    }
}

bool syn_seq_update(SYN_Sequencer *seq)
{
    if (seq == NULL || seq->steps == NULL) {
        return false;
    }
    SYN_ASSERT(seq != NULL);

    if (seq->state == SYN_SEQ_IDLE || seq->state == SYN_SEQ_DONE) {
        return false;
    }

    if (seq->state == SYN_SEQ_WAITING) {
        uint32_t elapsed = syn_port_get_tick_ms() - seq->wait_start;
        if (elapsed < seq->steps[seq->current].delay_ms) {
            return false; /* still waiting */
        }
        /* Delay complete — advance */
        seq->current++;
        seq->state = SYN_SEQ_RUNNING;
    }

    /* Execute steps (limit execution per tick to step_count + 1 to prevent infinite loops on
     * 0-delay loop sequences while allowing completion check) */
    size_t max_exec = (size_t)seq->step_count + 1;
    size_t executed = 0;
    while (seq->state == SYN_SEQ_RUNNING && executed < max_exec) {
        if (seq->current >= seq->step_count) {
            /* Sequence complete */
            seq->loop_count++;

            if (seq->on_complete != NULL) {
                seq->on_complete(seq, seq->on_complete_ctx);
            }

            if (seq->loop) {
                seq->current = 0;
                /* Continue running */
            } else {
                seq->state = SYN_SEQ_DONE;
                return true;
            }
        }

        execute_step(seq);
        executed++;
    }

    return false;
}

#endif /* SYN_USE_SEQUENCER */