diff --git a/src/dsm/dsm.hpp b/src/dsm/dsm.hpp index 2627078a8..4c71f828c 100644 --- a/src/dsm/dsm.hpp +++ b/src/dsm/dsm.hpp @@ -6,7 +6,7 @@ static constexpr uint8_t DSM_VERSION_MAJOR = 2; static constexpr uint8_t DSM_VERSION_MINOR = 2; -static constexpr uint8_t DSM_VERSION_PATCH = 3; +static constexpr uint8_t DSM_VERSION_PATCH = 4; static auto const DSM_VERSION = std::format("{}.{}.{}", DSM_VERSION_MAJOR, DSM_VERSION_MINOR, DSM_VERSION_PATCH); diff --git a/src/dsm/headers/Graph.cpp b/src/dsm/headers/Graph.cpp index 75a49e198..772bd03b9 100644 --- a/src/dsm/headers/Graph.cpp +++ b/src/dsm/headers/Graph.cpp @@ -446,6 +446,16 @@ namespace dsm { pNode = std::make_unique(*pNode); return dynamic_cast(*pNode); } + + Station& Graph::makeStation(Id nodeId, const unsigned int managementTime) { + if (!m_nodes.contains(nodeId)) { + throw std::invalid_argument(buildLog("Node does not exist.")); + } + auto& pNode = m_nodes[nodeId]; + pNode = std::make_unique(*pNode, managementTime); + return dynamic_cast(*pNode); + } + SpireStreet& Graph::makeSpireStreet(Id streetId) { if (!m_streets.contains(streetId)) { throw std::invalid_argument( @@ -524,5 +534,4 @@ namespace dsm { const auto& nodePair = m_streets.at(streetId)->nodePair(); return this->street(nodePair.second, nodePair.first); } - }; // namespace dsm diff --git a/src/dsm/headers/Graph.hpp b/src/dsm/headers/Graph.hpp index 6cedb6851..d6dabc945 100644 --- a/src/dsm/headers/Graph.hpp +++ b/src/dsm/headers/Graph.hpp @@ -31,6 +31,7 @@ #include "Intersection.hpp" #include "TrafficLight.hpp" #include "Roundabout.hpp" +#include "Station.hpp" #include "SparseMatrix.hpp" #include "Street.hpp" #include "../utility/DijkstraResult.hpp" @@ -183,6 +184,12 @@ namespace dsm { /// @return A reference to the spire street /// @throws std::invalid_argument if the street does not exist SpireStreet& makeSpireStreet(Id streetId); + /// @brief Convert an existing node into a station + /// @param nodeId The id of the node to convert to a station + /// @param managementTime The station's management time + /// @return A reference to the station + /// @throws std::invalid_argument if the node does not exist + Station& makeStation(Id nodeId, const unsigned int managementTime); /// @brief Add a street to the graph /// @param street A std::shared_ptr to the street to add diff --git a/src/dsm/headers/Node.hpp b/src/dsm/headers/Node.hpp index 6551ef21a..faefc7e7f 100644 --- a/src/dsm/headers/Node.hpp +++ b/src/dsm/headers/Node.hpp @@ -102,5 +102,7 @@ namespace dsm { virtual bool isIntersection() const noexcept { return false; } virtual bool isTrafficLight() const noexcept { return false; } virtual bool isRoundabout() const noexcept { return false; } + + virtual bool isStation() const noexcept { return false; } }; }; // namespace dsm diff --git a/src/dsm/headers/Station.cpp b/src/dsm/headers/Station.cpp new file mode 100644 index 000000000..64383ba41 --- /dev/null +++ b/src/dsm/headers/Station.cpp @@ -0,0 +1,36 @@ +#include "Station.hpp" + +namespace dsm { + Station::Station(Id id, Delay managementTime) + : Node(id), m_managementTime{managementTime} {} + + Station::Station(Id id, std::pair coords, Delay managementTime) + : Node(id, coords), m_managementTime{managementTime} {} + + Station::Station(Node const& node, Delay managementTime) + : Node(node), m_managementTime{managementTime} {} + + Station::Station(Station const& other) + : Node(other), m_managementTime{other.m_managementTime}, m_trains{other.m_trains} {} + + void Station::enqueue(Id trainId, train_t trainType) { + m_trains.emplace(trainType, trainId); + } + + Id Station::dequeue() { + auto it = m_trains.begin(); + Id trainId = it->second; + m_trains.erase(it); + return trainId; + } + + Delay Station::managementTime() const { return m_managementTime; } + + double Station::density() const { + return static_cast(m_trains.size()) / m_capacity; + } + + bool Station::isFull() const { return m_trains.size() >= m_capacity; } + + bool Station::isStation() const noexcept { return true; } +} // namespace dsm \ No newline at end of file diff --git a/src/dsm/headers/Station.hpp b/src/dsm/headers/Station.hpp new file mode 100644 index 000000000..dbb6e21f6 --- /dev/null +++ b/src/dsm/headers/Station.hpp @@ -0,0 +1,56 @@ +/// @file /src/dsm/headers/Station.hpp +/// @brief Defines the Station class. +/// +/// @details The Station class represents a train station in the network. +/// It is a derived class of the Node class. + +#pragma once + +#include "Node.hpp" + +#include + +namespace dsm { + class Station : public Node { + private: + Delay m_managementTime; + std::multimap> m_trains; + + public: + /// @brief Construct a new Station object + /// @param id The station's id + /// @param managementTime The time it takes between two train departures/arrivals + Station(Id id, Delay managementTime); + /// @brief Construct a new Station object + /// @param id The station's id + /// @param coords A std::pair containing the station's coordinates (lat, lon) + /// @param managementTime The time it takes between two train departures/arrivals + Station(Id id, std::pair coords, Delay managementTime); + /// @brief Construct a new Station object + /// @param node A Node object representing the station + /// @param managementTime The time it takes between two train departures/arrivals + Station(Node const& node, Delay managementTime); + /// @brief Construct a new Station object by copying another Station object + /// @param other The Station object to copy + Station(Station const& other); + /// @brief Enqueue a train in the station + /// @param trainId The id of the train to enqueue + /// @param trainType The type of the train to enqueue + void enqueue(Id trainId, train_t trainType); + /// @brief Dequeue a train from the station + /// @return The id of the dequeued train + Id dequeue(); + /// @brief Get the time it takes between two train departures/arrivals + /// @return The management time + Delay managementTime() const; + /// @brief Get the train density of the station + /// @return The train density of the station + double density() const final; + /// @brief Check if the station is full + /// @return True if the station is full, false otherwise + bool isFull() const final; + /// @brief Check if the node is a station + /// @return True + bool isStation() const noexcept final; + }; +} // namespace dsm \ No newline at end of file diff --git a/src/dsm/utility/Typedef.hpp b/src/dsm/utility/Typedef.hpp index f962a1055..698af3214 100644 --- a/src/dsm/utility/Typedef.hpp +++ b/src/dsm/utility/Typedef.hpp @@ -20,5 +20,15 @@ namespace dsm { ANY = 6 }; enum class TrafficLightOptimization : uint8_t { SINGLE_TAIL = 0, DOUBLE_TAIL = 1 }; + enum train_t : uint8_t { + BUS = 0, // Autobus + SFM = 1, // Servizio Ferroviario Metropolitano + R = 2, // Regionale + RV = 3, // Regionale Veloce + IC = 4, // InterCity (Notte) + FRECCIA = 5, // Frecciabianca / Frecciargento + FRECCIAROSSA = 6, // Frecciarossa + ES = 7, // Eurostar + }; }; // namespace dsm diff --git a/test/Test_node.cpp b/test/Test_node.cpp index fcaa87ece..99e5bf10a 100644 --- a/test/Test_node.cpp +++ b/test/Test_node.cpp @@ -3,12 +3,15 @@ #include "Node.hpp" #include "Intersection.hpp" #include "TrafficLight.hpp" +#include "Station.hpp" +#include "../utility/Typedef.hpp" #include "doctest.h" #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN using Intersection = dsm::Intersection; using TrafficLight = dsm::TrafficLight; +using Station = dsm::Station; TEST_CASE("Intersection") { SUBCASE("Constructor") { @@ -207,3 +210,68 @@ TEST_CASE("TrafficLight") { // } // } } + +TEST_CASE("Station") { + SUBCASE("Constructors") { + constexpr dsm::Id id = 1; + constexpr dsm::Delay managementTime = 2; + constexpr double lat = 2.5; + constexpr double lon = 3.5; + const std::string name = "S0001"; + GIVEN("A Station object") { + WHEN("The Station is created using only an Id") { + Station station{id, managementTime}; + THEN("Parameters are set correctly") { + CHECK_EQ(station.id(), id); + CHECK_EQ(station.managementTime(), managementTime); + CHECK_EQ(station.capacity(), 1); + CHECK_EQ(station.transportCapacity(), 1); + CHECK(station.name().empty()); + } + } + WHEN("The Station is created using an Id and coordinates") { + Station station{id, std::make_pair(lat, lon), managementTime}; + THEN("Parameters are set correctly") { + CHECK_EQ(station.id(), id); + CHECK_EQ(station.managementTime(), managementTime); + CHECK_EQ(station.capacity(), 1); + CHECK_EQ(station.transportCapacity(), 1); + CHECK(station.name().empty()); + } + } + WHEN("The Station is created using a copy constructor") { + Station base{id, std::make_pair(lat, lon), managementTime}; + base.setCapacity(2); + base.setTransportCapacity(3); + base.setName(name); + auto copy = base; + THEN("Parameters are set correctly") { + CHECK_EQ(copy.id(), id); + CHECK_EQ(copy.managementTime(), managementTime); + CHECK_EQ(copy.capacity(), 2); + CHECK_EQ(copy.transportCapacity(), 3); + CHECK_EQ(copy.name(), name); + } + } + } + } + SUBCASE("Enqueue and dequeue") { + constexpr dsm::Id id = 1; + constexpr dsm::Delay managementTime = 2; + GIVEN("A Station object") { + Station station{id, managementTime}; + WHEN("A train is enqueued") { + station.enqueue(1, dsm::train_t::BUS); + THEN("The train is enqueued correctly") { CHECK_EQ(station.dequeue(), 1); } + } + WHEN("Multiple trains are enqueued") { + station.enqueue(1, dsm::train_t::RV); + station.enqueue(2, dsm::train_t::FRECCIAROSSA); + THEN("The trains are enqueued correctly") { + CHECK_EQ(station.dequeue(), 2); + CHECK_EQ(station.dequeue(), 1); + } + } + } + } +} \ No newline at end of file