Hello everybody. One of our users asked me to find a solution for a small Lua project following some malfunctions in vehicle traffic. We will see together one of the solutions to correct the problem.

Summary

Presentation

Information : The original text presented on this page was written in French. To avoid introducing possible bugs, we have voluntarily kept the variable names in French, but the comments are translated into English to contribute to the general understanding of the script. Thank you for your understanding.

This project is composed of the following elements:

  1. A station with 2 trams running in turn on a single track,
  2. A bus station with 2 buses traveling on a two-way road. A yellow clockwise bus, a white counterclockwise bus,
  3. Cars always drive on the main road.

In the initial script received with this project, buses would start unexpectedly and hit trams or cars. In addition, the EEPMain () function checked 5 times per second two associative arrays totaling 51 track segments. A quick calculation went like this: 51 x 5 x 60 = 15,300 checks per minute, which was an additional workload for EEP.

So how to do it? We will see that nothing beats simplicity and a little logic to do this job. Don’t be fooled by the length of the tutorial as the script is not very long. Rather, we have tried to provide answers to questions that you might legitimately ask yourself.

You have the possibility to download this project by clicking on the button below. This was done with version EEP16. When the download is complete, unzip the archive file. Then open EEP and load the project.

Description en image

Figure n° 1

As you can see, the network is divided into two main parts: the part on the left (west side) and the part on the right (east side).

For now, don’t worry about tooltips, we’ll get to that later. You will notice in the upper part, a junction towards which is the bus station with the bus stops. The tram station is next to the bus station (For better legibility, the stations are not shown).

We are now going to zoom in on the East part in the 2D view:

Figure n° 1

The traffic rules are as follows:

  1. Trams have priority over buses and cars. When one tram enters the station, it instructs the other tram to start in the opposite direction, and so on.
  2. Buses have priority over cars. As with trams, when a bus enters the bus station, the other bus is ordered to start in the opposite direction.
  3. Cars have no influence on the behavior of buses and trams and always remain on the main road.

Of course, the same traffic rules apply for the west part.

Adding vehicles to the project

In EEP, when adding rolling stock, a window opens in which you can modify the default name (see figure n ° 3). The ideal being to give explicit and simple vehicle names. For the buses, we have chosen Bus_Jaune (Bus_Yellow) and Bus_Blanc (Bus_White). After the underscore (number 8 on the alphanumeric keypad), you can write any name such as the color for example or a city, a line number, etc. The important thing is to start with Bus_folloed by what ever you want. We will see later how to extract in the script, the part located to the left of the underscore.

Figure n° 3

For cars and trams, you can leave the default names as they are not relevant for the purposes of our tutorial.

Script

We will start by detailing the first parts of the script which concerns:

  1. The declaration of constants,
  2. The declaration of global variables,
  3. Recording of 6 track segments and 4 signals.

Declaration of constants

				
					-- Clear the contents of the event window
clearlog()

-- Constants for the course status (Start or end)
CONST_PARCOURS_DEBUT = 1
CONST_PARCOURS_TERMINE = 10
-- Constants for signals
CONST_VOIE_LIBRE = 1
CONST_ARRET = 2
-- Constants for turnouts
CONST_BRANCHE_PRINCIPALE = 1
CONST_EMBRANCHEMENT = 2 
				
			

Here we start the declaration of the constants of the courses, the signals and the turnouts. Indeed, it is more meaningful to assign meaningful names than numbers.

Note : with Lua, unlike other computer languages, there is no keyword Const  to declare constants. However, we will respect the convention of writing in capitals for the constants.

Figure n ° 4 (Position of the turnout parameters)

For the constants of the statuses concerning the paths, the values ​​1 and 10 were arbitrarily chosen. For the constants of the signals and the turnouts, it is necessary to respect the display order of the parameters as they appear in the Position in drop-down list. the properties window (see figure 4). Thus, when we call on the EEP functions, we will pass these constants as parameters instead of the numbers.

Important : Please note that depending on the signals, you may find several possible positions. In this case, it is advisable to make sure of the exact number of the position by looking directly in the properties of the corresponding signal.

Declaration of global variables

				
					-- Variables for vehicle movements (Buses and Tramways)
-- By default, all routes are set to DONE
-- East part = right part of the network
DEPART_Bus_Est = CONST_PARCOURS_TERMINE
DEPART_Tram_Est = CONST_PARCOURS_TERMINE
ARRIVEE_Tram_Est = CONST_PARCOURS_TERMINE
ARRIVEE_Bus_Est = CONST_PARCOURS_TERMINE
-- Partie Ouest = partie gauche du réseau
DEPART_Bus_Ouest = CONST_PARCOURS_TERMINE
DEPART_Tram_Ouest = CONST_PARCOURS_TERMINE
ARRIVEE_Bus_Ouest = CONST_PARCOURS_TERMINE
ARRIVEE_Tram_Ouest = CONST_PARCOURS_TERMINE
				
			

Here, our 8 variables for the paths are global and visible in all the functions of the script. They are defined by default on CONST_PARCOURS_TERMINE. It will therefore suffice to click on one of the two signals in the tram station to initiate the circulation process with the constant CONST_PARCOURS_DEBUT.

Recording of 4 track segments and 4 signals

				
					
-- RECORDING OF the 4 TRACK SEGMENTS --
-- Segment in front of the traffic light Bus entrance west side
EEPRegisterRoadTrack(30)
-- Segment in front of the traffic light Bus entrance east side
EEPRegisterRoadTrack(42)
-- Segment in front of the traffic light, bus exit east side
EEPRegisterRoadTrack(66)
-- Segment in front of the traffic light, bus exit west side
EEPRegisterRoadTrack(70)

-- Recording of signals to manage events in callback functions
-- East side traffic light signal in the bus station
EEPRegisterSignal(4)
-- West side traffic light signal in the bus station
EEPRegisterSignal(12)
-- West side tram signal in the station
EEPRegisterSignal(7)
-- East side tram signal in the station
EEPRegisterSignal(9)

				
			

We need to save the segments to be tested to retrieve some information when the vehicles will occupy these segments. We also record 4 signals to use the corresponding callback functions. Indeed, the fact of integrating the appropriate parameterization of the signals and switches in the reminder functions, will also make it possible to manage the traffic manually by clicking directly on the concerned signals .

Now let’s disable the EEPMain () function because for our project we don’t need it to run 5 times per second.

				
					-- Deactivate the EEPMain function
function EEPMain()
    return 0  -- 0 deactivates the function
end 
				
			

Before we get into contact points, we’ll think about and enumerate where, when, and how actions should be performed by the script (remember that we’re only dealing with the east side for now):

  1. We know that from the moment our tram leaves the station, the buses supposed to enter or leave the bus station must wait until it has left the station, the trams having priority over the bus,
  2. We must check when a bus wants to enter the bus station whether a tram is entering or leaving the station and if so, switch the signals accordingly,
  3. When a bus enters or leaves the station, we have to stop the circulation of cars,
  4. Of course, trams can not run simultaneously since there is only one track.

Finally, the common point between all these movements is the entry and exit of trams and buses. There is indeed a beginning of the course and an end of the course at a given moment. This is why we have our 2 constants CONST_PARCOURS_DEBUT (start) and CONST_PARCOURS_TERMINE (end) which will be assigned to the 4 binomials of the global variables DEPART_(start) Bus_Est, ARRIVEE_(arriveal) Bus_Est, etc …

So, where to position all these points of contact? Nothing could be easier, just look carefully at figure n ° 5 below (do not hesitate to click on the image to enlarge it):

Figure n° 5

Important : in the image above, note in which direction of circulation the contact points for the associated Lua functions are configured.

Vehicle contact points

To start, let’s group the functions classified by binomial:

  1. fncTramEntreeEst: this function is called by the contact point which is placed on the tram track to the far right of the image,
  2. fncFinTramEntreeEst: this function is called by the contact point which is placed on the tram track in the station.
  1. fncBusEntreeEst: this function is called by the contact point which is placed on the main axis road to the far right of the image (in front of the red car),
  2. fncFinBusEntreeEst: this function is called by the contact point which is placed on the road in the station towards the end of the curve.
  1. fncDepartBusEst: this function is called by the contact point which is placed on the road in the station (above signal n ° 4),
  2. fncEndDepartBusEst: this function is called by the contact point which is placed on the main  axis road in the direction of travel, on the right.

You will also notice a contact point (brown color) used as a speed regulator set at 30 km / h only for trams. This contact point reacts in both directions of traffic. Thus, the incoming tram sees its speed reduced to 30 km / h to enter the station while the outgoing tram, at the start of its journey, will run at maximum speed.

Figure n° 6

Concerning the outgoing tram, the contact point calling the fncEndTramExitEst function also takes the opportunity to increase the speed of the tram to 60 km / h, since it is now located on the main section.

Signal contact points

The red contact points are there to switch the signals (Tramways and Buses) to a stop as soon as the corresponding signal has been crossed. This makes our life easier and saves us from writing a specific Lua function to do this job. Of course, for trams and precisely because there are only trams running on the rails, it is useless to be interested in the type of vehicle. But what about the buses that share the main road axis with the cars?

It is obvious that only buses can switch signals since the cars always stay on the main axis. So how do you do it? Just look at the properties window for signal # 13 to get the answer:

Figure n° 7

In the red box, the use of the filter is particularly relevant in our case. We could get the same from a specific Lua function like filtering the vehicle name and switching the signal to Off. But here, our point of contact does the work for us so we are not going to deprive ourselves of it!

For the filter to be operational, we have to start with the sign #  followed by the characters we want to capture and that’s fine, because we want the contact to react to the Bus keyword. So each time a vehicle passes over the ignition, it will test if the name begins with Bus and ignore all the characters to the right from the underscore. The case of characters is irrelevant here.

If the vehicle name begins with Bus, then the ignition will switch signal 13 to the stop position. In all other cases, the contact will be simply ignored and the signal position will remain unchanged.

From now on, we’ll start by going through the functions one by one in script order.

Development

The binomial fncTramEntreeEst - fncFinTramEntreeEst

These two functions work in tandem, i.e. when the blue tram (in the right-left direction) wants to enter the station, it triggers the call to the fncTramEntreeEst function via a point contact for vehicle. As long as the tram has not passed the other point of contact (calls the function fncFinTramEntreeEst), the buses can not enter or leave the bus station and therefore cannot cross the road-rail intersection of the tram.

Detail of the function fncTramEntreeEst
				
					
-- This function is activated by the yellow tram via a contact point at the entrance to the station on the east side.
function fncTramEntreeEst()
    -- Position the ARRIVEE_Tram_Est route on DEBUT (start)
    ARRIVEE_Tram_Est = CONST_PARCOURS_DEBUT
    
    -- If no bus leaves or arrives at the bus station then...
    if DEPART_Bus_Est == CONST_PARCOURS_TERMINE and ARRIVEE_Bus_Est == CONST_PARCOURS_TERMINE then
            -- The yellow tram can enter the bus station
            -- Switches tram signal n ° 10 to Clear
            EEPSetSignal(10, CONST_VOIE_LIBRE)
            -- Switches turnout n ° 5 to Main Branch
            EEPSetSwitch(5, CONST_BRANCHE_PRINCIPALE)
        else
            -- ...otherwise a bus leaves or arrives at the bus station
            -- switches tram signal no.10 to stop
            EEPSetSignal(10, CONST_ARRET)
    end
end -- End of the fncTramEntreeEst function

				
			
  1. The tram arrives from the east side to prepare its entry into the station. One informs the variable ARRIVEE_Tram_Est with the constant CONST_PARCOURS_DEBUT (line n ° 4),
  2. Priority is given to trams, however, we must test if the buses are not already moving to exit or enter the station on the east side. If this were the case, the two variables DEPART_Bus_Est or ARRIVEE_Bus_Est would be equal to CONST_PARCOURS_DEBUT. Here, otherwise, they must both be equal to CONST_PARCOURS_TERMINE. Notice the keyword and in the test condition (line 7). This keyword indicates that the two variables tested must both return the value true for the block immediately following the test to be executed.
  3. So if our two variables are equal to CONST_PARCOURS_TERMINE, we switch signal n ° 10 on Clear as well as switch n ° 5 on Main branch (lines n ° 10 and 12),
  4. On the other hand, if one or both variables are not equal to CONST_PARCOURS_TERMINE, the program goes to the next block located immediately after the keyword else and signal n ° 10 is switched to Stop (line n ° 16),
  5. The function is now complete.
Detail of the function fncEndTramEntreeEst
				
					
-- This function is activated via a contact point by the yellow tram when it enters the station on the east side.
function fncFinTramEntreeEst()
    -- Set the ARRIVEE_Tram_Est route to TERMINE (finish)
    ARRIVEE_Tram_Est = CONST_PARCOURS_TERMINE
    
    -- If no bus leaves or arrives at the bus station then...
    if DEPART_Bus_Est == CONST_PARCOURS_TERMINE and ARRIVEE_Bus_Est == CONST_PARCOURS_TERMINE then
        -- Switches tram signal n ° 9 to Clear
        EEPSetSignal(9, CONST_VOIE_LIBRE, 1)
        -- The other signals are switched directly in the callback function of signal no.9
    end
end -- End of the function fncEndTramEntreeEst

				
			
  1. The tram has now entered the station completely. One informs the variable ARRIVEE_Tram_Est with the constant CONST_PARCOURS_TERMINE (line n ° 4),
  2. Priority is given to trams, however, we must test if the buses are not already moving to exit or enter the station on the east side. If this were the case, the two variables DEPART_Bus_Est or ARRIVEE_Bus_Est have to be equal to CONST_PARCOURS_DEBUT. Notice the keyword and in the test condition (line 7). This keyword indicates that the two variables tested must both return the value CONST_PARCOURS_TERMINE so that the condition is true and the block which immediately follows can be executed,
  3. Thus, if our two variables are equal to CONST_PARCOURS_TERMINE, signal n ° 9 is switched to Clear. Otherwise, nothing happens and the function ends.

Now is the time to take a look at the callback functions. What are they used for and why. Here, the two conditions which constitute the test return the value true, which has the effect of switching signal n ° 9 to Clear. Remember at the beginning of the script, we had recorded 4 signals including n ° 9. Indeed, EEP allows us to record thanks to the EEPRegisterSignal function any signal of a project. You just need to indicate the corresponding signal number in parentheses and you’re done.

However, when a signal is recorded, each change of its state induces the automatic call to the so-called callback function EEPOnSignal_9 (hStatus). As you will have understood, the number 9 corresponds to the number of the signal concerned. The hStatus variable in parenthesis returns the state (the position) of the signal each time it is modified.

To resume the thread of our function fncFinTramEntreeEst, signal n ° 9 has been switched to Clear. So at this precise moment, the EEPOnSignal_9 (hStatus) callback function was executed. Now let’s see what it contains:

				
					function EEPOnSignal_9(hEtat)

    if hEtat == CONST_VOIE_LIBRE then
        -- The blue tram can leave the bus station
        -- Position the DEPART_Tram_Est path on DEBUT (start)
        DEPART_Tram_Est = CONST_PARCOURS_DEBUT
        -- Switches turnout 5 to Branch
        EEPSetSwitch(5, CONST_EMBRANCHEMENT)
    end
    
end -- End of the EEPOnSignal_9 function
				
			

Here, we first test whether the signal is switched to Clear. If it is the case then one informs the variable DEPART_Tram_Est with the constant CONST_PARCOURS_DEBUT and one commutes the switch n ° 5 on branch. At this point, one may wonder why not also write these lines of code in the function fncEndTramEntreeEst? Well, quite simply because you will be able to execute this function directly if you manually switch the signal in EEP by clicking on it while holding the Shift key down.

If the code for the callback function had remained in the fncFinTramEntreeEst function, only the blue tram could execute it. Moving the code into the callback function gives us flexibility of use and wider operating possibilities.

Detail of the function fncEndTramExitEst
				
					
-- This function is activated via a contact point by the blue tram when it has left the station and passed the crossing on the east side.
function fncFinTramSortieEst()

    -- Set the DEPART_Tram_Est path to TERMINE (stop)
    DEPART_Tram_Est = CONST_PARCOURS_TERMINE
    
    -- Retrieve the information for segment 42 (track ID 42 with signal n ° 13)
    Result, Occupe42, Name42 = EEPIsRoadTrackReserved(42, true)
    -- Retrieve the information for segment 66 (ID track 66 with signal n ° 4)
    Result, Occupe66, Name66 = EEPIsRoadTrackReserved(66, true)
    -- If a vehicle occupies road segment 42
    if Occupe42 == true then
            -- Extract the name of the vehicle (Name42 variable) present on the segment in the strCategorieVehicule variable
            local strCategorieVehicule = string.sub(Name42, string.find(Name42, "%a+"))
            -- If the vehicle name begins with Bus then...
            if string.upper(strCategorieVehicule) == "BUS" then
                    -- Set the ARRIVEE_Bus_Est route to DEBUT (START)
                    ARRIVEE_Bus_Est = CONST_PARCOURS_DEBUT
                    -- Switch signal no.11 (traffic light) stop
                    EEPSetSignal(11, CONST_ARRET)
                    -- Switches turnout 17 to Branch
                    EEPSetSwitch(17, CONST_EMBRANCHEMENT)
                else
                    -- ...Otherwise we test the condition if the yellow bus had entered just before the yellow tram when
                    -- the latter has already crossed the fncTramEntreeEst contact point. When the yellow bus had passed the
                    -- fncFinBusEntreeEst contact point, the white bus could not start because the yellow tram was entering
                    -- in turn in the station.
                    if Occupe66 == true then
                        -- Switches signal 4 to Clear
                        EEPSetSignal(4, CONST_VOIE_LIBRE, 1)
                        -- The other signals are switched directly in the callback function of signal no.4
                    end
            end
            
            -- In both cases, switches signal n ° 13 (traffic light) to Clear
            EEPSetSignal(13, CONST_VOIE_LIBRE)
            
        else
            
			-- Same as the test above except that segment n ° 42 was not occupied
            if Occupe66 == true then
                --Switches signal 4 to Clear
                EEPSetSignal(4, CONST_VOIE_LIBRE, 1)
                -- The other signals are switched directly in the callback function of signal no.4
            end
    end

end -- End of the function fncEndTramExitEst

				
			

This function is called (via a vehicle contact point) when the blue tram has joined the main section and as already stated above, the speed of the tram is set at 60 km / h. Now we are going to discover some exciting new features!

  • The tram passed the intersection on the east side and joined the main section. One informs the variable DEPART_Tram_Est with the constant CONST_PARCOURS_TERMINE (line n ° 5),
  • Now we have to test which type of vehicle is stopped in front of the red light (signal n ° 13). Indeed, by the time our tram starts and leaves the station, maybe our yellow bus was able to arrive in front of the junction to enter the station, but as signal n ° 13 (traffic light), is positioned on Stop (red light), thanks to the function fncBusEntreeEst (to come below), we must now let our yellow bus enter the bus station.

To test which type of vehicle is stopped in front of a red light, we are going to call the EEP function EEPIsRoadTrackReserved. First of all, in order for this function to return the desired values, it is necessary to record the ID # of the segment concerned. Here in our case, it is segment n ° 42 (that of signal n ° 13). How does EEP register the segment? Simply by using the  EEPRegisterRoadTrack (42) function as we did in the section recording track segments and signals at the start of the script.

So what exactly does this EEPIsRoadTrackReserved (42, true) function do? It’s simple, it contains 2 arguments (between the parentheses) and returns 3 values ​​which will be stored in the following 3 variables: Result, Occupe42, Name42 (line n ° 8).

Explanation : to start, we need to pass two arguments which are, the ID # of the affected segment (here, # 42) and the keyword true. Why true? to retrieve in the 3rd variable Name42, the name of the vehicle occupying the segment. If you omit this 2nd argument, the vehicle name will not be returned. At this point, we have retrieved the values ​​returned in our 3 variables so let’s take a closer look at what information is contained inside them:

  1. The value returned in the Result variable is equal to true if the segment exists otherwise it is false. It is obvious that here in our case, the segment does exist. Which explains why we do not have the need to test the result,
  2. The value returned in the Occupe42 variable will tell us if the segment is occupied by a vehicle. This value is important for our function, so we will test it afterwards,
  3. The value returned in the Name42 variable will give us the name of the vehicle if it occupies segment 42. Otherwise, the returned value is an empty string.

We are going to do the same thing as above with the lane segment number 66. The value returned in the variable Occupe66 will indicate to us if the segment located in the bus station at the position of signal n ° 4, is occupied by the bus white (line n ° 10).

Let’s go ! in line 12 we test the value of the variable Occupe42. If the value is true then a vehicle occupies segment # 42, but we need to know what type of vehicle is in this segment. Indeed, remember, only buses are authorized to enter the bus station, as for the cars they always remain on the main road axis.

So how do we go about retrieving the name of the vehicle stored in the Name42 variable? First, here is line # 14 again: local strCategorieVehicule = string.sub (Name42, string.find (Name42, “% a +”)). Here several parameters deserve some explanation:

  1. First, we declare a local variable named strCategorieVehicule. It will be used quite simply to store the name of the vehicle,
  2. Then, we use the Lua language string.sub function which contains 2 arguments. The first is the name of the variable to test, here it is of course Name42. Then, the 2nd argument contains another Lua function string.find  which will allow us to search for a character string starting from the pattern ‘% a +’ contained in quotes. Uh … who is this intruder? let’s continue the explanation,
  3. The string.sub function returns a substring contained in the Name42 variable corresponding to the pattern found by the Lua string.find function. The latter searches for characters in the Name42 variable. But not just any characters!  letters  and only letters!
  4. Pattern decomposition : % a = any letter and only letters, the + sign instructs the string.find function to continue the search as long as letters are found. On the other hand, as soon as a character like the underscore or even a number is encountered, the function stops searching and returns only what it found before meeting the underscore. In short, all that to say this: anything that no longer corresponds to letters is ignored and the function stops its search. The returned result is then stored in the local variable strCategorieVehicule. ( pattern % a + is neither more nor less than a Regex or regular expression).

Note : for information, all the variables declared with the local keyword are only local to the function. This means that the variable will not be visible and/or usable in another part of the script. Local variables are less resource intensive than global variables.

When we first added the buses to the network, EEP asked us to name them. They are called Bus_Jaune and Bus_Blanc. Now the answer should be obvious! Everything that was before the underscore has been captured, so if a bus occupied segment # 42 and is stoped at a red light, our strCategorieVehicle variable contains the word Bus.

Now, we will anticipate and eliminate a possible problem: imagine that we named our model bUs_Jaune or buS_Jaune or even bUS_Jaune, as the equality test is case sensitive, bUs will never equal Bus or buS! So, to avoid any malfunction, before launching the equality test, we will use the Lua string.upper (strCategorieVehicule) function in the left operand which will transform our character string into uppercase regardless of the input! Thus, all we have to do is indicate in the operand on the right, the BUS string in capital letters and between quotes to test for sure that it is indeed the keyword Bus (line n ° 16).

Thus, if strCategorieVehicle contains the keyword Bus then the condition if string.upper (strCategorieVehicule) == “BUS” then is true (line no.16), execution continues immediately at the block of instructions located just below the line and perform the following operations (lines 17 to 22):

  1. The ARRIVEE_Bus_Est variable is filled in with the CONST_PARCOURS_DEBUT constant. Indeed, the bus will enter the bus station,
  2. Switches signal n ° 11 (traffic light) to stop using the EEP function EEPSetSignal (11, CONST_ARRET). Indeed, cars traveling in the opposite direction must stop at a red light to allow the yellow bus to enter the bus station,
  3. Switches turnout 17 to Branch using the EEPSetSwitch (17, CONST_EMBRANCHEMENT) function.

In both cases, bus or not, line 36 switches signal n ° 13 (traffic light) to Clear using the EEP function EEPSetSignal (13, CONST_VOIE_LIBRE).

If the keyword stored in the strCategorieVehicule variable was not equal to Bus then the second part of the test located after the else keyword would be executed.

Important : Let us compare line n ° 20: EEPSetSignal (11, CONST_ARRET) and line n ° 43: EEPSetSignal (4, CONST_VOIE_LIBRE, 1). The second uses an optional 3rd argument (with the number 1) which automatically calls the EEP callback function EEPOnSignal_4 (hStatus) whenever the signal position changes. If you omit this 3rd argument, the callback will never be executed.

We are now going to  analysis and show logic and consider the following possibility: if the yellow bus crosses the fncBusEntreeEst contact point a few meters before crossing the fncTramEntreeEst contact point by the yellow tram, what happens? The function fncBusEntreeEst called first by the yellow bus assigns to the ARRIVEE_Bus_Est variable the value of the CONST_PARCOURS_DEBUT constant and consequently switches signal n ° 10 to Stop.

It should be understood here that our yellow bus has ‘stolen’ the priority of the yellow tram and therefore this one will enter the bus station first and will then cross the fncFinBusEntreeEst contact point. We will detail this function later in this tutorial but remember for the moment that it is responsible for testing the ARRIVEE_Tram_Est variable. In this specific case, this variable quite naturally contains the value of the constant CONST_PARCOURS_DEBUT since our yellow tram has also crossed the fncTramEntreeEst contact point before being stopped in front of signal n ° 1.

By deduction, as the trams have priority over the buses, the fncFinBusEntreeEst function will prioritize the entry of the tram instead of the white bus as in the usual traffic pattern. Of course, otherwise, the function would have validated the departure of the white bus.

It is thus incumbent on the blue tram, once exiting the station, to check whether the white bus is stopped in front of signal n ° 4. If the variable Occupe66 is equal to true then we switch signal n ° 4 to Clear. (which in the process will call the callback function of signal n ° 4) and the white bus will start (line n ° 43).

Now we have to deal with the last case! In fact, it is strictly identical to the second part of the test above. The only difference (when the blue tram crosses the contact point linked to our fncEndTramExitEst function) lies in the fact that segment n ° 42 precisely at this moment, is free of any vehicle occupancy.

At this point, we have reached the end of the function.

Information : before continuing, let’s remember this: we have 3 functions for managing trams and 4 functions for managing buses. Indeed, for trams, the fncFinTramEntreeEst function manages the arrival of the yellow tram and the departure of the blue tram. For buses, arrival and departure are separated via two distinct functions. Why ? to demonstrate several possible methods in order to add, for example, new functionalities. Here, we could for example (for buses), introduce additional possibilities (such as the display of destinations in information panels) between the arrival and departure of a bus. Obviously, this would induce additional lines of code and wanting to write everything in a single function would affect the readability of the code.

We have finished with the functions of the trams. We will now focus on the functions that manage bus movements.

The binomial fncBusEntreeEst - fncFinBusEntreeEst

These two functions work in tandem, i.e. when the yellow bus (in the right-left direction) wants to enter the bus station, it triggers the call to the fncBusEntreeEst function via a vehicle contact point. As long as the bus has not passed the other point of contact (calls the function fncFinBusEntreeEst), the trams must wait to enter or leave the station and therefore can not cross the road-rail crossing of the tram.

Detail of the fncBusEntreeEst function
				
					-- This function is activated via a contact point via the yellow bus
-- when it must enter the bus station
function fncBusEntreeEst()

	-- If no tram leaves or arrives in the station then...
	if ARRIVEE_Tram_Est == CONST_PARCOURS_TERMINE and DEPART_Tram_Est == CONST_PARCOURS_TERMINE then
			-- Set the ARRIVEE_Bus_Est route to DEBUT (start)
			ARRIVEE_Bus_Est = CONST_PARCOURS_DEBUT
			-- Switch signal no.11 (traffic light) stop
			EEPSetSignal(11, CONST_ARRET)
			-- Switches turnout 17 to Branch
			EEPSetSwitch(17, CONST_EMBRANCHEMENT)

		else
			-- otherwise a tram leaves or arrives in the station and
			-- switches the traffic light (signal 13) to stop
			EEPSetSignal(13, CONST_ARRET)			
	end

end -- End of the fncTramEntreeEst function

				
			
  1. Priority being given to trams, we must test if they are not already moving to exit or enter the station on the east side. If such were the case, the two variables DEPART_Tram_Est or ARRIVEE_Tram_Est would be equal to CONST_PARCOURS_DEBUT. Here in the contrary case, they must both be equal to CONST_PARCOURS_TERMINE. Notice the keyword and in the test condition. This keyword indicates that the two variables tested must both return the value true for the block of instructions immediately following the test to be executed (libne # 6).
  2. Thus, if our two variables are equal to CONST_PARCOURS_TERMINE, we fill in the ARRIVEE_Bus_Est variable with the constant CONST_PARCOURS_DEBUT (line n ° 8),
  3. We switch signal n ° 11 to Clear as well as switch n ° 17 to branch (lines n ° 10 and 12),
  4. Conversely, if one or both variables are not equal to CONST_PARCOURS_TERMINE, the program goes to the next block located immediately after the else keyword and signal n ° 13 is switched to Stop (red light) on line n ° 17,
  5. The function is now complete.
Detail of the function fncEndBusEntreeEst
				
					-- This function is activated via a contact point via the yellow bus
-- when it has just entered the east side bus station
function fncFinBusEntreeEst()

	-- Set the ARRIVEE_Bus_Est route to TERMINE (DONE)
	ARRIVEE_Bus_Est = CONST_PARCOURS_TERMINE

	-- If the yellow tram is waiting in front of signal n ° 10 then...
	if ARRIVEE_Tram_Est == CONST_PARCOURS_DEBUT then
			-- ... Switches signals 10 and 11 to Clear
			EEPSetSignal(10, CONST_VOIE_LIBRE)
			EEPSetSignal(11, CONST_VOIE_LIBRE)
			-- Switches turnout n ° 5 to Main Branch
			EEPSetSwitch(5, CONST_BRANCHE_PRINCIPALE)

		else
			-- If no tram is waiting in front of signal n ° 10 and if no white bus is waiting in front of signal n ° 4
			-- then we switch turnout n ° 17 to Main Branch and signal n ° 11 to Clear
			EEPSetSwitch(17, CONST_BRANCHE_PRINCIPALE)
			EEPSetSignal(11, CONST_VOIE_LIBRE)
	end

	-- In both cases, switch signal n ° 13 (traffic light) to Clear
	EEPSetSignal(13, CONST_VOIE_LIBRE)

end -- End of the function fncEndBusEntreeEst


				
			
  1. The bus has now fully entered the bus station. One informs the variable ARRIVEE_Bus_Est with the constant CONST_PARCOURS_TERMINE (line n ° 5),
  2. Priority being given to trams, we will test whether the yellow tram is waiting behind signal n ° 10 (line n ° 9). If this is the case, the ARRIVEE_Tram_Est variable is equal to the value of the CONST_PARCOURS_DEBUT constant because the tram has already crossed the fncTramEntreeEst contact point a few seconds after the bus. We then have to switch signals 10 and 11 to Clear and switch 5 to Main Branch (lines 11 to 14),
  3. Otherwise, if there is no tram waiting in front of signal n ° 10 then switch n ° 17 to Main Branch and signal n ° 11 to Clear (lines n ° 19 and 21),
  4. In both cases, bus or not, line n ° 24 switches signal n ° 13 (traffic light) to Clear thanks to the EEP function EEPSetSignal (13, CONST_VOIE_LIBRE).
  5. The function is now complete.

We had announced above that we had added two new functions for the departure and arrival of buses, unlike the management of trams whose arrival (of the yellow tram) and departure (of the blue tram) are managed within a single and unique function fncEndTramEntreeEst. We are now going to study the fncDepartBusEstfncFinDepartBusEst binomial.

The binomial fncDepartBusEst - fncFinDepartBusEst

These two functions work in tandem. The process is started when the yellow bus (in the right-left direction) has entered the bus station, after passing the 1st contact point fncBusEntreeEst.

Detail of the fncDepartBusEst function
				
					-- This function is activated via a contact point by the yellow bus when
-- this one entered the bus station in a right-left direction
function fncDepartBusEst()
	-- Test whether the white bus is on the road segment in front of the traffic light (signal n ° 4)
	Result, Occupe66, Name66 = EEPIsRoadTrackReserved(66, true)
	-- If the white bus is at the stop and if no tram leaves or arrives in the station then...
	if Occupe66 == true and DEPART_Tram_Est == CONST_PARCOURS_TERMINE and ARRIVEE_Tram_Est == CONST_PARCOURS_TERMINE then
		-- Switches signal 4 to Clear
		EEPSetSignal(4, CONST_VOIE_LIBRE, 1)
		-- The other signals are switched directly in the callback function of signal no.4
	end
end -- End of the fncDepartBusEst function

				
			
  1. Priority being given to trams, we must test if they are not already moving to exit or enter the station on the east side. If such were the case, the two variables DEPART_Tram_Est or ARRIVEE_Tram_Est would be equal to CONST_PARCOURS_DEBUT. Here in the contrary case, they must both be equal to CONST_PARCOURS_TERMINE. Notice the keyword and in the test condition. This keyword indicates that the two variables tested must both return the value true so that the block of instructions which immediately follows the test is executed (line n ° 7),
  2. Thus, if our two variables are equal to CONST_PARCOURS_TERMINE, we switch signal n ° 4 to Clear, which will have the effect of calling the callback function of signal n ° 4 EEPOnSignal_4 (see below) and switching correctly the elements concerned,
  3. The function is now complete.
Detail of the EEPOnSignal_4 callback function
				
					function EEPOnSignal_4(hEtat)

	if hEtat == CONST_VOIE_LIBRE then
		-- Position the DEPART_Bus_Est route on DEBUT (start)		
		DEPART_Bus_Est = CONST_PARCOURS_DEBUT
		-- Switch signal no.11 to stop
		EEPSetSignal(11, CONST_ARRET)
		-- Switches turnout no.18 to branch		
		EEPSetSwitch(18, CONST_EMBRANCHEMENT)
	end

end

				
			

This function is called (hence its name: callback function) automatically each time the state of signal 4 changes. This behavior is made possible because:

  1. We recorded signal n ° 4 thanks to the EEP function EEPRegisterSignal (4) at the start of the script,
  2. We changed to the number 1 in the 3rd parameter in all the EEP functions EEPSetSignal (4, CONST_VOIE_LIBRE or CONST_ARRET, 1)

If these two conditions are not met, the callback function will be ignored altogether.

Important : If you enter the number 1 in the 3rd parameter when the corresponding signal has not been previously recorded at the start of the script, you can create malfunctions or even an EEP crash. Always remember to properly record your signals if you are using the corresponding callback functions.

The variable hStatus in parentheses in the declaration of the function returns the state of the signal when the function is called.

Detail of the function fncEndDepartBusEst
  1. The white bus is now completely out of the bus station and has joined the main road. One informs the variable ARRIVEE_Bus_Est with the constant CONST_PARCOURS_TERMINE (line n ° 6),
  2. The priority being given to the trams, we will test whether the yellow tram is waiting behind signal n ° 10. If this is the case, the ARRIVEE_Tram_Est variable is equal to the value of the CONST_PARCOURS_DEBUT constant because the tram has already crossed the contact point fncTramEntreeEst key a few seconds after the departure of the bus. We still have to switch signals 10 and 11 to Clear and switch 5 to Main Branch (lines 11 to 14).
  3. Otherwise, the information from segment 42 is retrieved to check whether it is occupied by a vehicle (line 19). If the segment is occupied, the name of the vehicle is retrieved in the strCategorieVehicule variable (line n ° 20).
  4. If the test returns true then the ARRIVEE_Bus_Est variable is filled in with the CONST_PARCOURS_DEBUT constant (line n ° 25),
  5. Signal n ° 11 (traffic light) is switched to Stop using the EEPSetSignal (11, CONST_ARRET) function on line n ° 27. In fact, cars traveling in the opposite direction must imperatively stop at the red light to allow entry. the yellow bus in the bus station.
  6. We switch turnout n ° 17 to Branch using the EEPSetSwitch (17, CONST_EMBRANCHEMENT) function to line n ° 29. The bus will thus be able to enter the bus station,
  7. Otherwise, the white bus is gone, we switch signal n ° 11 (traffic light) to Clear (line n ° 32) so that the cars can restart,
  8. If segment n ° 42 was not occupied, signal n ° 11 (traffic light) is also switched to Clear (line n ° 37),
  9. The function is now complete.

 

				
					-- This function is activated via a contact point by the white bus
-- when it has just entered the main road on the east side
function fncFinDepartBusEst()

	-- Set the DEPART_Bus_Est route to TERMINE (DONE)			
	DEPART_Bus_Est = CONST_PARCOURS_TERMINE

	-- If the yellow tram has already passed the fncTramEntreeEst contact point then...
	if ARRIVEE_Tram_Est == CONST_PARCOURS_DEBUT then
			-- Switches signals 10 and 11 to Clear
			EEPSetSignal(10, CONST_VOIE_LIBRE)
			EEPSetSignal(11, CONST_VOIE_LIBRE)
			-- Switches turnout n ° 5 to Main Branch
			EEPSetSwitch(5, CONST_BRANCHE_PRINCIPALE)
		else
			-- otherwise we retrieve the information of segment n ° 42
			Result, Occupe42, Name42 = EEPIsRoadTrackReserved(42, true)
			
			if Occupe42 == true then
					local strCategorieVehicule = string.sub(Name42, string.find(Name42, "%a+"))

					-- If a vehicle occupies road segment no.42 and if it is a bus then...
					if string.upper(strCategorieVehicule) == "BUS" then					
							-- Set the ARRIVEE_Bus_Est route to DEBUT (START)
							ARRIVEE_Bus_Est = CONST_PARCOURS_DEBUT
							-- Switch signal no.11 (traffic light) stop
							EEPSetSignal(11, CONST_ARRET)
							-- Switches turnout 17 to Branch
							EEPSetSwitch(17, CONST_EMBRANCHEMENT)
						else
							--Switch signal n ° 11 (traffic light) to Clear
							EEPSetSignal(11, CONST_VOIE_LIBRE)
					end

				else
					-- Switch signal n ° 11 (traffic light) to clear
					EEPSetSignal(11, CONST_VOIE_LIBRE)
			end
	end

end	-- End of fncEndDepartBusEst
				
			

A question might cross our mind: Why is the script designed to only test lane occupancy for buses and not for Trams?

The answer is simple: the trams have priority above all and once the fncTramEntreeEst and fncTramEntreeOuest contact points have been crossed, the ARRIVEE_Tram_Est and ARRIVEE_Tram_Ouest variables are filled in with the CONST_PARCOURS_DEBUT constant. So, it suffices to test the values ​​of these variables without waiting for the trams to occupy such or such a segment of rails to know if they show up at the entrance or at the exit of the station and trigger an action. For buses, the priority is lower, that’s why we don’t need to fill in a variable in the entrance functions of the buses. Only the relevant information that interests us is the possible occupation of segments 42, 30 and 66, 70.

Of course, if we had used variables for the movements of the buses at the entrance or exit of the bus station, the tests would be more numerous and we would not have had the possibility to discover the EEP functions for the occupation of the tracks. Indeed, these functions are regularly used, they are EEPIsRailTrackReserved, EEPIsRoadTrackReserved, EEPIsTramTrackReserved and EEPIsAuxiliaryTrackReserved. We remind you that the segments concerned must be registered beforehand. We invite you to consult the Lua manual for EEP16 available for download on the site for more information.

To conclude: We let you look at the western part. But there is still a difference! When the yellow bus leaves the bus station, it must “cut” the left lane from the main road axis to join the right lane. This requires management of additional signals but nothing very complicated!

We hope this tutorial gives you some insight into handling signals and turnouts in EEP with Lua.

You can also view the demo of this script by clicking on the video below:

This article is now complete. If you have any questions or suggestions, please give us your feedback in the leave a reply input box below.

Thank you for your helpful comments. Have fun reading an other article. 

eep-world.com team

This article was translated by Pierre for the English side of the EEP-World from the article written by Domi for the French side of the EEP-World.

Leave a Reply