diff --git a/examples/stalingrado.cpp b/examples/stalingrado.cpp index d0d6488ca..72c4a429a 100644 --- a/examples/stalingrado.cpp +++ b/examples/stalingrado.cpp @@ -82,8 +82,8 @@ int main() { graph.makeSpireStreet(19); auto& spire = dynamic_cast(*graph.street(19)); - std::cout << "Intersections: " << graph.nNodes() << '\n'; - std::cout << "Streets: " << graph.nEdges() << '\n'; + dsm::logger.info(std::format("Intersections: {}", graph.nNodes())); + dsm::logger.info(std::format("Streets: {}", graph.nEdges())); // Create the dynamics Dynamics dynamics{graph, 69, 0.95}; diff --git a/src/dsm/dsm.hpp b/src/dsm/dsm.hpp index 99d880361..20204350d 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 = 3; -static constexpr uint8_t DSM_VERSION_PATCH = 6; +static constexpr uint8_t DSM_VERSION_PATCH = 7; static auto const DSM_VERSION = std::format("{}.{}.{}", DSM_VERSION_MAJOR, DSM_VERSION_MINOR, DSM_VERSION_PATCH); diff --git a/src/dsm/headers/Agent.hpp b/src/dsm/headers/Agent.hpp index 3dfe5aaed..61f6cf304 100644 --- a/src/dsm/headers/Agent.hpp +++ b/src/dsm/headers/Agent.hpp @@ -159,7 +159,7 @@ namespace dsm { requires(is_numeric_v) void Agent::setSpeed(double speed) { if (speed < 0) { - throw std::invalid_argument(buildLog("Speed must be positive")); + logger.error(std::format("Speed ({}) of agent {} must be positive", speed, m_id)); } m_speed = speed; } @@ -184,7 +184,8 @@ namespace dsm { requires(is_numeric_v) void Agent::incrementDelay() { if (m_delay == std::numeric_limits::max()) { - throw std::overflow_error(buildLog("delay_t has reached its maximum value")); + throw std::overflow_error( + logger.buildExceptionMessage("delay_t has reached its maximum value")); } ++m_delay; } @@ -192,7 +193,8 @@ namespace dsm { requires(is_numeric_v) void Agent::incrementDelay(delay_t const delay) { if (m_delay + delay < m_delay) { - throw std::overflow_error(buildLog("delay_t has reached its maximum value")); + throw std::overflow_error( + logger.buildExceptionMessage("delay_t has reached its maximum value")); } m_delay += delay; } @@ -200,7 +202,8 @@ namespace dsm { requires(is_numeric_v) void Agent::decrementDelay() { if (m_delay == 0) { - throw std::underflow_error(buildLog("delay_t has reached its minimum value")); + throw std::underflow_error( + logger.buildExceptionMessage("delay_t has reached its minimum value")); } --m_delay; } @@ -209,7 +212,8 @@ namespace dsm { requires(is_numeric_v) void Agent::incrementDistance(double distance) { if (distance < 0) { - throw std::invalid_argument(buildLog("Distance travelled must be positive")); + logger.error(std::format( + "Distance travelled ({}) by agent {} must be positive", distance, m_id)); } m_distance += distance; } @@ -218,7 +222,8 @@ namespace dsm { requires(is_numeric_v) void Agent::incrementTime() { if (m_time == std::numeric_limits::max()) { - throw std::overflow_error(buildLog("Time has reached its maximum value")); + throw std::overflow_error( + logger.buildExceptionMessage("Time has reached its maximum value")); } ++m_time; } @@ -226,7 +231,8 @@ namespace dsm { requires(is_numeric_v) void Agent::incrementTime(unsigned int const time) { if (m_time + time < m_time) { - throw std::overflow_error(buildLog("Time has reached its maximum value")); + throw std::overflow_error( + logger.buildExceptionMessage("Time has reached its maximum value")); } m_time += time; } diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index eb5beca3e..7710a1e40 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -112,25 +112,17 @@ namespace dsm { path.insert(nodeId, nextNodeId, true); } } else if ((nextNodeId != destinationID)) { - std::cerr << std::format( - "\033[38;2;130;30;180mWARNING ({}:{}): No " - "path found " - "from node {} " - "to node {}\033[0m", - __FILE__, - __LINE__, - nextNodeId, - destinationID) - << std::endl; + logger.warning(std::format( + "No path found from node {} to node {}", nextNodeId, destinationID)); } } } if (path.size() == 0) { - throw std::runtime_error( - buildLog(std::format("Path with id {} and destination {} is empty. Please " - "check the adjacency matrix.", - pItinerary->id(), - pItinerary->destination()))); + logger.error( + std::format("Path with id {} and destination {} is empty. Please " + "check the adjacency matrix.", + pItinerary->id(), + pItinerary->destination())); } pItinerary->setPath(path); } @@ -307,8 +299,7 @@ namespace dsm { bool updatePaths) { for (const auto& nodeId : destinationNodes) { if (!m_graph.nodeSet().contains(nodeId)) { - throw std::invalid_argument( - buildLog(std::format("Node with id {} not found", nodeId))); + logger.error(std::format("Node with id {} not found", nodeId)); } this->addItinerary(Itinerary{nodeId, nodeId}); } @@ -320,13 +311,13 @@ namespace dsm { template void Dynamics::addAgent(std::unique_ptr agent) { if (m_agents.size() + 1 > m_graph.maxCapacity()) { - throw std::overflow_error(buildLog( + throw std::overflow_error(logger.buildExceptionMessage( std::format("Graph is already holding the max possible number of agents ({})", m_graph.maxCapacity()))); } if (m_agents.contains(agent->id())) { - throw std::invalid_argument( - buildLog(std::format("Agent with id {} already exists.", agent->id()))); + throw std::invalid_argument(logger.buildExceptionMessage( + std::format("Agent with id {} already exists.", agent->id()))); } m_agents.emplace(agent->id(), std::move(agent)); } diff --git a/src/dsm/headers/Edge.cpp b/src/dsm/headers/Edge.cpp index 53fed38a1..fb3be6494 100644 --- a/src/dsm/headers/Edge.cpp +++ b/src/dsm/headers/Edge.cpp @@ -19,30 +19,28 @@ namespace dsm { m_transportCapacity{transportCapacity}, m_angle{angle} { if (capacity < 1) { - throw std::invalid_argument( - buildLog(std::format("Edge capacity ({}) must be greater than 0.", capacity))); + logger.error(std::format("Edge capacity ({}) must be greater than 0.", capacity)); } if (transportCapacity < 1) { - throw std::invalid_argument(buildLog(std::format( - "Edge transport capacity ({}) must be greater than 0.", transportCapacity))); + logger.error(std::format("Edge transport capacity ({}) must be greater than 0.", + transportCapacity)); } if (std::abs(angle) > 2 * std::numbers::pi) { - throw std::invalid_argument(buildLog( - std::format("Edge angle ({}) must be in the range [-2pi, 2pi].", angle))); + logger.error( + std::format("Edge angle ({}) must be in the range [-2pi, 2pi].", angle)); } } void Edge::setCapacity(int capacity) { if (capacity < 1) { - throw std::invalid_argument( - buildLog(std::format("Edge capacity ({}) must be greater than 0.", capacity))); + logger.error(std::format("Edge capacity ({}) must be greater than 0.", capacity)); } m_capacity = capacity; } void Edge::setTransportCapacity(int capacity) { if (capacity < 1) { - throw std::invalid_argument(buildLog( - std::format("Edge transport capacity ({}) must be greater than 0.", capacity))); + logger.error( + std::format("Edge transport capacity ({}) must be greater than 0.", capacity)); } m_transportCapacity = capacity; } diff --git a/src/dsm/headers/FirstOrderDynamics.cpp b/src/dsm/headers/FirstOrderDynamics.cpp index 183d188e6..10863c4b9 100644 --- a/src/dsm/headers/FirstOrderDynamics.cpp +++ b/src/dsm/headers/FirstOrderDynamics.cpp @@ -4,12 +4,9 @@ namespace dsm { FirstOrderDynamics::FirstOrderDynamics(Graph& graph, std::optional seed, double alpha) - : RoadDynamics(graph, seed), m_alpha{0.}, m_speedFluctuationSTD{0.} { + : RoadDynamics(graph, seed), m_alpha{alpha}, m_speedFluctuationSTD{0.} { if (alpha < 0. || alpha > 1.) { - throw std::invalid_argument(buildLog(std::format( - "The minimum speed rateo must be between 0 and 1, but it is {}", alpha))); - } else { - m_alpha = alpha; + logger.error(std::format("The minimum speed rateo ({}) must be in [0, 1[", alpha)); } double globMaxTimePenalty{0.}; for (const auto& [streetId, street] : this->m_graph.streetSet()) { @@ -18,11 +15,11 @@ namespace dsm { std::ceil(street->length() / ((1. - m_alpha) * street->maxSpeed()))); } if (globMaxTimePenalty > static_cast(std::numeric_limits::max())) { - throw std::overflow_error( - buildLog(std::format("The maximum time penalty ({}) is greater than the " - "maximum value of delay_t ({})", - globMaxTimePenalty, - std::numeric_limits::max()))); + throw std::overflow_error(logger.buildExceptionMessage( + std::format("The maximum time penalty ({}) is greater than the " + "maximum value of delay_t ({})", + globMaxTimePenalty, + std::numeric_limits::max()))); } } @@ -40,8 +37,9 @@ namespace dsm { void FirstOrderDynamics::setSpeedFluctuationSTD(double speedFluctuationSTD) { if (speedFluctuationSTD < 0.) { - throw std::invalid_argument( - buildLog("The speed fluctuation standard deviation must be positive.")); + logger.error( + std::format("The speed fluctuation standard deviation ({}) must be positive", + speedFluctuationSTD)); } m_speedFluctuationSTD = speedFluctuationSTD; } diff --git a/src/dsm/headers/Graph.cpp b/src/dsm/headers/Graph.cpp index 947093150..218ac0b25 100644 --- a/src/dsm/headers/Graph.cpp +++ b/src/dsm/headers/Graph.cpp @@ -49,11 +49,11 @@ namespace dsm { const auto dstId{street->v()}; const auto newStreetId{static_cast(srcId * n + dstId)}; if (m_streets.contains(newStreetId)) { - throw std::invalid_argument( - buildLog(std::format("Street with same id ({}) from {} to {} already exists.", - newStreetId, - srcId, - dstId))); + throw std::invalid_argument(logger.buildExceptionMessage( + std::format("Street with same id ({}) from {} to {} already exists.", + newStreetId, + srcId, + dstId))); } if (street->isSpire() && street->isStochastic()) { m_streets.emplace(newStreetId, @@ -156,12 +156,14 @@ namespace dsm { if (fileExt == "dsm") { std::ifstream file{fileName}; if (!file.is_open()) { - throw std::invalid_argument(buildLog("Cannot find file: " + fileName)); + throw std::invalid_argument( + logger.buildExceptionMessage("Cannot find file: " + fileName)); } Size rows, cols; file >> rows >> cols; if (rows != cols) { - throw std::invalid_argument(buildLog("Adjacency matrix must be square")); + throw std::invalid_argument( + logger.buildExceptionMessage("Adjacency matrix must be square")); } Size n{rows}; m_adjacency = SparseMatrix(n, n); @@ -192,19 +194,20 @@ namespace dsm { // the following elements being the matrix elements std::ifstream file{fileName}; if (!file.is_open()) { - throw std::invalid_argument(buildLog("Cannot find file: " + fileName)); + throw std::invalid_argument( + logger.buildExceptionMessage("Cannot find file: " + fileName)); } Size rows, cols; file >> rows >> cols; if (rows != cols) { - throw std::invalid_argument( - buildLog("Adjacency matrix must be square. Rows: " + std::to_string(rows) + - " Cols: " + std::to_string(cols))); + throw std::invalid_argument(logger.buildExceptionMessage( + "Adjacency matrix must be square. Rows: " + std::to_string(rows) + + " Cols: " + std::to_string(cols))); } Size n{rows}; if (n * n > std::numeric_limits::max()) { - throw std::invalid_argument( - buildLog("Matrix size is too large for the current type of Id.")); + throw std::invalid_argument(logger.buildExceptionMessage( + "Matrix size is too large for the current type of Id.")); } m_adjacency = SparseMatrix(n, n); Id index{0}; @@ -213,7 +216,7 @@ namespace dsm { file >> value; if (value < 0) { throw std::invalid_argument( - buildLog("Adjacency matrix elements must be positive")); + logger.buildExceptionMessage("Adjacency matrix elements must be positive")); } if (value > 0) { m_adjacency.insert(index, true); @@ -247,8 +250,8 @@ namespace dsm { Size n; file >> n; if (n < m_nodes.size()) { - throw std::invalid_argument( - buildLog("Number of node cordinates in file is too small.")); + throw std::invalid_argument(logger.buildExceptionMessage( + "Number of node cordinates in file is too small.")); } double lat, lon; for (Size i{0}; i < n; ++i) { @@ -257,25 +260,20 @@ namespace dsm { if (it != m_nodes.cend()) { it->second->setCoords(std::make_pair(lat, lon)); } else { - std::cerr << std::format( - "\033[38;2;130;30;180mWARNING ({}:{}): Node with id {} not " - "found.\033[0m", - __FILE__, - __LINE__, - i) - << std::endl; + logger.warning(std::format("Node with id {} not found.", i)); } } } else if (fileExt == "csv") { std::ifstream ifs{fileName}; if (!ifs.is_open()) { - throw std::invalid_argument(buildLog("Cannot find file: " + fileName)); + throw std::invalid_argument( + logger.buildExceptionMessage("Cannot find file: " + fileName)); } // Check if the first line is nodeId;lat;lon std::string line; std::getline(ifs, line); if (line != "nodeId;lat;lon") { - throw std::invalid_argument(buildLog("Invalid file format.")); + throw std::invalid_argument(logger.buildExceptionMessage("Invalid file format.")); } double dLat, dLon; while (!ifs.eof()) { @@ -304,7 +302,8 @@ namespace dsm { } } } else { - throw std::invalid_argument(buildLog("File extension not supported.")); + throw std::invalid_argument( + logger.buildExceptionMessage("File extension not supported.")); } } @@ -313,7 +312,8 @@ namespace dsm { if (fileExt == "csv") { std::ifstream file{fileName}; if (!file.is_open()) { - throw std::invalid_argument(buildLog("Cannot find file: " + fileName)); + throw std::invalid_argument( + logger.buildExceptionMessage("Cannot find file: " + fileName)); } std::string line; std::getline(file, line); // skip first line @@ -343,7 +343,7 @@ namespace dsm { ++nodeIndex; } } else { - throw std::invalid_argument(buildLog("File extension not supported")); + logger.error(std::format("File extension ({}) not supported", fileExt)); } } @@ -354,7 +354,7 @@ namespace dsm { std::ifstream file{fileName}; if (!file.is_open()) { throw std::invalid_argument( - buildLog(std::format("File \'{}\' not found", fileName))); + logger.buildExceptionMessage(std::format("File \'{}\' not found", fileName))); } std::string line; std::getline(file, line); // skip first line @@ -394,30 +394,16 @@ namespace dsm { } } if (!m_nodeMapping.contains(sourceId)) { - std::cerr << std::format( - "\033[38;5;196mERROR ({}:{}): Node with id {} not " - "found.\033[0m", - __FILE__, - __LINE__, - sourceId) - << std::endl; - std::abort(); + logger.error(std::format("Node with id {} not found.", sourceId)); } if (!m_nodeMapping.contains(targetId)) { - std::cerr << std::format( - "\033[38;5;196mERROR ({}:{}): Node with id {} not " - "found.\033[0m", - __FILE__, - __LINE__, - targetId) - << std::endl; - std::abort(); + logger.error(std::format("Node with id {} not found.", targetId)); } auto const srcId{m_nodeMapping.at(sourceId)}; auto const dstId{m_nodeMapping.at(targetId)}; if (static_cast(srcId * nNodes + dstId) > std::numeric_limits::max()) { - throw std::invalid_argument(buildLog( + throw std::invalid_argument(logger.buildExceptionMessage( std::format("Street id {}->{} would too large for the current type of Id.", srcId, dstId))); @@ -431,14 +417,16 @@ namespace dsm { name); } } else { - throw std::invalid_argument(buildLog("File extension not supported")); + throw std::invalid_argument( + logger.buildExceptionMessage("File extension not supported")); } } void Graph::exportMatrix(std::string path, bool isAdj) { std::ofstream file{path}; if (!file.is_open()) { - throw std::invalid_argument(buildLog("Cannot open file: " + path)); + throw std::invalid_argument( + logger.buildExceptionMessage("Cannot open file: " + path)); } if (isAdj) { file << m_adjacency.getRowDim() << '\t' << m_adjacency.getColDim(); @@ -512,11 +500,11 @@ namespace dsm { void Graph::addStreet(std::unique_ptr street) { if (m_streets.contains(street->id())) { - throw std::invalid_argument( - buildLog(std::format("Street with id {} from {} to {} already exists.", - street->id(), - street->nodePair().first, - street->nodePair().second))); + throw std::invalid_argument(logger.buildExceptionMessage( + std::format("Street with id {} from {} to {} already exists.", + street->id(), + street->nodePair().first, + street->nodePair().second))); } // emplace nodes const auto srcId{street->nodePair().first}; @@ -533,11 +521,11 @@ namespace dsm { void Graph::addStreet(const Street& street) { if (m_streets.contains(street.id())) { - throw std::invalid_argument( - buildLog(std::format("Street with id {} from {} to {} already exists.", - street.id(), - street.nodePair().first, - street.nodePair().second))); + throw std::invalid_argument(logger.buildExceptionMessage( + std::format("Street with id {} from {} to {} already exists.", + street.id(), + street.nodePair().first, + street.nodePair().second))); } // emplace nodes const auto srcId{street.nodePair().first}; @@ -576,10 +564,10 @@ namespace dsm { const std::unique_ptr* Graph::oppositeStreet(Id streetId) const { if (!m_streets.contains(streetId)) { - throw std::invalid_argument( - buildLog(std::format("Street with id {} does not exist: maybe it has changed " - "id once called buildAdj.", - streetId))); + throw std::invalid_argument(logger.buildExceptionMessage( + std::format("Street with id {} does not exist: maybe it has changed " + "id once called buildAdj.", + streetId))); } const auto& nodePair = m_streets.at(streetId)->nodePair(); return this->street(nodePair.second, nodePair.first); diff --git a/src/dsm/headers/Graph.hpp b/src/dsm/headers/Graph.hpp index 88299bc30..2d1674523 100644 --- a/src/dsm/headers/Graph.hpp +++ b/src/dsm/headers/Graph.hpp @@ -309,8 +309,8 @@ namespace dsm { requires is_street_v> void Graph::addStreets(T1&& street) { if (m_streets.contains(street.id())) { - throw std::invalid_argument( - buildLog(std::format("Street with id {} already exists.", street.id()))); + throw std::invalid_argument(logger.buildExceptionMessage( + std::format("Street with id {} already exists.", street.id()))); } // emplace nodes auto const srcId{street.u()}; diff --git a/src/dsm/headers/Intersection.cpp b/src/dsm/headers/Intersection.cpp index a9f467446..d7379af2a 100644 --- a/src/dsm/headers/Intersection.cpp +++ b/src/dsm/headers/Intersection.cpp @@ -7,7 +7,7 @@ namespace dsm { void Intersection::setCapacity(Size capacity) { if (capacity < m_agents.size()) { - throw std::runtime_error(buildLog(std::format( + throw std::runtime_error(logger.buildExceptionMessage(std::format( "Intersection capacity ({}) is smaller than the current queue size ({}).", capacity, m_agents.size()))); @@ -17,12 +17,12 @@ namespace dsm { void Intersection::addAgent(double angle, Id agentId) { if (isFull()) { - throw std::runtime_error(buildLog("Intersection is full.")); + throw std::runtime_error(logger.buildExceptionMessage("Intersection is full.")); } for (auto const [angle, id] : m_agents) { if (id == agentId) { - throw std::runtime_error( - buildLog(std::format("Agent with id {} is already on the node.", agentId))); + throw std::runtime_error(logger.buildExceptionMessage( + std::format("Agent with id {} is already on the node.", agentId))); } } auto iAngle{static_cast(angle * 100)}; diff --git a/src/dsm/headers/Itinerary.cpp b/src/dsm/headers/Itinerary.cpp index 9121a0321..1a47ed71a 100644 --- a/src/dsm/headers/Itinerary.cpp +++ b/src/dsm/headers/Itinerary.cpp @@ -6,18 +6,18 @@ namespace dsm { void Itinerary::setPath(SparseMatrix path) { if (path.getRowDim() != path.getColDim()) { - throw std::invalid_argument(buildLog( + throw std::invalid_argument(logger.buildExceptionMessage( std::format("The path's row ({}) and column ({}) dimensions must be equal.", path.getRowDim(), path.getColDim()))); } if (path.getRowDim() < m_destination) { - throw std::invalid_argument( - buildLog(std::format("The path's row ({}) and column ({}) dimensions must be " - "greater than the itinerary's destination ({}).", - path.getRowDim(), - path.getColDim(), - m_destination))); + throw std::invalid_argument(logger.buildExceptionMessage( + std::format("The path's row ({}) and column ({}) dimensions must be " + "greater than the itinerary's destination ({}).", + path.getRowDim(), + path.getColDim(), + m_destination))); } m_path = std::move(path); } diff --git a/src/dsm/headers/Node.hpp b/src/dsm/headers/Node.hpp index 7ad426cde..113ea4975 100644 --- a/src/dsm/headers/Node.hpp +++ b/src/dsm/headers/Node.hpp @@ -81,8 +81,8 @@ namespace dsm { /// @param capacity The node's transport capacity virtual void setTransportCapacity(int capacity) { if (capacity < 1) { - throw std::invalid_argument(buildLog(std::format( - "The transport capacity of a node ({}) must be greater than 0.", capacity))); + logger.error(std::format( + "The transport capacity of a node ({}) must be greater than 0.", capacity)); } m_transportCapacity = capacity; } diff --git a/src/dsm/headers/Road.cpp b/src/dsm/headers/Road.cpp index e6692b6f0..54fa9bf51 100644 --- a/src/dsm/headers/Road.cpp +++ b/src/dsm/headers/Road.cpp @@ -33,22 +33,21 @@ namespace dsm { m_nLanes{nLanes}, m_name{std::move(name)} { if (!(length > 0.)) { - throw std::invalid_argument(buildLog( - std::format("The length of a road ({}) must be greater than 0.", length))); + logger.error(std::format("The road length ({}) must be greater than 0.", length)); } if (!(maxSpeed > 0.)) { - throw std::invalid_argument(buildLog(std::format( - "The maximum speed of a road ({}) must be greater than 0.", maxSpeed))); + logger.error(std::format("The maximum speed of a road ({}) must be greater than 0.", + maxSpeed)); } if (nLanes < 1) { - throw std::invalid_argument(buildLog(std::format( - "The number of lanes of a road ({}) must be greater than 0.", nLanes))); + logger.error(std::format( + "The number of lanes of a road ({}) must be greater than 0.", nLanes)); } } void Road::setMeanVehicleLength(double meanVehicleLength) { if (!(meanVehicleLength > 0.)) { - throw std::invalid_argument(buildLog(std::format( - "The mean vehicle length ({}) must be greater than 0.", meanVehicleLength))); + logger.error(std::format("The mean vehicle length ({}) must be greater than 0.", + meanVehicleLength)); } m_meanVehicleLength = meanVehicleLength; } @@ -56,8 +55,8 @@ namespace dsm { void Road::setMaxSpeed(double speed) { if (speed < 0.) { - throw std::invalid_argument(buildLog( - std::format("The maximum speed of a road ({}) cannot be negative.", speed))); + logger.error( + std::format("The maximum speed of a road ({}) cannot be negative.", speed)); } m_maxSpeed = speed; } diff --git a/src/dsm/headers/RoadDynamics.hpp b/src/dsm/headers/RoadDynamics.hpp index 797b263d6..80da78c05 100644 --- a/src/dsm/headers/RoadDynamics.hpp +++ b/src/dsm/headers/RoadDynamics.hpp @@ -504,8 +504,8 @@ namespace dsm { requires(is_numeric_v) void RoadDynamics::setErrorProbability(double errorProbability) { if (errorProbability < 0. || errorProbability > 1.) { - throw std::invalid_argument(buildLog(std::format( - "The error probability ({}) must be between 0 and 1", errorProbability))); + logger.error( + std::format("The error probability ({}) must be in [0, 1]", errorProbability)); } m_errorProbability = errorProbability; } @@ -514,8 +514,8 @@ namespace dsm { requires(is_numeric_v) void RoadDynamics::setPassageProbability(double passageProbability) { if (passageProbability < 0. || passageProbability > 1.) { - throw std::invalid_argument(buildLog(std::format( - "The passage probability ({}) must be between 0 and 1", passageProbability))); + logger.error(std::format("The passage probability ({}) must be between 0 and 1", + passageProbability)); } m_passageProbability = passageProbability; } @@ -526,8 +526,8 @@ namespace dsm { std::optional optItineraryId) { if (this->itineraries().empty()) { // TODO: make this possible for random agents - throw std::invalid_argument( - buildLog("It is not possible to add random agents without itineraries.")); + throw std::invalid_argument(logger.buildExceptionMessage( + "It is not possible to add random agents without itineraries.")); } Id itineraryId{0}; const bool randomItinerary{!optItineraryId.has_value()}; @@ -579,7 +579,7 @@ namespace dsm { const size_t minNodeDistance) { if (src_weights.size() == 1 && dst_weights.size() == 1 && src_weights.begin()->first == dst_weights.begin()->first) { - throw std::invalid_argument(buildLog( + throw std::invalid_argument(logger.buildExceptionMessage( std::format("The only source node {} is also the only destination node.", src_weights.begin()->first))); } @@ -589,8 +589,8 @@ namespace dsm { 0., [](double sum, const std::pair& p) { if (p.second < 0.) { - throw std::invalid_argument(buildLog(std::format( - "Negative weight ({}) for source node {}.", p.second, p.first))); + logger.error(std::format( + "Negative weight ({}) for source node {}.", p.second, p.first)); } return sum + p.second; })}; @@ -600,8 +600,8 @@ namespace dsm { 0., [](double sum, const std::pair& p) { if (p.second < 0.) { - throw std::invalid_argument(buildLog(std::format( - "Negative weight ({}) for destination node {}.", p.second, p.first))); + logger.error(std::format( + "Negative weight ({}) for destination node {}.", p.second, p.first)); } return sum + p.second; })}; @@ -656,8 +656,7 @@ namespace dsm { return itinerary.second->destination() == dstId; })}; if (itineraryIt == this->itineraries().cend()) { - throw std::invalid_argument( - buildLog(std::format("Itinerary with destination {} not found.", dstId))); + logger.error(std::format("Itinerary with destination {} not found.", dstId)); } this->addAgent(agentId, itineraryIt->first, srcId); --nAgents; @@ -702,10 +701,8 @@ namespace dsm { void RoadDynamics::optimizeTrafficLights( double const threshold, TrafficLightOptimization const optimizationType) { if (threshold < 0) { - throw std::invalid_argument( - buildLog(std::format("The threshold parameter is a percentage and must be " - "bounded between 0-1. Inserted value: {}", - threshold))); + logger.error( + std::format("The threshold parameter ({}) must be greater than 0.", threshold)); } auto const nCycles{static_cast(this->m_time - m_previousOptimizationTime) / m_dataUpdatePeriod.value()}; diff --git a/src/dsm/headers/Roundabout.cpp b/src/dsm/headers/Roundabout.cpp index f5290323e..2ab1527d4 100644 --- a/src/dsm/headers/Roundabout.cpp +++ b/src/dsm/headers/Roundabout.cpp @@ -13,11 +13,11 @@ namespace dsm { void Roundabout::enqueue(Id agentId) { if (isFull()) { - throw std::runtime_error(buildLog("Roundabout is full.")); + throw std::runtime_error(logger.buildExceptionMessage("Roundabout is full.")); } for (const auto id : m_agents) { if (id == agentId) { - throw std::runtime_error(buildLog( + throw std::runtime_error(logger.buildExceptionMessage( std::format("Agent with id {} is already on the roundabout.", agentId))); } } @@ -26,7 +26,7 @@ namespace dsm { Id Roundabout::dequeue() { if (m_agents.empty()) { - throw std::runtime_error(buildLog("Roundabout is empty.")); + throw std::runtime_error(logger.buildExceptionMessage("Roundabout is empty.")); } Id agentId{m_agents.front()}; m_agents.pop(); diff --git a/src/dsm/headers/SparseMatrix.hpp b/src/dsm/headers/SparseMatrix.hpp index 52f9d8570..ec538557c 100644 --- a/src/dsm/headers/SparseMatrix.hpp +++ b/src/dsm/headers/SparseMatrix.hpp @@ -235,7 +235,12 @@ namespace dsm { template SparseMatrix operator+(const SparseMatrix& other) { if (this->_rows != other._rows || this->_cols != other._cols) { - throw std::runtime_error(buildLog("Dimensions do not match")); + throw std::runtime_error(logger.buildExceptionMessage( + std::format("Dimensions do not match ({}x{} and {}x{})", + this->_rows, + this->_cols, + other._rows, + other._cols))); } auto result = SparseMatrix(this->_rows, this->_cols); std::unordered_map unique; @@ -260,7 +265,12 @@ namespace dsm { template SparseMatrix operator-(const SparseMatrix& other) { if (this->_rows != other._rows || this->_cols != other._cols) { - throw std::runtime_error(buildLog("Dimensions do not match")); + throw std::runtime_error(logger.buildExceptionMessage( + std::format("Dimensions do not match ({}x{} and {}x{})", + this->_rows, + this->_cols, + other._rows, + other._cols))); } auto result = SparseMatrix(this->_rows, this->_cols); std::unordered_map unique; @@ -321,8 +331,8 @@ namespace dsm { template void SparseMatrix::insert(Id i, T value) { if (i > _rows * _cols - 1) { - throw std::out_of_range( - buildLog(std::format("Id {} out of range 0-{}", i, _rows * _cols - 1))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id {} out of range 0-{}", i, _rows * _cols - 1))); } _matrix.emplace(std::make_pair(i, value)); } @@ -336,8 +346,8 @@ namespace dsm { template void SparseMatrix::insert_or_assign(Id index, T value) { if (index > _rows * _cols - 1) { - throw std::out_of_range( - buildLog(std::format("Id {} out of range 0-{}", index, _rows * _cols - 1))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id {} out of range 0-{}", index, _rows * _cols - 1))); } _matrix.insert_or_assign(index, value); } @@ -364,12 +374,12 @@ namespace dsm { template void SparseMatrix::erase(Id i, Id j) { if (i >= _rows || j >= _cols) { - throw std::out_of_range( - buildLog(std::format("Id ({}, {}) out of range ({}, {})", i, j, _rows, _cols))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id ({}, {}) out of range ({}, {})", i, j, _rows, _cols))); } if (_matrix.find(i * _cols + j) == _matrix.end()) { - throw std::runtime_error( - buildLog(std::format("Element with index {} not found", i * _cols + j))); + throw std::runtime_error(logger.buildExceptionMessage( + std::format("Element with index {} not found", i * _cols + j))); } _matrix.erase(i * _cols + j); } @@ -377,12 +387,12 @@ namespace dsm { template void SparseMatrix::erase(Id index) { if (index > _rows * _cols - 1) { - throw std::out_of_range( - buildLog(std::format("Id {} out of range 0-{}", index, _rows * _cols - 1))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id {} out of range 0-{}", index, _rows * _cols - 1))); } if (_matrix.find(index) == _matrix.end()) { - throw std::runtime_error( - buildLog(std::format("Element with index {} not found", index))); + throw std::runtime_error(logger.buildExceptionMessage( + std::format("Element with index {} not found", index))); } _matrix.erase(index); } @@ -390,8 +400,8 @@ namespace dsm { template void SparseMatrix::eraseRow(Id index) { if (index > _rows - 1) { - throw std::out_of_range( - buildLog(std::format("Id {} out of range 0-{}", index, _rows - 1))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id {} out of range 0-{}", index, _rows - 1))); } for (Id i = 0; i < _cols; ++i) { _matrix.erase(index * _cols + i); @@ -411,8 +421,8 @@ namespace dsm { template void SparseMatrix::eraseColumn(Id index) { if (index > _cols - 1) { - throw std::out_of_range( - buildLog(std::format("Id {} out of range 0-{}", index, _cols - 1))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id {} out of range 0-{}", index, _cols - 1))); } for (Id i = 0; i < _rows; ++i) { _matrix.erase(i * _cols + index); @@ -454,8 +464,8 @@ namespace dsm { template bool SparseMatrix::contains(Id i, Id j) const { if (i >= _rows || j >= _cols) { - throw std::out_of_range( - buildLog(std::format("Id ({}, {}) out of range ({}, {})", i, j, _rows, _cols))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id ({}, {}) out of range ({}, {})", i, j, _rows, _cols))); } return _matrix.contains(i * _cols + j); } @@ -463,8 +473,8 @@ namespace dsm { template bool SparseMatrix::contains(Id const index) const { if (index > _rows * _cols - 1) { - throw std::out_of_range( - buildLog(std::format("Id {} out of range 0-{}", index, _rows * _cols - 1))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id {} out of range 0-{}", index, _rows * _cols - 1))); } return _matrix.contains(index); } @@ -472,7 +482,8 @@ namespace dsm { template SparseMatrix SparseMatrix::getDegreeVector() const { if (_rows != _cols) { - throw std::runtime_error(buildLog("getDegreeVector only works on square matrices")); + throw std::runtime_error( + logger.buildExceptionMessage("getDegreeVector only works on square matrices")); } auto degreeVector = SparseMatrix(_rows, 1); for (auto& i : _matrix) { @@ -485,8 +496,8 @@ namespace dsm { template SparseMatrix SparseMatrix::getStrengthVector() const { if (_rows != _cols) { - throw std::runtime_error( - buildLog("getStrengthVector only works on square matrices")); + throw std::runtime_error(logger.buildExceptionMessage( + "getStrengthVector only works on square matrices")); } auto strengthVector = SparseMatrix(_rows, 1); for (auto& i : _matrix) { @@ -499,7 +510,8 @@ namespace dsm { template SparseMatrix SparseMatrix::getLaplacian() const { if (_rows != _cols) { - throw std::runtime_error(buildLog("getLaplacian only works on square matrices")); + throw std::runtime_error( + logger.buildExceptionMessage("getLaplacian only works on square matrices")); } auto laplacian = SparseMatrix(_rows, _cols); for (auto& i : _matrix) { @@ -515,8 +527,8 @@ namespace dsm { template SparseMatrix SparseMatrix::getRow(Id index, bool keepId) const { if (index >= _rows) { - throw std::out_of_range( - buildLog(std::format("Id {} out of range 0-{}", index, _rows - 1))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id {} out of range 0-{}", index, _rows - 1))); } SparseMatrix row(1, _cols); if (keepId) { @@ -534,8 +546,8 @@ namespace dsm { template SparseMatrix SparseMatrix::getCol(Id index, bool keepId) const { if (index >= _cols) { - throw std::out_of_range( - buildLog(std::format("Id {} out of range 0-{}", index, _cols - 1))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id {} out of range 0-{}", index, _cols - 1))); } SparseMatrix col(_rows, 1); if (keepId) { @@ -623,8 +635,8 @@ namespace dsm { template const T& SparseMatrix::operator()(Id i, Id j) const { if (i >= _rows || j >= _cols) { - throw std::out_of_range( - buildLog(std::format("Id ({}, {}) out of range ({}, {})", i, j, _rows, _cols))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id ({}, {}) out of range ({}, {})", i, j, _rows, _cols))); } auto const& it = _matrix.find(i * _cols + j); return it != _matrix.end() ? it->second : _defaultReturn; @@ -633,8 +645,8 @@ namespace dsm { template T& SparseMatrix::operator()(Id i, Id j) { if (i >= _rows || j >= _cols) { - throw std::out_of_range( - buildLog(std::format("Id ({}, {}) out of range ({}, {})", i, j, _rows, _cols))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id ({}, {}) out of range ({}, {})", i, j, _rows, _cols))); } auto const& it = _matrix.find(i * _cols + j); return it != _matrix.end() ? it->second : _defaultReturn; @@ -643,8 +655,8 @@ namespace dsm { template const T& SparseMatrix::operator()(Id index) const { if (index >= _rows * _cols) { - throw std::out_of_range( - buildLog(std::format("Id {} out of range 0-{}", index, _rows * _cols - 1))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id {} out of range 0-{}", index, _rows * _cols - 1))); } auto const& it = _matrix.find(index); return it != _matrix.end() ? it->second : _defaultReturn; @@ -653,8 +665,8 @@ namespace dsm { template T& SparseMatrix::operator()(Id index) { if (index >= _rows * _cols) { - throw std::out_of_range( - buildLog(std::format("Id {} out of range 0-{}", index, _rows * _cols - 1))); + throw std::out_of_range(logger.buildExceptionMessage( + std::format("Id {} out of range 0-{}", index, _rows * _cols - 1))); } auto const& it = _matrix.find(index); return it != _matrix.end() ? it->second : _defaultReturn; @@ -673,12 +685,11 @@ namespace dsm { template SparseMatrix& SparseMatrix::operator+=(const SparseMatrix& other) { if (this->_rows != other._rows || this->_cols != other._cols) { - throw std::runtime_error( - buildLog(std::format("Dimensions ({}, {}) and ({}, {}) do not match", + logger.error(std::format("Dimensions do not match ({}x{} and {}x{})", this->_rows, this->_cols, other._rows, - other._cols))); + other._cols)); } for (auto& it : other._matrix) { this->contains(it.first) @@ -692,12 +703,11 @@ namespace dsm { template SparseMatrix& SparseMatrix::operator-=(const SparseMatrix& other) { if (this->_rows != other._rows || this->_cols != other._cols) { - throw std::runtime_error( - buildLog(std::format("Dimensions ({}, {}) and ({}, {}) do not match", + logger.error(std::format("Dimensions ({}, {}) and ({}, {}) do not match", this->_rows, this->_cols, other._rows, - other._cols))); + other._cols)); } for (auto& it : other._matrix) { this->contains(it.first) diff --git a/src/dsm/headers/Street.cpp b/src/dsm/headers/Street.cpp index 74d57b79c..0e85e8ed0 100644 --- a/src/dsm/headers/Street.cpp +++ b/src/dsm/headers/Street.cpp @@ -146,8 +146,7 @@ namespace dsm { } void StochasticStreet::setFlowRate(double const flowRate) { if (flowRate < 0. || flowRate > 1.) { - throw std::invalid_argument( - buildLog(std::format("Flow rate ({}) must be between 0 and 1", flowRate))); + logger.error(std::format("Flow rate ({}) must be in [0, 1]", flowRate)); } m_flowRate = flowRate; } diff --git a/src/dsm/headers/TrafficLight.cpp b/src/dsm/headers/TrafficLight.cpp index de696cb83..6a000a1ce 100644 --- a/src/dsm/headers/TrafficLight.cpp +++ b/src/dsm/headers/TrafficLight.cpp @@ -17,10 +17,14 @@ namespace dsm { Direction direction, TrafficLightCycle const& cycle) { if ((cycle.greenTime() > m_cycleTime)) { - throw std::invalid_argument(buildLog("Green time must not exceed the cycle time.")); + logger.error(std::format("Green time ({}) must not exceed the cycle time ({}).", + cycle.greenTime(), + m_cycleTime)); } if (!(cycle.phase() < m_cycleTime)) { - throw std::invalid_argument(buildLog("Phase must be less than the cycle time.")); + logger.error(std::format("Phase ({}) must be less than the cycle time ({}).", + cycle.phase(), + m_cycleTime)); } if (direction == Direction::UTURN) { direction = Direction::LEFT; @@ -57,10 +61,11 @@ namespace dsm { void TrafficLight::setComplementaryCycle(Id const streetId, Id const existingCycle) { if (m_cycles.contains(streetId)) { - throw std::invalid_argument(buildLog("Street id already exists.")); + throw std::invalid_argument( + logger.buildExceptionMessage("Street id already exists.")); } if (!m_cycles.contains(existingCycle)) { - throw std::invalid_argument(buildLog("Cycle does not exist.")); + throw std::invalid_argument(logger.buildExceptionMessage("Cycle does not exist.")); } m_cycles.emplace(streetId, m_cycles.at(existingCycle)); for (auto& cycle : m_cycles.at(streetId)) { @@ -71,7 +76,8 @@ namespace dsm { void TrafficLight::moveCycle(Id const oldStreetId, Id const newStreetId) { if (!m_cycles.contains(oldStreetId)) { - throw std::invalid_argument(buildLog("Old street id does not exist.")); + throw std::invalid_argument( + logger.buildExceptionMessage("Old street id does not exist.")); } auto handler{m_cycles.extract(oldStreetId)}; handler.key() = newStreetId; @@ -174,7 +180,7 @@ namespace dsm { bool TrafficLight::isGreen(Id const streetId, Direction direction) const { if (!m_cycles.contains(streetId)) { - throw std::invalid_argument(buildLog( + throw std::invalid_argument(logger.buildExceptionMessage( std::format("Street id {} is not valid for node {}.", streetId, id()))); } switch (direction) { diff --git a/src/dsm/utility/Logger.hpp b/src/dsm/utility/Logger.hpp index a79053ef8..a516454a7 100644 --- a/src/dsm/utility/Logger.hpp +++ b/src/dsm/utility/Logger.hpp @@ -1,25 +1,58 @@ #pragma once -#include -#include +#include #include +#include +#include +#include + +static std::string buildMessage(const std::string& type, + const std::string& message, + const std::source_location& location) { + return std::format("{}({}:{}) \'{}\': {}", + type, + location.file_name(), + location.line(), + location.function_name(), + message); +} namespace dsm { /// @brief The Logger class is a simple logging class. struct Logger { - std::string operator()( + inline std::string buildExceptionMessage( + const std::string& message, + const std::source_location& location = std::source_location::current()) { + return buildMessage(std::string(), message, location); + }; + inline void info( + const std::string& message, + const std::source_location& location = std::source_location::current()) { + std::clog << buildMessage("\033[1;32mINFO ", message, location) + "\033[1;0m\n"; + }; + inline void debug( const std::string& message, - const std::source_location location = std::source_location::current()) { - return std::format("File: {} ({}:{}) \'{}\': {}\n", - location.file_name(), - location.line(), - location.column(), - location.function_name(), - message); + const std::source_location& location = std::source_location::current()) { +#ifndef NDEBUG + std::clog << buildMessage("\033[38;2;0;255;0mDEBUG ", message, location) + + "\033[0m\n"; +#endif }; + inline void warning( + const std::string& message, + const std::source_location& location = std::source_location::current()) { + std::clog << buildMessage("\033[38;2;130;30;180mWARNING ", message, location) + + "\033[1;0m\n"; + }; + inline void error( + const std::string& message, + const std::source_location& location = std::source_location::current()) { + std::cerr << buildMessage("\033[1;31mERROR ", message, location) + "\033[1;0m\n"; + std::abort(); + } }; - static Logger buildLog; + static Logger logger; } // namespace dsm diff --git a/test/Test_dynamics.cpp b/test/Test_dynamics.cpp index e9e7727eb..7857a5720 100644 --- a/test/Test_dynamics.cpp +++ b/test/Test_dynamics.cpp @@ -109,12 +109,6 @@ TEST_CASE("Dynamics") { } } } - WHEN("We add a span with non existing nodes") { - std::array nodes{0, 1, 169}; - THEN("An exception is thrown") { - CHECK_THROWS_AS(dynamics.setDestinationNodes(nodes), std::invalid_argument); - } - } } } SUBCASE("addAgent") { @@ -140,11 +134,6 @@ TEST_CASE("Dynamics") { auto graph = Graph{}; graph.importMatrix("./data/matrix.dsm"); Dynamics dynamics{graph, 69}; - WHEN("We add agents without adding itineraries") { - THEN("An exception is thrown") { - CHECK_THROWS_AS(dynamics.addAgentsUniformly(1), std::invalid_argument); - } - } Itinerary itinerary{0, 2}; WHEN("We add a random agent") { dynamics.addItinerary(itinerary); @@ -259,13 +248,6 @@ TEST_CASE("Dynamics") { CHECK_EQ(dynamics.agents().at(2)->srcNodeId().value(), 118); } } - WHEN("We add agents without adding itineraries") { - THEN("An exception is thrown") { - std::unordered_map src{{0, 1.}}; - std::unordered_map dst{{10, 1.}}; - CHECK_THROWS_AS(dynamics.addAgentsRandomly(1, src, dst), std::invalid_argument); - } - } } } SUBCASE("addRandomAgents") { @@ -336,20 +318,6 @@ TEST_CASE("Dynamics") { } } SUBCASE("Update paths") { - GIVEN("A dynamics object with a single street") { - Street s1{0, std::make_pair(0, 1), 2.}; - Graph graph2; - graph2.addStreets(s1); - graph2.buildAdj(); - Dynamics dynamics{graph2, 69}; - WHEN("We add a topologically impossible itinerary") { - Itinerary itinerary{0, 0}; - dynamics.addItinerary(itinerary); - THEN("When updating paths, empty itinerary throws exception") { - CHECK_THROWS_AS(dynamics.updatePaths(), std::runtime_error); - } - } - } GIVEN("A dynamics object, many streets and an itinerary") { Street s1{0, std::make_pair(0, 1), 2.}; Street s2{1, std::make_pair(1, 2), 5.}; diff --git a/test/Test_sparsematrix.cpp b/test/Test_sparsematrix.cpp index 08493a4d9..8a7dbbf1f 100644 --- a/test/Test_sparsematrix.cpp +++ b/test/Test_sparsematrix.cpp @@ -532,19 +532,6 @@ TEST_CASE("Boolean Matrix") { CHECK(m3(1, 2)); CHECK(m3.size() == 5); } - SUBCASE("+= operator - exception") { - /*This test tests if the += operator throws an exception correctly - The += operator should throw an exception if the matrices have different - dimensions - GIVEN: the += operator is called - WHEN: the function is called on two matrices - THEN: the function should throw an exception if the matrices have different - dimensions - */ - SparseMatrix m(3, 3); - SparseMatrix m2(3, 4); - CHECK_THROWS(m += m2); - } SUBCASE("+= operator") { /*This test tests if the += operator works correctly The += operator should sum two matrices @@ -604,19 +591,6 @@ TEST_CASE("Boolean Matrix") { CHECK(m3(1, 2) == 3); CHECK(m3.size() == 5); } - SUBCASE("-= operator - exception") { - /*This test tests if the -= operator throws an exception correctly - The -= operator should throw an exception if the matrices have different - dimensions - GIVEN: the -= operator is called - WHEN: the function is called on two matrices - THEN: the function should throw an exception if the matrices have different - dimensions - */ - SparseMatrix m(3, 3); - SparseMatrix m2(3, 4); - CHECK_THROWS(m -= m2); - } SUBCASE("-= operator") { /*This test tests if the -= operator works correctly The -= operator should subtract two matrices