#include "tdcell.h" // header class #include "message.h" // class InternalMessage #include "coupcell.h" // CoupledCell #include "realfunc.h" // calculateWithQuantum #include "strutil.h" #include "JackyDebugStream.h" //jacky-debug-mode /******************************************************************* * TRANSPORT DELAY CELL STATE *******************************************************************/ /****************************************************************** * Method: operator= ******************************************************************/ TDCellState &TDCellState::operator=(TDCellState& thisState) { (AtomicCellState &)*this = (AtomicCellState &)thisState; queueVal = thisState.queueVal; transientVal = thisState.transientVal; recordTime = thisState.recordTime; //copy the saved values of state variables at the beginning of Round 0 vars_round0 = thisState.vars_round0; return *this; } /************************************************************************ * Method: copyState ************************************************************************/ void TDCellState::copyState(BasicState *rhs){ *this = *((TDCellState *) rhs); } /*************************************** * Method: getSize() ***************************************/ int TDCellState::getSize() const{ return sizeof(TDCellState); } /************************************************************************ * TRANSPORT DELAY CELL ************************************************************************/ /******************************************************************* * Method: TransportDelayCell ********************************************************************/ TransportDelayCell::TransportDelayCell( const CellPosition& cellPos, const string &name, const LocalTransAdmin::Function &fn ) : AtomicCell( cellPos, name, fn ){ } /******************************************************************* * Method: initializeCell ********************************************************************/ void TransportDelayCell::initializeCell() { AtomicCell::initializeCell(); //This function requires the cell state to be already created. if ( UseQuantum().Active() ) QuantumValue( Real(UseQuantum().Value()) ); } /******************************************************************* * Method: initFunction ********************************************************************/ Model &TransportDelayCell::initFunction() { PortList::iterator cursor; //To start the simulation, hold in for 0 time and queue the present value in the output queue list. //This will cause all models to evaluate their local transition function at time zero. for (cursor = outNCPortList().begin(); cursor != outNCPortList().end(); cursor++) { string out_port(cursor->second->name()); string in_port(calculateInPort(out_port)); TDCellState::QueueValue QV(out_port, value(in_port)); //create a QueueValueWithFlag, pair< QueueValue, boolFlag > //the default value for the boolFlag is FALSE TDCellState::QueueValueWithFlag QVF(QV, false); //element of TransientQueue //create a QueueValueWithScheduleTime, pair< QueueValue, VTime > //the scheduleTime is set to Zero TDCellState::QueueValueWithScheduleTime QVST(QV, VTime::Zero); //push the QueueValueWithFlag, pair , //onto the transient queue transientVal().push_back( QVF ); //save the StateVars (variables) in AtomicCellState to the StateVars //(vars_round0) in TDCellState recordStateVariables() = cellStateVariables(); //push the QVST on to the Queue queueVal().push_back( TDCellState::QueueElement( VTime::Zero, QVST ) ); } //initialize the RT to Zero setRecordTime( VTime::Zero ); holdIn(AtomicState::active, VTime::Zero); return *this; } /******************************************************************* * Method: externalFunction ********************************************************************/ Model &TransportDelayCell::externalFunction( const MessageBag &msgs ){ //flag to record a time change bool timeChanged; //let's check whether there is a time change (usually time advance, but time //can go back in case of a rollback) if( getRecordTime() != msgs.time() ){ //there is a time change //update the RT setRecordTime( msgs.time() ); //set the "time change" flag to true timeChanged = true; //reset the flags in TransientValue to FALSE for the new time resetTransientFlags( transientVal() ); //save the state variables in the RV recordStateVariables() = cellStateVariables(); } //end if time change else { //set the "time change" flag to false timeChanged = false; } localTransitionConfluent( msgs ); //This is should be removed once the new Cell Devs definition language is //approved and considers multiple messages from external models bool executedLocal = false; VTime delay( static_cast( parent() )->defaultDelay() ) ; //[2006-01-11] //this is the current simulated time, which is also the schedule time for all queue elements VTime actualTime( msgs.time() ); //Before we evaluate the transition functions, reset the state variables using the recorded one cellStateVariables() = recordStateVariables(); list tv; //Jacky: a list of , NewValues! ////////////////////////////////////////////////////////////////////// //Right now, for this implementation, give priority to portInTransitions for ( MessageBag::iterator cursor = msgs.begin(); cursor != msgs.end(); cursor++) { ExternalMessage msg = *((ExternalMessage *)(*cursor)); if( !executedLocal && !isInNCPort( msg.port().name() ) ) { string functionName = inputPortFunction()[ msg.port().name() ]; if (functionName == DEFAULT_FUNCTION_InPort) { PortValue pv(msg.port().name(), msg.value()); tv.push_back(pv); } else { // sino es un PortInTransition valido VirtualPortList *vpl = new VirtualPortList; getOutPorts(vpl); tv = SingleLocalTransAdmin::Instance().evaluate( functionName, neighborhood(), &(inputPortValues()), delay, actualTime, vpl, this, msg.port().name() ) ; delete vpl; }//if executedLocal = true; }//if else if ( executedLocal && !isInNCPort( msg.port().name() ) ) { cerr << "At: " << msgs.time() << endl; cerr << "Warning: Current implementation can not handle multiple messages" << " from external models!!. Some messages have been ignored!" << endl; MASSERTMSG( false, "TDCell::externalFunction() -> check the EV file! Multiple inputs to a cell at the same time!" ); } }//for //If there were no messages in ports that were not neighbor ports if ( !executedLocal ) { VirtualPortList *vpl = new VirtualPortList; getOutPorts(vpl); tv = SingleLocalTransAdmin::Instance().evaluate( localFunction(), neighborhood(), NULL, delay, actualTime, vpl, this ) ; delete vpl; } /////////////////////////////////////////////////////////////////////// Real ltb; // Previous value (P) Real tranValue; // Transient value (T) bool tranValueFlag; // Transient flag (TF) //[2006-01-11] //valueTime is the output time (OT) when the internal event on the queue will be output VTime valueTime = actualTime + delay; list::iterator pvCursor; /****************************************************************************************************************** ** Jacky Note: ** "tv" (a list of ) ---> contains the NewValues calculated from the inputPortFunction & the ** localFunction. ** In the following iteration over the "tv" list, we first get the PreviousValues from the corresponding ** "neighborChange" or "in_XXX" or other input ports in "ltb", and then compare the corresponding NewValue in ** "tv" and the PreviousValue in "ltb" ** 1. if a> Quantization (static or dynamic) is used && the quantized values are different OR ** b> Quantization is NOT used && the values are different ** -> state change happens! insert element onto the Queue ** 2. if dynamic quantization is used, adjust the Quantum size based on the comparison of the quantized values ** ** In my revision, the transientVal list holds temporary values on all output port of the cell ** We will go through the values in the "tv" (the NewValues), compare the three values: ** A> previous value : "ltb" ** B> transient value: value in the TransientQueue ** C> new value : value in the "tv" ** There are 5 possibilities [see document] */ //iterate over all new values for (pvCursor = tv.begin(); pvCursor != tv.end(); pvCursor++){ string in_port = calculateInPort(pvCursor->first); string out_port = calculateOutPort(pvCursor->first); ltb = value(in_port); //Jacky: P tranValue = getTransientValue(out_port); //Jacky: T tranValueFlag = getTransientValueFlag(out_port); //Jacky: TF // [2006-01-06] //The following condition is added to avoid offset results derived from external events //Change back & change further of the cell state is only allowed if no external event received at this time //Once an event is received: // if the event causes a state change -> change the state, and set flag=TRUE, thus no further change // if the event does not cause a change -> just set flag=TRUE, thus no further change as well // for ALL the message-passing rounds at this time. // When time changes, the flag will be reset to false. if( tranValueFlag == false ){ // if flag in T is FALSE, i.e. TF = FALSE //1> new state change happens if( ( ( UseQuantum().Active() || UseDynQuantum().Active() ) && ( valueWithQuantum(pvCursor->second, QuantumValue()) != valueWithQuantum(ltb, QuantumValue()) ) && //N != P ( valueWithQuantum(ltb, QuantumValue()) == valueWithQuantum(tranValue, QuantumValue()) ) //P == T ) || ( !UseQuantum().Active() && ( ltb != pvCursor->second ) && //N != P ( ltb == tranValue ) //P == T ) ) { //a> set T = N if( isInputPort(pvCursor->first) ){ //Value change is derived from external event! //set the flag in the transient value to TRUE setTransientValue(out_port, pvCursor->second, true); } else { setTransientValue(out_port, pvCursor->second); } //end update [T] //b> Queue insertion TDCellState::Queue::iterator cursorAux; for ( cursorAux = queueVal().begin(); cursorAux != queueVal().end() && cursorAux->first <= valueTime; cursorAux++ ); //Jacky: Now cursorAux points to the 1st element with time > valueTime or NULL // the new element will be inserted before cursorAux //Jacky: create a new element with NEW value to be inserted onto the Queue TDCellState::QueueValue qv(out_port, pvCursor->second); //the schedule time is actualTime TDCellState::QueueValueWithScheduleTime qvst(qv, actualTime); //insert the qvst onto the queue at the place given by "cursorAux" //the output time is valueTime queueVal().insert( cursorAux, TDCellState::QueueElement(valueTime, qvst) ); holdIn( AtomicState::active, firstQueuedTime() - msgs.time() ); } //end if 1> [new state change] //2> change back else if( ( ( UseQuantum().Active() || UseDynQuantum().Active() ) && ( valueWithQuantum(pvCursor->second, QuantumValue()) == valueWithQuantum(ltb, QuantumValue()) ) && //N == P ( valueWithQuantum(ltb, QuantumValue()) != valueWithQuantum(tranValue, QuantumValue()) ) && //P != T ( valueWithQuantum(pvCursor->second, QuantumValue()) != valueWithQuantum(tranValue, QuantumValue()) ) //N != T ) || ( !UseQuantum().Active() && ( pvCursor->second == ltb ) && //N == P ( ltb != tranValue ) && //P != T ( pvCursor->second != tranValue ) //N != T ) ) { //a> reset T = P //setTransientValue(out_port, ltb); //[2006-01-11] if( isInputPort(pvCursor->first) ){ //Value change is derived from external event! //set the flag in the transient value to TRUE setTransientValue(out_port, ltb, true); } else { setTransientValue(out_port, pvCursor->second); } //end update [T] //end [2006-01-11] //[2006-01-11] //if this is within the process of multiple-round message passing, we need //to do Queue Removal; if it is not (i.e. getRecordTime() != msgs.time(), //msgTime has changed), we do nothing here! if( !timeChanged ){ //[2006-01-11] //Time has not changed, we are within the multiple rounds at the given time! //Do Queue removal! //b> Queue removal TDCellState::Queue::iterator cursorAux; bool found = false; for ( cursorAux = queueVal().begin(); cursorAux != queueVal().end(); cursorAux++) { //scheduleTime = actualTime if( ( (cursorAux->second).second == actualTime ) && ( (cursorAux->second).first.first == out_port ) ) { //same schedule time & same port found = true; break; } } MASSERTMSG( found, "TDCell::externalFunction()->no element found, ChangeBack:Removal failed!"); queueVal().erase( cursorAux ); if( !queueVal().empty() ){ holdIn( AtomicState::active, firstQueuedTime() - msgs.time() ); } else { passivate(); } } //end if ( !timeChanged ) else { //The time has changed, it's NOT within the multiple rounds! //In this case, since N = P (i.e. No state change), no new element should be //added onto the Queue, nor do we need to remove some element from the Queue //leave the Queue along & no state change! } //end if ( timeChanged ) } //end if 2> [change back] //3> change further else if( ( ( UseQuantum().Active() || UseDynQuantum().Active() ) && ( valueWithQuantum(pvCursor->second, QuantumValue()) != valueWithQuantum(ltb, QuantumValue()) ) && //N != P ( valueWithQuantum(ltb, QuantumValue()) != valueWithQuantum(tranValue, QuantumValue()) ) && //P != T ( valueWithQuantum(pvCursor->second, QuantumValue()) != valueWithQuantum(tranValue, QuantumValue()) ) //N != T ) || ( !UseQuantum().Active() && ( pvCursor->second != ltb ) && //N != P ( ltb != tranValue ) && //P != T ( pvCursor->second != tranValue ) //N != T ) ) { //a> set T = N //setTransientValue(out_port, pvCursor->second); //[2006-01-11] if( isInputPort(pvCursor->first) ){ //Value change is derived from external event! //set the flag in the transient value to TRUE setTransientValue(out_port, pvCursor->second, true); } else { setTransientValue(out_port, pvCursor->second); } //end update [T] //end [2006-01-11] //[2006-01-11] //if this is within the process of multiple-round message passing, we need //to do Queue Replacement; if it is not (i.e. getRecordTime() != msgs.time(), //msgTime has changed), we do Queue Insertion here (i.e. a new state change //happened at a new time)! if( !timeChanged ){ //[2006-01-11] //Time has not changed, we are within the multiple rounds at the given time! //Do Queue replacement! //b> Queue replacement TDCellState::Queue::iterator cursorAux; bool found = false; for ( cursorAux = queueVal().begin(); cursorAux != queueVal().end(); cursorAux++) { //scheduleTime = actualTime if( ( (cursorAux->second).second == actualTime ) && ( (cursorAux->second).first.first == out_port ) ) { //same schedule time & same port found = true; break; } } MASSERTMSG( found, "TDCell::externalFunction()->no element found, ChangeFurther:Replacement failed!"); //replace the value on that port (cursorAux->second).first.second = pvCursor->second; holdIn( AtomicState::active, firstQueuedTime() - msgs.time() ); } //end if ( !timeChanged ) else { //The time has changed, it's NOT within the multiple rounds! //In this case, since N != P (i.e. state change), new element should be //added onto the Queue //insert element on the queue //c> Queue insertion TDCellState::Queue::iterator cursorAux; for ( cursorAux = queueVal().begin(); cursorAux != queueVal().end() && cursorAux->first <= valueTime; cursorAux++ ); //Jacky: Now cursorAux points to the 1st element with time > valueTime or NULL // the new element will be inserted before cursorAux //Jacky: create a new element with NEW value to be inserted onto the Queue TDCellState::QueueValue qv(out_port, pvCursor->second); //the schedule time is actualTime TDCellState::QueueValueWithScheduleTime qvst(qv, actualTime); //insert the qvst onto the queue at the place given by "cursorAux" //the output time is valueTime queueVal().insert( cursorAux, TDCellState::QueueElement(valueTime, qvst) ); holdIn( AtomicState::active, firstQueuedTime() - msgs.time() ); } //end if ( timeChanged ) } //end if 3> [change further] // [ ****************** 2006-01-06 *********************] // Note:If the external event does not cause cell's value changes, i.e. N = P // We still need to set the flag in the TransientValue to TRUE so that further messages // from neighbors will NOT cause new changes to this cell!!! // Ex: At time 04:000, cell's value is 1 (P), and an external event arrives at the cell // at the same time 04:000 with value 1. Thus, the cell's value at time 05:000 (after // a delay of 1000) should be 1 (the same as the external event based on Default-Port-In) // The resulting tv is , which is the same as the event value. In the following // rounds of message-passing, the cell gets values from its neighbors (all 1's), and the // resulting tv is based on Local-Transition. // Since in the 1st round, the event didn't cause state change, the flag in T is NOT set // to TRUE, the cell's value will be changed to 0 in the 2nd round!!! // This is NOT what we want! We need to set the flag in T to TRUE event when the external // event does not trigger a state change in the cell! // The following else clause will set the flag in T to TRUE (but do NOT change the value in T) // if the tv is derived from an event. else { // No state change, N = P if( isInputPort(pvCursor->first) ){ // set the flag in T to TRUE // In this case, external event's value is the same as the current cell's value // No value change! And we will NOT allow any potential urther changes derived // from neighbor's values in the later rounds of message-passing //portName = out_port //value is the current value, getTransientValue(out_port) //TF = true setTransientValue(out_port, getTransientValue(out_port), true); } } } //end "result-NOT-derived-from-external-events", TF = false else { //[2006-01-06] // If TF = TRUE, i.e. the value of the cell has been changed (or is the same as the // external event) according to the external event, do NOTHING! } //If using DynamicQuantum, calculate the new Quantum value. if ( UseDynQuantum().Active() ) { if ( valueWithQuantum(pvCursor->second, QuantumValue()) == valueWithQuantum(ltb, QuantumValue())) {//Jacky: N == P if ( UseDynQuantum().Strat() ) QuantumValue ( QuantumValue() * (1-UseDynQuantum().Ratio())); else QuantumValue ( QuantumValue() * (1+UseDynQuantum().Ratio())); } //GW // Esta es una nueva modificacion: si son distintos, lo reduzco. else { // Jacky: N != P if ( !UseDynQuantum().Strat() ) QuantumValue ( QuantumValue() * (1-UseDynQuantum().Ratio())); else QuantumValue ( QuantumValue() * (1+UseDynQuantum().Ratio())); } } //end if } // for return *this ; } /****************************************************************** * Method: internalFunction * Jacky : This function will remove all elements on the Queue that have * the same time as the received * msg. ********************************************************************/ Model &TransportDelayCell::internalFunction( const InternalMessage &msg ) { MASSERT( !queueVal().empty() && firstQueuedTime() == msg.time() ) ; //Delete all messages that are scheduled for the current time while (!queueVal().empty() && firstQueuedTime() == msg.time() ){ queueVal().erase( queueVal().begin() ); } if( queueVal().empty() ) { passivate() ; } else { holdIn( AtomicState::active, ( firstQueuedTime() - msg.time() ) ); } return *this ; } /******************************************************************* * Method: outputFunction * Jacky : This function will send all elements on the Queue that have * the same time as the time of the received @ msg ********************************************************************/ Model &TransportDelayCell::outputFunction( const CollectMessage &msg ) { //Send all messages that are scheduled for the current time TDCellState::Queue::iterator cursor; for( cursor = queueVal().begin() ; cursor != queueVal().end() && cursor->first == msg.time() ; cursor++ ) { sendOutput( msg.time(), outputPort((cursor->second).first.first), (cursor->second).first.second.value() ) ; } return *this ; } /******************************************************************* * Function Name: firstQueuedTime ********************************************************************/ const VTime & TransportDelayCell::firstQueuedTime() const { return queueVal().begin()->first; } /******************************************************************* * Function Name: firstQueuedValue ********************************************************************/ const Real &TransportDelayCell::firstQueuedValue() const { TDCellState::QueueValue &QV = (queueVal().begin()->second).first; return QV.second; } /******************************************************************* * Function Name: firstQueuedPort ********************************************************************/ const string &TransportDelayCell::firstQueuedPort() const { TDCellState::QueueValue &QV = (queueVal().begin()->second).first; return QV.first; } /******************************************************************* * Function Name: updateRemainingTime * Description : this function is to update the output time for all elements on the Queue to reflect the elapse of time ********************************************************************/ TransportDelayCell & TransportDelayCell::updateRemainingTime( const VTime &elapsed ) { for( TDCellState::Queue::iterator cursor = queueVal().begin() ; cursor != queueVal().end() ; cursor++ ) cursor->first -= elapsed ; return *this; } /******************************************************************* * Function Name: getTransientValue * Description : function to get the transient value on a given port ********************************************************************/ const Real &TransportDelayCell::getTransientValue(const string &portName) const{ TDCellState::TransientQueue::iterator it; bool result = false; for( it = transientVal().begin(); it != transientVal().end() ; it++){ if( (it->first).first == portName){ result = true; break; } } MASSERTMSG( result, "TDCell::getTransientValue() -> no transient value on \"" + portName + "\" is found!" ); return (it->first).second; } //function to get the flag in the transient value const bool TransportDelayCell::getTransientValueFlag(const string &portName) const{ TDCellState::TransientQueue::iterator it; bool result = false; for( it = transientVal().begin(); it != transientVal().end() ; it++){ if( (it->first).first == portName){ result = true; break; } } MASSERTMSG(result, "TDCell::getTransientValueFlag() -> no transient value flag on \"" + portName + "\" is found!"); return it->second; } // [2006-01-06] //function to reset all flags in the transient queue to FALSE, //values in the transient queue are not changed // TD -> list, //where QueueValueWithFlag -> pair, QueueValue -> pair void TransportDelayCell::resetTransientFlags( TDCellState::TransientQueue &tq ){ TDCellState::TransientQueue::iterator it; for(it = tq.begin(); it != tq.end(); it++){ it->second = false; } } /******************************************************************* * Function Name: setTransientValue ********************************************************************/ void TransportDelayCell::setTransientValue(const string &portName, const Real &value, const bool &flag){ TDCellState::TransientQueue::iterator it; for( it = transientVal().begin(); it != transientVal().end() ; it++){ if( (it->first).first == portName ){ (it->first).second = value; it->second = flag; } } }