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.2 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.3
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 which is defined in VendingMachine.cs
file. The class
VM
has three input port idollar
, pepsi_btn
and coke_btn
; three output port odollar
,
pepsi
, coke
, all assigned by returning values of the
AddIP
and AddOP
functions in the constructor.
public class VM : Atomic { public InputPort idollar, pepsi_btn, coke_btn; public OutputPort odollar, pepsi, coke; enum PHASE { Idle, Wait, O_Pepsi, O_Coke } PHASE m_phase; public VM(string name) : base(name, TimeUnit.Sec) { 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
for Wait
, O_Pepsi
, O_Coke
, and
Idle
, respectively.
public override void init() { m_phase = PHASE.Idle; } public override double tau() { if (m_phase == PHASE.Wait) return 15; else if (m_phase == PHASE.O_Pepsi) return 2; else if (m_phase == PHASE.O_Coke) return 2; else return double.MaxValue; }
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.
public override bool delta_x(PortValue x) { if (m_phase == PHASE.Idle && x.port == idollar) { m_phase = PHASE.Wait; return true; // Reschedule Me } else if (m_phase == PHASE.Wait && x.port == pepsi_btn) { m_phase = PHASE.O_Pepsi; return true; // Reschedule Me } else if (m_phase == PHASE.Wait && x.port == coke_btn) { m_phase = PHASE.O_Coke; return true; // Reschedule Me } return false; // Ignore the input }The output transition function
delta_y
defines every arc
generating an output event in Figure 3.2.
public override void delta_y(ref PortValue ys) { if (m_phase == PHASE.Wait) ys.Set(odollar); else if (m_phase == PHASE.O_Pepsi) ys.Set(pepsi); else if (m_phase == PHASE.O_Coke) ys.Set(coke); m_phase = PHASE.Idle; }The virtual function
Get_s()
is also overridden and returns
an m_phase.ToString()
.
public override string Get_s() { return m_phase.ToString(); } }
Since this vending machine example needs the user input during a
simulation run, we need to define a callback function for the user
input. In Program_VM.cs
file, we can see the following
static function.
static PortValue InjectMsg(Devs model) { if (model is VM) { VM vm = (VM)model; Console.Write("[d]ollar [p]epsi_botton [c]oca_botton > "); string input = Console.ReadLine(); if (input == "d") return new PortValue(vm.idollar); else if (input == "p") return new PortValue(vm.pepsi_btn); else if (input == "c") return new PortValue(vm.coke_btn); else { Console.WriteLine("Invalid input! Try again!"); return new PortValue(null,null); } } else throw new Exception("Invalid Model!"); }The callback function
InjectMsg
casts the type of md from
Devs
to 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 Program_VM.cs
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.
static void Main(string[] args) { VM vm = new VM("VM"); SRTEngine Engine = new SRTEngine(vm, 10000, InjectMsg); Engine.RunConsoleMenu(); }
Let's try the command step
. Observe that since the initial
state
of VM
is Idle
and its lifespan
tau(Idle)
=
, and the initial schedule is also
t_s
=
. In this case, the elapsed time t_e
cannot ever reach t_s
. Thus this command step
doesn't stop until
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
command, 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
at 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.