//$file${src::qf::qf_actq.c} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
//
// Model: qpc.qm
// File:  ${src::qf::qf_actq.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_actq.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_actq")

//============================================================================
//$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::QActive::post_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

//${QF::QActive::post_} ......................................................
//! @private @memberof QActive
bool QActive_post_(QActive * const me,
    QEvt const * const e,
    uint_fast16_t const margin,
    void const * const sender)
{
    #ifndef Q_SPY
    Q_UNUSED_PAR(sender);
    #endif

    #ifdef Q_UTEST // test?
    #if Q_UTEST != 0 // testing QP-stub?
    if (me->super.temp.fun == Q_STATE_CAST(0)) { // QActiveDummy?
        return QActiveDummy_fakePost_(me, e, margin, sender);
    }
    #endif
    #endif

    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QF_MEM_SYS();

    Q_REQUIRE_INCRIT(102, QEvt_verify_(e));

    QEQueueCtr nFree = me->eQueue.nFree; // get volatile into temporary

    // test-probe#1 for faking queue overflow
    QS_TEST_PROBE_DEF(&QActive_post_)
    QS_TEST_PROBE_ID(1,
        nFree = 0U;
    )

    bool status;
    if (margin == QF_NO_MARGIN) {
        if (nFree > 0U) {
            status = true; // can post
        }
        else {
            status = false; // cannot post
            Q_ERROR_INCRIT(190); // must be able to post the event
        }
    }
    else if (nFree > (QEQueueCtr)margin) {
        status = true; // can post
    }
    else {
        status = false; // cannot post, but don't assert
    }

    // is it a mutable event?
    if (QEvt_getPoolId_(e) != 0U) {
        QEvt_refCtr_inc_(e); // increment the reference counter
    }

    if (status) { // can post the event?

        --nFree; // one free entry just used up
        me->eQueue.nFree = nFree; // update the original
        if (me->eQueue.nMin > nFree) {
            me->eQueue.nMin = nFree; // increase minimum so far
        }

        QS_BEGIN_PRE_(QS_QF_ACTIVE_POST, me->prio)
            QS_TIME_PRE_();       // timestamp
            QS_OBJ_PRE_(sender);  // the sender object
            QS_SIG_PRE_(e->sig);  // the signal of the event
            QS_OBJ_PRE_(me);      // this active object (recipient)
            QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr
            QS_EQC_PRE_(nFree);   // # free entries
            QS_EQC_PRE_(me->eQueue.nMin); // min # free entries
        QS_END_PRE_()

    #ifdef Q_UTEST
        // callback to examine the posted event under the same conditions
        // as producing the #QS_QF_ACTIVE_POST trace record, which are:
        // the local filter for this AO ('me->prio') is set
        if (QS_LOC_CHECK_(me->prio)) {
            QS_onTestPost(sender, me, e, status);
        }
    #endif

        if (me->eQueue.frontEvt == (QEvt *)0) { // empty queue?
            me->eQueue.frontEvt = e; // deliver event directly

    #ifdef QXK_H_
            if (me->super.state.act == Q_ACTION_CAST(0)) { // eXtended?
                QXTHREAD_EQUEUE_SIGNAL_(me); // signal the event queue
            }
            else {
                QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue
            }
    #else
            QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue
    #endif
        }
        // queue is not empty, insert event into the ring-buffer
        else {
            // insert event into the ring buffer (FIFO)
            me->eQueue.ring[me->eQueue.head] = e;

            if (me->eQueue.head == 0U) { // need to wrap head?
                me->eQueue.head = me->eQueue.end; // wrap around
            }
            --me->eQueue.head; // advance the head (counter clockwise)
        }

        QF_MEM_APP();
        QF_CRIT_EXIT();
    }
    else { // cannot post the event

        QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_ATTEMPT, me->prio)
            QS_TIME_PRE_();       // timestamp
            QS_OBJ_PRE_(sender);  // the sender object
            QS_SIG_PRE_(e->sig);  // the signal of the event
            QS_OBJ_PRE_(me);      // this active object (recipient)
            QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr
            QS_EQC_PRE_(nFree);   // # free entries
            QS_EQC_PRE_(margin);  // margin requested
        QS_END_PRE_()

    #ifdef Q_UTEST
        // callback to examine the posted event under the same conditions
        // as producing the #QS_QF_ACTIVE_POST trace record, which are:
        // the local filter for this AO ('me->prio') is set
        if (QS_LOC_CHECK_(me->prio)) {
            QS_onTestPost(sender, me, e, status);
        }
    #endif

        QF_MEM_APP();
        QF_CRIT_EXIT();

    #if (QF_MAX_EPOOL > 0U)
        QF_gc(e); // recycle the event to avoid a leak
    #endif
    }

    return status;
}
//$enddef${QF::QActive::post_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//$define${QF::QActive::postLIFO_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

//${QF::QActive::postLIFO_} ..................................................
//! @private @memberof QActive
void QActive_postLIFO_(QActive * const me,
    QEvt const * const e)
{
    #ifdef Q_UTEST // test?
    #if Q_UTEST != 0 // testing QP-stub?
    if (me->super.temp.fun == Q_STATE_CAST(0)) { // QActiveDummy?
        QActiveDummy_fakePostLIFO_(me, e);
        return;
    }
    #endif
    #endif

    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QF_MEM_SYS();

    Q_REQUIRE_INCRIT(202, QEvt_verify_(e));

    #ifdef QXK_H_
    Q_REQUIRE_INCRIT(200, me->super.state.act != Q_ACTION_CAST(0));
    #endif

    QEQueueCtr nFree = me->eQueue.nFree; // get volatile into temporary

    // test-probe#1 for faking queue overflow
    QS_TEST_PROBE_DEF(&QActive_postLIFO_)
    QS_TEST_PROBE_ID(1,
        nFree = 0U;
    )

    Q_REQUIRE_INCRIT(201, nFree != 0U);

    if (QEvt_getPoolId_(e) != 0U) { // is it a mutable event?
        QEvt_refCtr_inc_(e); // increment the reference counter
    }

    --nFree; // one free entry just used up
    me->eQueue.nFree = nFree; // update the original
    if (me->eQueue.nMin > nFree) {
        me->eQueue.nMin = nFree; // update minimum so far
    }

    QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_LIFO, me->prio)
        QS_TIME_PRE_();       // timestamp
        QS_SIG_PRE_(e->sig);  // the signal of this event
        QS_OBJ_PRE_(me);      // this active object
        QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_);// poolId & refCtr
        QS_EQC_PRE_(nFree);   // # free entries
        QS_EQC_PRE_(me->eQueue.nMin); // min # free entries
    QS_END_PRE_()

    #ifdef Q_UTEST
    // callback to examine the posted event under the same conditions
    // as producing the #QS_QF_ACTIVE_POST trace record, which are:
    // the local filter for this AO ('me->prio') is set
    if (QS_LOC_CHECK_(me->prio)) {
        QS_onTestPost((QActive *)0, me, e, true);
    }
    #endif

    QEvt const * const frontEvt = me->eQueue.frontEvt;
    me->eQueue.frontEvt = e; // deliver the event directly to the front

    if (frontEvt == (QEvt *)0) { // was the queue empty?
        QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue
    }
    else { // queue was not empty, leave the event in the ring-buffer
        ++me->eQueue.tail;
        if (me->eQueue.tail == me->eQueue.end) { // need to wrap the tail?
            me->eQueue.tail = 0U; // wrap around
        }

        me->eQueue.ring[me->eQueue.tail] = frontEvt;
    }

    QF_MEM_APP();
    QF_CRIT_EXIT();
}
//$enddef${QF::QActive::postLIFO_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//$define${QF::QActive::get_} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

//${QF::QActive::get_} .......................................................
//! @private @memberof QActive
QEvt const * QActive_get_(QActive * const me) {
    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QF_MEM_SYS();

    QACTIVE_EQUEUE_WAIT_(me); // wait for event to arrive directly

    // always remove event from the front
    QEvt const * const e = me->eQueue.frontEvt;
    QEQueueCtr const nFree = me->eQueue.nFree + 1U; // get volatile into tmp
    me->eQueue.nFree = nFree; // update the # free

    if (nFree <= me->eQueue.end) { // any events in the ring buffer?
        // remove event from the tail
        me->eQueue.frontEvt = me->eQueue.ring[me->eQueue.tail];
        if (me->eQueue.tail == 0U) { // need to wrap the tail?
            me->eQueue.tail = me->eQueue.end; // wrap around
        }
        --me->eQueue.tail;

        QS_BEGIN_PRE_(QS_QF_ACTIVE_GET, me->prio)
            QS_TIME_PRE_();       // timestamp
            QS_SIG_PRE_(e->sig);  // the signal of this event
            QS_OBJ_PRE_(me);      // this active object
            QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr
            QS_EQC_PRE_(nFree);   // # free entries
        QS_END_PRE_()
    }
    else {
        me->eQueue.frontEvt = (QEvt *)0; // queue becomes empty

        // all entries in the queue must be free (+1 for fronEvt)
        Q_ASSERT_INCRIT(310, nFree == (me->eQueue.end + 1U));

        QS_BEGIN_PRE_(QS_QF_ACTIVE_GET_LAST, me->prio)
            QS_TIME_PRE_();       // timestamp
            QS_SIG_PRE_(e->sig);  // the signal of this event
            QS_OBJ_PRE_(me);      // this active object
            QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr
        QS_END_PRE_()
    }

    QF_MEM_APP();
    QF_CRIT_EXIT();

    return e;
}
//$enddef${QF::QActive::get_} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

//$define${QF::QF-base::getQueueMin} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

//${QF::QF-base::getQueueMin} ................................................
//! @static @public @memberof QF
uint_fast16_t QF_getQueueMin(uint_fast8_t const prio) {
    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    Q_REQUIRE_INCRIT(400, (prio <= QF_MAX_ACTIVE)
                      && (QActive_registry_[prio] != (QActive *)0));
    uint_fast16_t const min =
         (uint_fast16_t)QActive_registry_[prio]->eQueue.nMin;
    QF_CRIT_EXIT();

    return min;
}
//$enddef${QF::QF-base::getQueueMin} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//$define${QF::QTicker} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

//${QF::QTicker} .............................................................

//${QF::QTicker::ctor} .......................................................
//! @public @memberof QTicker
void QTicker_ctor(QTicker * const me,
    uint_fast8_t const tickRate)
{
    QActive_ctor(&me->super, Q_STATE_CAST(0)); // superclass' ctor

    static struct QAsmVtable const vtable = { // QTicker virtual table
        &QTicker_init_,
        &QTicker_dispatch_
    #ifdef Q_SPY
        ,&QHsm_getStateHandler_
    #endif
    };
    me->super.super.vptr = &vtable; // hook the vptr

    // reuse eQueue.head for tick-rate
    me->super.eQueue.head = (QEQueueCtr)tickRate;
}

//${QF::QTicker::init_} ......................................................
//! @private @memberof QTicker
void QTicker_init_(
    QAsm * const me,
    void const * const par,
    uint_fast8_t const qs_id)
{
    Q_UNUSED_PAR(me);
    Q_UNUSED_PAR(par);
    Q_UNUSED_PAR(qs_id);

    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QF_MEM_SYS();

    QACTIVE_CAST_(me)->eQueue.tail = 0U;

    QF_MEM_APP();
    QF_CRIT_EXIT();
}

//${QF::QTicker::dispatch_} ..................................................
//! @private @memberof QTicker
void QTicker_dispatch_(
    QAsm * const me,
    QEvt const * const e,
    uint_fast8_t const qs_id)
{
    Q_UNUSED_PAR(e);
    Q_UNUSED_PAR(qs_id);

    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QF_MEM_SYS();

    QEQueueCtr nTicks = QACTIVE_CAST_(me)->eQueue.tail; // save # of ticks
    QACTIVE_CAST_(me)->eQueue.tail = 0U; // clear # ticks

    QF_MEM_APP();
    QF_CRIT_EXIT();

    for (; nTicks > 0U; --nTicks) {
        QTimeEvt_tick_((uint_fast8_t)QACTIVE_CAST_(me)->eQueue.head, me);
    }
}

//${QF::QTicker::trig_} ......................................................
//! @private @memberof QTicker
void QTicker_trig_(
    QActive * const me,
    void const * const sender)
{
    #ifndef Q_SPY
    Q_UNUSED_PAR(sender);
    #endif

    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QF_MEM_SYS();

    if (me->eQueue.frontEvt == (QEvt *)0) {

        static QEvt const tickEvt = QEVT_INITIALIZER(0);
        me->eQueue.frontEvt = &tickEvt; // deliver event directly
        --me->eQueue.nFree; // one less free event

        QACTIVE_EQUEUE_SIGNAL_(me); // signal the event queue
    }

    ++me->eQueue.tail; // account for one more tick event

    QS_BEGIN_PRE_(QS_QF_ACTIVE_POST, me->prio)
        QS_TIME_PRE_();      // timestamp
        QS_OBJ_PRE_(sender); // the sender object
        QS_SIG_PRE_(0U);     // the signal of the event
        QS_OBJ_PRE_(me);     // this active object
        QS_2U8_PRE_(0U, 0U); // poolId & refCtr
        QS_EQC_PRE_(0U);     // # free entries
        QS_EQC_PRE_(0U);     // min # free entries
    QS_END_PRE_()

    QF_MEM_APP();
    QF_CRIT_EXIT();
}
//$enddef${QF::QTicker} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
