Timer zum Erkennen von Timeouts



Timeouts erkennen ist ein häufiger Einsatzzweck von Timern

In diesem Beispiel simulieren wir einen externen Trigger mit einem Timer. Der Trigger soll 10x alle 3 Sekunden zuschlagen. Dann setzt er aus, und wir wollen dieses Aussetzen mit einem weiteren Timer auf die Spur kommen. Zuerst entwerfen wir die entsprechenden Records in XML.

<record id="748131bf-f554-4302-a521-385948a2802a" name="TIMER_TARGET_015">

    <description>
        A named Timeout Timer for Target015.
    </description>
    
</record>

<record id="d3470f68-b679-4624-8da2-6bc933db3742" name="TRIGGER_TARGET_015">

    <description>
        An external trigger for Target015.
    </description>
    
</record>

Wir starten den Timer für die Timeout-Erkennung zeitgleich mit dem Trigger-Timer. Der Timeout-Timer hat einen längeren Timeout als der Trigger, daher wird der Trigger immer vor dem Timeout-Timer zuschlagen. Wenn der Trigger aufgefangen wird, wird der Timeout-Timer gelöscht. Nach 10-maligem Starten "vergessen" wir das Starten des Triggers. Der letzte Timeout-Timer läuft noch und wir können ihn erstmalig auffangen. Und wir wissen: wenn das geschieht, hat der Trigger pausiert. Aufgabe erfüllt.

final class CTarget015 extends CTarget
{
    // the trigger
    private static final long TRIGGER = 3000L;
    private static final int MAX_NUMBER = 10;
 
    // the timer
    private static final long TIMEOUT = 5000L;

    private int mTriggerNumber = 0;
    private long mTimerHandle = ITimerManager.INVALID;

    CTarget015()
    {
        addMessageHandler(CRecordStartTarget.ID, new IMessageHandler()
        {
            @Override
            public boolean handleMessage(final CEnvelope aEnvelope,
                                         final CRecord aRecord) throws Exception
            {
                startTrigger();

                aEnvelope.setResult(null);
                return true;
            }

        });

        //
        // a (simulated) external trigger comes 10x all 3 seconds
        //
        addMessageHandler(CRecordTriggerTarget015.ID, new IMessageHandler()
        {
            @Override
            public boolean handleMessage(final CEnvelope aEnvelope,
                                         final CRecord aRecord) throws Exception
            {
                // all okay, start trigger again
                mTriggerNumber++;
                if (mTriggerNumber < MAX_NUMBER)
                {
                    startTrigger();
                }

                aEnvelope.setResult(null);
                return true;
            }

        });

        //
        // catch our timeout timer message
        //
        addMessageHandler(CRecordTimerTarget015.ID, new IMessageHandler()
        {
            @Override
            public boolean handleMessage(final CEnvelope aEnvelope,
                                         final CRecord aRecord) throws Exception
            {
                // timer hit, that means a timeout because the trigger didn't hit in time

                // reset timer handle, because no timer is running now
                mTimerHandle = ITimerManager.INVALID;

                CConstants.LOG.debug("Uh, timeout! TriggerNumber is {}", mTriggerNumber);
                // reset trigger number and start trigger series again
                mTriggerNumber = 0;
                startTrigger();

                aEnvelope.setResult(null);
                return true;
            }

        });
    }

    protected void startTimeoutTimer() throws CException
    {
        // if no timer is running
        if (mTimerHandle == ITimerManager.INVALID)
        {
            // start the timer
            mTimerHandle = getTimerManager().startTimer(CRecordTimerTarget015.ID, getAddress(), TIMEOUT, false, null);
        }
        else
        {
            // timer is running, so retrigger it (reset the time for this timer)
            getTimerManager().retrigger(mTimerHandle);
        }
    }

    protected void startTrigger() throws CException
    {
        // We simulate an external trigger message with a Timer. All 3 seconds the trigger hits.
        // After 10 hits the trigger fails (stops), and we recognize this with a timer 5 seconds after the last trigger
        // or 2 seconds after the trigger should hit.
        getTimerManager().startTimer(CRecordTriggerTarget015.ID, getAddress(), TRIGGER, false, null);

        // start Timeout with 5 seconds
        startTimeoutTimer();
    }
}

Das Beispiel zeigt auch schön die Verwendung des Timer-Handles. Wir bekommen es beim Anlegen der Timer. Wenn es gespeichert wird, kann es zum Stoppen oder zum Zurücksetzen (Retrigger) des Timers verwendet werden. Das nutzen wir in der Methode startTimeoutTimer(). Wenn der Timer noch läuft (TimerHandle ungleich INVALID), wird die Zeit des laufenden Timers zurückgesetzt. Andernfalls wird ein neuer Timer gestartet.

Sequence Monitor

Hier ein Output des Sequence Monitors. Unser Target ist hier das Target "29" (die ID wurde vom System vergeben) im Namespace "EXAMPLES" im NODE "dd6503". Wir bekommen Nachrichten vom TimerManager im Namespace "SYSTEM". Nach 10 Triggern schlägt immer wieder ein Timeout durch.

Diese Beispiele findest du im Projekt D1ExamplesNode001 im package de.softdevel.d1.examples.node001.example002.