| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // 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_LGenBaseDemo_ResponderFunctions |
| // |
| // Purpose: |
| // This module contains the functions of the Responder component of the LGenBase demo |
| // |
| // Module Parameters: |
| // - |
| // |
| // Module depends on: |
| // |
| // Current Owner: |
| // ELSZSKU |
| // |
| // Last Review Date: |
| // 20 - - |
| // |
| // Detailed Comments: |
| // |
| /////////////////////////////////////////////////////////// |
| module EPTF_LGenBaseDemo_ResponderFunctions |
| { |
| import from EPTF_CLL_LGenBase_EventHandlingFunctions all; |
| import from EPTF_CLL_Variable_Definitions all; |
| import from EPTF_CLL_Variable_Functions all; |
| import from EPTF_CLL_Common_Definitions all; |
| import from EPTF_CLL_LGenBase_Definitions all; |
| import from EPTF_LGenBaseDemo_TransportDefinitions all |
| import from EPTF_LGenBaseDemo_TransportFunctions all |
| import from EPTF_CLL_Base_Functions all; |
| import from EPTF_CLL_LGenBase_ConfigFunctions all; |
| import from EPTF_CLL_LGenBase_Functions all; |
| import from EPTF_CLL_LGenBase_ConfigDefinitions all; |
| import from EPTF_CLL_LGenBase_TrafficFunctions all |
| import from EPTF_CLL_HashMapInt2Int_Functions all |
| import from EPTF_CLL_Logging_Functions all |
| import from EPTF_LGenBaseDemo_ResponderDefinitions all |
| import from EPTF_CLL_LGenBase_StepFunctions all |
| import from EPTF_LGenBaseDemo_CallerFunctions all |
| |
| modulepar float tsp_targetCPS := 100.0 |
| modulepar float tsp_maxDelay := 0.5 |
| modulepar integer tsp_eGrpCount := 0 |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // This is the starter function of the Responder component |
| /////////////////////////////////////////////////////////////////////////////// |
| public function f_LGenBaseDemo_Responder_behavior( |
| in charstring pl_name, |
| in float pl_targetCPS, |
| in float pl_maxDelay) |
| runs on LGenBaseDemo_Responder_CT{ |
| // This case there is no direct connection between the LGenBase entities |
| // and the simulated users. Therefore the minimal necessary amount of entities |
| // is target CPS * max. delay. |
| // Let's allocate a bit more... |
| var integer vl_eGrpSize := float2int(pl_targetCPS*pl_maxDelay*1.2) |
| if(0 < tsp_eGrpCount){ |
| vl_eGrpSize := tsp_eGrpCount |
| } |
| // Initialize |
| f_LGenBaseDemo_Responder_init(pl_name, vl_eGrpSize, pl_maxDelay) |
| // Create the terminating scenario |
| // declared in the initialization |
| f_EPTF_LGenBase_createScenario2EntityGroup({c_LGenBaseDemo_Responder_entityGroupName,c_LGenBaseDemo_Responder_scenarioName}) |
| v_LGenBaseDemo_Responder_responderTcIdx := f_EPTF_LGenBase_trafficCaseId( |
| c_LGenBaseDemo_Responder_entityGroupName, |
| c_LGenBaseDemo_Responder_scenarioName, |
| c_LGenBaseDemo_Responder_tcName) |
| // Come what may... |
| f_EPTF_Base_wait4Shutdown() |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Initialize the Responder component |
| /////////////////////////////////////////////////////////////////////////////// |
| public function f_LGenBaseDemo_Responder_init( |
| in charstring pl_name, |
| in integer pl_eGrpSize, |
| in float pl_maxDelay) |
| runs on LGenBaseDemo_Responder_CT{ |
| f_LGenBaseDemo_Transport_init( |
| pl_name, |
| refers(f_LGenBaseDemo_Responder_processCallerMsgs)) |
| f_EPTF_LGenBase_init(pl_name) |
| f_EPTF_Base_registerCleanup(refers(f_LGenBaseDemo_Responder_cleanup)) |
| f_EPTF_int2int_HashMap_Init() |
| |
| // This hashmap is used to identify which entity handles which response message. |
| // The acknowledgement message must be forwarded to the appropriate entity |
| v_LGenBaseDemo_Responder_msgHashMap := f_EPTF_int2int_HashMap_New(c_LGenBaseDemo_Responder_msgHashMapName) |
| |
| // Create the background |
| // The behavior in the demo is used to identify |
| // - events |
| // - data related to the behavior |
| v_LGenBaseDemo_Responder_myBehavId := f_EPTF_LGenBase_declareBehaviorType(c_LGenBaseDemo_Responder_BehaviorName, -1, null, null, null) |
| f_EPTF_LGenBase_declareFsmEvent(c_LGenBaseDemo_Responder_BehaviorName, c_LGenBaseDemo_Responder_InputRegisterReceived) |
| f_EPTF_LGenBase_declareFsmEvent(c_LGenBaseDemo_Responder_BehaviorName, c_LGenBaseDemo_Responder_InputReRegisterReceived) |
| f_EPTF_LGenBase_declareFsmEvent(c_LGenBaseDemo_Responder_BehaviorName, c_LGenBaseDemo_Responder_InputDoMsgReceived) |
| f_EPTF_LGenBase_declareFsmEvent(c_LGenBaseDemo_Responder_BehaviorName, c_LGenBaseDemo_Responder_InputAckReceived) |
| |
| // Declare the custom test step functions used in the responder FSMs |
| // To see their comments see the function definitions |
| f_EPTF_LGenBase_declareStep(c_LGenBaseDemo_Responder_BehaviorName, {c_LGenBaseDemo_Responder_StepName_reply2Register, refers(f_LGenBaseDemo_Responder_reply2Register)}) |
| f_EPTF_LGenBase_declareStep(c_LGenBaseDemo_Responder_BehaviorName, {c_LGenBaseDemo_Responder_StepName_reply2ReRegister, refers(f_LGenBaseDemo_Responder_reply2ReRegister)}) |
| f_EPTF_LGenBase_declareStep(c_LGenBaseDemo_Responder_BehaviorName, {c_LGenBaseDemo_Responder_StepName_reply2DoMsg, refers(f_LGenBaseDemo_Responder_Reply2DoMsg)}) |
| f_EPTF_LGenBase_declareStep(c_LGenBaseDemo_Responder_BehaviorName, {c_LGenBaseDemo_Responder_StepName_delay, refers(f_LGenBaseDemo_Responder_Delay)}) |
| f_EPTF_LGenBase_declareStep(c_LGenBaseDemo_Responder_BehaviorName, {c_LGenBaseDemo_Responder_StepName_watchdogTimerTimedOut, refers(f_LGenBaseDemo_Responder_WatchdogTimerTimedOut)}) |
| f_EPTF_LGenBase_declareStep(c_LGenBaseDemo_Responder_BehaviorName, {c_LGenBaseDemo_Responder_StepName_storeMsg, refers(f_LGenBaseDemo_Responder_storeMsg)}) |
| f_EPTF_LGenBase_declareStep(c_LGenBaseDemo_Responder_BehaviorName, {c_LGenBaseDemo_Responder_StepName_ackReceived, refers(f_LGenBaseDemo_Responder_ackReceived)}) |
| f_EPTF_LGenBase_declareStep(c_LGenBaseDemo_Responder_BehaviorName, {c_LGenBaseDemo_Responder_StepName_inputInInvalidState, refers(f_LGenBaseDemo_Responder_InputInInvalidState)}) |
| |
| // Declare the custom function(s) used in the responder FSMs |
| f_EPTF_LGenBase_declareFunction(c_LGenBaseDemo_Responder_NextStateCalcName, {nextStateCalcFunction := refers(f_LGenBaseDemo_Responder_calcNextStateOfEvent)}) |
| |
| // Declare the group of entities used to simulate the users |
| f_EPTF_LGenBase_declareEntityType(c_LGenBaseDemo_Responder_entityTypeName, {c_LGenBaseDemo_Responder_BehaviorName}) |
| f_EPTF_LGenBase_createEntityGroup({c_LGenBaseDemo_Responder_entityGroupName, c_LGenBaseDemo_Responder_entityTypeName, pl_eGrpSize}) |
| |
| //Declare the responder FSM |
| f_EPTF_LGenBase_declareFSMTable(f_LGenBaseDemo_ResponderFSM()) |
| |
| //Declare the traffic case type to use it in the scenario declaration |
| v_dummyInt := f_EPTF_LGenBase_declareTcType2( |
| { |
| name := c_LGenBaseDemo_Responder_tcName, |
| fsmName := c_LGenBaseDemo_Responder_fsmName, |
| entityType := c_LGenBaseDemo_Responder_entityTypeName, |
| customEntitySucc := "" |
| } |
| ) |
| |
| //Declare the scenario |
| //It's a simple responder, there is nothing interesting |
| f_EPTF_LGenBase_declareScenarioType3({ |
| name := c_LGenBaseDemo_Responder_scenarioName, |
| tcList := { |
| { |
| tcName := c_LGenBaseDemo_Responder_tcName, |
| tcParamsList := {} |
| } |
| }, |
| scParamsList := { |
| {trafficType := terminating} |
| } |
| }) |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Describes the FSM of the Responder. |
| // |
| // The basic flow is: |
| // - When a message arrives, the f_LGenBaseDemo_Responder_processCallerMsgs |
| // function dispatches an event, depending on the type of the message to |
| // the first available entity. |
| // - The FSM |
| // - notifies the LGenBase that it's started by an external event, |
| // - stores the received message into an FSM variable, |
| // - sets the timeout value of an FSM timer to a random value, |
| // - starts the timer, and |
| // - puts the FSM to a state depending on the type of the received message. |
| // - When the timer timed out, the Responder |
| // - sends out the appropriate response message, and |
| // - starts a watchdog timer |
| // - When the acknowledgement of the response message arrives, the FSM |
| // - cancels the watchdog timer, and |
| // - reports successful execution to free the entity, and |
| // - puts back the FSM to the "idle" state. |
| // - If the watchdog timed out, the FSM |
| // - reports timed out execution, and |
| // - puts back the FSM to the "idle" state. |
| // |
| // To demonstrate how to handle an event in separated FSM rows, the |
| // timeout of the "responseTimer" is handled in two rows. |
| // First row sends out the responses, the next row starts the watchdog. |
| /////////////////////////////////////////////////////////////////////////////// |
| function f_LGenBaseDemo_ResponderFSM() |
| return EPTF_LGenBase_FsmTableDeclarator{ |
| return { |
| name := c_LGenBaseDemo_Responder_fsmName, |
| fsmParams := { |
| {stateList := c_LGenBaseDemo_Responder_fsmStateList}, |
| {varList := { |
| { |
| name := c_LGenBaseDemo_Responder_varNameOfLastMsg, |
| initValue := {charstringVal := ""}, |
| scope := FSM |
| }} |
| }, |
| {timerList := { |
| {name := "responseTimer", startValue := 0.1}, |
| {name := "watchdog", startValue := c_LGenBaseDemo_Responder_Watchdog} |
| }} |
| }, |
| table := { |
| extendedTable := { |
| { |
| //This row replies to the 'Register', 'ReRegister' and 'DoMsg' events |
| events2Listen := { |
| events := { |
| { |
| eventList := { |
| bName := c_LGenBaseDemo_Responder_BehaviorName, |
| iNameList := { |
| c_LGenBaseDemo_Responder_InputRegisterReceived, |
| c_LGenBaseDemo_Responder_InputReRegisterReceived, |
| c_LGenBaseDemo_Responder_InputDoMsgReceived |
| }, |
| eventType := fsm |
| } |
| } |
| } |
| }, |
| cellRow := { |
| statedCellRow := { |
| //If they arrive in the 'idle' state, it's OK |
| { inState := { state := "idle"}, |
| cell := { actionList := { |
| // Notify the LGenBase that the entity had been started by an external event. |
| { stepOrFunctionName := c_EPTF_LGenBase_stepName_recordEntityStart, contextArgs := omit}, |
| // Store the message content into an FSM variable, and |
| // the required next state to the behavior context of the entity. |
| // See the f_LGenBaseDemo_Responder_storeMsg function. |
| { stepOrFunctionName := c_LGenBaseDemo_Responder_StepName_storeMsg, contextArgs := omit}, |
| // Set the timeout value of the "responseTimer" to a random value. |
| { stepOrFunctionName := c_LGenBaseDemo_Responder_StepName_delay, contextArgs := {timerName := "responseTimer"}}, |
| // Start the timer |
| { stepOrFunctionName := c_EPTF_LGenBase_stepName_timerStart, contextArgs := {timerName := "responseTimer"}} |
| }, |
| // The next state calculation function gives back the state to put in the FSM |
| // based on the data stored by the f_LGenBaseDemo_Responder_storeMsg function. |
| // See the f_LGenBaseDemo_Responder_calcNextStateOfEvent function. |
| nextStateCalculation := {c_LGenBaseDemo_Responder_NextStateCalcName, omit}, |
| nextState := omit |
| }}, |
| //In any other state it's a programming fault. |
| { inState := { anyUndefinedState := {}}, |
| cell := { actionList := { |
| //Cancel all running FSM timers, orelse they'd cause errors!!! |
| {stepOrFunctionName := c_EPTF_LGenBase_stepName_cancelAllTimers,contextArgs := omit}, |
| //Handle the error |
| {stepOrFunctionName := c_LGenBaseDemo_Responder_StepName_inputInInvalidState,contextArgs := omit}, |
| // Report erroneous execution |
| {stepOrFunctionName := c_EPTF_LGenBase_stepName_trafficError,contextArgs := omit} |
| }, |
| nextStateCalculation := omit, |
| nextState := "idle" |
| }} |
| }} |
| }, |
| { |
| //Listens to the timeout of the started "responseTimer". |
| //This row sends out the responses. Since the responses |
| //are different in different states, the FSM handles the |
| //event in different cells. |
| //The next row listens to the same event in the same states, |
| //therefore this row leaves the FSM in its current state |
| events2Listen := { events := {{ singleEvent := { |
| bName := c_EPTF_LGenBase_specialBName_timerTimeout, |
| iName := "responseTimer", |
| eventType := fsm |
| }}}}, |
| cellRow := { statedCellRow := { |
| { inState := { state := "inRegister"}, |
| cell := { |
| actionList := { |
| {c_LGenBaseDemo_Responder_StepName_reply2Register, omit} |
| }, |
| //Stay in the same state yet |
| nextStateCalculation := omit, |
| nextState := omit |
| }}, |
| { inState := { state := "inReRegister"}, |
| cell := { actionList := { |
| {c_LGenBaseDemo_Responder_StepName_reply2ReRegister, omit} |
| }, |
| nextStateCalculation := omit, |
| nextState := omit |
| }}, |
| { inState := { state := "inDoMsg"}, |
| cell := { actionList := { |
| {c_LGenBaseDemo_Responder_StepName_reply2DoMsg, omit} |
| }, |
| nextStateCalculation := omit, |
| nextState := omit |
| }}, |
| //In any other state it's a programming fault. |
| { inState := { anyUndefinedState := {}}, |
| cell := { actionList := { |
| {c_EPTF_LGenBase_stepName_cancelAllTimers, omit}, |
| {c_LGenBaseDemo_Responder_StepName_inputInInvalidState, omit}, |
| {c_EPTF_LGenBase_stepName_trafficError, omit} |
| }, |
| nextStateCalculation := omit, |
| nextState := "idle" |
| }} |
| }} |
| }, |
| { |
| //Listens to the timeout of the started "responseTimer", |
| //just like the previous row. This case both rows will be processed |
| //in their declaration order. |
| //This row starts a watchdog timer in the |
| //states "inRegister","inReRegister","inDoMsg". |
| events2Listen := { events := {{ singleEvent := { |
| bName := c_EPTF_LGenBase_specialBName_timerTimeout, |
| iName := "responseTimer", |
| eventType := fsm |
| }}}}, |
| cellRow := { statedCellRow := { |
| { inState := { stateList := {"inRegister","inReRegister","inDoMsg"}}, |
| cell := { |
| actionList := { |
| {c_EPTF_LGenBase_stepName_timerStart, {timerName := "watchdog"}} |
| }, |
| nextStateCalculation := omit, |
| nextState := "waiting4Ack" |
| }} |
| }} |
| }, |
| { |
| // Listens to the acknowledgment of the respons messages |
| events2Listen := { events := {{ singleEvent := { |
| bName := c_LGenBaseDemo_Responder_BehaviorName, |
| iName := c_LGenBaseDemo_Responder_InputAckReceived, |
| eventType := fsm |
| }}}}, |
| cellRow := { statedCellRow := { |
| { inState := { state := "waiting4Ack"}, |
| cell := { actionList := { |
| // Cancel the watchdog timer |
| {c_EPTF_LGenBase_stepName_timerCancel, {timerName := "watchdog"}}, |
| // Reply successful or failed execution |
| // depending on the content of the message |
| {c_LGenBaseDemo_Responder_StepName_ackReceived, omit} |
| }, |
| nextStateCalculation := omit, |
| nextState := "idle" |
| }}, |
| { inState := { anyUndefinedState := {}}, |
| cell := { actionList := { |
| {c_EPTF_LGenBase_stepName_cancelAllTimers, omit}, |
| {c_LGenBaseDemo_Responder_StepName_inputInInvalidState, omit} |
| // Don't report the finish of the execution here! |
| // Since the entity is not in state "waiting4Ack", |
| // Probably it isn't started or finished already. |
| // That would cause assertion fail. |
| //{c_EPTF_LGenBase_stepName_trafficError, omit} |
| }, |
| nextStateCalculation := omit, |
| nextState := "idle" |
| }} |
| } |
| }//cellRow |
| }, |
| { |
| // Handle the watchdog timeout |
| events2Listen := { events := {{ singleEvent := { |
| bName := c_EPTF_LGenBase_specialBName_timerTimeout, |
| iName := "watchdog", |
| eventType := fsm |
| }}}}, |
| cellRow := { statedCellRow := { |
| { inState := { state := "waiting4Ack"}, |
| cell := { actionList := { |
| {c_EPTF_LGenBase_stepName_trafficTimeout, omit} |
| }, |
| nextStateCalculation := omit, |
| nextState := "idle" |
| }}, |
| { inState := { anyUndefinedState := {}}, |
| cell := { actionList := { |
| {c_EPTF_LGenBase_stepName_cancelAllTimers, omit}, |
| {c_LGenBaseDemo_Responder_StepName_inputInInvalidState, omit}, |
| {c_EPTF_LGenBase_stepName_trafficError, omit} |
| }, |
| nextStateCalculation := omit, |
| nextState := "idle" |
| }} |
| } |
| }//cellRow |
| }, |
| { |
| // Handle all unexpected events |
| events2Listen := { unhandled := {}}, |
| cellRow := { statedCellRow := { |
| { inState := { anyUndefinedState := {}}, |
| cell := { actionList := { |
| {c_EPTF_LGenBase_stepName_cancelAllTimers, omit}, |
| {c_LGenBaseDemo_Responder_StepName_inputInInvalidState, omit}, |
| {c_EPTF_LGenBase_stepName_trafficError, omit} |
| }, |
| nextStateCalculation := omit, |
| nextState := "idle" |
| }} |
| } |
| }//cellRow |
| } |
| } |
| } |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // The Responder FSM starts a timer when a Caller message arrives, and |
| // puts the FSM to the appropriate state depending on the type of the |
| // incoming event. |
| // See the FSM declaration in the f_LGenBaseDemo_ResponderFSM function. |
| // |
| // This function returns the index of the state where the FSM must be put. |
| // See also the f_LGenBaseDemo_Responder_storeMsg function. |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_state4Event(in integer pl_eventId) |
| runs on LGenBaseDemo_Responder_CT |
| return integer{ |
| f_EPTF_Base_assert(%definitionId&": Invalid event ID:"&int2str(pl_eventId), pl_eventId > -1 and pl_eventId < sizeof(c_LGenBaseDemo_Responder_fsmStateList4Events)) |
| return c_LGenBaseDemo_Responder_fsmStateList4Events[pl_eventId] |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Handle the timeout. |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_WatchdogTimerTimedOut(in EPTF_LGenBase_TestStepArgs pl_args) |
| runs on LGenBaseDemo_Responder_CT{ |
| log(%definitionId&": The 'watchdog' timer timed out.") |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Handle the case when an event arrives in a state where it shouldn't have come. |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_InputInInvalidState(in EPTF_LGenBase_TestStepArgs pl_args) |
| runs on LGenBaseDemo_Responder_CT{ |
| log(%definitionId&": '"& |
| f_EPTF_LGenBase_iIdx2Str(pl_args.reportedEvent.event.bIdx, pl_args.reportedEvent.event.iIdx)& |
| "' event arrived in state "&f_EPTF_LGenBase_getFsmStateNameByStepArgs(pl_args)& |
| ". Entity#"&int2str(pl_args.eIdx)) |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Reports succesful or failed execution depending on the step arguments |
| // of the received event. |
| // This function demonstrates how to call step functions from step functions, |
| // and how to retrieve step arguments reported in the <f_EPTF_LGenBase_dispatchEvent>. |
| // See the f_LGenBaseDemo_Responder_processAck for the demo how to dispatch |
| // the event with step arguments. |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_ackReceived(in EPTF_LGenBase_TestStepArgs pl_args) |
| runs on LGenBaseDemo_Responder_CT{ |
| if(1 == pl_args.stepArgs[0]){ |
| f_EPTF_LGenBase_step_trafficSuccess(pl_args) |
| }else{ |
| f_EPTF_LGenBase_step_trafficFailed(pl_args) |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Retrieves the content of a message. The content is stored in an FSM variable. |
| // See also the f_LGenBaseDemo_Responder_storeMsg function. |
| // This function demonstrates how to retrive the content of an FSM variable |
| // from an FSM step function. |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_getLastMsg(in EPTF_LGenBase_TestStepArgs pl_args) |
| runs on LGenBaseDemo_Responder_CT |
| return charstring{ |
| // Retrieve the name of the variable |
| // The variables of the FSMs have special names generated from the name declared |
| // in the FSM declaration. Don't create these names "manualy", |
| // use the function <f_EPTF_LGenBase_varNameOfFSMVar> instead. |
| var charstring vl_varNameOfMsg := f_EPTF_LGenBase_varNameOfFSMVar( |
| pl_args.eIdx, |
| pl_args.refContext.fCtxIdx, |
| c_LGenBaseDemo_Responder_varNameOfLastMsg) |
| //The ID from the name |
| var integer vl_varIdOfMsg := f_EPTF_Var_getId(vl_varNameOfMsg) |
| var EPTF_Var_DirectContent vl_content |
| //Get the content |
| f_EPTF_Var_getContent(vl_varIdOfMsg, vl_content) |
| return vl_content.charstringVal |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // This function demonstrates how to store simple temporary data during an |
| // FSM process |
| // It can be used when the data to be stored contains integer values. |
| // This case the data can be stored in the behavior context of the entity. |
| // This is a fast way. |
| // |
| // LGenBase provides also many more data storage methods. |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_storeNextState(in EPTF_LGenBase_TestStepArgs pl_args) |
| runs on LGenBaseDemo_Responder_CT{ |
| //Store the indices as behavior context data |
| f_EPTF_LGenBase_setBehaviorCtx( |
| pl_args.eIdx, |
| v_LGenBaseDemo_Responder_myBehavId, |
| { |
| pl_args.reportedEvent.event.iIdx, |
| f_LGenBaseDemo_Responder_state4Event(pl_args.reportedEvent.event.iIdx) |
| }) |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // This function demonstrates how to store temporary data during an |
| // FSM process when the data to be stored contains other data than integer values. |
| // This case the data can be stored in an FSM variable. |
| // The data can be any type handled in the <EPTF_Var_DirectContent>. |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_storeMsg(in EPTF_LGenBase_TestStepArgs pl_args) |
| runs on LGenBaseDemo_Responder_CT{ |
| //Store the indices of the next state as behavior context data |
| f_EPTF_LGenBase_setBehaviorCtx( |
| pl_args.eIdx, |
| v_LGenBaseDemo_Responder_myBehavId, |
| { |
| pl_args.reportedEvent.event.iIdx, |
| f_LGenBaseDemo_Responder_state4Event(pl_args.reportedEvent.event.iIdx) |
| }) |
| //Store the incoming message in an FSM variable |
| var charstring vl_varNameOfMsg := f_EPTF_LGenBase_varNameOfFSMVar( |
| pl_args.eIdx, |
| pl_args.refContext.fCtxIdx, |
| c_LGenBaseDemo_Responder_varNameOfLastMsg) |
| var integer vl_varIdOfMsg := f_EPTF_Var_getId(vl_varNameOfMsg) |
| var EPTF_Var_DirectContent vl_content |
| vl_content.charstringVal := f_LGenBaseDemo_Transport_getLastMsg() |
| f_EPTF_Var_adjustContent(vl_varIdOfMsg, vl_content) |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Retrieves the data stored by the f_LGenBaseDemo_Responder_storeMsg |
| // function in the behavior context of the related entity. |
| /////////////////////////////////////////////////////////////////////////////// |
| function f_LGenBaseDemo_Responder_calcNextStateOfEvent( |
| in integer eIdx, |
| in integer fIdx, |
| in EPTF_IntegerList contextArgs, |
| in EPTF_LGenBase_FsmTableCellReference pl_cell, |
| in EPTF_IntegerList pl_stepArgs) |
| runs on LGenBaseDemo_Responder_CT |
| return integer{ |
| return f_EPTF_LGenBase_getBehaviorCtxItem(eIdx, v_LGenBaseDemo_Responder_myBehavId, c_LGenBaseDemo_Responder_behavCtxDataIdxNextStateIdx) |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // This function demonstrates how to set the timeout value of an FSM timer |
| // when the context argument contains the name of the timer. |
| // |
| // When the context argument has been declared in the FSM declaration as |
| // contextArgs := {timerName := "anyTimerName"} |
| // the 1st (idx==0) element of the fRefArgs <EPTF_IntegerList> field |
| // of the refContext field of the <EPTF_LGenBase_TestStepArgs> |
| // type pl_args parameter of the step function contains the index of the |
| // specified timer. |
| // |
| // See also <f_LGenBaseDemo_ResponderFSM> |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_Delay(in EPTF_LGenBase_TestStepArgs pl_args) |
| runs on LGenBaseDemo_Responder_CT{ |
| //Get the timer idx |
| var integer vl_timerIdx := pl_args.refContext.fRefArgs[0] |
| //Set the duration |
| f_EPTF_LGenBase_setDuration4TimerAtFsmCtx( |
| pl_args.eIdx, |
| pl_args.refContext.fCtxIdx, |
| vl_timerIdx, |
| rnd()*v_LGenBaseDemo_Responder_maxDelay) |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Gets the first available entity and dispatches an FSM event to it |
| // about the incoming external event. |
| // See also the f_LGenBaseDemo_Responder_processCallerMsgs function. |
| // The message ID - entity index relation is stored in a hashmap. |
| // |
| // Demonstrates how to |
| // - find an available (free) entity |
| // - get the FSM context index of an entity associated to a traffic case |
| // - dispatch an FSM event to a specific FSM |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_dispatchToEntity(in integer pl_inputId) |
| runs on LGenBaseDemo_Responder_CT{ |
| var integer vl_eIdx := f_EPTF_LGenBase_getNextFreeEntity(v_LGenBaseDemo_Responder_responderTcIdx) |
| if(-1 < vl_eIdx){ |
| var EPTF_LGenBase_ReportedEventDescriptor vl_event := c_EPTF_LGenBase_emptyReportedEventDescriptor |
| vl_event.event.bIdx := v_LGenBaseDemo_Responder_myBehavId |
| vl_event.event.iIdx := pl_inputId |
| vl_event.event.target.eIdx := vl_eIdx |
| vl_event.event.target.fsmCtxIdx := f_EPTF_LGenBase_getFSMCtxIdx(vl_eIdx, v_LGenBaseDemo_Responder_responderTcIdx) |
| log(%definitionId&": Event "&f_EPTF_LGenBase_iIdx2Str(v_LGenBaseDemo_Responder_myBehavId,pl_inputId)& |
| " dispatched to entity#"&int2str(vl_eIdx)) |
| f_EPTF_LGenBase_dispatchEvent(vl_event) |
| }else{ |
| //Error |
| log(%definitionId&": There is no free entity to process the message. Allocate more!") |
| f_EPTF_Base_stopAll(fail) |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Processes an incoming acknowledgement message, and dispatches an FSM event |
| // about it. The value of the response code is dispatched in the step argument |
| // of the FSM event. |
| // Demonstrates how to |
| // - attach data to the step argument of the dispatched event |
| // - dispatch an event |
| // - get the FSM context index of an entity associated to a traffic case |
| // To see how to retrieve the step arguments see the |
| // f_LGenBaseDemo_Responder_ackReceived function. |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_processAck(in LGenBaseDemo_MsgAck pl_msg) |
| runs on LGenBaseDemo_Responder_CT{ |
| var integer vl_eIdx |
| log(%definitionId&": Ack message arrived: "&log2str(pl_msg)) |
| //Get entity index from the message |
| if(not f_EPTF_int2int_HashMap_Find(v_LGenBaseDemo_Responder_msgHashMap, pl_msg.msgId, vl_eIdx)){ |
| f_EPTF_Logging_warning(true,%definitionId&": Invalid message ID in Ack msg: "&log2str(pl_msg)) |
| }else{ |
| //Found |
| log(%definitionId&": The message handled by entity#"&int2str(vl_eIdx)) |
| //Dispatch event to the FSM of the appropriate traffic case |
| var EPTF_LGenBase_ReportedEventDescriptor vl_event := c_EPTF_LGenBase_emptyReportedEventDescriptor |
| vl_event.event.bIdx := v_LGenBaseDemo_Responder_myBehavId |
| vl_event.event.iIdx := c_LGenBaseDemo_Responder_InputIdxAckReceived |
| vl_event.event.target.eIdx := vl_eIdx |
| vl_event.event.target.fsmCtxIdx := f_EPTF_LGenBase_getFSMCtxIdx(vl_eIdx, v_LGenBaseDemo_Responder_responderTcIdx) |
| //The result of the Ack will be passed through the reportedArgs |
| if(pl_msg.resp){ |
| vl_event.reportedArgs := {1} |
| }else{ |
| vl_event.reportedArgs := {0} |
| } |
| f_EPTF_int2int_HashMap_Erase(v_LGenBaseDemo_Responder_msgHashMap, pl_msg.msgId) |
| f_EPTF_LGenBase_dispatchEvent(vl_event) |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // The functions placed into this group have no demo content, but necessary |
| // for the application. |
| /////////////////////////////////////////////////////////////////////////////// |
| group noDemoContent{ |
| /////////////////////////////////////////////////////////////////////////////// |
| // Send response to the Register message, and sets the registration state |
| // Sets the verdict to fail if a Register call arrived to a user already |
| // registered successfully. |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_reply2Register(in EPTF_LGenBase_TestStepArgs pl_args) |
| runs on LGenBaseDemo_Responder_CT{ |
| var boolean vl_resp := (rnd()<v_LGenBaseDemo_Responder_registerSuccessRate) |
| var charstring vl_msgIn := f_LGenBaseDemo_Responder_getLastMsg(pl_args) |
| var charstring vl_userData := f_LGenBaseDemo_Caller_getUserData(vl_msgIn) |
| var LGenBaseDemo_Transport_MessageData vl_msgData |
| f_LGenBaseDemo_Caller_decodeUserData(vl_msgIn, vl_msgData) |
| if(f_LGenBaseDemo_Responder_getUserState(vl_msgData.entityIdx)){ |
| setverdict(fail, log2str("The user #"&int2str(vl_msgData.entityIdx)&" had been registered earlier.")); |
| }else{ |
| f_LGenBaseDemo_Responder_setUserStateSuccess(vl_msgData.entityIdx,vl_resp) |
| f_LGenBaseDemo_Responder_sendResponse(vl_userData, vl_resp, c_LGenBaseDemo_ResponseType_register, pl_args.eIdx) |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Send response to the ReRegister message |
| // Sets the verdict to fail if a ReRegister call arrived, but the user had not |
| // been registered successfully. |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_reply2ReRegister(in EPTF_LGenBase_TestStepArgs pl_args) |
| runs on LGenBaseDemo_Responder_CT{ |
| var boolean vl_resp := (rnd()<v_LGenBaseDemo_Responder_reRegisterSuccessRate) |
| var charstring vl_msgIn := f_LGenBaseDemo_Responder_getLastMsg(pl_args) |
| var charstring vl_userData := f_LGenBaseDemo_Caller_getUserData(vl_msgIn) |
| var LGenBaseDemo_Transport_MessageData vl_msgData |
| f_LGenBaseDemo_Caller_decodeUserData(vl_msgIn, vl_msgData) |
| if(f_LGenBaseDemo_Responder_getUserState(vl_msgData.entityIdx)){ |
| f_LGenBaseDemo_Responder_sendResponse(vl_userData, vl_resp, c_LGenBaseDemo_ResponseType_reRegister, pl_args.eIdx) |
| }else{ |
| setverdict(fail, log2str("ReRegister received for user #"&int2str(vl_msgData.entityIdx)&" which had not been registered earlier.")); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Send response to the DoMsg message |
| // Sets the verdict to fail if a ReRegister call arrived, but the user had not |
| // been registered successfully. |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_Reply2DoMsg(in EPTF_LGenBase_TestStepArgs pl_args) |
| runs on LGenBaseDemo_Responder_CT{ |
| var boolean vl_resp := (rnd()<v_LGenBaseDemo_Responder_doMsgSuccessRate) |
| var charstring vl_msgIn := f_LGenBaseDemo_Responder_getLastMsg(pl_args) |
| var charstring vl_userData := f_LGenBaseDemo_Caller_getUserData(vl_msgIn) |
| var LGenBaseDemo_Transport_MessageData vl_msgData |
| f_LGenBaseDemo_Caller_decodeUserData(vl_msgIn, vl_msgData) |
| log(%definitionId&log2str(vl_msgData), " ", vl_userData) |
| log(v_LGenBaseDemo_Responder_userStates[vl_msgData.entityIdx]) |
| log(v_LGenBaseDemo_Responder_userStates) |
| if(f_LGenBaseDemo_Responder_getUserState(vl_msgData.entityIdx)){ |
| f_LGenBaseDemo_Responder_sendResponse(vl_userData, vl_resp, c_LGenBaseDemo_ResponseType_doMsg, pl_args.eIdx) |
| }else{ |
| setverdict(fail, "DoMsg received for user #"&int2str(vl_msgData.entityIdx)&" which had not been registered earlier.") |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Returns the registration state of a user |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_getUserState(in integer pl_userIdx) |
| runs on LGenBaseDemo_Responder_CT |
| return boolean{ |
| return isvalue(v_LGenBaseDemo_Responder_userStates[pl_userIdx]) and v_LGenBaseDemo_Responder_userStates[pl_userIdx] |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Sets the registration state of a user |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_setUserStateSuccess(in integer pl_userIdx, in boolean pl_success) |
| runs on LGenBaseDemo_Responder_CT{ |
| v_LGenBaseDemo_Responder_userStates[pl_userIdx] := pl_success |
| } |
| const float c_LGenBaseDemo_Responder_Watchdog := 0.5 |
| |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Cleanup of the Responder component |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_cleanup() |
| runs on LGenBaseDemo_Responder_CT{ |
| f_EPTF_int2int_HashMap_Delete(c_LGenBaseDemo_Responder_msgHashMapName) |
| v_LGenBaseDemo_Responder_msgHashMap := -1 |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Fills in a response message related to the message type, and |
| // sends out the message. |
| /////////////////////////////////////////////////////////////////////////////// |
| public function f_LGenBaseDemo_Responder_sendResponse( |
| in charstring pl_msgUserData, |
| in boolean pl_response, |
| in LGenBaseDemo_ResponseType pl_msgType, |
| in integer pl_eIdx) |
| runs on LGenBaseDemo_Responder_CT{ |
| var integer vl_msgId := v_LGenBaseDemo_Responder_msgCounter |
| v_LGenBaseDemo_Responder_msgCounter := v_LGenBaseDemo_Responder_msgCounter + 1 |
| f_EPTF_int2int_HashMap_Insert(v_LGenBaseDemo_Responder_msgHashMap, vl_msgId, pl_eIdx) |
| var LGenBaseDemo_Msg vl_msg |
| select( pl_msgType ){ |
| case ( c_LGenBaseDemo_ResponseType_register ){ |
| vl_msg.registerResp.msgId := vl_msgId |
| vl_msg.registerResp.resp := pl_response |
| vl_msg.registerResp.msgUserData := pl_msgUserData |
| log(%definitionId&": Registration result: "&log2str(pl_response)&" UserData: "&pl_msgUserData) |
| } |
| case ( c_LGenBaseDemo_ResponseType_reRegister ){ |
| vl_msg.reRegisterResp.msgId := vl_msgId |
| vl_msg.reRegisterResp.resp := pl_response |
| vl_msg.reRegisterResp.msgUserData := pl_msgUserData |
| } |
| case ( c_LGenBaseDemo_ResponseType_doMsg ){ |
| vl_msg.doMsgResp.msgId := vl_msgId |
| vl_msg.doMsgResp.resp := pl_response |
| vl_msg.doMsgResp.msgUserData := pl_msgUserData |
| } |
| case else{ |
| //TODO error |
| } |
| } |
| log(%definitionId&": The message_id#"&int2str(vl_msgId)&" is handled by entity#"&int2str(pl_eIdx)) |
| f_LGenBaseDemo_Transport_sendMessage(vl_msg) |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Processes an incoming message, and calls the |
| // f_LGenBaseDemo_Responder_dispatchToEntity function to dispatch the |
| // appropriate event to an entity. |
| /////////////////////////////////////////////////////////////////////////////// |
| private function f_LGenBaseDemo_Responder_processCallerMsgs(in LGenBaseDemo_Msg pl_msg) |
| runs on LGenBaseDemo_Responder_CT{ |
| log(%definitionId&": "&log2str(pl_msg)) |
| if ( ischosen(pl_msg.register) ){ |
| f_LGenBaseDemo_Responder_dispatchToEntity(c_LGenBaseDemo_Responder_InputIdxRegisterReceived) |
| } |
| else if ( ischosen(pl_msg.reRegister) ){ |
| f_LGenBaseDemo_Responder_dispatchToEntity(c_LGenBaseDemo_Responder_InputIdxReRegisterReceived) |
| } |
| else if ( ischosen(pl_msg.doMsg) ){ |
| f_LGenBaseDemo_Responder_dispatchToEntity(c_LGenBaseDemo_Responder_InputIdxDoMsgReceived) |
| } |
| else if ( ischosen(pl_msg.msgAck) ){ |
| f_LGenBaseDemo_Responder_processAck(pl_msg.msgAck) |
| } else { |
| //TODO Error |
| } |
| } |
| } |
| |
| } // end of module
|