├── .github
└── workflows
│ └── manual.yml
├── .gitignore
├── CMakeLists.txt
├── CODEOWNERS
├── LICENSE.md
├── README.md
├── data
├── nyc.jpg
├── nyc.webp
├── paris.jpg
├── paris.webp
└── traffic_simulation.gif
└── src
├── Graphics.cpp
├── Graphics.h
├── Intersection.cpp
├── Intersection.h
├── Street.cpp
├── Street.h
├── TrafficLight.cpp
├── TrafficLight.h
├── TrafficObject.cpp
├── TrafficObject.h
├── TrafficSimulator-Final.cpp
├── Vehicle.cpp
└── Vehicle.h
/.github/workflows/manual.yml:
--------------------------------------------------------------------------------
1 | # Workflow to ensure whenever a Github PR is submitted,
2 | # a JIRA ticket gets created automatically.
3 | name: Manual Workflow
4 |
5 | # Controls when the action will run.
6 | on:
7 | # Triggers the workflow on pull request events but only for the master branch
8 | pull_request_target:
9 | types: [assigned, opened, reopened]
10 |
11 | # Allows you to run this workflow manually from the Actions tab
12 | workflow_dispatch:
13 |
14 | jobs:
15 | test-transition-issue:
16 | name: Convert Github Issue to Jira Issue
17 | runs-on: ubuntu-latest
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@master
21 |
22 | - name: Login
23 | uses: atlassian/gajira-login@master
24 | env:
25 | JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
26 | JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
27 | JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
28 |
29 | - name: Create NEW JIRA ticket
30 | id: create
31 | uses: atlassian/gajira-create@master
32 | with:
33 | project: CONUPDATE
34 | issuetype: Task
35 | summary: |
36 | Github PR - nd213 C++ Nanodegree | Repo: ${{ github.repository }} | PR# ${{github.event.number}}
37 | description: |
38 | Repo link: https://github.com/${{ github.repository }}
39 | PR no. ${{ github.event.pull_request.number }}
40 | PR title: ${{ github.event.pull_request.title }}
41 | PR description: ${{ github.event.pull_request.description }}
42 | In addition, please resolve other issues, if any.
43 | fields: '{"components": [{"name":"nd213 C++"}], "customfield_16449":"https://classroom.udacity.com/nanodegrees/nd213/dashboard/overview", "customfield_16450":"Resolve the PR", "labels": ["github"], "priority":{"id": "4"}}'
44 |
45 | - name: Log created issue
46 | run: echo "Issue ${{ steps.create.outputs.issue }} was created"
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .github/**
2 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.11.3)
2 |
3 | # set(CMAKE_CXX_STANDARD 17)
4 | project(traffic_simulation)
5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -pthread")
6 |
7 | find_package(OpenCV 4.1 REQUIRED)
8 |
9 | include_directories(${OpenCV_INCLUDE_DIRS})
10 | link_directories(${OpenCV_LIBRARY_DIRS})
11 | add_definitions(${OpenCV_DEFINITIONS})
12 |
13 | # Find all executables
14 | file(GLOB project_SRCS src/*.cpp) #src/*.h
15 |
16 | # Add project executable
17 | add_executable(traffic_simulation ${project_SRCS})
18 | target_link_libraries(traffic_simulation ${OpenCV_LIBRARIES})
19 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @udacity/active-public-content
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 |
2 | Copyright © 2012 - 2021, Udacity, Inc.
3 |
4 | Udacity hereby grants you a license in and to the Educational Content, including but not limited to homework assignments, programming assignments, code samples, and other educational materials and tools (as further described in the Udacity Terms of Use), subject to, as modified herein, the terms and conditions of the Creative Commons **Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)** License located at
5 | [http://creativecommons.org/licenses/by-nc-nd/4.0](http://creativecommons.org/licenses/by-nc-nd/4.0) and successor locations for such license (the "CC License") provided that, in each case, the Educational Content is specifically marked as being subject to the CC License.
6 | Udacity expressly defines the following as falling outside the definition of "non-commercial":
7 | 1. the sale or rental of (i) any part of the Educational Content, (ii) any derivative works based at least in part on the Educational Content, or (iii) any collective work that includes any part of the Educational Content;
8 | 2. the sale of access or a link to any part of the Educational Content without first obtaining informed consent from the buyer (that the buyer is aware that the Educational Content, or such part thereof, is available at the Website free of charge);
9 | 3. providing training, support, or editorial services that use or reference the Educational Content in exchange for a fee;
10 | 4. the sale of advertisements, sponsorships, or promotions placed on the Educational Content, or any part thereof, or the sale of advertisements, sponsorships, or promotions on any website or blog containing any part of the Educational Material, including without limitation any "pop-up advertisements";
11 | 5. the use of Educational Content by a college, university, school, or other educational institution for instruction where tuition is charged; and
12 | 6. the use of Educational Content by a for-profit corporation or non-profit entity for internal professional development or training.
13 |
14 |
15 |
16 | THE SERVICES AND ONLINE COURSES (INCLUDING ANY CONTENT) ARE PROVIDED "AS IS" AND "AS AVAILABLE" WITH NO REPRESENTATIONS OR WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. YOU ASSUME TOTAL RESPONSIBILITY AND THE ENTIRE RISK FOR YOUR USE OF THE SERVICES, ONLINE COURSES, AND CONTENT. WITHOUT LIMITING THE FOREGOING, WE DO NOT WARRANT THAT (A) THE SERVICES, WEBSITES, CONTENT, OR THE ONLINE COURSES WILL MEET YOUR REQUIREMENTS OR EXPECTATIONS OR ACHIEVE THE INTENDED PURPOSES, (B) THE WEBSITES OR THE ONLINE COURSES WILL NOT EXPERIENCE OUTAGES OR OTHERWISE BE UNINTERRUPTED, TIMELY, SECURE OR ERROR-FREE, (C) THE INFORMATION OR CONTENT OBTAINED THROUGH THE SERVICES, SUCH AS CHAT ROOM SERVICES, WILL BE ACCURATE, COMPLETE, CURRENT, ERROR- FREE, COMPLETELY SECURE OR RELIABLE, OR (D) THAT DEFECTS IN OR ON THE SERVICES OR CONTENT WILL BE CORRECTED. YOU ASSUME ALL RISK OF PERSONAL INJURY, INCLUDING DEATH AND DAMAGE TO PERSONAL PROPERTY, SUSTAINED FROM USE OF SERVICES.
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CPPND: Program a Concurrent Traffic Simulation
2 |
3 |
4 |
5 | This is the project for the fourth course in the [Udacity C++ Nanodegree Program](https://www.udacity.com/course/c-plus-plus-nanodegree--nd213): Concurrency.
6 |
7 | Throughout the Concurrency course, you have been developing a traffic simulation in which vehicles are moving along streets and are crossing intersections. However, with increasing traffic in the city, traffic lights are needed for road safety. Each intersection will therefore be equipped with a traffic light. In this project, you will build a suitable and thread-safe communication protocol between vehicles and intersections to complete the simulation. Use your knowledge of concurrent programming (such as mutexes, locks and message queues) to implement the traffic lights and integrate them properly in the code base.
8 |
9 | ## Dependencies for Running Locally
10 | * cmake >= 2.8
11 | * All OSes: [click here for installation instructions](https://cmake.org/install/)
12 | * make >= 4.1 (Linux, Mac), 3.81 (Windows)
13 | * Linux: make is installed by default on most Linux distros
14 | * Mac: [install Xcode command line tools to get make](https://developer.apple.com/xcode/features/)
15 | * Windows: [Click here for installation instructions](http://gnuwin32.sourceforge.net/packages/make.htm)
16 | * OpenCV >= 4.1
17 | * The OpenCV 4.1.0 source code can be found [here](https://github.com/opencv/opencv/tree/4.1.0)
18 | * gcc/g++ >= 5.4
19 | * Linux: gcc / g++ is installed by default on most Linux distros
20 | * Mac: same deal as make - [install Xcode command line tools](https://developer.apple.com/xcode/features/)
21 | * Windows: recommend using [MinGW](http://www.mingw.org/)
22 |
23 | ## Basic Build Instructions
24 |
25 | 1. Clone this repo.
26 | 2. Make a build directory in the top level directory: `mkdir build && cd build`
27 | 3. Compile: `cmake .. && make`
28 | 4. Run it: `./traffic_simulation`.
29 |
30 | ## Project Tasks
31 |
32 | When the project is built initially, all traffic lights will be green. When you are finished with the project, your traffic simulation should run with red lights controlling traffic, just as in the .gif file above. See the classroom instruction and code comments for more details on each of these parts.
33 |
34 | - **Task FP.1** : Define a class `TrafficLight` which is a child class of `TrafficObject`. The class shall have the public methods `void waitForGreen()` and `void simulate()` as well as `TrafficLightPhase getCurrentPhase()`, where `TrafficLightPhase` is an enum that can be either `red` or `green`. Also, add the private method `void cycleThroughPhases()`. Furthermore, there shall be the private member `_currentPhase` which can take `red` or `green` as its value.
35 | - **Task FP.2** : Implement the function with an infinite loop that measures the time between two loop cycles and toggles the current phase of the traffic light between red and green and sends an update method to the message queue using move semantics. The cycle duration should be a random value between 4 and 6 seconds. Also, the while-loop should use `std::this_thread::sleep_`for to wait 1ms between two cycles. Finally, the private method `cycleThroughPhases` should be started in a thread when the public method `simulate` is called. To do this, use the thread queue in the base class.
36 | - **Task FP.3** : Define a class `MessageQueue` which has the public methods send and receive. Send should take an rvalue reference of type TrafficLightPhase whereas receive should return this type. Also, the class should define an `std::dequeue` called `_queue`, which stores objects of type `TrafficLightPhase`. Finally, there should be an `std::condition_variable` as well as an `std::mutex` as private members.
37 | - **Task FP.4** : Implement the method `Send`, which should use the mechanisms `std::lock_guard` as well as `_condition.notify_one()` to add a new message to the queue and afterwards send a notification. Also, in class `TrafficLight`, create a private member of type `MessageQueue` for messages of type `TrafficLightPhase` and use it within the infinite loop to push each new `TrafficLightPhase` into it by calling send in conjunction with move semantics.
38 | - **Task FP.5** : The method receive should use `std::unique_lock` and `_condition.wait()` to wait for and receive new messages and pull them from the queue using move semantics. The received object should then be returned by the receive function. Then, add the implementation of the method `waitForGreen`, in which an infinite while-loop runs and repeatedly calls the `receive` function on the message queue. Once it receives `TrafficLightPhase::green`, the method returns.
39 | - **Task FP.6** : In class Intersection, add a private member `_trafficLight` of type `TrafficLight`. In method `Intersection::simulate()`, start the simulation of `_trafficLight`. Then, in method `Intersection::addVehicleToQueue`, use the methods `TrafficLight::getCurrentPhase` and `TrafficLight::waitForGreen` to block the execution until the traffic light turns green.
40 |
--------------------------------------------------------------------------------
/data/nyc.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/udacity/CppND-Program-a-Concurrent-Traffic-Simulation/eab5dac9e2c0ede403450731c72356028f651c88/data/nyc.jpg
--------------------------------------------------------------------------------
/data/nyc.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/udacity/CppND-Program-a-Concurrent-Traffic-Simulation/eab5dac9e2c0ede403450731c72356028f651c88/data/nyc.webp
--------------------------------------------------------------------------------
/data/paris.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/udacity/CppND-Program-a-Concurrent-Traffic-Simulation/eab5dac9e2c0ede403450731c72356028f651c88/data/paris.jpg
--------------------------------------------------------------------------------
/data/paris.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/udacity/CppND-Program-a-Concurrent-Traffic-Simulation/eab5dac9e2c0ede403450731c72356028f651c88/data/paris.webp
--------------------------------------------------------------------------------
/data/traffic_simulation.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/udacity/CppND-Program-a-Concurrent-Traffic-Simulation/eab5dac9e2c0ede403450731c72356028f651c88/data/traffic_simulation.gif
--------------------------------------------------------------------------------
/src/Graphics.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "Graphics.h"
6 | #include "Intersection.h"
7 |
8 | void Graphics::simulate()
9 | {
10 | this->loadBackgroundImg();
11 | while (true)
12 | {
13 | // sleep at every iteration to reduce CPU usage
14 | std::this_thread::sleep_for(std::chrono::milliseconds(1));
15 |
16 | // update graphics
17 | this->drawTrafficObjects();
18 | }
19 | }
20 |
21 | void Graphics::loadBackgroundImg()
22 | {
23 | // create window
24 | _windowName = "Concurrency Traffic Simulation";
25 | cv::namedWindow(_windowName, cv::WINDOW_NORMAL);
26 |
27 | // load image and create copy to be used for semi-transparent overlay
28 | cv::Mat background = cv::imread(_bgFilename);
29 | _images.push_back(background); // first element is the original background
30 | _images.push_back(background.clone()); // second element will be the transparent overlay
31 | _images.push_back(background.clone()); // third element will be the result image for display
32 | }
33 |
34 | void Graphics::drawTrafficObjects()
35 | {
36 | // reset images
37 | _images.at(1) = _images.at(0).clone();
38 | _images.at(2) = _images.at(0).clone();
39 |
40 | // create overlay from all traffic objects
41 | for (auto it : _trafficObjects)
42 | {
43 | double posx, posy;
44 | it->getPosition(posx, posy);
45 |
46 | if (it->getType() == ObjectType::objectIntersection)
47 | {
48 | // cast object type from TrafficObject to Intersection
49 | std::shared_ptr intersection = std::dynamic_pointer_cast(it);
50 |
51 | // set color according to traffic light and draw the intersection as a circle
52 | cv::Scalar trafficLightColor = intersection->trafficLightIsGreen() == true ? cv::Scalar(0, 255, 0) : cv::Scalar(0, 0, 255);
53 | cv::circle(_images.at(1), cv::Point2d(posx, posy), 25, trafficLightColor, -1);
54 | }
55 | else if (it->getType() == ObjectType::objectVehicle)
56 | {
57 | cv::RNG rng(it->getID());
58 | int b = rng.uniform(0, 255);
59 | int g = rng.uniform(0, 255);
60 | int r = sqrt(255*255 - g*g - b*b); // ensure that length of color vector is always 255
61 | cv::Scalar vehicleColor = cv::Scalar(b,g,r);
62 | cv::circle(_images.at(1), cv::Point2d(posx, posy), 50, vehicleColor, -1);
63 | }
64 | }
65 |
66 | float opacity = 0.85;
67 | cv::addWeighted(_images.at(1), opacity, _images.at(0), 1.0 - opacity, 0, _images.at(2));
68 |
69 | // The following code allows for resizing in case of graphics window taking up a large space
70 | cv::Mat img;
71 | cv::resize(_images.at(2), img, cv::Size(1040,720),0,0,1);
72 | cv::imshow(_windowName, img);
73 |
74 | cv::waitKey(33);
75 | }
76 |
--------------------------------------------------------------------------------
/src/Graphics.h:
--------------------------------------------------------------------------------
1 | #ifndef GRAPHICS_H
2 | #define GRAPHICS_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "TrafficObject.h"
8 |
9 | class Graphics
10 | {
11 | public:
12 | // constructor / desctructor
13 |
14 | // getters / setters
15 | void setBgFilename(std::string filename) { _bgFilename = filename; }
16 | void setTrafficObjects(std::vector> &trafficObjects) { _trafficObjects = trafficObjects; };
17 |
18 | // typical behaviour methods
19 | void simulate();
20 |
21 | private:
22 | // typical behaviour methods
23 | void loadBackgroundImg();
24 | void drawTrafficObjects();
25 |
26 | // member variables
27 | std::vector> _trafficObjects;
28 | std::string _bgFilename;
29 | std::string _windowName;
30 | std::vector _images;
31 | };
32 |
33 | #endif
--------------------------------------------------------------------------------
/src/Intersection.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #include "Street.h"
8 | #include "Intersection.h"
9 | #include "Vehicle.h"
10 |
11 | /* Implementation of class "WaitingVehicles" */
12 |
13 | int WaitingVehicles::getSize()
14 | {
15 | std::lock_guard lock(_mutex);
16 |
17 | return _vehicles.size();
18 | }
19 |
20 | void WaitingVehicles::pushBack(std::shared_ptr vehicle, std::promise &&promise)
21 | {
22 | std::lock_guard lock(_mutex);
23 |
24 | _vehicles.push_back(vehicle);
25 | _promises.push_back(std::move(promise));
26 | }
27 |
28 | void WaitingVehicles::permitEntryToFirstInQueue()
29 | {
30 | std::lock_guard lock(_mutex);
31 |
32 | // get entries from the front of both queues
33 | auto firstPromise = _promises.begin();
34 | auto firstVehicle = _vehicles.begin();
35 |
36 | // fulfill promise and send signal back that permission to enter has been granted
37 | firstPromise->set_value();
38 |
39 | // remove front elements from both queues
40 | _vehicles.erase(firstVehicle);
41 | _promises.erase(firstPromise);
42 | }
43 |
44 | /* Implementation of class "Intersection" */
45 |
46 | Intersection::Intersection()
47 | {
48 | _type = ObjectType::objectIntersection;
49 | _isBlocked = false;
50 | }
51 |
52 | void Intersection::addStreet(std::shared_ptr street)
53 | {
54 | _streets.push_back(street);
55 | }
56 |
57 | std::vector> Intersection::queryStreets(std::shared_ptr incoming)
58 | {
59 | // store all outgoing streets in a vector ...
60 | std::vector> outgoings;
61 | for (auto it : _streets)
62 | {
63 | if (incoming->getID() != it->getID()) // ... except the street making the inquiry
64 | {
65 | outgoings.push_back(it);
66 | }
67 | }
68 |
69 | return outgoings;
70 | }
71 |
72 | // adds a new vehicle to the queue and returns once the vehicle is allowed to enter
73 | void Intersection::addVehicleToQueue(std::shared_ptr vehicle)
74 | {
75 | std::unique_lock lck(_mtx);
76 | std::cout << "Intersection #" << _id << "::addVehicleToQueue: thread id = " << std::this_thread::get_id() << std::endl;
77 | lck.unlock();
78 |
79 | // add new vehicle to the end of the waiting line
80 | std::promise prmsVehicleAllowedToEnter;
81 | std::future ftrVehicleAllowedToEnter = prmsVehicleAllowedToEnter.get_future();
82 | _waitingVehicles.pushBack(vehicle, std::move(prmsVehicleAllowedToEnter));
83 |
84 | // wait until the vehicle is allowed to enter
85 | ftrVehicleAllowedToEnter.wait();
86 | lck.lock();
87 | std::cout << "Intersection #" << _id << ": Vehicle #" << vehicle->getID() << " is granted entry." << std::endl;
88 |
89 | // FP.6b : use the methods TrafficLight::getCurrentPhase and TrafficLight::waitForGreen to block the execution until the traffic light turns green.
90 |
91 | lck.unlock();
92 | }
93 |
94 | void Intersection::vehicleHasLeft(std::shared_ptr vehicle)
95 | {
96 | //std::cout << "Intersection #" << _id << ": Vehicle #" << vehicle->getID() << " has left." << std::endl;
97 |
98 | // unblock queue processing
99 | this->setIsBlocked(false);
100 | }
101 |
102 | void Intersection::setIsBlocked(bool isBlocked)
103 | {
104 | _isBlocked = isBlocked;
105 | //std::cout << "Intersection #" << _id << " isBlocked=" << isBlocked << std::endl;
106 | }
107 |
108 | // virtual function which is executed in a thread
109 | void Intersection::simulate() // using threads + promises/futures + exceptions
110 | {
111 | // FP.6a : In Intersection.h, add a private member _trafficLight of type TrafficLight. At this position, start the simulation of _trafficLight.
112 |
113 | // launch vehicle queue processing in a thread
114 | threads.emplace_back(std::thread(&Intersection::processVehicleQueue, this));
115 | }
116 |
117 | void Intersection::processVehicleQueue()
118 | {
119 | // print id of the current thread
120 | //std::cout << "Intersection #" << _id << "::processVehicleQueue: thread id = " << std::this_thread::get_id() << std::endl;
121 |
122 | // continuously process the vehicle queue
123 | while (true)
124 | {
125 | // sleep at every iteration to reduce CPU usage
126 | std::this_thread::sleep_for(std::chrono::milliseconds(1));
127 |
128 | // only proceed when at least one vehicle is waiting in the queue
129 | if (_waitingVehicles.getSize() > 0 && !_isBlocked)
130 | {
131 | // set intersection to "blocked" to prevent other vehicles from entering
132 | this->setIsBlocked(true);
133 |
134 | // permit entry to first vehicle in the queue (FIFO)
135 | _waitingVehicles.permitEntryToFirstInQueue();
136 | }
137 | }
138 | }
139 |
140 | bool Intersection::trafficLightIsGreen()
141 | {
142 | // please include this part once you have solved the final project tasks
143 | /*
144 | if (_trafficLight.getCurrentPhase() == TrafficLightPhase::green)
145 | return true;
146 | else
147 | return false;
148 | */
149 |
150 | return true; // makes traffic light permanently green
151 | }
--------------------------------------------------------------------------------
/src/Intersection.h:
--------------------------------------------------------------------------------
1 | #ifndef INTERSECTION_H
2 | #define INTERSECTION_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "TrafficObject.h"
9 |
10 | // forward declarations to avoid include cycle
11 | class Street;
12 | class Vehicle;
13 |
14 | // auxiliary class to queue and dequeue waiting vehicles in a thread-safe manner
15 | class WaitingVehicles
16 | {
17 | public:
18 | // getters / setters
19 | int getSize();
20 |
21 | // typical behaviour methods
22 | void pushBack(std::shared_ptr vehicle, std::promise &&promise);
23 | void permitEntryToFirstInQueue();
24 |
25 | private:
26 | std::vector> _vehicles; // list of all vehicles waiting to enter this intersection
27 | std::vector> _promises; // list of associated promises
28 | std::mutex _mutex;
29 | };
30 |
31 | class Intersection : public TrafficObject
32 | {
33 | public:
34 | // constructor / desctructor
35 | Intersection();
36 |
37 | // getters / setters
38 | void setIsBlocked(bool isBlocked);
39 |
40 | // typical behaviour methods
41 | void addVehicleToQueue(std::shared_ptr vehicle);
42 | void addStreet(std::shared_ptr street);
43 | std::vector> queryStreets(std::shared_ptr incoming); // return pointer to current list of all outgoing streets
44 | void simulate();
45 | void vehicleHasLeft(std::shared_ptr vehicle);
46 | bool trafficLightIsGreen();
47 |
48 | private:
49 |
50 | // typical behaviour methods
51 | void processVehicleQueue();
52 |
53 | // private members
54 | std::vector> _streets; // list of all streets connected to this intersection
55 | WaitingVehicles _waitingVehicles; // list of all vehicles and their associated promises waiting to enter the intersection
56 | bool _isBlocked; // flag indicating wether the intersection is blocked by a vehicle
57 | };
58 |
59 | #endif
60 |
--------------------------------------------------------------------------------
/src/Street.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "Vehicle.h"
3 | #include "Intersection.h"
4 | #include "Street.h"
5 |
6 |
7 | Street::Street()
8 | {
9 | _type = ObjectType::objectStreet;
10 | _length = 1000.0; // in m
11 | }
12 |
13 | void Street::setInIntersection(std::shared_ptr in)
14 | {
15 | _interIn = in;
16 | in->addStreet(get_shared_this()); // add this street to list of streets connected to the intersection
17 | }
18 |
19 | void Street::setOutIntersection(std::shared_ptr out)
20 | {
21 | _interOut = out;
22 | out->addStreet(get_shared_this()); // add this street to list of streets connected to the intersection
23 | }
24 |
--------------------------------------------------------------------------------
/src/Street.h:
--------------------------------------------------------------------------------
1 | #ifndef STREET_H
2 | #define STREET_H
3 |
4 | #include "TrafficObject.h"
5 |
6 | // forward declaration to avoid include cycle
7 | class Intersection;
8 |
9 | class Street : public TrafficObject, public std::enable_shared_from_this
10 | {
11 | public:
12 | // constructor / desctructor
13 | Street();
14 |
15 | // getters / setters
16 | double getLength() { return _length; }
17 | void setInIntersection(std::shared_ptr in);
18 | void setOutIntersection(std::shared_ptr out);
19 | std::shared_ptr getOutIntersection() { return _interOut; }
20 | std::shared_ptr getInIntersection() { return _interIn; }
21 |
22 | // typical behaviour methods
23 |
24 | // miscellaneous
25 | std::shared_ptr get_shared_this() { return shared_from_this(); }
26 |
27 | private:
28 | double _length; // length of this street in m
29 | std::shared_ptr _interIn, _interOut; // intersections from which a vehicle can enter (one-way streets is always from 'in' to 'out')
30 | };
31 |
32 | #endif
--------------------------------------------------------------------------------
/src/TrafficLight.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "TrafficLight.h"
4 |
5 | /* Implementation of class "MessageQueue" */
6 |
7 | /*
8 | template
9 | T MessageQueue::receive()
10 | {
11 | // FP.5a : The method receive should use std::unique_lock and _condition.wait()
12 | // to wait for and receive new messages and pull them from the queue using move semantics.
13 | // The received object should then be returned by the receive function.
14 | }
15 |
16 | template
17 | void MessageQueue::send(T &&msg)
18 | {
19 | // FP.4a : The method send should use the mechanisms std::lock_guard
20 | // as well as _condition.notify_one() to add a new message to the queue and afterwards send a notification.
21 | }
22 | */
23 |
24 | /* Implementation of class "TrafficLight" */
25 |
26 | /*
27 | TrafficLight::TrafficLight()
28 | {
29 | _currentPhase = TrafficLightPhase::red;
30 | }
31 |
32 | void TrafficLight::waitForGreen()
33 | {
34 | // FP.5b : add the implementation of the method waitForGreen, in which an infinite while-loop
35 | // runs and repeatedly calls the receive function on the message queue.
36 | // Once it receives TrafficLightPhase::green, the method returns.
37 | }
38 |
39 | TrafficLightPhase TrafficLight::getCurrentPhase()
40 | {
41 | return _currentPhase;
42 | }
43 |
44 | void TrafficLight::simulate()
45 | {
46 | // FP.2b : Finally, the private method „cycleThroughPhases“ should be started in a thread when the public method „simulate“ is called. To do this, use the thread queue in the base class.
47 | }
48 |
49 | // virtual function which is executed in a thread
50 | void TrafficLight::cycleThroughPhases()
51 | {
52 | // FP.2a : Implement the function with an infinite loop that measures the time between two loop cycles
53 | // and toggles the current phase of the traffic light between red and green and sends an update method
54 | // to the message queue using move semantics. The cycle duration should be a random value between 4 and 6 seconds.
55 | // Also, the while-loop should use std::this_thread::sleep_for to wait 1ms between two cycles.
56 | }
57 |
58 | */
--------------------------------------------------------------------------------
/src/TrafficLight.h:
--------------------------------------------------------------------------------
1 | #ifndef TRAFFICLIGHT_H
2 | #define TRAFFICLIGHT_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "TrafficObject.h"
8 |
9 | // forward declarations to avoid include cycle
10 | class Vehicle;
11 |
12 |
13 | // FP.3 Define a class „MessageQueue“ which has the public methods send and receive.
14 | // Send should take an rvalue reference of type TrafficLightPhase whereas receive should return this type.
15 | // Also, the class should define an std::dequeue called _queue, which stores objects of type TrafficLightPhase.
16 | // Also, there should be an std::condition_variable as well as an std::mutex as private members.
17 |
18 | template
19 | class MessageQueue
20 | {
21 | public:
22 |
23 | private:
24 |
25 | };
26 |
27 | // FP.1 : Define a class „TrafficLight“ which is a child class of TrafficObject.
28 | // The class shall have the public methods „void waitForGreen()“ and „void simulate()“
29 | // as well as „TrafficLightPhase getCurrentPhase()“, where TrafficLightPhase is an enum that
30 | // can be either „red“ or „green“. Also, add the private method „void cycleThroughPhases()“.
31 | // Furthermore, there shall be the private member _currentPhase which can take „red“ or „green“ as its value.
32 |
33 | class TrafficLight
34 | {
35 | public:
36 | // constructor / desctructor
37 |
38 | // getters / setters
39 |
40 | // typical behaviour methods
41 |
42 | private:
43 | // typical behaviour methods
44 |
45 | // FP.4b : create a private member of type MessageQueue for messages of type TrafficLightPhase
46 | // and use it within the infinite loop to push each new TrafficLightPhase into it by calling
47 | // send in conjunction with move semantics.
48 |
49 | std::condition_variable _condition;
50 | std::mutex _mutex;
51 | };
52 |
53 | #endif
--------------------------------------------------------------------------------
/src/TrafficObject.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "TrafficObject.h"
5 |
6 | // init static variable
7 | int TrafficObject::_idCnt = 0;
8 |
9 | std::mutex TrafficObject::_mtx;
10 |
11 | void TrafficObject::setPosition(double x, double y)
12 | {
13 | _posX = x;
14 | _posY = y;
15 | }
16 |
17 | void TrafficObject::getPosition(double &x, double &y)
18 | {
19 | x = _posX;
20 | y = _posY;
21 | }
22 |
23 | TrafficObject::TrafficObject()
24 | {
25 | _type = ObjectType::noObject;
26 | _id = _idCnt++;
27 | }
28 |
29 | TrafficObject::~TrafficObject()
30 | {
31 | // set up thread barrier before this object is destroyed
32 | std::for_each(threads.begin(), threads.end(), [](std::thread &t) {
33 | t.join();
34 | });
35 | }
36 |
--------------------------------------------------------------------------------
/src/TrafficObject.h:
--------------------------------------------------------------------------------
1 | #ifndef TRAFFICOBJECT_H
2 | #define TRAFFICOBJECT_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | enum ObjectType
9 | {
10 | noObject,
11 | objectVehicle,
12 | objectIntersection,
13 | objectStreet,
14 | };
15 |
16 | class TrafficObject
17 | {
18 | public:
19 | // constructor / desctructor
20 | TrafficObject();
21 | ~TrafficObject();
22 |
23 | // getter and setter
24 | int getID() { return _id; }
25 | void setPosition(double x, double y);
26 | void getPosition(double &x, double &y);
27 | ObjectType getType() { return _type; }
28 |
29 | // typical behaviour methods
30 | virtual void simulate(){};
31 |
32 | protected:
33 | ObjectType _type; // identifies the class type
34 | int _id; // every traffic object has its own unique id
35 | double _posX, _posY; // vehicle position in pixels
36 | std::vector threads; // holds all threads that have been launched within this object
37 | static std::mutex _mtx; // mutex shared by all traffic objects for protecting cout
38 |
39 | private:
40 | static int _idCnt; // global variable for counting object ids
41 | };
42 |
43 | #endif
--------------------------------------------------------------------------------
/src/TrafficSimulator-Final.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "Vehicle.h"
6 | #include "Street.h"
7 | #include "Intersection.h"
8 | #include "Graphics.h"
9 |
10 |
11 | // Paris
12 | void createTrafficObjects_Paris(std::vector> &streets, std::vector> &intersections, std::vector> &vehicles, std::string &filename, int nVehicles)
13 | {
14 | // assign filename of corresponding city map
15 | // Note: You can use the webp format instead of jpeg
16 | // According to Google - WebP lossless images are 26% smaller in size compared to PNGs.
17 | // WebP lossy images are 25-34% smaller than comparable JPEG images at equivalent SSIM quality index.
18 | filename = "../data/paris.jpg";
19 |
20 | // init traffic objects
21 | int nIntersections = 9;
22 | for (size_t ni = 0; ni < nIntersections; ni++)
23 | {
24 | intersections.push_back(std::make_shared());
25 | }
26 |
27 | // position intersections in pixel coordinates (counter-clockwise)
28 | intersections.at(0)->setPosition(385, 270);
29 | intersections.at(1)->setPosition(1240, 80);
30 | intersections.at(2)->setPosition(1625, 75);
31 | intersections.at(3)->setPosition(2110, 75);
32 | intersections.at(4)->setPosition(2840, 175);
33 | intersections.at(5)->setPosition(3070, 680);
34 | intersections.at(6)->setPosition(2800, 1400);
35 | intersections.at(7)->setPosition(400, 1100);
36 | intersections.at(8)->setPosition(1700, 900); // central plaza
37 |
38 | // create streets and connect traffic objects
39 | int nStreets = 8;
40 | for (size_t ns = 0; ns < nStreets; ns++)
41 | {
42 | streets.push_back(std::make_shared());
43 | streets.at(ns)->setInIntersection(intersections.at(ns));
44 | streets.at(ns)->setOutIntersection(intersections.at(8));
45 | }
46 |
47 | // add vehicles to streets
48 | for (size_t nv = 0; nv < nVehicles; nv++)
49 | {
50 | vehicles.push_back(std::make_shared());
51 | vehicles.at(nv)->setCurrentStreet(streets.at(nv));
52 | vehicles.at(nv)->setCurrentDestination(intersections.at(8));
53 | }
54 | }
55 |
56 | // NYC
57 | void createTrafficObjects_NYC(std::vector> &streets, std::vector> &intersections, std::vector> &vehicles, std::string &filename, int nVehicles)
58 | {
59 | // assign filename of corresponding city map
60 | // Note: You can use the webp format instead of jpeg
61 | filename = "../data/nyc.jpg";
62 |
63 | // init traffic objects
64 | int nIntersections = 6;
65 | for (size_t ni = 0; ni < nIntersections; ni++)
66 | {
67 | intersections.push_back(std::make_shared());
68 | }
69 |
70 | // position intersections in pixel coordinates
71 | intersections.at(0)->setPosition(1430, 625);
72 | intersections.at(1)->setPosition(2575, 1260);
73 | intersections.at(2)->setPosition(2200, 1950);
74 | intersections.at(3)->setPosition(1000, 1350);
75 | intersections.at(4)->setPosition(400, 1000);
76 | intersections.at(5)->setPosition(750, 250);
77 |
78 | // create streets and connect traffic objects
79 | int nStreets = 7;
80 | for (size_t ns = 0; ns < nStreets; ns++)
81 | {
82 | streets.push_back(std::make_shared());
83 | }
84 |
85 | streets.at(0)->setInIntersection(intersections.at(0));
86 | streets.at(0)->setOutIntersection(intersections.at(1));
87 |
88 | streets.at(1)->setInIntersection(intersections.at(1));
89 | streets.at(1)->setOutIntersection(intersections.at(2));
90 |
91 | streets.at(2)->setInIntersection(intersections.at(2));
92 | streets.at(2)->setOutIntersection(intersections.at(3));
93 |
94 | streets.at(3)->setInIntersection(intersections.at(3));
95 | streets.at(3)->setOutIntersection(intersections.at(4));
96 |
97 | streets.at(4)->setInIntersection(intersections.at(4));
98 | streets.at(4)->setOutIntersection(intersections.at(5));
99 |
100 | streets.at(5)->setInIntersection(intersections.at(5));
101 | streets.at(5)->setOutIntersection(intersections.at(0));
102 |
103 | streets.at(6)->setInIntersection(intersections.at(0));
104 | streets.at(6)->setOutIntersection(intersections.at(3));
105 |
106 | // add vehicles to streets
107 | for (size_t nv = 0; nv < nVehicles; nv++)
108 | {
109 | vehicles.push_back(std::make_shared());
110 | vehicles.at(nv)->setCurrentStreet(streets.at(nv));
111 | vehicles.at(nv)->setCurrentDestination(intersections.at(nv));
112 | }
113 | }
114 |
115 | /* Main function */
116 | int main()
117 | {
118 | /* PART 1 : Set up traffic objects */
119 |
120 | // create and connect intersections and streets
121 | std::vector> streets;
122 | std::vector> intersections;
123 | std::vector> vehicles;
124 | std::string backgroundImg;
125 | int nVehicles = 6;
126 | createTrafficObjects_Paris(streets, intersections, vehicles, backgroundImg, nVehicles);
127 |
128 | /* PART 2 : simulate traffic objects */
129 |
130 | // simulate intersection
131 | std::for_each(intersections.begin(), intersections.end(), [](std::shared_ptr &i) {
132 | i->simulate();
133 | });
134 |
135 | // simulate vehicles
136 | std::for_each(vehicles.begin(), vehicles.end(), [](std::shared_ptr &v) {
137 | v->simulate();
138 | });
139 |
140 | /* PART 3 : Launch visualization */
141 |
142 | // add all objects into common vector
143 | std::vector> trafficObjects;
144 | std::for_each(intersections.begin(), intersections.end(), [&trafficObjects](std::shared_ptr &intersection) {
145 | std::shared_ptr trafficObject = std::dynamic_pointer_cast(intersection);
146 | trafficObjects.push_back(trafficObject);
147 | });
148 |
149 | std::for_each(vehicles.begin(), vehicles.end(), [&trafficObjects](std::shared_ptr &vehicles) {
150 | std::shared_ptr trafficObject = std::dynamic_pointer_cast(vehicles);
151 | trafficObjects.push_back(trafficObject);
152 | });
153 |
154 | // draw all objects in vector
155 | Graphics *graphics = new Graphics();
156 | graphics->setBgFilename(backgroundImg);
157 | graphics->setTrafficObjects(trafficObjects);
158 | graphics->simulate();
159 | }
160 |
--------------------------------------------------------------------------------
/src/Vehicle.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "Street.h"
4 | #include "Intersection.h"
5 | #include "Vehicle.h"
6 |
7 | Vehicle::Vehicle()
8 | {
9 | _currStreet = nullptr;
10 | _posStreet = 0.0;
11 | _type = ObjectType::objectVehicle;
12 | _speed = 400; // m/s
13 | }
14 |
15 |
16 | void Vehicle::setCurrentDestination(std::shared_ptr destination)
17 | {
18 | // update destination
19 | _currDestination = destination;
20 |
21 | // reset simulation parameters
22 | _posStreet = 0.0;
23 | }
24 |
25 | void Vehicle::simulate()
26 | {
27 | // launch drive function in a thread
28 | threads.emplace_back(std::thread(&Vehicle::drive, this));
29 | }
30 |
31 | // virtual function which is executed in a thread
32 | void Vehicle::drive()
33 | {
34 | // print id of the current thread
35 | std::unique_lock lck(_mtx);
36 | std::cout << "Vehicle #" << _id << "::drive: thread id = " << std::this_thread::get_id() << std::endl;
37 | lck.unlock();
38 |
39 | // initalize variables
40 | bool hasEnteredIntersection = false;
41 | double cycleDuration = 1; // duration of a single simulation cycle in ms
42 | std::chrono::time_point lastUpdate;
43 |
44 | // init stop watch
45 | lastUpdate = std::chrono::system_clock::now();
46 | while (true)
47 | {
48 | // sleep at every iteration to reduce CPU usage
49 | std::this_thread::sleep_for(std::chrono::milliseconds(1));
50 |
51 | // compute time difference to stop watch
52 | long timeSinceLastUpdate = std::chrono::duration_cast(std::chrono::system_clock::now() - lastUpdate).count();
53 | if (timeSinceLastUpdate >= cycleDuration)
54 | {
55 | // update position with a constant velocity motion model
56 | _posStreet += _speed * timeSinceLastUpdate / 1000;
57 |
58 | // compute completion rate of current street
59 | double completion = _posStreet / _currStreet->getLength();
60 |
61 | // compute current pixel position on street based on driving direction
62 | std::shared_ptr i1, i2;
63 | i2 = _currDestination;
64 | i1 = i2->getID() == _currStreet->getInIntersection()->getID() ? _currStreet->getOutIntersection() : _currStreet->getInIntersection();
65 |
66 | double x1, y1, x2, y2, xv, yv, dx, dy, l;
67 | i1->getPosition(x1, y1);
68 | i2->getPosition(x2, y2);
69 | dx = x2 - x1;
70 | dy = y2 - y1;
71 | l = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (x1 - x2));
72 | xv = x1 + completion * dx; // new position based on line equation in parameter form
73 | yv = y1 + completion * dy;
74 | this->setPosition(xv, yv);
75 |
76 | // check wether halting position in front of destination has been reached
77 | if (completion >= 0.9 && !hasEnteredIntersection)
78 | {
79 | // request entry to the current intersection (using async)
80 | auto ftrEntryGranted = std::async(&Intersection::addVehicleToQueue, _currDestination, get_shared_this());
81 |
82 | // wait until entry has been granted
83 | ftrEntryGranted.get();
84 |
85 | // slow down and set intersection flag
86 | _speed /= 10.0;
87 | hasEnteredIntersection = true;
88 | }
89 |
90 | // check wether intersection has been crossed
91 | if (completion >= 1.0 && hasEnteredIntersection)
92 | {
93 | // choose next street and destination
94 | std::vector> streetOptions = _currDestination->queryStreets(_currStreet);
95 | std::shared_ptr nextStreet;
96 | if (streetOptions.size() > 0)
97 | {
98 | // pick one street at random and query intersection to enter this street
99 | std::random_device rd;
100 | std::mt19937 eng(rd());
101 | std::uniform_int_distribution<> distr(0, streetOptions.size() - 1);
102 | nextStreet = streetOptions.at(distr(eng));
103 | }
104 | else
105 | {
106 | // this street is a dead-end, so drive back the same way
107 | nextStreet = _currStreet;
108 | }
109 |
110 | // pick the one intersection at which the vehicle is currently not
111 | std::shared_ptr nextIntersection = nextStreet->getInIntersection()->getID() == _currDestination->getID() ? nextStreet->getOutIntersection() : nextStreet->getInIntersection();
112 |
113 | // send signal to intersection that vehicle has left the intersection
114 | _currDestination->vehicleHasLeft(get_shared_this());
115 |
116 | // assign new street and destination
117 | this->setCurrentDestination(nextIntersection);
118 | this->setCurrentStreet(nextStreet);
119 |
120 | // reset speed and intersection flag
121 | _speed *= 10.0;
122 | hasEnteredIntersection = false;
123 | }
124 |
125 | // reset stop watch for next cycle
126 | lastUpdate = std::chrono::system_clock::now();
127 | }
128 | } // eof simulation loop
129 | }
130 |
--------------------------------------------------------------------------------
/src/Vehicle.h:
--------------------------------------------------------------------------------
1 | #ifndef VEHICLE_H
2 | #define VEHICLE_H
3 |
4 | #include "TrafficObject.h"
5 |
6 | // forward declarations to avoid include cycle
7 | class Street;
8 | class Intersection;
9 |
10 | class Vehicle : public TrafficObject, public std::enable_shared_from_this
11 | {
12 | public:
13 | // constructor / desctructor
14 | Vehicle();
15 |
16 | // getters / setters
17 | void setCurrentStreet(std::shared_ptr street) { _currStreet = street; };
18 | void setCurrentDestination(std::shared_ptr destination);
19 |
20 | // typical behaviour methods
21 | void simulate();
22 |
23 | // miscellaneous
24 | std::shared_ptr get_shared_this() { return shared_from_this(); }
25 |
26 | private:
27 | // typical behaviour methods
28 | void drive();
29 |
30 | std::shared_ptr _currStreet; // street on which the vehicle is currently on
31 | std::shared_ptr _currDestination; // destination to which the vehicle is currently driving
32 | double _posStreet; // position on current street
33 | double _speed; // ego speed in m/s
34 | };
35 |
36 | #endif
--------------------------------------------------------------------------------