| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // Copyright (c) 2000-2019 Ericsson Telecom AB // |
| // // |
| // All rights reserved. This program and the accompanying materials // |
| // are made available under the terms of the Eclipse Public License v2.0 // |
| // which accompanies this distribution, and is available at // |
| // https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| /////////////////////////////////////////////////////////// |
| // Module: EPTF_CLL_RBTScheduler_Functions |
| // |
| // Purpose: |
| // This module contains the event queue handling functions of the |
| // Red-Black tree based scheduler. |
| // |
| // Module Parameters: |
| // tsp_EPTF_Scheduler_enableRounding - Enables/disables rounding the desired time of the scheduled event. |
| // |
| // Module depends on: |
| // <EPTF_CLL_Common_Definitions> |
| // <EPTF_CLL_Common_Functions> |
| // <EPTF_CLL_Scheduler_Definitions> |
| // <EPTF_CLL_Scheduler_Functions.ttcnin> |
| // <EPTF_CLL_RBTScheduler_Definitions> |
| // <EPTF_CLL_RBT_Functions> |
| // <EPTF_CLL_Base_Functions> |
| // <EPTF_CLL_Logging_Definitions> |
| // <EPTF_CLL_Logging_Functions> |
| // |
| // Current Owner: |
| // Rita Kovacs (ERITKOV), Gabor Ziegler (egbozie), Gabor Tatarka (egbotat), Jozsef Gyurusi (ethjgi) |
| // |
| // Last Review Date: |
| // 2007-06-19 |
| // |
| // Detailed Comments: |
| // This module contains generic functions for Red-blak tree based event scheduling. |
| // These functions handle the <EPTF_EventQueue> database created from <EPTF_RBT>. |
| // |
| // This module uses the RBT scheduler functions in this file to define |
| // the functions that run on the <EPTF_Scheduler_CT> generic scheduler component. |
| // |
| // - To initialize the scheduler, call <f_EPTF_Scheduler_init_CT>. |
| // |
| // - To register an event for the queue, call <f_EPTF_SchedulerComp_scheduleAction>. |
| // |
| // - To remove a registered event from the queue <f_EPTF_SchedulerComp_CancelEvent> |
| // |
| // - Upon the action timer timeout the user defined call-back function |
| // <EPTF_Scheduler_ActionHandler> is called with the action to be processed. |
| // |
| // - To refresh the current time accessible at <v_EPTF_snapshotTime> call <f_EPTF_SchedulerComp_refreshSnapshotTime>. |
| // |
| // - To access the current snapshotTime call <f_EPTF_SchedulerComp_snapshotTime>. |
| // |
| // Public functions: |
| // <f_EPTF_Scheduler_init_CT> |
| // <f_EPTF_SchedulerComp_scheduleAction> |
| // <f_EPTF_SchedulerComp_CancelEvent> |
| // <f_EPTF_SchedulerComp_refreshSnapshotTime> |
| // <f_EPTF_SchedulerComp_snapshotTime> |
| // <f_EPTF_SchedulerComp_eventIsValid> |
| // <f_EPTF_SchedulerComp_eventIsInvalid> |
| // <f_EPTF_Scheduler_enableLoadMeasurement> |
| // <f_EPTF_Scheduler_setLoadMeasurementPeriod> |
| // <f_EPTF_Scheduler_setMaxLoadThreshold> |
| // <f_EPTF_Scheduler_getLoad> |
| // <f_EPTF_Scheduler_getLoadMeasurementInterval> |
| // |
| /////////////////////////////////////////////////////////// |
| module EPTF_CLL_RBTScheduler_Functions { |
| |
| import from EPTF_CLL_Scheduler_Definitions all; |
| import from EPTF_CLL_RBTScheduler_Definitions all; |
| import from EPTF_CLL_RBT_Functions all; |
| import from EPTF_CLL_Common_Definitions all; |
| import from EPTF_CLL_Common_Functions all; |
| import from EPTF_CLL_Base_Functions all; |
| import from EPTF_CLL_Logging_Definitions all; |
| import from EPTF_CLL_Logging_Functions all; |
| |
| modulepar boolean tsp_EPTF_Scheduler_enableRounding := true; |
| |
| modulepar boolean tsp_EPTF_Scheduler_enableLoadMeasurement := true; |
| |
| /////////////////////////////////////////////////////////// |
| // Group: Scheduler_GenericFunctions |
| // |
| // Purpose: |
| // Group of functions without "runs on EPTF_Scheduler_CT" clause |
| // |
| // Detailed comments: |
| // Alternative version of these functions are in the group <Scheduler_RunsOnFunctions> |
| /////////////////////////////////////////////////////////// |
| group Scheduler_GenericFunctions { |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_setSchedulerAction |
| // |
| // Purpose: |
| // function to register a new action to the action queue, |
| // the when parameter of the action will be rounded. |
| // |
| // Parameters: |
| // pl_NextEventQueue - *inout* <EPTF_EventQueue> - the event queue |
| // |
| // pl_scheduledAction - *in* <EPTF_ScheduledAction> - action to be scheduled |
| // |
| // pl_eventIndex - *out* *integer* - returns the position within the queue |
| // |
| // Errors: |
| // - |
| // |
| // Detailed description: |
| // it just passess the call to <f_EPTF_Scheduler_setSchedulerActionRoundable> |
| // |
| // Return Value: |
| // boolean - false if operation failed |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_setSchedulerAction( |
| inout EPTF_EventQueue pl_NextEventQueue, |
| in EPTF_ScheduledAction pl_scheduledAction, |
| out integer pl_eventIndex |
| ) |
| return boolean |
| { |
| if (c_EPTF_Common_debugSwitch and tsp_debug_EPTF_SchedulerFunctions and tsp_debugVerbose_EPTF_SchedulerFunctions) |
| { |
| f_EPTF_Common_user(log2str("f_SetSchedulerAction, params: actionId:", |
| pl_scheduledAction.actionId, ", when:",pl_scheduledAction.when)); |
| f_EPTF_Common_user("Passing the call along to f_EPTF_Scheduler_setSchedulerActionRoundable with rounding==true..."); |
| } |
| |
| return f_EPTF_Scheduler_setSchedulerActionRoundable( |
| pl_NextEventQueue, |
| pl_scheduledAction, |
| tsp_EPTF_Scheduler_enableRounding, |
| pl_eventIndex); |
| |
| }//SetSchedulerAction |
| |
| } //group Scheduler_GenericFunctions |
| |
| /////////////////////////////////////////////////////////// |
| // Group: Scheduler_RunsOnFunctions |
| // |
| // Purpose: |
| // Group of functions with "runs on EPTF_Scheduler_CT" clause. |
| // |
| // Detailed comments: |
| // Alternative version of these functions are in the groups <Scheduler_GenericFunctions>, |
| // <FBQScheduler_GenericFunctions> or <RBTScheduler_GenericFunctions> |
| // |
| /////////////////////////////////////////////////////////// |
| group Scheduler_RunsOnFunctions { |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_initEventQueue |
| // |
| // Purpose: |
| // OBSOLETE, use <f_EPTF_Scheduler_init_CT> instead |
| // function to init the eventQueue of <EPTF_Scheduler_CT> |
| // |
| // Parameters: |
| // - |
| // |
| // Return Value: |
| // - |
| // |
| /////////////////////////////////////////////////////////// |
| /*public*/ function f_EPTF_SchedulerComp_initEventQueue() |
| runs on EPTF_Scheduler_CT { |
| f_EPTF_Scheduler_initEventQueue(v_EPTF_eventQueue); |
| } |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_getNewTimerVal |
| // |
| // Purpose: |
| // function to get relative time of next event on <EPTF_Scheduler_CT> |
| // |
| // Parameters: |
| // - |
| // |
| // Return Value: |
| // float |
| // |
| // Errors: |
| // - Calling the function when v_EPTF_eventQueue is empty makes no sense, |
| // therefore it is an error, which will cause the execution to stop |
| // |
| // Detailed Comments: |
| // This function returns a time value (float) that is the difference between |
| // the |
| // - current simulation time (obtained as T_EPTF_componentClock.read()) and |
| // - the ideal schedule (absolute timepoint) of the event at the head of the |
| // queue. |
| // |
| // If the return value is |
| // - positive, then the event shall happen in the future, i.e., use this value |
| // as the timeout value for your scheduler timer |
| // - non-positive (zero, or negative) then event is already "due", that is, |
| // the scheduler is late and the event shall be executed immediately. |
| // |
| // Use this functions as follows: |
| // > |
| // >var float currentTime, duration; |
| // > alt{ |
| // > [] schedulerTimer.timeout { |
| // > currentTime := clockTimer.read(); |
| // > label processevents; |
| // > //... |
| // > //... process the head event of the event queue.... |
| // > //... |
| // > if (f_EPTF_SchedulerComp_eventQueueIsNotEmpty()) { |
| // > duration := f_EPTF_SchedulerComp_getNewTimerVal(); |
| // > if (duration <= 0.0) {f_processevents();} |
| // > else {T_EPTF_nextEvent.start(duration)} |
| // > } |
| // > repeat; |
| // > } |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_SchedulerComp_getNewTimerVal() |
| runs on EPTF_Scheduler_CT return float { |
| return f_EPTF_Scheduler_getNewTimerVal(v_EPTF_eventQueue, T_EPTF_componentClock); |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_refreshEventTimer |
| // |
| // Purpose: |
| // function update T_EPTF_nextEvent timer to the next event |
| // |
| // Parameters: |
| // - |
| // |
| // Return Value: |
| // - |
| // |
| // Detailed Comments: |
| // This function sets the event timer to the next scheduled event time. |
| // If necessary (e.g., an event has been inserted before the head of the queue), |
| // then it restarts the timer. |
| // This function is automatically called in <as_EPTF_SchedulerComp_ActionHandler>, |
| // <f_EPTF_SchedulerComp_scheduleAction> and <f_EPTF_SchedulerComp_CancelEvent>. |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_SchedulerComp_refreshEventTimer() |
| runs on EPTF_Scheduler_CT { |
| f_EPTF_Scheduler_refreshEventTimer(v_EPTF_eventQueue, T_EPTF_componentClock, T_EPTF_nextEvent, v_EPTF_nextEventIsActive) |
| } |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_refreshSnapshotTime |
| // |
| // Purpose: |
| // function to update v_EPTF_snapshotTime |
| // |
| // Parameters: |
| // - |
| // |
| // Return Value: |
| // - |
| // |
| // Detailed Comments: |
| // This function reads the T_EPTF_componentClock and stores the |
| // time into v_EPTF_snapshotTime. Proposed usage is to call it |
| // in the begining of any altstep where v_EPTF_snapshotTime is |
| // used e.g. to schedule a new event. |
| // |
| /////////////////////////////////////////////////////////// |
| public function f_EPTF_SchedulerComp_refreshSnapshotTime() |
| runs on EPTF_Scheduler_CT { |
| v_EPTF_snapshotTime := T_EPTF_componentClock.read; |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_snapshotTime |
| // |
| // Purpose: |
| // function to get the value of v_EPTF_snapshotTime |
| // |
| // Parameters: |
| // - |
| // |
| // Return Value: |
| // float |
| // |
| // Detailed Comments: |
| // This function returns the current value of v_EPTF_snapshotTime. |
| // |
| /////////////////////////////////////////////////////////// |
| public function f_EPTF_SchedulerComp_snapshotTime() |
| runs on EPTF_Scheduler_CT return float { |
| return v_EPTF_snapshotTime; |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_schedulerIsNotLate |
| // |
| // Purpose: |
| // function to check whether the event queue is not empty, or is empty |
| // |
| // Parameters: |
| // - |
| // |
| // Return Value: |
| // boolean |
| // |
| // Errors: |
| // - Calling the function with an empty v_EPTF_eventQueue makes no sense, |
| // therefore it is an error, which will cause the execution to stop |
| // |
| // Detailed Comments: |
| // This function returns a true if the head event within the event queue |
| // is scheduled later than "T_EPTF_componentClock.read", otherwise returns false. |
| // See also <f_EPTF_Scheduler_getNewTimerVal> |
| // |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_SchedulerComp_schedulerIsNotLate() |
| runs on EPTF_Scheduler_CT return boolean { |
| return f_EPTF_Scheduler_schedulerIsNotLate(v_EPTF_eventQueue, T_EPTF_componentClock.read) |
| } // f_EPTF_SchedulerComp_schedulerIsNotLate |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_eventQueueIsNotEmpty |
| // |
| // Purpose: |
| // function to check whether the event queue is not empty, or is empty |
| // |
| // Parameters: |
| // - |
| // |
| // Return Value: |
| // boolean |
| // |
| // Errors: |
| // - (none) |
| // |
| // Detailed Comments: |
| // This function returns a true if the event queue is not empty, false otherwise. |
| // Use this function to decide whether calling <f_EPTF_SchedulerComp_getNewTimerVal> () |
| // makes sense, or not. |
| // |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_SchedulerComp_eventQueueIsNotEmpty() |
| runs on EPTF_Scheduler_CT return boolean { |
| return f_EPTF_Scheduler_eventQueueIsNotEmpty(v_EPTF_eventQueue); |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_eventQueueIsEmpty |
| // |
| // Purpose: |
| // function to check whether the event queue is empty, or not empty |
| // |
| // Parameters: |
| // - |
| // |
| // Return Value: |
| // boolean |
| // |
| // Errors: |
| // - (none) |
| // |
| // Detailed Comments: |
| // This function returns a true if the event queue is empty, false otherwise. |
| // |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_SchedulerComp_eventQueueIsEmpty() |
| runs on EPTF_Scheduler_CT return boolean { |
| return f_EPTF_Scheduler_eventQueueIsEmpty(v_EPTF_eventQueue); |
| } |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_eventIsValid |
| // |
| // Purpose: |
| // function to check whether an event is valid within the queue |
| // |
| // Parameters: |
| // pl_qidx - *in* *integer* - index of the element to be checked |
| // |
| // Return Value: |
| // boolean - true, if valid, false otherwise |
| // |
| // Errors: |
| // - |
| /////////////////////////////////////////////////////////// |
| public function f_EPTF_SchedulerComp_eventIsValid(in integer pl_qidx) |
| runs on EPTF_Scheduler_CT return boolean { |
| return f_EPTF_Scheduler_eventIsValid(v_EPTF_eventQueue, pl_qidx); |
| }//f_EPTF_Scheduler_eventIsValid |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_eventIsInvalid |
| // |
| // Purpose: |
| // function to check whether an event is valid within the queue |
| // |
| // Parameters: |
| // pl_qidx - *in* *integer* - index of the element to be checked |
| // |
| // Return Value: |
| // boolean - true, if invalid, false otherwise |
| // |
| // Errors: |
| // - |
| /////////////////////////////////////////////////////////// |
| public function f_EPTF_SchedulerComp_eventIsInvalid(in integer pl_qidx) |
| runs on EPTF_Scheduler_CT return boolean { |
| return not f_EPTF_Scheduler_eventIsValid(v_EPTF_eventQueue,pl_qidx); |
| }//f_EPTF_Scheduler_eventIsInvalid |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_setSchedulerAction |
| // |
| // Purpose: |
| // function to register a new action to the action queue, |
| // the when parameter of the action will be rounded. |
| // |
| // Parameters: |
| // pl_scheduledAction - *in* <EPTF_ScheduledAction> - action to be scheduled |
| // |
| // pl_eventIndex - *out* *integer* - returns the position within the queue |
| // |
| // Errors: |
| // - if the same action exists in the queue, and that is not in the busy queue |
| // then f_EPTF_SchedulerComp_setSchedulerAction fails |
| // |
| // Detailed description: |
| // it just passess the call to <f_EPTF_Scheduler_setSchedulerActionRoundable> |
| // |
| // Return Value: |
| // boolean - false if operation failed |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_SchedulerComp_setSchedulerAction( |
| in EPTF_ScheduledAction pl_scheduledAction, |
| out integer pl_eventIndex |
| ) |
| runs on EPTF_Scheduler_CT return boolean { |
| return f_EPTF_Scheduler_setSchedulerAction( |
| v_EPTF_eventQueue, |
| pl_scheduledAction, |
| pl_eventIndex |
| ) |
| }//SetSchedulerAction |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_setSchedulerActionRoundable |
| // |
| // Purpose: |
| // function to register a new action to the action queue, |
| // the rounding of the schedule is switchable |
| // |
| // Parameters: |
| // pl_scheduledAction - *in* <EPTF_ScheduledAction> - action to be scheduled |
| // |
| // pl_roundIt - *in boolean* - whether to round, or not the schedule |
| // |
| // pl_eventIndex - *out* *integer* - returns the position within the queue |
| // |
| // Errors: |
| // - if the same action exists in the queue, and that is not in the busy queue |
| // then f_EPTF_SchedulerComp_setSchedulerActionRoundable fails |
| // |
| // Return Value: |
| // boolean - false if operation failed |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_SchedulerComp_setSchedulerActionRoundable( |
| in EPTF_ScheduledAction pl_scheduledAction, |
| in boolean pl_roundIt, |
| out integer pl_eventIndex |
| ) |
| runs on EPTF_Scheduler_CT return boolean { |
| return f_EPTF_Scheduler_setSchedulerActionRoundable( |
| v_EPTF_eventQueue, |
| pl_scheduledAction, |
| pl_roundIt, |
| pl_eventIndex); |
| }//SetSchedulerActionRoundable |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_getBusyEventHeadIndex |
| // |
| // Purpose: |
| // function to get head index of busy queue |
| // |
| // Parameters: |
| // - |
| // |
| // Return Value: |
| // integer - found head index, -1 if empty |
| // |
| // Errors: |
| // - (none) |
| // |
| // Detailed Comments: |
| // - |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_SchedulerComp_getBusyEventHeadIndex() |
| runs on EPTF_Scheduler_CT return integer { |
| return f_EPTF_Scheduler_getBusyEventHeadIndex (v_EPTF_eventQueue); |
| } //f_EPTF_Scheduler_getBusyEventHeadIndex |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_CancelEvent |
| // |
| // Purpose: |
| // function to remove a valid event from the event queue |
| // |
| // Parameters: |
| // pl_qidx - *in* *integer* - index of the event to be removed |
| // |
| // Return Value: |
| // boolean - false if operation failed |
| // |
| // Errors: |
| // - If the event at index pl_qidx is not valid, |
| // then <f_EPTF_SchedulerComp_CancelEvent> fails |
| // |
| // |
| /////////////////////////////////////////////////////////// |
| public function f_EPTF_SchedulerComp_CancelEvent(in integer qidx) runs on EPTF_Scheduler_CT |
| return boolean |
| { |
| if ( c_EPTF_Common_debugSwitch and tsp_debug_EPTF_SchedulerFunctions) |
| {f_EPTF_Common_user(log2str("f_EPTF_SchedulerComp_CancelEvent:", qidx) );} |
| |
| |
| //check validity and emptiness of the event |
| if (not f_EPTF_SchedulerComp_eventIsValid(qidx)) |
| { |
| f_EPTF_Scheduler_warning(log2str("Error: f_EPTF_SchedulerComp_CancelEvent: clearing invalid_chain, or non-empty event at qidx:", qidx)); |
| return false; |
| } |
| //dequeue the event |
| f_EPTF_RBT_removeItem(v_EPTF_eventQueue.order,qidx); |
| //refresh the timer |
| f_EPTF_SchedulerComp_refreshEventTimer(); |
| |
| return true; |
| }// f_EPTF_SchedulerComp_CancelEvent |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_scheduleAction |
| // |
| // Purpose: |
| // function to register a new action to the action queue |
| // |
| // Parameters: |
| // pl_when - *in* *float* - the time of the action when it should be processed |
| // |
| // pl_actionHandler - *in* <EPTF_Scheduler_ActionHandler> - the handler function that will be called to handle the action |
| // |
| // pl_action - *in* <EPTF_ActionId> - action parameters passed to the action handler when the event is processed |
| // |
| // pl_eventIndex - *out* *integer* - returns the position within the queue |
| // |
| // pl_roundIt - *in boolean* - whether to round, or not the schedule time |
| // (default: true) |
| // |
| // pl_dteHandler - *in* <EPTF_Scheduler_DTE_Handler> - the handler function that will be called when DTE happens |
| // when executing pl_actionHandler |
| // |
| // |
| // Errors: |
| // - |
| // |
| // Detailed description: |
| // It just passess the call to <f_EPTF_Scheduler_setSchedulerAction> |
| // The pl_when parameter of the action will be rounded. |
| // |
| // Return Value: |
| // boolean - false if operation failed |
| // |
| /////////////////////////////////////////////////////////// |
| public function f_EPTF_SchedulerComp_scheduleAction( |
| in float pl_when, |
| in EPTF_Scheduler_ActionHandler pl_actionHandler, |
| in EPTF_ActionId pl_action, |
| out integer pl_eventIndex, |
| in boolean pl_roundIt := tsp_EPTF_Scheduler_enableRounding, |
| in EPTF_Scheduler_DTE_Handler pl_dteHandler := null |
| ) |
| runs on EPTF_Scheduler_CT return boolean |
| { |
| if ( c_EPTF_Common_debugSwitch and tsp_debug_EPTF_SchedulerFunctions) |
| {f_EPTF_Common_user(log2str("f_EPTF_SchedulerComp_scheduleAction, params: pl_a:",pl_action, ", pl_t:",pl_when));} |
| |
| if (not tsp_EPTF_Scheduler_enableRounding) { pl_roundIt := false; } |
| |
| if (v_Scheduler_loadMeasurementEventId!=-1 and v_Scheduler_maxLoadThreshold<v_Scheduler_lastMeasuredLoad) { |
| // in case of overload, events will be scheduled to the future |
| if (pl_when<v_EPTF_snapshotTime) { |
| pl_when := v_EPTF_snapshotTime + tsp_EPTF_ELEMENTARY_TIMESTEP_PARAM; |
| } |
| } |
| |
| if (pl_roundIt) { |
| // corrigate: |
| pl_when := pl_when - tsp_EPTF_ELEMENTARY_TIMESTEP_PARAM*0.99; |
| } |
| |
| var boolean vl_result := f_EPTF_Scheduler_setSchedulerActionRoundable( |
| v_EPTF_eventQueue, |
| {when:=pl_when, actionHandler:=pl_actionHandler, actionId:= pl_action, dteHandler:=pl_dteHandler}, |
| pl_roundIt, |
| pl_eventIndex); |
| |
| f_EPTF_SchedulerComp_refreshEventTimer(); |
| |
| return vl_result; |
| |
| } // f_EPTF_SchedulerComp_setSchedulerAction |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_performActions |
| // |
| // Purpose: |
| // function for handling all actions |
| // |
| // Parameters: |
| // - |
| // |
| // Errors: |
| // - |
| // |
| // Detailed description: |
| // - |
| // |
| // Return Value: |
| // boolean - false if operation failed |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_SchedulerComp_performActions() runs on EPTF_Scheduler_CT |
| return boolean |
| { |
| var boolean actresult := true; |
| var integer currentIdx := f_EPTF_Scheduler_getBusyEventHeadIndex(v_EPTF_eventQueue); |
| |
| var float vl_now := v_EPTF_eventQueue.events[currentIdx].when; |
| if (v_Scheduler_loadMeasurementEventId!=-1) { |
| v_Scheduler_loadMeasurementCumulativeDelay := v_Scheduler_loadMeasurementCumulativeDelay+v_EPTF_snapshotTime-vl_now; |
| } |
| |
| if ( c_EPTF_Common_debugSwitch and tsp_debug_EPTF_SchedulerFunctions) |
| {f_EPTF_Common_user("f_EPTF_SchedulerComp_performActions() has been called.");} |
| |
| if(f_EPTF_RBT_getRoot(v_EPTF_eventQueue.order) < 0) { |
| f_EPTF_Scheduler_warning(%definitionId&": Has been called with events length <=0"); |
| return false; |
| } |
| |
| //use same scheduling time base for all new events for scheduling efficiency |
| if ( c_EPTF_Common_debugSwitch and tsp_debug_EPTF_SchedulerFunctions) |
| {f_EPTF_Common_user(log2str("f_EPTF_SchedulerComp_performActions: v_EPTF_snapshotTime:", v_EPTF_snapshotTime));} |
| |
| var boolean vl_eventExecuted := false; |
| var float vl_currentTime := v_EPTF_snapshotTime; // if snapshotTime is updated in an event handler, it will not affect the while cycle |
| while ( |
| f_EPTF_RBT_getRoot(v_EPTF_eventQueue.order) >= 0 |
| and v_EPTF_eventQueue.events[f_EPTF_Scheduler_getBusyEventHeadIndex(v_EPTF_eventQueue)].when <= vl_currentTime) |
| { |
| vl_eventExecuted := true; |
| //prepare next cycle |
| currentIdx := f_EPTF_SchedulerComp_getBusyEventHeadIndex(); |
| |
| if ( c_EPTF_Common_debugSwitch and tsp_debug_EPTF_SchedulerFunctions) |
| {f_EPTF_Common_user(log2str("f_EPTF_SchedulerComp_performActions() processing expired events" |
| ,", vl_now:",vl_now |
| ,", head event.actionId:", v_EPTF_eventQueue.events[currentIdx].actionId |
| ,", head event.when:", v_EPTF_eventQueue.events[currentIdx].when));} |
| |
| //handle event we had been sleeping for |
| //temporarily remove event from busy |
| f_EPTF_RBT_removeItemWithoutFree(v_EPTF_eventQueue.order, currentIdx); |
| //check event we have been sleeping for |
| if ( c_EPTF_Common_debugSwitch and tsp_debug_EPTF_SchedulerFunctions) |
| {f_EPTF_Common_user(log2str("Processing event action: ", |
| v_EPTF_eventQueue.events[currentIdx].actionHandler, " with arguments: ", |
| v_EPTF_eventQueue.events[currentIdx].actionId));} |
| if (v_EPTF_eventQueue.events[currentIdx].actionHandler != null) { |
| if (f_EPTF_Base_isEnabledDTEHandling()) { |
| @try { |
| actresult := v_EPTF_eventQueue.events[currentIdx].actionHandler.apply(v_EPTF_eventQueue.events[currentIdx],currentIdx); |
| } @catch(dte_str) { |
| var charstring vl_errorMessage := log2str("Dynamic test case error occured during executing scheduled action ", |
| v_EPTF_eventQueue.events[currentIdx], |
| ". Error message: "&dte_str); |
| f_EPTF_Scheduler_warning(%definitionId&": "&vl_errorMessage); |
| @try { |
| if(v_EPTF_eventQueue.events[currentIdx].dteHandler != null) { |
| v_EPTF_eventQueue.events[currentIdx].dteHandler.apply(v_EPTF_eventQueue.events[currentIdx],currentIdx,dte_str); |
| } |
| } @catch(dte_in_dte_str) { |
| var charstring vl_errorMessage2 := log2str("Dynamic test case error occured during executing DTE handler of the action ", |
| v_EPTF_eventQueue.events[currentIdx], |
| ", it was handling the DTE: "&dte_str, |
| ". Error message: "&dte_in_dte_str); |
| f_EPTF_Scheduler_warning(%definitionId&": "&vl_errorMessage2); |
| } |
| } |
| } else { |
| actresult := v_EPTF_eventQueue.events[currentIdx].actionHandler.apply(v_EPTF_eventQueue.events[currentIdx],currentIdx); |
| } |
| } |
| |
| |
| if (actresult) { |
| if (c_EPTF_Common_debugSwitch and tsp_debug_EPTF_SchedulerFunctions) |
| {f_EPTF_Common_user(log2str("Succesfully executed event action: ", |
| v_EPTF_eventQueue.events[currentIdx].actionHandler, " with arguments: ", |
| v_EPTF_eventQueue.events[currentIdx].actionId));} |
| } else { |
| f_EPTF_Scheduler_warning(log2str("f_EPTF_SchedulerComp_performActions has failed to handle event:", v_EPTF_eventQueue.events[currentIdx])); |
| actresult := true; |
| } |
| //remove lead event |
| |
| f_EPTF_RBT_freeInvalidItem(v_EPTF_eventQueue.order, currentIdx); |
| }//while |
| |
| if (not vl_eventExecuted) { |
| // time warp detected! |
| // the nextEvent timeout event happened before the time of the first event "when" |
| // no event was handled |
| var float vl_warpTime := 0.0; |
| if (f_EPTF_RBT_getRoot(v_EPTF_eventQueue.order) >= 0) { |
| vl_warpTime := v_EPTF_eventQueue.events[f_EPTF_Scheduler_getBusyEventHeadIndex(v_EPTF_eventQueue)].when - v_EPTF_snapshotTime; |
| } |
| f_EPTF_Scheduler_warning("f_EPTF_SchedulerComp_performActions: Time warp detected! Amout of warp (difference between the scheduled event time and the real time): "& |
| float2str(vl_warpTime)); |
| // trigger the restart of the eventTimer by f_EPTF_Scheduler_refreshEventTimer: |
| v_EPTF_eventQueue.schedulerRunsFor := -1.0; |
| } |
| |
| if ( c_EPTF_Common_debugSwitch and tsp_debug_EPTF_SchedulerFunctions) |
| {f_EPTF_Common_user("f_EPTF_SchedulerComp_performActions is finished.");} |
| |
| if ( c_EPTF_Common_debugSwitch and tsp_debug_EPTF_SchedulerFunctions) { |
| var float tmp:=T_EPTF_componentClock.read; |
| f_EPTF_Common_user(log2str("f_EPTF_SchedulerComp_performActions: current clock time:",tmp)); |
| } |
| return true; |
| }; // f_EPTF_SchedulerComp_performActions |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Altstep: as_EPTF_SchedulerComp_ActionHandler |
| // |
| // Purpose: |
| // To handle scheduled events in the queue |
| // |
| // Parameters: |
| // - |
| // |
| // Detailed description: |
| // This altstep should be placed into the main alt-loop. |
| // The component on which the main alt-loop runs should |
| // extend <EPTF_Scheduler_CT>. |
| // This altstep is activated as default by <f_EPTF_Scheduler_init_CT> |
| // |
| // In this altstep the following is done: |
| // |
| // 1. Calls the <f_EPTF_SchedulerComp_freezeBusyEventHead> to indicate that the processing the |
| // current event has started. |
| // |
| // 2. Calls your <EPTF_Scheduler_ActionHandler> call-back function handling the event. |
| // |
| // 3. Calls the <f_EPTF_SchedulerComp_removeInvalidEvent> to clear the event from the |
| // scheduler. |
| // |
| // |
| /////////////////////////////////////////////////////////// |
| private altstep as_EPTF_SchedulerComp_ActionHandler() runs on EPTF_Scheduler_CT |
| { |
| [] T_EPTF_nextEvent.timeout |
| { |
| v_EPTF_snapshotTime := T_EPTF_componentClock.read; |
| v_EPTF_nextEventIsActive := false; |
| if (not f_EPTF_SchedulerComp_performActions()) { |
| f_EPTF_Scheduler_warning("f_EPTF_SchedulerComp_performActions() has been failed"); |
| } |
| f_EPTF_SchedulerComp_refreshEventTimer(); |
| repeat; |
| } |
| } // as_EPTF_SchedulerComp_ActionHandler |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_enableLoadMeasurement |
| // |
| // Purpose: |
| // function to enable/disable load measurement for overload detection |
| // |
| // Parameters: |
| // pl_enable - *in* *boolean* - if true: enable load measurement, |
| // false: disable |
| // |
| // Errors: |
| // - |
| // |
| // Detailed description: |
| // The load is measured as the cumulative sum of lag of events during the measurement period |
| // |
| // Return Value: |
| // - |
| // |
| /////////////////////////////////////////////////////////// |
| public function f_EPTF_Scheduler_enableLoadMeasurement(in boolean pl_enable) runs on EPTF_Scheduler_CT { |
| if (pl_enable and v_Scheduler_loadMeasurementEventId==-1) { |
| // schedule the load measurement event: |
| v_Scheduler_loadMeasurementCumulativeDelay := 0.0; |
| v_Scheduler_lastMeasuredLoad := 0.0; |
| v_Scheduler_lastMeasureTime := v_EPTF_snapshotTime; |
| var integer vl_eventIdx := v_Scheduler_loadMeasurementEventId; |
| f_EPTF_SchedulerComp_scheduleAction( |
| pl_when := v_EPTF_snapshotTime + v_Scheduler_loadMeasurementInterval, |
| pl_actionHandler := refers(f_EPTF_Scheduler_loadMeasurmentHandler), |
| pl_action := {}, |
| pl_eventIndex := vl_eventIdx |
| ); |
| v_Scheduler_loadMeasurementEventId := vl_eventIdx; |
| } else if (not pl_enable and v_Scheduler_loadMeasurementEventId!=-1) { |
| // cancel the load measurement event: |
| f_EPTF_SchedulerComp_CancelEvent(v_Scheduler_loadMeasurementEventId); |
| v_Scheduler_loadMeasurementEventId := -1; |
| } |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_setLoadMeasurementPeriod |
| // |
| // Purpose: |
| // function for setting the load measurement period |
| // |
| // Parameters: |
| // pl_loadMeasurementPeriod - *in* *float* - the new period in seconds |
| // |
| // Errors: |
| // - |
| // |
| // Detailed description: |
| // Load measurement has to be enabled to get measurements |
| // |
| // Return Value: |
| // - |
| // |
| /////////////////////////////////////////////////////////// |
| public function f_EPTF_Scheduler_setLoadMeasurementPeriod(in float pl_loadMeasurementPeriod) runs on EPTF_Scheduler_CT { |
| v_Scheduler_loadMeasurementInterval := pl_loadMeasurementPeriod; |
| //reschedule the event: |
| if (v_Scheduler_loadMeasurementEventId!=-1) { |
| f_EPTF_SchedulerComp_CancelEvent(v_Scheduler_loadMeasurementEventId); |
| var integer vl_eventIdx := v_Scheduler_loadMeasurementEventId; |
| f_EPTF_SchedulerComp_scheduleAction( |
| pl_when := v_EPTF_snapshotTime + v_Scheduler_loadMeasurementInterval, |
| pl_actionHandler := refers(f_EPTF_Scheduler_loadMeasurmentHandler), |
| pl_action := {}, |
| pl_eventIndex := vl_eventIdx |
| ); |
| v_Scheduler_loadMeasurementEventId := vl_eventIdx; |
| } |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_setMaxLoadThreshold |
| // |
| // Purpose: |
| // function for setting the maximal load value above which |
| // the Scheduler will actively control the overload situation by |
| // scheduling events into the future only |
| // |
| // Parameters: |
| // pl_maxLoadThreshold - *in* *float* - the maximal load threshold |
| // value > 1.0 switches off overload control |
| // |
| // Errors: |
| // - |
| // |
| // Detailed description: |
| // Load measurement has to be enabled to get load measurements. |
| // If maxLoadThreshold>1.0, there will be no overload control |
| // Event scheduling is affected only if the measured load is greater |
| // than maxLoadThreshold. In this situation events to be scheduled |
| // into the past will be scheduled into the future. |
| // |
| // Return Value: |
| // - |
| // |
| /////////////////////////////////////////////////////////// |
| public function f_EPTF_Scheduler_setMaxLoadThreshold(in float pl_maxLoadThreshold) runs on EPTF_Scheduler_CT { |
| v_Scheduler_maxLoadThreshold := pl_maxLoadThreshold; |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_getLoad |
| // |
| // Purpose: |
| // function for returning the last measured load value |
| // |
| // Parameters: |
| // - |
| // |
| // Errors: |
| // - |
| // |
| // Detailed description: |
| // Load measurement has to be enabled |
| // |
| // Return Value: |
| // float - the measured load. The value is between 0.0 and 1.0 |
| // 0.0: no load, 1.0: full load |
| // |
| /////////////////////////////////////////////////////////// |
| public function f_EPTF_Scheduler_getLoad() runs on EPTF_Scheduler_CT return float { |
| return v_Scheduler_lastMeasuredLoad; |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_getLoadMeasurementInterval |
| // |
| // Purpose: |
| // function for returning the last load measurement interval |
| // |
| // Parameters: |
| // - |
| // |
| // Errors: |
| // - |
| // |
| // Detailed description: |
| // Load measurement has to be enabled. If not enabled -1.0 is returned |
| // |
| // Return Value: |
| // float - the load measurement period. If load measurement is not enabled |
| // -1.0 is returned |
| // |
| /////////////////////////////////////////////////////////// |
| public function f_EPTF_Scheduler_getLoadMeasurementInterval() runs on EPTF_Scheduler_CT return float { |
| if (v_Scheduler_loadMeasurementEventId==-1) { |
| return -1.0; |
| } |
| return v_Scheduler_loadMeasurementInterval; |
| } |
| |
| private function f_EPTF_Scheduler_loadMeasurmentHandler( |
| in EPTF_ScheduledAction pl_action, |
| in integer pl_eventIndex |
| ) runs on EPTF_Scheduler_CT return boolean { |
| var float vl_measurementIntervalReal := v_EPTF_snapshotTime - v_Scheduler_lastMeasureTime; |
| |
| if (vl_measurementIntervalReal>0.0) { |
| // calculate load value: |
| v_Scheduler_lastMeasuredLoad := v_Scheduler_loadMeasurementCumulativeDelay/vl_measurementIntervalReal; |
| v_Scheduler_loadMeasurementCumulativeDelay := 0.0; |
| v_Scheduler_lastMeasureTime := v_EPTF_snapshotTime; |
| } |
| |
| // reschedule the event: |
| var integer vl_eventIdx := v_Scheduler_loadMeasurementEventId; |
| f_EPTF_SchedulerComp_scheduleAction( |
| pl_when := pl_action.when + v_Scheduler_loadMeasurementInterval, |
| pl_actionHandler := refers(f_EPTF_Scheduler_loadMeasurmentHandler), |
| pl_action := {}, |
| pl_eventIndex := vl_eventIdx |
| ); |
| v_Scheduler_loadMeasurementEventId := vl_eventIdx; |
| return true; |
| } |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_init_CT |
| // |
| // Purpose: |
| // function for initializing the scheduler event queue and parameters |
| // |
| // Parameters: |
| // pl_f_EPTF_Scheduler_ActionHandler - *in* <EPTF_Scheduler_ActionHandler> - call back function |
| // to handle actions |
| // |
| // Errors: |
| // - |
| // |
| // Detailed description: |
| // This function should be called before using the EPTF_Scheduler. |
| // The call back function should be implemented by the user. If it is to be a function |
| // that must run on a user defined component, that function can be implemented as an |
| // external function in which the handler function running on the user component |
| // can be called. |
| // |
| // Return Value: |
| // - |
| // |
| /////////////////////////////////////////////////////////// |
| public function f_EPTF_Scheduler_init_CT(in charstring pl_selfName) |
| runs on EPTF_Scheduler_CT |
| { |
| if (v_Scheduler_initialized) { |
| return; |
| } |
| f_EPTF_RBT_init_CT(pl_selfName); |
| f_EPTF_Logging_init_CT(pl_selfName); |
| v_Scheduler_loggingMaskId := f_EPTF_Logging_registerComponentMasks(tsp_EPTF_Scheduler_loggingComponentMask, c_EPTF_Scheduler_loggingEventClasses, EPTF_Logging_CLL); |
| if(tsp_debug_EPTF_SchedulerFunctions) { |
| f_EPTF_Logging_enableLocalMask(v_Scheduler_loggingMaskId, c_EPTF_Scheduler_loggingClassIdx_Debug); |
| } else { |
| f_EPTF_Logging_disableLocalMask(v_Scheduler_loggingMaskId, c_EPTF_Scheduler_loggingClassIdx_Debug); |
| } |
| |
| |
| f_EPTF_SchedulerComp_initEventQueue(); |
| v_EPTF_Scheduler_def := activate(as_EPTF_SchedulerComp_ActionHandler()); |
| |
| f_EPTF_SchedulerComp_refreshSnapshotTime(); |
| v_Scheduler_loadMeasurementInterval := 10.0; |
| v_Scheduler_maxLoadThreshold := 0.99; |
| v_Scheduler_loadMeasurementEventId := -1; |
| if (tsp_EPTF_Scheduler_enableLoadMeasurement) { |
| f_EPTF_Scheduler_enableLoadMeasurement(true); |
| } |
| |
| v_Scheduler_initialized := true; |
| f_EPTF_Base_registerCleanup(refers(f_EPTF_Scheduler_cleanup_CT)); |
| }; |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_SchedulerComp_InitScheduler |
| // |
| // Purpose: |
| // OBSOLETE |
| // Function for initializing the scheduler event queue and parameters |
| // Kept for backward compatibility purpose, will be removed later! |
| // |
| // Parameters: |
| // pl_name - *in* *charstring* - name of the component, default is "default component name" |
| // |
| // Errors: |
| // - |
| // |
| // Detailed description: |
| // This function is deprecated and will be removed! Use <f_EPTF_Scheduler_init_CT> instead! |
| // |
| // Return Value: |
| // - |
| // |
| /////////////////////////////////////////////////////////// |
| /*public*/ function f_EPTF_SchedulerComp_InitScheduler(in charstring pl_name := "default component name") |
| runs on EPTF_Scheduler_CT |
| { |
| f_EPTF_Scheduler_warning("Warning: "&%definitionId& ": This function is deprecated and will be removed! Use f_EPTF_Scheduler_init_CT instead!"); |
| f_EPTF_Scheduler_init_CT(pl_name); |
| }; |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_cleanup_CT |
| // |
| // Purpose: |
| // Destructs the EPTF_Scheduler_CT |
| // |
| // Parameters: |
| // - |
| // |
| // Return Value: |
| // - |
| // |
| // Errors: |
| // none |
| // |
| // Detailed Comments: |
| // This function mustn't be called directly. You must call f_EPTF_Base_cleanup_CT instead. |
| /// |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_cleanup_CT() runs on EPTF_Scheduler_CT { |
| if (v_Scheduler_initialized == false) { |
| return; |
| } |
| deactivate(v_EPTF_Scheduler_def); |
| v_EPTF_Scheduler_def := null; |
| v_Scheduler_initialized := false; |
| if ( c_EPTF_Common_debugSwitch and tsp_debug_EPTF_SchedulerFunctions) |
| {f_EPTF_Scheduler_debug("----EPTF_Scheduler cleanup DONE----");} |
| } |
| |
| group Logging { |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_error |
| // |
| // Purpose: |
| // Function to log an error from Scheduler feature. |
| // |
| // Parameters: |
| // - pl_message - *in* *charstring* - the message to log |
| // |
| // Return Value: |
| // - |
| // |
| // Errors & assertions: |
| // - |
| // |
| // Detailed Comments: |
| // - |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_error(in charstring pl_message) |
| runs on EPTF_Scheduler_CT |
| { |
| f_EPTF_Logging_error(true, tsp_EPTF_Scheduler_loggingComponentMask&": "&pl_message); |
| f_EPTF_Base_stopAll(); |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_warning |
| // |
| // Purpose: |
| // Function to log a warning from Scheduler feature. |
| // |
| // Parameters: |
| // - pl_message - *in* *charstring* - the message to log |
| // |
| // Return Value: |
| // - |
| // |
| // Errors & assertions: |
| // - |
| // |
| // Detailed Comments: |
| // - |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_warning(in @lazy charstring pl_message) |
| runs on EPTF_Scheduler_CT |
| { |
| f_EPTF_Logging_warningV2(pl_message, v_Scheduler_loggingMaskId, {c_EPTF_Scheduler_loggingClassIdx_Warning}); |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_debug |
| // |
| // Purpose: |
| // Function to log a debug message from Scheduler feature. |
| // |
| // Parameters: |
| // - pl_message - *in* *charstring* - the message to log |
| // |
| // Return Value: |
| // - |
| // |
| // Errors & assertions: |
| // - |
| // |
| // Detailed Comments: |
| // - |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_debug(in @lazy charstring pl_message) |
| runs on EPTF_Scheduler_CT |
| { |
| f_EPTF_Logging_debugV2(pl_message, v_Scheduler_loggingMaskId, {c_EPTF_Scheduler_loggingClassIdx_Debug}); |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_debugEnabled |
| // |
| // Purpose: |
| // Function to check if debug is enabled for Scheduler |
| // |
| // Parameters: |
| // - |
| // |
| // Return Value: |
| // *boolean* - true if debug enalbed |
| // |
| // Errors & assertions: |
| // - |
| // |
| // Detailed Comments: |
| // - |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_debugEnabled() |
| runs on EPTF_Scheduler_CT |
| return boolean |
| { |
| return f_EPTF_Logging_isEnabled(v_Scheduler_loggingMaskId, c_EPTF_Scheduler_loggingClassIdx_Debug); |
| } |
| } // group Logging |
| |
| |
| } //group Scheduler_RunsOnFunctions |
| |
| /////////////////////////////////////////////////////////// |
| // Group: RBTScheduler_GenericFunctions |
| // |
| // Purpose: |
| // Group of functions without "runs on EPTF_Scheduler_CT" clause |
| // |
| // Detailed comments: |
| // Alternative version of these functions are in the group <Scheduler_RunsOnFunctions>. |
| // This group is included from <EPTF_CLL_Scheduler_Functions.ttcnin> |
| // Additional functions are in the group <RBTScheduler_RunsOnFunctions> |
| /////////////////////////////////////////////////////////// |
| group RBTScheduler_GenericFunctions { |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_initEventQueue |
| // |
| // Purpose: |
| // function to init an empty eventQueue |
| // |
| // Parameters: |
| // pl_EventQueue - *inout* <EPTF_EventQueue> |
| // |
| // Return Value: |
| // - |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_initEventQueue(inout EPTF_EventQueue pl_EventQueue) { |
| pl_EventQueue.schedulerRunsFor := -1.0; |
| pl_EventQueue.order := f_EPTF_RBT_createFloatTree("Scheduler event queue"); |
| pl_EventQueue.events :={} |
| } |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_getNewTimerVal |
| // |
| // Purpose: |
| // function to get relative time of next event |
| // |
| // Parameters: |
| // pl_NextEventQueue - *inout* <EPTF_EventQueue> - the event queue |
| // |
| // pl_componentClock - *timer* - the component timer counting absolute time |
| // |
| // Return Value: |
| // float |
| // |
| // Errors: |
| // - Calling the function with an empty pl_NextEventQueue makes no sense, |
| // therefore it is an error, which will cause the execution to stop |
| // |
| // Detailed Comments: |
| // This function returns a time value (float) that is the difference between |
| // the |
| // - current simulation time (obtained as pl_componentClock.read()) and |
| // - the ideal schedule (absolute timepoint) of the event at the head of the |
| // queue. |
| // |
| // If the return value is |
| // - positive, then the event shall happen in the future, i.e., use this value |
| // as the timeout value for your scheduler timer |
| // - non-positive (zero, or negative) then event is already "due", that is, |
| // the scheduler is late and the event shall be executed immediately. |
| // |
| // Use this functions as follows: |
| // > |
| // >var float currentTime, duration; |
| // > alt{ |
| // > [] schedulerTimer.timeout { |
| // > currentTime := clockTimer.read(); |
| // > label processevents; |
| // > //... |
| // > //... process the head event of the event queue.... |
| // > //... |
| // > if (f_EPTF_Scheduler_eventQueueIsNotEmpty(myQueue)) { |
| // > duration := f_EPTF_Scheduler_getNewTimerVal(clockTimer); |
| // > if (duration <= 0.0) {goto processevents;} |
| // > else {schedulerTimer.start(duration)} |
| // > } |
| // > repeat; |
| // > } |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_getNewTimerVal( |
| inout EPTF_EventQueue pl_NextEventQueue, |
| timer pl_componentClock |
| ) |
| return float |
| { |
| var integer qidx := f_EPTF_RBT_getItemWithSmallestKey(pl_NextEventQueue.order); |
| |
| if(qidx < 0) { |
| f_EPTF_Common_error("[f_EPTF_Scheduler_getNewTimerVal] Error: getNewTimerVal: Cannot get smallest element. Tree could be empty"); |
| var verdicttype tmpverdict:=error; |
| setverdict(tmpverdict); |
| stop; |
| } |
| //indexes within queue |
| var float timerval:= pl_NextEventQueue.events[qidx].when - pl_componentClock.read + pl_NextEventQueue.events[qidx].when*1e-10; // 1e-10 is needed to avoid timeout before event.when |
| if (timerval <= 0.0) { |
| if (c_EPTF_Common_debugSwitch and tsp_debug_EPTF_SchedulerFunctions) |
| { |
| f_EPTF_Common_warning(log2str("Warning: getNewTimerVal: scheduler is behind clock, timerval:", |
| timerval, ", event:",pl_NextEventQueue.events[qidx])); |
| } |
| timerval:=tsp_EPTF_ELEMENTARY_TIMESTEP_PARAM; |
| } |
| return timerval; |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_refreshEventTimer |
| // |
| // Purpose: |
| // function to update event timer to the next event |
| // |
| // Parameters: |
| // pl_NextEventQueue - *inout* <EPTF_EventQueue> - the event queue |
| // |
| // pl_componentClock - *float* - the "current simulated time" |
| // |
| // pl_eventTimer - *float* - the event timer |
| // |
| // pl_isActive - *inout* *boolean* - boolean flag to indicate if the scheduler is active |
| // |
| // Return Value: |
| // - |
| // |
| // Detailed Comments: |
| // This function sets the event timer to the next scheduled event time. |
| // If necessary (e.g., an event has been inserted before the head of the queue), |
| // then it restarts the timer. It takes a boolean flag (which will be updated as needed) |
| // that indicates the activeness of the timer (workaround for the limitations of the |
| // timer.running operator of the TTCN-3 language) |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_refreshEventTimer( |
| inout EPTF_EventQueue pl_NextEventQueue, |
| timer pl_componentClock, |
| timer pl_eventTimer, |
| inout boolean pl_isActive) |
| { //if queue is empty and schedulerRunsFor is not invalid, i.e., |
| // the timer has been started previously |
| var integer head_idx := f_EPTF_RBT_getItemWithSmallestKey(pl_NextEventQueue.order); |
| if ((head_idx < 0) |
| and pl_NextEventQueue.schedulerRunsFor != -1.0 |
| ) |
| { |
| pl_eventTimer.stop; pl_isActive := false; |
| pl_NextEventQueue.schedulerRunsFor := -1.0; |
| } |
| |
| //if there is any event in the queue |
| if ((head_idx >= 0) |
| //and if the timer is not running for the schedule of the current head |
| and pl_NextEventQueue.schedulerRunsFor != |
| pl_NextEventQueue.events[head_idx].when) |
| { |
| //avoid unecessary timer restart warnings |
| if (pl_isActive) {pl_eventTimer.stop;} else {pl_isActive := true} |
| //(re-)start timer |
| pl_eventTimer.start(f_EPTF_Scheduler_getNewTimerVal(pl_NextEventQueue, pl_componentClock)); |
| //remember that the timer runs for what schedule |
| pl_NextEventQueue.schedulerRunsFor := |
| pl_NextEventQueue.events[head_idx].when |
| } |
| } |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_schedulerIsNotLate |
| // |
| // Purpose: |
| // function to check whether the event queue is not empty, or is empty |
| // |
| // Parameters: |
| // pl_NextEventQueue - *inout* <EPTF_EventQueue> - the event queue |
| // |
| // pl_now - *float* - the "current simulated time" |
| // |
| // Return Value: |
| // boolean |
| // |
| // Errors: |
| // - Calling the function with an empty pl_NextEventQueue makes no sense, |
| // therefore it is an error, which will cause the execution to stop |
| // |
| // Detailed Comments: |
| // This function returns a true if the head event within the event queue |
| // is scheduled later than "pl_now", itherwise returns false. |
| // See also <f_EPTF_Scheduler_getNewTimerVal> |
| // |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_schedulerIsNotLate( |
| inout EPTF_EventQueue pl_NextEventQueue, |
| in float pl_now |
| ) |
| return boolean |
| { |
| var integer idx := f_EPTF_RBT_getItemWithSmallestKey(pl_NextEventQueue.order); |
| |
| if(idx < 0) { |
| f_EPTF_Common_error("[f_EPTF_Scheduler_schedulerIsNotLate] Error: Cannot get smallest element, maybe tree is empty"); |
| var verdicttype tmpverdict:=error; |
| setverdict(tmpverdict); |
| stop; |
| } |
| |
| if (pl_NextEventQueue.events[idx].when - pl_now> 0.0) |
| {return true} else {return false}; |
| } // f_EPTF_Scheduler_schedulerIsNotLate |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_eventQueueIsNotEmpty |
| // |
| // Purpose: |
| // function to check whether the event queue is not empty, or is empty |
| // |
| // Parameters: |
| // pl_NextEventQueue - *inout* <EPTF_EventQueue> - the event queue |
| // |
| // Return Value: |
| // boolean |
| // |
| // Errors: |
| // - (none) |
| // |
| // Detailed Comments: |
| // This function returns a true if the event queue is not empty, false otherwise. |
| // Use this function to decide whether calling <f_EPTF_Scheduler_getNewTimerVal> () |
| // makes sense, or not. |
| // |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_eventQueueIsNotEmpty( |
| inout EPTF_EventQueue pl_NextEventQueue |
| ) |
| return boolean |
| { |
| return not f_EPTF_Scheduler_eventQueueIsEmpty(pl_NextEventQueue); |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_eventQueueIsEmpty |
| // |
| // Purpose: |
| // function to check whether the event queue is empty, or not empty |
| // |
| // Parameters: |
| // pl_NextEventQueue - *inout* <EPTF_EventQueue> - the event queue |
| // |
| // Return Value: |
| // boolean |
| // |
| // Errors: |
| // - (none) |
| // |
| // Detailed Comments: |
| // This function returns a true if the event queue is empty, false otherwise. |
| // |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_eventQueueIsEmpty( |
| inout EPTF_EventQueue pl_NextEventQueue |
| ) |
| return boolean |
| { |
| return f_EPTF_RBT_getRoot(pl_NextEventQueue.order)==-1 ; |
| } |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_eventIsValid |
| // |
| // Purpose: |
| // function to check whether an event is valid within the queue |
| // |
| // Parameters: |
| // pl_NextEventQueue - *in* <EPTF_EventQueue> - the event queue |
| // |
| // pl_qidx - *in* *integer* - index of the element to be checked |
| // |
| // Return Value: |
| // boolean - true, if valid, false otherwise |
| // |
| // Errors: |
| // - |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_eventIsValid( |
| inout EPTF_EventQueue pl_NextEventQueue, |
| in integer pl_qidx |
| ) |
| return boolean |
| { |
| if (c_EPTF_Common_debugSwitch and tsp_debugVerbose_EPTF_SchedulerFunctions) {f_EPTF_Common_user(log2str("f_EPTF_Scheduler_eventIsValid:", pl_qidx));} |
| |
| return f_EPTF_RBT_isItemValid(pl_NextEventQueue.order, pl_qidx); |
| }//f_EPTF_Scheduler_eventIsValid |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_eventIsInvalid |
| // |
| // Purpose: |
| // function to check whether an event is valid within the queue |
| // |
| // Parameters: |
| // pl_NextEventQueue - *in* <EPTF_EventQueue> - the event queue |
| // |
| // pl_qidx - *in* *integer* - index of the element to be checked |
| // |
| // Return Value: |
| // boolean - true, if invalid, false otherwise |
| // |
| // Errors: |
| // - |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_eventIsInvalid( |
| inout EPTF_EventQueue pl_NextEventQueue, |
| in integer pl_qidx |
| ) |
| return boolean |
| { |
| return not f_EPTF_Scheduler_eventIsValid(pl_NextEventQueue, pl_qidx); |
| }//f_EPTF_Scheduler_eventIsInvalid |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_setSchedulerActionRoundable |
| // |
| // Purpose: |
| // function to register a new action to the action queue, |
| // the rounding of the schedule is switchable |
| // |
| // Parameters: |
| // pl_NextEventQueue - *inout* <EPTF_FreeBusyQueue> - the event queue |
| // |
| // pl_scheduledAction - *in* <EPTF_ScheduledAction> - action to be scheduled |
| // |
| // pl_roundIt - *in boolean* - whether to round, or not the schedule |
| // |
| // pl_eventIndex - *out* *integer* - returns the position within the queue |
| // |
| // Errors: |
| // - |
| // |
| // Return Value: |
| // boolean - false if operation failed |
| // |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_setSchedulerActionRoundable( |
| inout EPTF_EventQueue pl_NextEventQueue, |
| in EPTF_ScheduledAction pl_scheduledAction, |
| in boolean pl_roundIt, |
| out integer pl_eventIndex |
| ) |
| return boolean |
| { |
| pl_eventIndex := -1; |
| |
| if (c_EPTF_Common_debugSwitch and tsp_debug_EPTF_SchedulerFunctions and tsp_debugVerbose_EPTF_SchedulerFunctions) |
| { |
| f_EPTF_Common_user(log2str("f_SetSchedulerActionRoundable, params: actionId:", |
| pl_scheduledAction.actionId, ", when:",pl_scheduledAction.when)); |
| } |
| |
| //if round timer |
| if (pl_roundIt) { |
| pl_scheduledAction.when := tsp_EPTF_ELEMENTARY_TIMESTEP_PARAM*int2float( |
| 1+float2int(pl_scheduledAction.when/tsp_EPTF_ELEMENTARY_TIMESTEP_PARAM)); |
| if (c_EPTF_Common_debugSwitch and tsp_debugVerbose_EPTF_SchedulerFunctions) |
| { |
| f_EPTF_Common_user(log2str("f_SetSchedulerActionRoundable: rounded pl_scheduledAction.when to:" |
| ,pl_scheduledAction.when)); |
| } |
| } |
| |
| //we need to create new item |
| pl_eventIndex := f_EPTF_RBT_insertFloatItem( |
| pl_NextEventQueue.order, |
| pl_scheduledAction.when |
| ); |
| |
| //update the element |
| pl_NextEventQueue.events[pl_eventIndex] := pl_scheduledAction; |
| |
| if (c_EPTF_Common_debugSwitch and tsp_debugVerbose_EPTF_SchedulerFunctions) { |
| f_EPTF_Common_user(log2str("Created new event at pl_eventIndex:",pl_eventIndex)); |
| } |
| |
| if (c_EPTF_Common_debugSwitch and tsp_debugVerbose_EPTF_SchedulerFunctions) { |
| f_EPTF_Common_user(log2str("New event at pl_eventIndex:",pl_eventIndex |
| ,", event data:", pl_NextEventQueue.events[pl_eventIndex])); |
| } |
| |
| return true; |
| }//SetSchedulerActionRoundable |
| |
| |
| |
| /////////////////////////////////////////////////////////// |
| // Function: f_EPTF_Scheduler_getBusyEventHeadIndex |
| // |
| // Purpose: |
| // function to get head index of busy queue |
| // |
| // Parameters: |
| // pl_NextEventQueue - *in* <EPTF_EventQueue> - the event queue |
| // |
| // Return Value: |
| // integer - found head index, -1 if empty |
| // |
| // Errors: |
| // - (none) |
| // |
| // Detailed Comments: |
| // - |
| /////////////////////////////////////////////////////////// |
| private function f_EPTF_Scheduler_getBusyEventHeadIndex (inout EPTF_EventQueue pl_EventQueue) return integer { |
| |
| return f_EPTF_RBT_getItemWithSmallestKey(pl_EventQueue.order); |
| |
| } //f_EPTF_Scheduler_getBusyEventHeadIndex |
| |
| |
| } //group RBTScheduler_GenericFunctions |
| |
| |
| } //module |