COMP2401
COMP2401 - Assignment #5
(Due: Wednesday, March 31st, 2021 @ 6pm)
In this assignment, you will make a simulator for a singe 4-way traffic-monitor at an intersection that
uses multiple threads and allows multiple vehicles to connect to it … with each vehicle and traffic light
running as its own process.
To begin this assignment, you should download ALL of the following files:
• makefile – use this to compile everything.
• simulator.c – this is the simulator program
• simulator.h – contains definitions and structs that will be used throughout your code
• trafficServer.c – this is the code to run the traffic server that communicates with the vehicles
• movementTimer.c – this code updates the positions of the vehicles
• trafficLight.c – this code simulates a traffic light
• trafficLight.h – contains definitions for the code pertaining to traffic lights
• vehicle.c – this is the code that runs a single vehicle
• vehicle.h – contains definitions for the vehicle-related information
• display.c – contains the window/drawing code that you will use to display everything
• generator.c – this is the code that repeatedly generates vehicles
• stop.c – this is the code that stops the server cleanly.
There are 4 programs that you will be running:
• simulator – runs the server that vehicles communicate with
• stop – stops the server when run
• vehicle – simulates a single vehicle
• generator – generates a bunch of vehicles repeatedly
Follow the steps below (in order) to complete the assignment:
(1) Use the makefile to compile everything. Run the simulator program. You should see the
intersection showing with 4 red lights and nothing moving. A getchar() has been placed at the
bottom of the main() in simulator.c so that you can stop the program either by pressing
ENTER in the terminal window or pressing the X on the simulator window.
(2) Four traffic lights have been created in the main() function of simulator.c. Do not alter this
code. However, you must add code to the main() function so that it creates 4 threads … one
for each traffic light. The thread should start by calling the runTrafficLight() function in the
trafficLight.c file. You will want to pass in a traffic light to that function. The code currently
runs in an infinite loop doing nothing but pausing for half a second. The TrafficLight struct
has been defined in trafficLight.h. Do not alter it. Add code to the function so that it makes
the traffic lights work properly by setting the currentState and countDownTimer values. If
you look at the trafficLight.h header, it also defines the following constants:
define RED 1
define DELAY_RED_1 2 // Transition from yellow to red
define YELLOW 3
define GREEN 4
define DELAY_RED_2 5 // Transition from red to green
define RED_COUNTDOWN 13 // Time units for each state
define DELAY_RED_COUNTDOWN 2
define YELLOW_COUNTDOWN 3
define GREEN_COUNTDOWN 10
The first 5 definitions represent the 5 states of the traffic light. The horizontal lights will start off
in state DELAY_RED_1 whereas the vertical lights will start off in state DELAY_RED_2. That
means, they all start off red. The traffic lights must go from state i to state i-1 and wrap back
around in an infinite loop. So … from DELAY_RED_2 to GREEN to YELLOW to
DELAY_RED_1 to RED to DELAY_RED_2 to GREEN etc.. The amount of time that it should
remain in each state is specified as the 4 other COUNTDOWN constants shown. Each
number represents the number of half-seconds. So 10 means a delay of 10 half-seconds …
or 5 seconds. Both DELAY-RED states last for 1 second. The idea is to count down from the
current state every half second and then change the state accordingly. Complete the
setState() function as well so that the appropriate countDownTimer value is used. Run the
simulator to test your code. Look at the sample run video and watch the lights to make sure
that yours are changing in the same way. Note that all 4 lights should be red for 1 second
during a switch over from horizontal to vertical and vice-versa.
(3) You will now set up the simulator to have an additional thread to represent a traffic monitor that
will interact with the vehicles coming into the intersection by using TCP server socket
communication. Add code to the main() function in simulator.c so that it creates a single
thread that calls the handleIncomingRequests() function in the trafficServer.c file. This
function should take in an Intersection parameter (defined in simulator.h).
A traffic Intersection has already been created in the main() for you and it’s traffic lights have
been set. This struct has a TrafficMonitor struct inside of it, which represents the data that
the monitor requires as follows:
typedef struct {
char online; // 0 = no, 1 = yes
TrafficLineup traffic[4];
unsigned char idCounter[4];
} TrafficMonitor;
In essence, the traffic monitor keeps track of 4 vehicle lineups … one for each of the 4
directions coming into and leaving the traffic intersection. It also keeps track of 4 ID counters
(one for each direction) which is used to assign ID’s to the individual vehicles coming in so that
they can be uniquely identified. Finally, it keeps track of whether it is online or has been shut
down.
The handleIncomingRequests() function in trafficServer.c must set up the TCP server
before entering the infinite loop. It should then loop as long as the intersection’s monitor is
online, otherwise the thread should stop. Inside the loop, you should accept any incoming
requests. There may be only three types: STOP, CONNECT and UPDATE. For now, you will
get the STOP request working. Upon receiving this request, the traffic monitor should go
offline and the loop should end.
Create a program called stop.c that simply sends a STOP request to the traffic monitor. The
simulator should shut down once it has been received. You will need to remove the getchar()
from the main() function in simulator.c and also wait for the traffic server thread to stop and
rejoin the main() function otherwise the simulator will stop right away, not even showing the
window. Test your code by running the simulator in the background (i.e., use ./simulator&)
and then run ./stop to see if it all works. Make sure to do ps in the terminal window to ensure
that the simulator has stopped. You can always kill the simulator process from the terminal
window by using the kill command.
Once you have this working, you have ensured that server/client communications are working.
Do not go any further until this is the case. Make sure to check for errors in all the client server
communications (as was done in the notes) and print out appropriate error messages if
something goes wrong. Refer to the example programs in the notes if you are having trouble,
as it can be tricky to get everything “plugged in” properly.
(4) Now lets get started with the vehicles. The TrafficMonitor monitors traffic in 4 directions
defined in simulator.h as NORTHBOUND, SOUTHBOUND, EASTBOUND and
WESTBOUND. Each direction has its own lineup of vehicles which is defined as follows:
typedef struct {
short entryX;
short entryY;
short stopX;
short stopY;
short exitX;
short exitY;
Vehicle lineup[MAX_CONNECTIONS];
unsigned char lineCount;
} TrafficLineup;
The 6 short values defined therein have been set for you by the main() function in
simulator.c, so you should not change them. A vehicle will enter the intersection at
(entryX, entryY), stop for a red light at (stopX, stopY) and finally exit the intersection at
(exitX, exitY). The lineup array will contain all the vehicles that are currently in the
intersection facing in this particular direction. It has been set to allow at most
MAX_CONNECTIONS vehicles at a time and lineCount indicates how many are there at any
current moment. Below is an example showing 5 vehicles in the WESTBOUND lineup. The
vehicle at position 0 in the array is always at the front of the lineup. The vehicle at position
lineCount-1 is always the last one that entered the intersection.
Adjust the handleIncomingRequests() function so that it accepts a CONNECT command
from a vehicle. This is a request for the vehicle to register with the traffic monitor so that it can
send and receive updates afterwards. Along with this CONNECT request, the vehicle should
also send it’s current speed (i.e., a value from 0 to VEHICLE_TOP_SPEED) and direction.
You will notice that the Vehicle struct define in vehicle.h maintains an (x, y) position, a speed
and an id. When the traffic monitor receives the CONNECT request, it should first check to
make sure that there are less than MAX_CONNECTIONS vehicles in the lineup for that
vehicle’s direction. If the capacity has already been reached, the vehicle should be informed
that it may NOT connect at this time, and nothing else should be done … although you may
want some kind of prinf() statement here to help debug.
If the capacity has not been reached yet, then the vehicle may be registered. The lineup array
in the TrafficMonitor already has MAX_CONNECTIONS vehicles created. So, you just need
to set the values accordingly. The vehicle’s (x, y) should be the monitor’s (entryX, entryY)
values, the speed should be what was sent from the vehicle, and the id should be set to the
next available id according to the idCounter for that direction. The idCounter starts at 1 and
when reaching 255, it should wrap back around to 1 … with 0 being reserved as an undefined
id value. The vehicle then needs to be informed that all went ok and its newly assigned id
value should be sent back to it. Make sure to adjust the lineCount value accordingly.
Create a vehicle.c program that represents a vehicle. The program must take 2 incoming
command line parameters … the direction followed by the speed. The direction should be a
single digit that matches the 4 directions specified in simulator.h (i.e., 0 to 3). The speed in
the parameter will be an integer representing the percentage of the vehicle’s top speed. 50
means half speed whereas 100 means full speed. You’ll have to convert this number to the
vehicle’s actual speed (which is in pixels as a number from 0 to VEHICLE_TOP_SPEED as
defined in vehicle.h.
The program should then attempt to register with the traffic monitor by sending a CONNECT
request. If unable to connect, the program should exit, otherwise it should go into an infinite
loop which has usleep(50000); at the bottom of the loop to provide some delay. Test your
code to make sure that the vehicle is able to register with the traffic monitor. Use some
printf() statements to know what is being passed back and forth. Test the program by
running the simulator in the background (you may have to stop and restart from the last time
you ran it). While the simulator is running, test your vehicle by using ./vehicle 0 50 for a
NORTHBOUND vehicle. Try the other directions and some other speeds to make sure that
the values are sent/received properly. However, you won’t see anything on the simulator
screen. You will also need to use Cntrl-C to stop the vehicle program, for now.
(5) Now we will get the vehicle’s moving. In the main() function of the simulator.c file, create
another thread that will be used to move the vehicles. The thread should call a function
(named whatever you’d like) defined in a file called movementTimer.c which takes a single
Intersection parameter. This function must loop forever. It should go through the lineups in
each of the 4 directions and for each vehicle in the intersection, it should move that vehicle
forward. Moving the vehicle forward should simply add or subtract the vehicle’s speed value
from the x or y value. Put usleep(20000); at the bottom of the loop to provide the right amount
of delay for testing. You will need to #include
Test your code by running the simulator in the background (make sure to restart it) and then
running the vehicle program a few times for varying directions and speeds (test with positive
non-zero speeds only). You will also notice a counter in the display … showing the number of
vehicles that entered the intersection from that direction.
(6) Now we will get the vehicle sending updates. Adjust your vehicle.c main() function so that
within the infinite loop it sends an UPDATE request to the traffic monitor. You may want to
create a function for this. For the UPDATE, the vehicle should send its speed, direction and
connection ID (i.e., the ID returned from when it was registered). The traffic monitor should
send back a YES or a NO. If a NO is returned, the vehicle process should gracefully shut
down. If a YES is returned, control should go back to the main() function to delay a bit and
then send for the next update.
When the traffic monitor receives the UPDATE request, it will need to read in the speed,
direction and ID and then look through the vehicles in the lineup that corresponds to the given
direction to see if one is in there that matches the given ID. If a vehicle is not found with that
ID, it should simply send back a NO. Otherwise, the vehicle is there and needs to be checked
to see if it went out of range. Compare the vehicle’s current (x, y) location with the direction,
INTERSECTION_WIDTH, INTERSECTION_HEIGHT and VEHICLE_WIDTH values to see if it
has gone off the window’s top, bottom, left or right border. If it has not, then send back a YES.
If it has gone off the window, send back a NO and remove that vehicle from the direction’s
lineup. To remove the vehicle, you will need to first determine where it is in the lineup … that
is … the index it is at within the lineup array. Normally, it is the first vehicle at index 0 in the
lineup array, but in some cases, due to the nature of process-timing, it may be at a different
index. Once found, say at index i, you should move all vehicles in the lineup back by one
index within the lineup so that order is maintained. You’ll have to copy all the vehicle data
over each time, since the lineup is an array of vehicles, not pointers. Don’t forget to reduce the
lineCount when you are done.
Test your code as before. Start the simulator in the background… and start up some vehicles
in various directions. If all is working … you should see vehicles coming in and then leaving.
The counts shown for each direction should reflect how many vehicles you are seeing. Try
starting 3 or 4 vehicles quickly in the same direction (use & to run them in the background).
Make sure that when all vehicles are gone in a direction, that the count is back to 0. When you
are convinced that things are working … keep the simulator running and check the terminal
window, using ps, to make sure that all the vehicle processes are gone. If some vehicle
processes are still there … then they are not shutting down properly. Remember that you can
slow things down by using smaller speeds in your command line argument for your test
vehicles.
(7) We will not make the vehicles obey the traffic lights. Adjust the code for the UPDATE in the
traffic monitor so that when a YES is sent back to the vehicle, the state of the traffic light in
that vehicle’s direction is also sent back as well as the distance to the traffic light (in pixels).
The state corresponds to one of the fixed values in trafficlight.h. The distance will be the
distance from the vehicle (x, y) location to the lineup’s (stopX, stopY) location … which has
already been set for you. If the vehicle has already gone past the (stopX, stopY) location,
then you should send back a distance value of MAX_DISTANCE and a traffic light state of
GREEN, since that vehicle has no traffic light ahead of it anymore.
Now adjust the vehicle.c code so that it reads in these traffic light state and distance to
traffic light values. Print them out to make sure that they look proper. Once it works, adjust
the vehicle code so that it adjusts the speed of the vehicle based on this traffic light information
by using this logic:
IF (the light is not green and the distance is not MAX_DISTANCE) THEN
IF (distance <= (twice the vehicle width + speed)) THEN
we are too close … slam on the brakes by setting speed to 0
OTHERWISE IF (distance <= (twice the vehicle width * speed)) THEN
We are getting close, so slow down by setting speed to speed/1.5, as long
as the speed is > 1. Make sure the speed is not lower than 1 though, so
that there is movement.
OTHERWISE
speed up by 1 but never go faster that VEHICLE_TOP_SPEED
Test your code to make sure that the vehicles are stopping at yellow/red lights properly and
then speeding up again once the light turns green.
(8) Now we need to stop the cars from hitting one another. Adjust the code for the UPDATE in the
traffic monitor so that when a YES is sent back to the vehicle, along with the traffic light
information you also send back the speed of the vehicle ahead of this one as well as the
distance to that vehicle. You will need to look at the vehicle in the lineup ahead of this one.
If there is no vehicle ahead, the distance should be MAX_DISTANCE and the speed should be
VEHICLE_TOP_SPEED + 1. Otherwise, you should determine the vehicle ahead, get its
speed, and calculate the distance between the vehicles.
In the vehicle.c file, you will need to replace the traffic light logic used earlier, with the
following code logic, which takes into account the traffic light information as well as the
information from the vehicle ahead…
IF (distance to car ahead is MAX_DISTANCE) THEN
IF (the light is not green and the distance to light is not MAX_DISTANCE) THEN
IF (distance to light <= (twice the vehicle width + speed)) THEN
we are too close … slam on the brakes by setting speed to 0
ELSE IF (distance to light <= (3 times the vehicle width * speed)) THEN
We are getting close, so slow down by setting speed to speed/1.5, as long
as the speed is > 1. Make sure the speed is not lower than 1 though, so
that there is movement.
ELSE
speed up by 1 but never go faster that VEHICLE_TOP_SPEED
ELSE
IF (the light is not green and the distance to light is not MAX_DISTANCE) THEN
IF (distance to light <= (twice the vehicle width + speed)) THEN
we are too close … slam on the brakes by setting speed to 0
ELSE IF (distance to light <= (3 times the vehicle width * speed)) THEN
we are getting close, so slow down by setting speed to speed/1.5, as long
as the speed is > 1. Make sure the speed is not lower than 1 though, so
that there is movement.
IF (the vehicle ahead is going slower that this vehicle) THEN
IF (distance to vehicle ahead <= (4 times the VEHICLE_WIDTH + speed)) THEN
we are too close … slam on the brakes by setting speed to 0
ELSE IF (distance to vehicle ahead <= (3 times the vehicle width * speed)) THEN
slow down by 1. Make sure the speed is not lower than 1 though, so
that there is movement.
ELSE IF ( (the vehicle ahead is going faster that this vehicle) AND
((distance to light is MAX_DISTANCE) OR (the light is green)) ) THEN
speed up by 1 but do NOT go faster that the vehicle ahead
Once you make these changes, your code should allow cars to stop properly at traffic lights,
line up one after another nicely, and avoid crashing into one another. Try lining up a few cars
in the same direction to make sure that they don’t collide and that they slow-down and speedup
properly. If things don’t work out … check your logic carefully with the logic above. If you
add too many vehicles quickly in the same direction though … the above code will not leave
enough time for vehicles to slow down quick enough and they may collide. Do not worry about
this.
(9) We will finally get some traffic flowing automatically. Create a program called generator.c. It
should run in an infinite loop constantly generating vehicles. Use usleep(500000); to make
sure that there is an appropriate delay between vehicles. DO NOT change this sleep value.
Your code MUST do a system call to run the vehicle program with a random direction and
with a speed of 50%. Restart the simulator and run the generator code. You should have
results similar to what the sample run video shows. You may get an occasional very rare glitch
once in a while (e.g., once a minute), but this is ok since we are looking at overall behavior.
But vehicles should always slow down, stop, speed up and avoid collisions in an organized
manner.
IMPORTANT SUBMISSION INSTRUCTIONS:
Submit:
- A Readme text file containing
• your name and studentNumber
• a list of source files submitted
• any specific instructions for compiling and/or running your code - All of your .c source files and all other files needed for testing/running your programs.
- Any output files required, if there are any.
The code MUST compile and run on the course VM.
• If your internet connection at home is down or does not work, we will not accept this as a reason for
handing in an assignment late ... so make sure to submit the assignment WELL BEFORE it is due !
• You WILL lose marks on this assignment if any of your files are missing. So, make sure that you hand
in the correct files and version of your assignment. You will also lose marks if your code is not written
neatly with proper indentation and containing a reasonable number of comments. See course
notes for examples of what is proper indentation, writing style and reasonable commenting).