//$file${src::qf::qf_time.c} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
//
// Model: qpc.qm
// File:  ${src::qf::qf_time.c}
//
// This code has been generated by QM 5.3.0 <www.state-machine.com/qm>.
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// This code is covered by the following QP license:
// License #    : LicenseRef-QL-dual
// Issued to    : Any user of the QP/C real-time embedded framework
// Framework(s) : qpc
// Support ends : 2024-12-31
// License scope:
//
// Copyright (C) 2005 Quantum Leaps, LLC <state-machine.com>.
//
//                    Q u a n t u m  L e a P s
//                    ------------------------
//                    Modern Embedded Software
//
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
//
// This software is dual-licensed under the terms of the open source GNU
// General Public License version 3 (or any later version), or alternatively,
// under the terms of one of the closed source Quantum Leaps commercial
// licenses.
//
// The terms of the open source GNU General Public License version 3
// can be found at: <www.gnu.org/licenses/gpl-3.0>
//
// The terms of the closed source Quantum Leaps commercial licenses
// can be found at: <www.state-machine.com/licensing>
//
// Redistributions in source code must retain this top-level comment block.
// Plagiarizing this software to sidestep the license obligations is illegal.
//
// Contact information:
// <www.state-machine.com/licensing>
// <info@state-machine.com>
//
//$endhead${src::qf::qf_time.c} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#define QP_IMPL           // this is QP implementation
#include "qp_port.h"      // QP port
#include "qp_pkg.h"       // QP package-scope interface
#include "qsafe.h"        // QP Functional Safety (FuSa) Subsystem
#ifdef Q_SPY              // QS software tracing enabled?
    #include "qs_port.h"  // QS port
    #include "qs_pkg.h"   // QS facilities for pre-defined trace records
#else
    #include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY

Q_DEFINE_THIS_MODULE("qf_time")

//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Check for the minimum required QP version
#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U))
#error qpc version 7.3.0 or higher required
#endif
//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

//$define${QF::QTimeEvt} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

//${QF::QTimeEvt} ............................................................
QTimeEvt QTimeEvt_timeEvtHead_[QF_MAX_TICK_RATE];

//${QF::QTimeEvt::ctorX} .....................................................
//! @public @memberof QTimeEvt
void QTimeEvt_ctorX(QTimeEvt * const me,
    QActive * const act,
    enum_t const sig,
    uint_fast8_t const tickRate)
{
    me->next     = (QTimeEvt *)0;
    me->act      = act;
    me->ctr      = 0U;
    me->interval = 0U;

    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    Q_REQUIRE_INCRIT(300, (sig != 0)
        && (tickRate < QF_MAX_TICK_RATE));
    QF_CRIT_EXIT();

    // This default event constructor initializes the event
    // as NOT allocated from any event-pool, which must be
    // the case for Time Events.
    (void)QEvt_ctor(&me->super, sig);

    // The refCtr_ attribute is not used in time events, so it is
    // reused to hold the tickRate as well as other information
    me->super.refCtr_ = (uint8_t)tickRate;
}

//${QF::QTimeEvt::armX} ......................................................
//! @public @memberof QTimeEvt
void QTimeEvt_armX(QTimeEvt * const me,
    QTimeEvtCtr const nTicks,
    QTimeEvtCtr const interval)
{
    uint_fast8_t const tickRate
                       = ((uint_fast8_t)me->super.refCtr_ & QTE_TICK_RATE);
    QTimeEvtCtr const ctr = me->ctr;
    #ifdef Q_SPY
    uint_fast8_t const qs_id = ((QActive *)(me->act))->prio;
    #endif

    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QF_MEM_SYS();

    Q_REQUIRE_INCRIT(400, (me->act != (void *)0)
        && (ctr == 0U)
        && (nTicks != 0U)
        && (tickRate < (uint_fast8_t)QF_MAX_TICK_RATE)
        && (me->super.sig >= (QSignal)Q_USER_SIG));
    #ifdef Q_UNSAFE
    Q_UNUSED_PAR(ctr);
    #endif

    me->ctr = nTicks;
    me->interval = interval;

    // is the time event unlinked?
    // NOTE: For the duration of a single clock tick of the specified tick
    // rate a time event can be disarmed and yet still linked into the list
    // because un-linking is performed exclusively in QTimeEvt_tick_().
    if ((me->super.refCtr_ & QTE_IS_LINKED) == 0U) {
        // mark as linked
        me->super.refCtr_ |= QTE_IS_LINKED;

        // The time event is initially inserted into the separate
        // "freshly armed" link list based on QTimeEvt_timeEvtHead_[tickRate].act.
        // Only later, inside the QTimeEvt_tick_() function, the "freshly armed"
        // list is appended to the main list of armed time events based on
        // QTimeEvt_timeEvtHead_[tickRate].next. Again, this is to keep any
        // changes to the main list exclusively inside the QTimeEvt_tick_().
        me->next = (QTimeEvt *)QTimeEvt_timeEvtHead_[tickRate].act;
        QTimeEvt_timeEvtHead_[tickRate].act = me;
    }

    QS_BEGIN_PRE_(QS_QF_TIMEEVT_ARM, qs_id)
        QS_TIME_PRE_();        // timestamp
        QS_OBJ_PRE_(me);       // this time event object
        QS_OBJ_PRE_(me->act);  // the active object
        QS_TEC_PRE_(nTicks);   // the # ticks
        QS_TEC_PRE_(interval); // the interval
        QS_U8_PRE_(tickRate);  // tick rate
    QS_END_PRE_()

    QF_MEM_APP();
    QF_CRIT_EXIT();
}

//${QF::QTimeEvt::disarm} ....................................................
//! @public @memberof QTimeEvt
bool QTimeEvt_disarm(QTimeEvt * const me) {
    #ifdef Q_SPY
    uint_fast8_t const qs_id = QACTIVE_CAST_(me->act)->prio;
    #endif

    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QF_MEM_SYS();

    // is the time event actually armed?
    bool wasArmed;
    if (me->ctr != 0U) {
        wasArmed = true;
        me->super.refCtr_ |= QTE_WAS_DISARMED;

        QS_BEGIN_PRE_(QS_QF_TIMEEVT_DISARM, qs_id)
            QS_TIME_PRE_();            // timestamp
            QS_OBJ_PRE_(me);           // this time event object
            QS_OBJ_PRE_(me->act);      // the target AO
            QS_TEC_PRE_(me->ctr);      // the # ticks
            QS_TEC_PRE_(me->interval); // the interval
            QS_U8_PRE_(me->super.refCtr_ & QTE_TICK_RATE); // tick rate
        QS_END_PRE_()

        me->ctr = 0U; // schedule removal from the list
    }
    else { // the time event was already disarmed automatically
        wasArmed = false;
        me->super.refCtr_ &= (uint8_t)(~QTE_WAS_DISARMED & 0xFFU);

        QS_BEGIN_PRE_(QS_QF_TIMEEVT_DISARM_ATTEMPT, qs_id)
            QS_TIME_PRE_();            // timestamp
            QS_OBJ_PRE_(me);           // this time event object
            QS_OBJ_PRE_(me->act);      // the target AO
            QS_U8_PRE_(me->super.refCtr_ & QTE_TICK_RATE); // tick rate
        QS_END_PRE_()
    }

    QF_MEM_APP();
    QF_CRIT_EXIT();

    return wasArmed;
}

//${QF::QTimeEvt::rearm} .....................................................
//! @public @memberof QTimeEvt
bool QTimeEvt_rearm(QTimeEvt * const me,
    QTimeEvtCtr const nTicks)
{
    uint_fast8_t const tickRate
                       = (uint_fast8_t)me->super.refCtr_ & QTE_TICK_RATE;
    #ifdef Q_SPY
    uint_fast8_t const qs_id = ((QActive *)(me->act))->prio;
    #endif

    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QF_MEM_SYS();

    Q_REQUIRE_INCRIT(600, (me->act != (void *)0)
        && (tickRate < QF_MAX_TICK_RATE)
        && (nTicks != 0U)
        && (me->super.sig >= (QSignal)Q_USER_SIG));

    // is the time evt not running?
    bool wasArmed;
    if (me->ctr == 0U) {
        wasArmed = false;

        // NOTE: For the duration of a single clock tick of the specified
        // tick rate a time event can be disarmed and yet still linked into
        // the list, because unlinking is performed exclusively in the
        // QTimeEvt_tick_() function.

        // is the time event unlinked?
        if ((me->super.refCtr_ & QTE_IS_LINKED) == 0U) {
            // mark as linked
            me->super.refCtr_ |= QTE_IS_LINKED;

            // The time event is initially inserted into the separate
            // "freshly armed" list based on QTimeEvt_timeEvtHead_[tickRate].act.
            // Only later, inside the QTimeEvt_tick_() function, the "freshly
            // armed" list is appended to the main list of armed time events
            // based on QTimeEvt_timeEvtHead_[tickRate].next. Again, this is
            // to keep any changes to the main list exclusively inside the
            // QTimeEvt_tick_().
            me->next = (QTimeEvt *)QTimeEvt_timeEvtHead_[tickRate].act;
            QTimeEvt_timeEvtHead_[tickRate].act = me;
        }
    }
    else { // the time event was armed
        wasArmed = true;
    }
    me->ctr = nTicks; // re-load the tick counter (shift the phasing)

    QS_BEGIN_PRE_(QS_QF_TIMEEVT_REARM, qs_id)
        QS_TIME_PRE_();            // timestamp
        QS_OBJ_PRE_(me);           // this time event object
        QS_OBJ_PRE_(me->act);      // the target AO
        QS_TEC_PRE_(me->ctr);      // the # ticks
        QS_TEC_PRE_(me->interval); // the interval
        QS_2U8_PRE_(tickRate, (wasArmed ? 1U : 0U));
    QS_END_PRE_()

    QF_MEM_APP();
    QF_CRIT_EXIT();

    return wasArmed;
}

//${QF::QTimeEvt::wasDisarmed} ...............................................
//! @public @memberof QTimeEvt
bool QTimeEvt_wasDisarmed(QTimeEvt * const me) {
    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QF_MEM_SYS();

    uint8_t const wasDisarmed = (me->super.refCtr_ & QTE_WAS_DISARMED);
    me->super.refCtr_ |= QTE_WAS_DISARMED; // mark as disarmed

    QF_MEM_APP();
    QF_CRIT_EXIT();

    return wasDisarmed != 0U;
}

//${QF::QTimeEvt::currCtr} ...................................................
//! @public @memberof QTimeEvt
QTimeEvtCtr QTimeEvt_currCtr(QTimeEvt const * const me) {
    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QTimeEvtCtr const ctr = me->ctr;
    QF_CRIT_EXIT();

    return ctr;
}

//${QF::QTimeEvt::tick_} .....................................................
//! @static @private @memberof QTimeEvt
void QTimeEvt_tick_(
    uint_fast8_t const tickRate,
    void const * const sender)
{
    #ifndef Q_SPY
    Q_UNUSED_PAR(sender);
    #endif

    QTimeEvt *prev = &QTimeEvt_timeEvtHead_[tickRate];

    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QF_MEM_SYS();

    QS_BEGIN_PRE_(QS_QF_TICK, 0U)
        ++prev->ctr;
        QS_TEC_PRE_(prev->ctr);   // tick ctr
        QS_U8_PRE_(tickRate);     // tick rate
    QS_END_PRE_()

    // scan the linked-list of time events at this rate...
    for (;;) {
        QTimeEvt *t = prev->next; // advance down the time evt. list

        if (t == (QTimeEvt *)0) { // end of the list?

            // any new time events armed since the last QTimeEvt_tick_()?
            if (QTimeEvt_timeEvtHead_[tickRate].act != (void *)0) {

                // sanity check
                Q_ASSERT_INCRIT(110, prev != (QTimeEvt *)0);
                prev->next = (QTimeEvt *)QTimeEvt_timeEvtHead_[tickRate].act;
                QTimeEvt_timeEvtHead_[tickRate].act = (void *)0;
                t = prev->next; // switch to the new list
            }
            else {
                break; // all currently armed time evts. processed
            }
        }

        if (t->ctr == 0U) { // time event scheduled for removal?
            prev->next = t->next;
            // mark time event 't' as NOT linked
            t->super.refCtr_ &= (uint8_t)(~QTE_IS_LINKED & 0xFFU);
            // do NOT advance the prev pointer
            QF_MEM_APP();
            QF_CRIT_EXIT(); // exit crit. section to reduce latency

            // NOTE: prevent merging critical sections
            // In some QF ports the critical section exit takes effect only
            // on the next machine instruction. If the next instruction is
            // another entry to a critical section, the critical section
            // might not be really exited, but rather the two adjacent
            // critical sections would be MERGED. The QF_CRIT_EXIT_NOP()
            // macro contains minimal code required to prevent such merging
            // of critical sections in QF ports, in which it can occur.
            QF_CRIT_EXIT_NOP();
        }
        else {
            --t->ctr;

            if (t->ctr == 0U) { // is time event about to expire?
                QActive * const act = (QActive *)t->act;

                if (t->interval != 0U) { // periodic time evt?
                    t->ctr = t->interval; // rearm the time event
                    prev = t; // advance to this time event
                }
                else { // one-shot time event: automatically disarm
                    prev->next = t->next;

                    // mark time event 't' as NOT linked
                    t->super.refCtr_ &= (uint8_t)(~QTE_IS_LINKED & 0xFFU);
                    // do NOT advance the prev pointer

                    QS_BEGIN_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, act->prio)
                        QS_OBJ_PRE_(t);        // this time event object
                        QS_OBJ_PRE_(act);      // the target AO
                        QS_U8_PRE_(tickRate);  // tick rate
                    QS_END_PRE_()
                }

                QS_BEGIN_PRE_(QS_QF_TIMEEVT_POST, act->prio)
                    QS_TIME_PRE_();            // timestamp
                    QS_OBJ_PRE_(t);            // the time event object
                    QS_SIG_PRE_(t->super.sig); // signal of this time event
                    QS_OBJ_PRE_(act);          // the target AO
                    QS_U8_PRE_(tickRate);      // tick rate
                QS_END_PRE_()

    #ifdef QXK_H_
                if (t->super.sig < Q_USER_SIG) {
                    QXThread_timeout_(act);
                }
                else {
                    QF_MEM_APP();
                    QF_CRIT_EXIT(); // exit crit. section before posting

                    // QACTIVE_POST() asserts if the queue overflows
                    QACTIVE_POST(act, &t->super, sender);
                }
    #else
                QF_MEM_APP();
                QF_CRIT_EXIT(); // exit crit. section before posting

                // QACTIVE_POST() asserts if the queue overflows
                QACTIVE_POST(act, &t->super, sender);
    #endif
            }
            else {
                prev = t; // advance to this time event

                QF_MEM_APP();
                QF_CRIT_EXIT(); // exit crit. section to reduce latency

                // prevent merging critical sections, see NOTE above
                QF_CRIT_EXIT_NOP();
            }
        }
        QF_CRIT_ENTRY(); // re-enter crit. section to continue the loop
        QF_MEM_SYS();
    }

    QF_MEM_APP();
    QF_CRIT_EXIT();
}

//${QF::QTimeEvt::noActive} ..................................................
//! @static @public @memberof QTimeEvt
bool QTimeEvt_noActive(uint_fast8_t const tickRate) {
    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    Q_REQUIRE_INCRIT(800, tickRate < QF_MAX_TICK_RATE);
    QF_CRIT_EXIT();

    bool inactive;
    if (QTimeEvt_timeEvtHead_[tickRate].next != (QTimeEvt *)0) {
        inactive = false;
    }
    else if ((QTimeEvt_timeEvtHead_[tickRate].act != (void *)0)) {
        inactive = false;
    }
    else {
        inactive = true;
    }
    return inactive;
}
//$enddef${QF::QTimeEvt} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
