© 2020 (CC BY) Gerd Wagner, Brandenburg University of Technology, Germany.
You can inspect the model's OESjs code on the OES GitHub repo.
A haul service company has resource pools for dump trucks and wheel loaders. While the activities go (back) to loading site, haul, dump and go home just require a truck (or a wheel loader) as a resource, load activities require both a truck and a wheel loader.
The potentially relevant object types are:
Potentially relevant types of events and activities are:
Both object types and event types, together with their participation associations, can be visually described in a conceptual information model in the form of a UML class diagram, as shown below.
Notice that the association end annotations «rr» and «rp» denote resource roles and resource pools. A haul service company has resource pools for trucks and wheel loaders. The activity types haul, dump and go back to loading site have a resource role truck for assigning a truck to any activity of one of those types. The activity types go to loading site and go home have either a a resource role truck or a resource role wheel loader, as indicated by the alternative association constraint expressed with a dashed line annotated with {xor}. The activity type load has both resource role truck and a resource role wheel loaders for assigning at least one and at most two wheel loaders to any load activity (as indicated by the multiplicity "1..2" at the «rr» association end at the class wheel loaders).
The involved types of events and activities can be related with each other via resource-dependent activity start arrows and event scheduling arrows, as shown in the following DPMN process diagram:
Notice that there are three types of arrows in this DPMN diagram:
The model shows that when a haul request comes in, the haul service company deploys multiple trucks and wheel loaders to the loading site, each of them performing a go to loading site activity, as indicated by the double arrow between the haul requests event circle and the go to loading site activity rectangle. Each of these activities leads to enqueuing a new planned load activity, as indicated by the resource-dependent activity start arrow from the go to loading site activity shape to the load activity shape. Such an enqueued (planned) activity is going to be dequeued and started as soon as the required resources become available. This means that as soon as a wheel loader is available, the next load activity is going to be started. When a load activity is completed, a haul activity and then a dump activity are going to start immediately, as indicated by the event scheduling arrows between them.
A more complete model prevents trucks to go back to the loading site and perform a load activity even when the job has been completed during the go back activity (resulting in haul and dump activities with an empty truck). For avoiding this uneconomic behavior, a second decision if the job has been done needs to be taken after the go back activity. In addition, the model has to describe that wheel loaders also go home when their job has been done. This is shown in the following refined model:
In our simulation design, we consider only one particular haul service company, which does not have to be modeled as an explicit object. Also, we abstract away from the fact that also wheel loaders have to go to, and return from, the loading site by assuming that they are already at the site when the dump trucks arrive.
In the information design model, we need to define a status attribute for all resource object types, such as Truck and WheelLoader, and a duration function, typically representing a random variable, for each activity type:
Notice how functions representing random
variables, like the duration
function of all activity types,
are marked with the keyword (or UML 'stereotype') «rv» standing for "random
variable". These random variable functions sample from a probability
distribution function (PDF), which is symbolically indicated with
expressions like Tri(30,50,40) standing for the triangular PDF with lower
and upper bounds 30 and 50, and a median of 40.
Each activity type is associated with Truck or WheelLoader as their resource role(s), indicated with the association end stereotype «rr» standing for "resource role".
In the process design model, we need to specify the state changes and follow-up events caused by events, including activity start and end events, and the constructor arguments for scheduling follow-up events, using model variables (possibly referencing resource pools), as shown in the following DPMN diagram:
The JavaScript-based simulator OESjs implements the Object Event Simulation paradigm, and, consequently, allows a straight-forward coding of OEM class models and DPMN process models.
For implementing the information design model, we have to code all object types, event types and activity types specified in the model in the form of classes.
The Truck
object class can be coded with OESjs-Core2 in the following way:
class Truck extends oBJECT { constructor({ id, name, status}) { super( id, name); // invoke the oBJECT constructor this.status = status; } } // a class-level attribute Truck.capacity = 15; // m3
All object classes inherit an
id
attribute and a name
attribute from the
pre-defined OES foundation class oBJECT
. Since trucks are
resource objects, we need to define a status
property for them.
We also define a class-level attribute capacity
for modeling
their load capacity, assuming that all trucks have the same
capacity.
The WheelLoader object class is coded in the same way as Truck.
The
HaulRequest
event class can be coded in the following
way:
class HaulRequest extends eVENT { constructor({ occTime, delay, quantity}) { super({occTime, delay}); this.quantity = quantity; } onEvent() { ... } }
All event classes inherit an occTime
attribute and a
delay
attribute from the pre-defined OES foundation class
eVENT
. Any event in OES can be created either with a value for
the attribute occTime
(standing for occurrence time)
or with a value for the attribute delay
. In the latter case,
the event's occurrence time is automatically derived by adding the value of
delay
to the current simulation time. In addition, the
HaulRequest event class has a property quantity
for
specifying the quantity to be hauled..
The onEvent
method of the HaulRequest event class is not part of the information
design model. Rather, it is implementing an event rule specified in the
process design model. Consequently, it will be discussed below.
The GoToLoadingSite activity class can be coded in the following way:
class GoToLoadingSite extends aCTIVITY { constructor({id, startTime, duration}={}) { super({id, startTime, duration}); } static duration() {return rand.triangular( 30, 50, 40);} } GoToLoadingSite.resourceRoles = { "truck": {range: Truck} }
All activity classes inherit the attributes id
,
startTime
and duration
from the pre-defined OES
foundation class aCTIVITY
. When an activity is created as a JS
object during a simulation run, the value of its duration
property is obtained by invoking the duration()
function
defined as a class-level ("static") function for its activity class. These
activity duration functions typically implement a random variable by
invoking a random variate sampling function, such as
rand.triangular(30,50,40)
, which samples from the triangular
probability distribution function (with min/max=30/50 and
mode=40).
Notice how the resource role association between
GoToLoadingSite and Truck, which defines the resource
reference property GoToLoadingSite::truck, is coded by a
corresponding entry in the map-valued class-level property
resourceRoles
.
A
DPMN process design model can be decomposed into a set of event rule design
models, one for each type of event specified in the design model. Starting
with the HaulRequest
event rule design model, we show how the
event rules specified by each of these event rule design models can be coded
in the form of an onEvent
method.
In the following
HaulRequest
event rule method onEvent
, all
available trucks are allocated to the current haul request, and, after
computing the number of loads, for each of the allocated trucks a new
GoToLoadingSite
activity is started:
class HaulRequest extends eVENT { ... onEvent() { const followupEvents=[], allocatedTrucks = sim.resourcePools["trucks"].allocateAll(); // assign model variable sim.model.v.nmrOfLoads = Math.ceil( this.quantity / Truck.capacity); for (const t of allocatedTrucks) { const goActy = new GoToLoadingSite(); // assign truck as required resource goActy.truck = t; // start GoToLoadingSite activity followupEvents.push( new aCTIVITYsTART({plannedActivity: goActy})); } return followupEvents; } }
Since activities are composite events, we also have event rules for them. The following GoToLoadingSite event rule is triggered whenever a GoToLoadingSite activity is completed, since the completion of an activity counts as its occurrence event.
This rule states that whenever a GoToLoadingSite activity ends (or is completed), then a new planned Load activity is enqueued, if no wheel loader is available, or, otherwise, a new Load activity is started. In OESjs, it is coded in the following declarative way:
GoToLoadingSite.successorActivity = "Load"
Such a successor activity assignment allows the simulator to check if the required resources are available and then start the successor activity, or, otherwise, enqueue a new planned successor activity.
This rule states that whenever a Load activity ends, the model variable nmrOfLoads is decremented by 1, and a Haul activity is immediately started (as a successor activity). Since the Haul activity doesn't require any additional resources, there is no need to enqueue a planned activity and wait for the availability of resources. In OESjs, this rule is coded in the following way:
class Load extends aCTIVITY { ... onActivityEnd() { const followupEvents = []; // decrement nmrOfLoads counter sim.model.v.nmrOfLoads--; return followupEvents; } ... } Load.successorActivity = "Haul";
Notice that the state change
expressed in the modelVariables object rectangle, the decrementation
of nmrOfLoads, is taken care of in the onActivityEnd
method of the Load
activity class. Instead of explicitly
scheduling the start of the succeeding Haul
activity in that
method, we simply define Haul
to be the successor activity of
Load
.
The Haul event rule states that whenever a Haul activity ends, it is immediately succeeded by a Dump activity. It is coded in the following way:
Haul.successorActivity = "Dump";
The Dump event rule states that when a Dump activity ends and the model variable nmrOfLoads has the value 0, it is immediately succeeded by a GoHome activity, otherwise it is immediately succeeded by a GoBackToLoadingSite activity. The rule is coded by defining the successor activity as a function returning either "GoBackToLoadingSite" or "GoHome" in the following way:
Dump.successorActivity = function () { return sim.model.v.nmrOfLoads === 0 ? "GoHome":"GoBackToLoadingSite"; }
The GoBackToLoadingSite event rule states that when a GoBackToLoadingSite activity ends and the model variable nmrOfLoads still has a value greater than 0, a new planned Load activity is enqueued; otherwise a GoHome activity is immediately started. The rule is coded by defining the successor activity of GoBackToLoadingSite as a function returning either "Load" or "GoHome" in the following way:
GoBackToLoadingSite.successorActivity = function () { return sim.model.v.nmrOfLoads > 0 ? "Load":"GoHome"; }