Consider a simple vending machine (VM) from which we can
get Pepsi and Coke. Figure 3.2 illustrates the state
transition diagram of VM we are considering.
There are three input events such as ?dollar for ``input a
dollar'', ?pepsi_btn for ``push the Pepsi button'',
?coke_btn for ``push the Coke button''. Similarly, we can
model three output events such as !dollar for ``a dollar
out (because of timeout of menu selection)'', !pepsi for
``Pepsi out'' and !coke for ``Coke out'. 4.1 The state of VM can be either Idle
for ``Idle'', Wait for ``Wait''(that is waiting for
selection of Pesi or Coke), O_Pepsi for ``output Pepsi''
and O_Coke for ``output Coke''. And their life times are:
15 time units for Wait, 2 time unites for both
O_Pepsi and O_Coke,
for Idle which
is denoted by inf in Figure 3.2. 4.2
At the beginning (t=0), VM is at Idle. If we put
?dollar in, it changes the state into Wait
simultaneously updating
and
for the state. While
in the state, if VM receives ?pepsi_btn (resp.
?coke_btn), it enters into the state O_Pepsi (resp.
O_Coke) and simultaneously updates
and
.
While in the state O_Pepsi or O_Coke, VM
ignores any input and preserves the state. Similarly, while in the
state Wait, VM ignores ?dollar input.
After staying at Wait for 15 time unites, VM returns
to Idle state and outputs the dollar if we don't select
Pepi or Coke within the 15 time units. However, if we had selected
one of them, VM changes its state into O_Pepsi
(resp. O_Coke). Then after 2 time unites, VM outputs
!pepsi (resp. !coke) and returns to Idle.
The example of Ex_VendingMachine shows an atomic DEVS model
of VM. First of all, there are some constant strings we use for
describing states as follows.
const string IDLE="Idle";
const string WAIT="Wait";
const string O_PEPSI="O_Pepsi";
const string O_COKE="O_Coke";
The class VM has three input port pointers idollar,
pepsi_btn and coke_btn; three output port pointers
odollar, pepsi, coke, all assigned by
returning values of the AddIP and AddOP functions in
the constructor.
class VM: public Atomic {
public:
InputPort * idollar, *pepsi_btn, *coke_btn;
OutputPort * odollar, *pepsi, *coke;
VM(const string& name=""): Atomic(name)
{
idollar = AddIP("dollar");
pepsi_btn = AddIP("pepsi_btn");
coke_btn = AddIP("coke_btn");
odollar = AddOP("dollar");
pepsi = AddOP("pepsi");
coke = AddOP("coke");
init();
}
VM's initial state is set to IDLE in init().
The lifespan of each state is defined in tau() as 15, 2, 2,
and infinity for WAIT, O_PEPSI, O_COKE, and
IDLE, respectively.
/*virtual*/ void init()
{
m_phase = IDLE;
}
/*virtual*/ Time tau() const
{
if(m_phase == WAIT)
return 15;
else if(m_phase == O_PEPSI)
return 2;
else if(m_phase == O_COKE)
return 2;
else
return DBL_MAX;
}
The input transition function delta_x defines every arc
triggered by an input event in Figure 3.2 and returns
true for each such arc. If the input event idollar
arrives while VM is not in state Idle, or if the
input events pepsi_btn or coke_btn arrive while
VM is not in state Wait, delta_x returns
false, and the input is ignored.
/*virtual*/ bool delta_x(const PortValue& x)
{
if(m_phase == IDLE && x.port == idollar){
m_phase = WAIT;
return true;
} else if(m_phase == WAIT && x.port == pepsi_btn) {
m_phase = O_PEPSI;
return true;
} else if(m_phase == WAIT && x.port == coke_btn) {
m_phase = O_COKE;
return true;
}else
return false;
}
The output transition function delta_y defines every arc
generating an output event in Figure 3.2.
/*virtual*/ void delta_y(PortValue& y)
{
if(m_phase == WAIT)
y.Set(odollar);
else if(m_phase == O_PEPSI)
y.Set(pepsi));
else if(m_phase == O_COKE)
y.Set(coke);
m_phase = IDLE;
}
The virtual function Get_s() is also overridden and returns
an m_phase variable that is a string.
/*virtual*/ string Get_s() const
{
return m_phase;
}
protected:
string m_phase;
};
The following example demonstrates the use of a callback function
to inject a user-input into an instance of VM.
PortValue InjectMsg(Devs& md)
{
VM& vm = (VM&)md;
string input;
cout << "[d]ollar [p]epsi_botton [c]oca_botton > " ;
cin >> input;
if(input == "d")
return PortValue(vm.idollar);
else if(input == "p")
return PortValue(vm.pepsi_btn);
else if(input == "c")
return PortValue(vm.coke_btn);
else {
cout <<"Invalid input! Try again! \n";
return PortValue();
}
}
The callback function InjectMsg casts the type of
Devs& md to VM& vm. And the user-input of either
d, p, or c is mapped to
PortValue(vm.idollar), PortValue(vm.pepsi_btn), or
PortValue(vm.coke_btn), respectively.
The last part the the code in Ex_VendingMachine runs the
simulation engine. First we make vm as an instance of
VM, and plug vm into an instance of SRTEngine
with the simulation ending time=10000 using the above callback
function.
void main( void ) {
VM* vm = new VM("VM") ; //-- simulation model
SRTEngine simEngine(*vm, 10000, InjectMsg); // see above function
simEngine.RunConsoleMenu();
delete vm;
}
Let's try the first step. Observe that since
tau(IDLE)=
and the initial t_s=
also, the elapsed time t_e cannot ever reach t_s.
Thus this command step doesn't stop until the
becomes
1000 which is the simulation ending time (unless the user
interrupts the simulation).
In this case, we can stop the simulation run using pause
or p, followed by Enter key. The following screen
shows the situation if we make it pause at 8.859.
(VM:Idle, t_s=inf, t_e=8.859) at 8.859
Let's try inject or i. Then we can see the console
output which is produced by the above InjectMsg(Devs&
md) as follows.
[d]ollar [p]epsi_botton [c]oca_botton >If we input
d, we can see the input causes the state to transition from
Idle to Wait as follows.
(VM:Idle, t_s=inf, t_e=8.859)
--({?dollar,?VM.dollar}, t_c=8.859)-->
(VM:Wait, t_s=15.000, t_e=0.000)
Now, we use continue or c to
resume stepping again. If we want to pause again and inject a menu
selection such as pepsi_btn or coke_btn, we can do
that just like before.
VM model in EX_VendingMachine
in order to add the behavior of rejecting a second dollar
input when VM is the state Wait. To model this,
let's add a state Reject whose lifespan is 0. We define the
output transition Reject as
delta_y(Reject) = (!dollar, Wait). However there are
two ways of rescheduling of t_s and t_e of the the
state Wait when VM comes back to the state. Let's
try each of the following two ways.
t_s=15 and t_e=0.
t_s and t_e to the values they had right
before the input of the additional dollar.