diff --git a/traffxml/traff_model.cpp b/traffxml/traff_model.cpp index 278208a2b..68e099daa 100644 --- a/traffxml/traff_model.cpp +++ b/traffxml/traff_model.cpp @@ -200,8 +200,14 @@ std::optional TraffMessage::GetTrafficImpact() impact.m_maxspeed = event.m_speed.value(); // TODO if no explicit speed given, look up in kEventMaxspeedMap (once we have entries) - // TODO if event is in delay class and has an explicit duration quantifier, use that and skip the map lookup. - if (auto it = kEventDelayMap.find(event.m_type); it != kEventDelayMap.end()) + if (event.m_class == EventClass::Delay + && event.m_type != EventType::DelayClearance + && event.m_type != EventType::DelayForecastWithdrawn + && event.m_type != EventType::DelaySeveralHours + && event.m_type != EventType::DelayUncertainDuration + && event.m_qDurationMins) + impact.m_delayMins = event.m_qDurationMins.value(); + else if (auto it = kEventDelayMap.find(event.m_type); it != kEventDelayMap.end()) impact.m_delayMins = it->second; // TempBlock overrules everything else, return immediately @@ -458,7 +464,12 @@ std::string DebugPrint(TraffEvent event) os << "type: " << DebugPrint(event.m_type) << ", "; os << "length: " << (event.m_length ? std::to_string(event.m_length.value()) : "nullopt") << ", "; os << "probability: " << (event.m_probability ? std::to_string(event.m_probability.value()) : "nullopt") << ", "; - // TODO optional quantifier + os << "q_duration: " + << (event.m_qDurationMins + ? std::format("{:1d}:{:02d}", event.m_qDurationMins.value() / 60, event.m_qDurationMins.value() % 60) + : "nullopt") + << ", "; + // TODO other quantifiers os << "speed: " << (event.m_speed ? std::to_string(event.m_speed.value()) : "nullopt"); // TODO supplementary information os << " }"; diff --git a/traffxml/traff_model.hpp b/traffxml/traff_model.hpp index 43e84e30b..3de56b0e0 100644 --- a/traffxml/traff_model.hpp +++ b/traffxml/traff_model.hpp @@ -109,6 +109,19 @@ enum class RoadClass Other }; +enum class QuantifierType +{ + Dimension, + Duration, + Int, + Ints, + Speed, + Temperature, + Time, + Weight, + Invalid +}; + /* * When adding a new event class to this enum, be sure to do the following: * @@ -312,7 +325,17 @@ struct TraffEvent EventType m_type = EventType::Invalid; std::optional m_length; std::optional m_probability; - // TODO optional quantifier + std::optional m_qDurationMins; + /* + * TODO remaining quantifiers + * q_dimension + * q_int + * q_ints + * q_speed + * q_temperature + * q_time + * q_weight + */ std::optional m_speed; // TODO supplementary information }; diff --git a/traffxml/traff_model_xml.cpp b/traffxml/traff_model_xml.cpp index dcc719c9a..3edaa3f0b 100644 --- a/traffxml/traff_model_xml.cpp +++ b/traffxml/traff_model_xml.cpp @@ -480,6 +480,53 @@ bool LocationFromXml(pugi::xml_node node, TraffLocation & location) return true; } +/** + * @brief Retrieves a `TraffQuantifier` from an XML element. + * + * The TraFF specification allows only one quantifier per event. The quantifier type depends on the + * event type, and not all events allow quantifiers. + * + * Quantifiers which violate these constraints are not filtered out, i.e. this function may return a + * quantifier for event types that do not allow quantifiers, or of a type illegal for the event type. + * If an event contains multiple quantifiers of different types, any one of these quantifiers may be + * returned, with no preference for legal quantifiers over illegal ones. + * + * @param node The node from which to retrieve the quantifier (`event`). + * @return The quantifier, or `std::nullopt` + */ +std::optional OptionalDurationFromXml(pugi::xml_attribute attribute) +{ + std::string durationString; + if (!StringFromXml(attribute, durationString)) + return std::nullopt; + + /* + * Valid time formats: + * 01:30 (hh:mm) + * 1 h + * 30 min + */ + std::regex durationRegex("(([0-9]+):([0-9]{2}))|(([0-9]+) *h)|(([0-9]+) *min)"); + std::smatch durationMatcher; + + if (std::regex_search(durationString, durationMatcher, durationRegex)) + { + if (!durationMatcher.str(2).empty() && !durationMatcher.str(3).empty()) + return std::stoi(durationMatcher[2]) * 60 + std::stoi(durationMatcher[3]); + else if (!durationMatcher.str(5).empty()) + return std::stoi(durationMatcher[5]) * 60; + else if (!durationMatcher.str(7).empty()) + return std::stoi(durationMatcher[7]); + UNREACHABLE(); + return std::nullopt; + } + else + { + LOG(LINFO, ("Not a valid duration:", durationString)); + return std::nullopt; + } +} + /** * @brief Retrieves a `TraffEvent` from an XML element. * @param node The XML element to retrieve (`event`). @@ -514,7 +561,9 @@ bool EventFromXml(pugi::xml_node node, TraffEvent & event) event.m_length = OptionalIntegerFromXml(node.attribute("length")); event.m_probability = OptionalIntegerFromXml(node.attribute("probability")); - // TODO optional quantifier (not yet implemented in struct) + event.m_qDurationMins = OptionalDurationFromXml(node.attribute("q_duration")); + + // TODO other quantifiers (not yet implemented in struct) event.m_speed = OptionalIntegerFromXml(node.attribute("speed"));