Because it was developed with one application in mind, does not mean that it was not intended as a general purpose language. Its authors always aimed at a design with the widest range of use. The idea of a common base language, referred to earlier, was central to their purpose. The language called SIMULA is extremely powerful and has proved suited to many areas of programming. Its recent evolution has takem it further in this direction.
The area where SIMULA reveals the background of its designers is not in the language itself, but in the standard packages which it provides. We have seen SIMSET, which is useful in all sorts of programs. The other package builds on SIMSET to provide tools for discrete event simulation. This package is called SIMSET class SIMULATION.
It is not the purpose of this book to describe how discrete event simulation works. If you are unfamiliar with it, consult the bibliography for references. You should still be able to follow the examples in this chapter, since they are intended as demonstrative of the features of class SIMULATION, not of the underlying theory.
Example 19.1: Simple simulation using Time and Process.
SIMULATION begin integer Count; Process class Man(Mill); ref(Machine) Mill; begin while Time<400 do begin OutText("Loading starts"); OutFix(Time,2,10); OutImage; ! Report made; Count := Count + 1; ! Keep a tally; Hold(5.0); Mill.Components := Mill.Components + 50; ! Load up; Activate Mill; ! Restart machine; while Mill.Components>0 do Hold(0.5); ! Check regularly; Cancel(Mill); ! Switch off; Hold(10.0); ! Unloading takes longer; OutText("Unloading finishes"); OutFix(Time,2,10); OutImage; ! Report made; end--of--loop; Passivate end++of++Man; Process class Machine; begin integer Components; while True do begin OutText("Machine starts"); OutFix(Time,2,10); OutImage; while Components>0 do begin Hold(2.0); ! Machining time for one component; Components := Components - 1 end..of..inner..loop; Passivate end--of--loop end++of++Machine; ref(Man) Worker; Worker :- new Man(new Machine); Activate Worker; OutText("Count = "); OutInt(Count,4); OutImage; Hold(800); OutText("Simulation ends"); OutImage end**of**simulationProcess class Man shows the actions of the man. He performs four basic actions. He loads the machine with a new supply of components. He starts the machine. He checks at regular intervals to see if the machine has finished. He unloads the machine when it is finished.
Each action involves two things. The first is some representation of what the action achieves, such as adding 50 to the attribute component of the machine that he is operating, to represent loading it. The second is some representation of the time taken for this. In the case of loading, this is a call of procedure Hold (5.0), where 5.0 is the time taken to complete the loading.
The starting of the machine involves specifying that the machine is to become active. Since we are dealing with coroutines this implies that the man will cease to be active at the same moment. The time element specifies that the activation is instantaneous. There are other scheduling possibilities when reactivating another Process, as we shall see.
The unloading involves resetting the Product attribute of the machine to zero. This also involves a Hold to represent time taken.
A third, optional element of the representation of an action is a report from the program. This can be made before or after an action, or both. Clearly the program would have no point if it did not report on what was happening.
An alternative to printing every time an action is performed is to keep a count of the number of times it is carried out. Other statistics can also be accumulated. In the program, the number of times the machine is loaded is counted and printed at the end.
Each EventNotice has a real attribute called EvTime. This is the time at which the associated Process coroutine is to be restarted. The list of EventNotices is in order of increasing time. The Process whose EventNotice has the lowest EvTime is the currently active Process and its EvTime is the current time, which is returned by real procedure Time.
Diagram 19.1 shows the sequence of events in our simple program as changes in the sequencing set. This list is shown as a simple SIMSET Set, but is not necessarily held in that form. A tree list is often used.
Figure 19.1: Starting and ending events in example 19.1.
a) Time 0.0. SIMULATION activates MAIN. Current --------------- I MAIN I I EvTime= 0.0 I --------------- b) Time 0.0. MAIN activates Worker. Current --------------- --------------- I Worker I --> I MAIN I I EvTime=0.0 I <-- I EvTime= 0.0 I --------------- --------------- c) Time 0.0. Worker holds for 5.0. Current --------------- --------------- I MAIN I --> I Worker I I EvTime=0.0 I <-- I EvTime= 5.0 I --------------- --------------- d) Time 0.0. MAIN holds for 800.0. Current --------------- --------------- I Worker I --> I MAIN I I EvTime= 5.0 I <-- I EvTime= 800 I --------------- --------------- e) Time 5.0. Mill holds for 2.0. Current --------------- --------------- --------------- I Worker I --> I Mill I --> I MAIN I I EvTime= 5.0 I <-- I EvTime= 7.0 I <-- I EvTime= 800 I --------------- --------------- --------------- f) Time 5.0. Worker holds for 0.5. Current --------------- --------------- --------------- I Worker I --> I Mill I --> I MAIN I I EvTime= 5.5 I <-- I EvTime= 7.0 I <-- I EvTime= 800 I --------------- --------------- --------------- g) After numerous events: Time >= 400.0. Worker passivates . Current --------------- I MAIN I I EvTime= 800 I --------------- h) Time 800. MAIN terminates.Note the effects of Hold, Cancel, Passivate and Activate.
All the visible attributes of Process are type procedures, giving information about the current state of the Process object. The possible state are:
passive is equivalent to Idle and not Terminated.
This special statement is called an activation statement. It can only be used in SIMULA blocks or classes prefixed by class SIMULATION. Its task is to set the event time of a Process and insert it into the sequencing set, known as scheduling the Process.
activate MillThis form makes the specified Process object the currently active one. This means giving it an event time equal to the currently simulated time and placing it in the sequencing set in front of the currently active Process.
Note that the previously current process remains in the sequencing set, with its event time unchanged and one behind the newly activated Process.
activate Mill6 before Lathe2will place Process object Mill6 in front of Lathe2 in the sequencing set. Lathe2 may be anywhere in the sequencing set. Mill6 will be given the same event time as Lathe2.
activate Mill6 after Lathe2will place Process object Mill6 behind Lathe2 in the sequencing set, giving it the same event time.
In either case, if Lathe2 happened not to be in the sequencing set, nothing would be done to Mill6.
activate Mill6 delay 6.5which will give Mill6 an event time equal to the current simulated time plus 6.5. it is then added to the sequencing set at the appropriate place. If one or more Process objects are already present in the sequencing set with the same event time, the newly scheduled one will be placed after them.
If it is wished to place a newly scheduled Process object in front of any which are already in the sequnecing set with the same event time, an additional keyword specifier is used, as in
activate Mill6 delay 6.5 prior
activate Mill6 at 10.2The statement above gives Mill6 an event time of 10.2 and adds it to the sequencing set. If other Process objects are already present with the same event time, it is added behind them. To force it to be inserted in front of any such objects, the final version of the activate statement must be used, as in the example below.
activate Mill6 at 10.2 prior
The reactivate statement has exactly the same forms as the activate statement, with the keyword activate replaced by the keyword reactivate. Equivalent statements to those used above are
reactivate Mill6; reactivate Mill6 before Lathe2; reactivate Mill6 after Lathe2; reactivate Mill6 delay 6.5; reactivate Mill6 delay 6.5 prior; reactivate Mill6 at 10.2; reactivate Mill6 at 10.2 prior;
There are other scheduling mechanisms and scheduling support mechanisms, all in the form of procedures. These are not language extensions and do not introduce new keywords. Thus they could, in theory, be written in SIMULA.
None of the following procedures are attributes of Process and so they are all used without remote accessing.
Example 19.2: An employment office queuing model.
SIMULATION begin ref(Head) ReceptionistQ, InterviewQ1, InterviewQ2; integer I, Manual; Process class Interviewer(Title, MyQueue); text Title; ref(Head) MyQueue; begin ref(Link) Next; inspect MyQueue do while True do ! Indefinite loop; begin if not Empty then begin Hold(3.5); ! Interview time taken as 3.5 minutes; Next :- First; Next.Out; activate Next after Current; Hold(3.0); ! 3 minutes to clear desk; end else begin Hold(5.0); ! Wait 5 minutes before checking queue again; end--of--one--sequence end++of++loop end==of==Interviewer; Process class JobHunter(SkillCategory); integer SkillCategory; begin OutText("Job hunter "); OutInt(SkillCategory,4); OutText(" joins receptionist queue at time "); OutFix(Time,4,8); OutImage; Wait(ReceptionistQ); OutText("Job hunter "); OutInt(SkillCategory,4); OutText(" joins interview queue"); OutImage; Hold(1.0); ! 1 minute to join new queue; if SkillCategory=Manual then Wait(InterviewQ1) else Wait(InterviewQ2); OutText("Job hunter "); OutInt(SkillCategory,4); OutText(" leaves employment office"); OutImage end==of==JobHunter; Process class Receptionist; begin ref(Link) Customer; while True do begin if not ReceptionistQ.Empty then begin Hold(2.0); Customer :- ReceptionistQ.First; Customer.Out; activate Customer; end else Hold(1.0) end end==of==Receptionist; Manual := 1; ReceptionistQ :- new Head; InterviewQ1 :- new Head; InterviewQ2 :- new Head; activate new Receptionist; activate new Interviewer("Manual",InterviewQ1); activate new Interviewer("Skilled",InterviewQ2); for I := 1,2,2,1 do begin Activate new JobHunter(I); Hold(2.0) end; Hold(100) end**of**programThe model shows an employment office, with two interviewers and a shared receptionist. New arrivals looking for work must register with the receptionist and then wait in one of the queues (lines), according to their desired type of employment. When the job hunter has been interviewed, he leaves.
Job hunters, the receptionist and the two interviewers are all active participants and so are represented as Process sub-class objects. Each has appropriate attributes and actions, including reporting on their actions, defined in the body of their sub-class.
Arrivals are parameterised, allowing job hunters with different skills to be generated from the same basic class. These attributes will determine which interviewer they see.
The queues are represented by sets and arrivals join them by calling Wait with the Set appropriate to their skill or the initial queue for the receptionist as parameter. The receptionist and the two interviewers check the queue of arrivals waiting to see them and take the first member of the Set, if any, each time they have completed an interview. They then reschedule this Process, allowing it to move to the next stage in its job search.
The delays are all given as real constants in this example. To produce a more realistically random effect, random drawing procedures could be used to provide delay values, according to appropriately chosen distributions. Those provided in SIMULA are outlined briefly in chapter 20. Others are usually available from standard libraries on particular systems, such as the NAG library.
To achieve this class SIMULATION includes a hidden declaration of a sub-class of Process, called Main_Program. This is used to create the main block's representative in the sequencing set. The body of this sub-class consists of a single statement, as shown below.
Process class Main_Program; begin while True do Detach; end--of--Main_Program;As the last of its initialisation actions, class SIMULATION creates a Main_Program object and schedules it for simulated time 0.0. This means that when the actions of the prefixed block commence, they follow a call of Detach inside this Main_Program object. This object is still at the front of the sequencing set and remains the currently active Process, but its Detach causes the statements in the prefixed block to be performed. Thus the currently active Process object uses the actions in the prefixed block as its own.
If Hold is called inside this block, it will act on the currently active Process object, i.e. the Main_Program object, rescheduling it for a later event time. This will cause the currently executing actions to leave the prefixed block's statements and enter those of the body of the new first in the sequencing set.
When the Main_Program object again reaches the front of the sequencing set and becomes active, it immediately calls Detach again. This takes it back to the prefixed block, at the instruction immediately following the one which caused the Main_Program object to be rescheduled, in this case Hold. Thus the Main_Program object again uses the actions of the prefixed block as its own and the effect of rescheduling it is seen to be equivalent to rescheduling the prefixed block.
This explains the method used to set the period of time covered by all or part of a simulation program. Both out examples so far contain a call on Hold in the main program block. This follows the setting up and starting of the other Process objects and puts the Main_Program object at the end of the sequencing set, with an event time equal to the desired length of time to be simulated. When this object reaches the front of the sequencing set it detaches back to the program block, allowing final reporting and termination of the program.
Where a simulation consists of several phases, it is possible to use several Holds in the program block. Each represents a particular part of the period being simulated.
If Passivated were to be called instead of Hold in the program block, the Main_Program block would never be re-entered and the simulation would continue for as long as any Process object remained in the sequencing set. Such a simulation's duration would be hard to predict, possibly running indefinitely. Normally Hold is used to fix a limit to the duration.
If you attempt to Detach or Resume a Process object explicitly, the effects could be disastrous for the scheduling mechanisms of SIMULATION. Detach and Resume should never be used in SIMULATION programs.
The procedure Accum is used to accumulate the "system time integral" of a real variable, C. This is regarded as a step function of simulated time. Its integral is collected in real variable A. B is the time of the last update and D the current increment. Accum is given in full.
procedure Accum (A,B,C,D); name A,B,C; long real A,B,C,D; begin A:=A + C * (Time - B); B:=Time; C:=C + D; end ** of ** Accum;
Example 19.3: Simulation with two time periods.
SIMULATION begin procedure Report; begin OutText(" *** Report ***"); OutImage; OutInt(Count,6); OutText(" documents printed, at time"); OutFix(Time,2,8); OutImage end--of--Report; Process class Writer; begin ref(Typer) Typist; ref(Document) Doc; while True do begin Hold(8.0); Doc :- new Document; Typist :- TypingPool.First; Typist.Out; activate Typist end end--of--Writer; Process class Typer; begin Wait(TypingPool); while True do begin Hold(4.0); activate PhotoCopier; Wait(TypingPool); end end--of--Typer; Process class Copier; begin while True do begin Hold(1.0); Count := Count + 1; OutText("Document printed at "); OutFix(Time,2,10); OutImage; Passivate end end--of--Copier; class Document;; ref(Head) TypingPool; ref(Copier) PhotoCopier; integer I, Count; TypingPool :- new Head; for I := 1 step 1 until 10 do activate new Typer; PhotoCopier :- new Copier; activate new Writer delay 2.0; activate new Writer delay 4.5; Hold(100); Report; Hold(100); Report end++of++programThe model runs for 100 time units and then reports. It then restarts for a further 100 time units, reports and stops.
Note that the main program makes two holds in this time. It is often useful to create models which report at regular intervals, not just at the end. It may also be useful to change some factors at certain points. This may represent day and night, changes in weather, the seasons, rest breaks or many other alterations in the conditions under which the model is operating.
The class Process has been explained.
The procedures for scheduling and enquiring about the state of the simulation have been explained.
The mechanism for scheduling the main program block has been outlined.