mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 10:40:05 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			286 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			286 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Prevent redefinition of __cpp_lib_format
 | |
| #if defined(_MSC_VER)
 | |
| #   include <yvals_core.h>
 | |
| #endif
 | |
| 
 | |
| // Enable chrono formatters
 | |
| #ifndef __cpp_lib_format
 | |
| #   define __cpp_lib_format 202110L
 | |
| #endif
 | |
| 
 | |
| #include <scwx/awips/pvtec.hpp>
 | |
| #include <scwx/util/logger.hpp>
 | |
| 
 | |
| #include <chrono>
 | |
| 
 | |
| #include <boost/assign.hpp>
 | |
| #include <boost/bimap.hpp>
 | |
| #include <boost/bimap/unordered_set_of.hpp>
 | |
| 
 | |
| #if (__cpp_lib_chrono < 201907L)
 | |
| #   include <date/date.h>
 | |
| #endif
 | |
| 
 | |
| namespace scwx
 | |
| {
 | |
| namespace awips
 | |
| {
 | |
| 
 | |
| static const std::string logPrefix_ = "scwx::awips::pvtec";
 | |
| static const auto        logger_    = util::Logger::Create(logPrefix_);
 | |
| 
 | |
| typedef boost::bimap<boost::bimaps::unordered_set_of<PVtec::ProductType>,
 | |
|                      boost::bimaps::unordered_set_of<std::string>>
 | |
|    ProductTypeCodesBimap;
 | |
| 
 | |
| static const ProductTypeCodesBimap productTypeCodes_ =
 | |
|    boost::assign::list_of<ProductTypeCodesBimap::relation>    //
 | |
|    (PVtec::ProductType::Operational, "O")                     //
 | |
|    (PVtec::ProductType::Test, "T")                            //
 | |
|    (PVtec::ProductType::Experimental, "E")                    //
 | |
|    (PVtec::ProductType::OperationalWithExperimentalVtec, "X") //
 | |
|    (PVtec::ProductType::Unknown, "??");
 | |
| 
 | |
| typedef boost::bimap<boost::bimaps::unordered_set_of<PVtec::Action>,
 | |
|                      boost::bimaps::unordered_set_of<std::string>>
 | |
|    ActionCodesBimap;
 | |
| 
 | |
| static const ActionCodesBimap actionCodes_ =
 | |
|    boost::assign::list_of<ActionCodesBimap::relation> //
 | |
|    (PVtec::Action::New, "NEW")                        //
 | |
|    (PVtec::Action::Continued, "CON")                  //
 | |
|    (PVtec::Action::ExtendedInArea, "EXA")             //
 | |
|    (PVtec::Action::ExtendedInTime, "EXT")             //
 | |
|    (PVtec::Action::ExtendedInAreaAndTime, "EXB")      //
 | |
|    (PVtec::Action::Upgraded, "UPG")                   //
 | |
|    (PVtec::Action::Canceled, "CAN")                   //
 | |
|    (PVtec::Action::Expired, "EXP")                    //
 | |
|    (PVtec::Action::Routine, "ROU")                    //
 | |
|    (PVtec::Action::Correction, "COR")                 //
 | |
|    (PVtec::Action::Unknown, "???");
 | |
| 
 | |
| class PVtecImpl
 | |
| {
 | |
| public:
 | |
|    explicit PVtecImpl() :
 | |
|        pVtecString_ {},
 | |
|        fixedIdentifier_ {PVtec::ProductType::Unknown},
 | |
|        action_ {PVtec::Action::Unknown},
 | |
|        officeId_ {"????"},
 | |
|        phenomenon_ {Phenomenon::Unknown},
 | |
|        significance_ {Significance::Unknown},
 | |
|        eventTrackingNumber_ {-1},
 | |
|        eventBegin_ {},
 | |
|        eventEnd_ {},
 | |
|        valid_ {false}
 | |
|    {
 | |
|    }
 | |
| 
 | |
|    ~PVtecImpl() {}
 | |
| 
 | |
|    std::string pVtecString_;
 | |
| 
 | |
|    PVtec::ProductType fixedIdentifier_;
 | |
|    PVtec::Action      action_;
 | |
|    std::string        officeId_;
 | |
|    Phenomenon         phenomenon_;
 | |
|    Significance       significance_;
 | |
|    int16_t            eventTrackingNumber_;
 | |
| 
 | |
|    std::chrono::system_clock::time_point eventBegin_;
 | |
|    std::chrono::system_clock::time_point eventEnd_;
 | |
| 
 | |
|    bool valid_;
 | |
| };
 | |
| 
 | |
| PVtec::PVtec() : p(std::make_unique<PVtecImpl>()) {}
 | |
| PVtec::~PVtec() = default;
 | |
| 
 | |
| PVtec::PVtec(PVtec&&) noexcept            = default;
 | |
| PVtec& PVtec::operator=(PVtec&&) noexcept = default;
 | |
| 
 | |
| PVtec::ProductType PVtec::fixed_identifier() const
 | |
| {
 | |
|    return p->fixedIdentifier_;
 | |
| }
 | |
| 
 | |
| PVtec::Action PVtec::action() const
 | |
| {
 | |
|    return p->action_;
 | |
| }
 | |
| 
 | |
| std::string PVtec::office_id() const
 | |
| {
 | |
|    return p->officeId_;
 | |
| }
 | |
| 
 | |
| Phenomenon PVtec::phenomenon() const
 | |
| {
 | |
|    return p->phenomenon_;
 | |
| }
 | |
| 
 | |
| Significance PVtec::significance() const
 | |
| {
 | |
|    return p->significance_;
 | |
| }
 | |
| 
 | |
| int16_t PVtec::event_tracking_number() const
 | |
| {
 | |
|    return p->eventTrackingNumber_;
 | |
| }
 | |
| 
 | |
| std::chrono::system_clock::time_point PVtec::event_begin() const
 | |
| {
 | |
|    return p->eventBegin_;
 | |
| }
 | |
| 
 | |
| std::chrono::system_clock::time_point PVtec::event_end() const
 | |
| {
 | |
|    return p->eventEnd_;
 | |
| }
 | |
| 
 | |
| bool PVtec::Parse(const std::string& s)
 | |
| {
 | |
|    using namespace std::chrono;
 | |
| 
 | |
| #if (__cpp_lib_chrono < 201907L)
 | |
|    using namespace date;
 | |
| #endif
 | |
| 
 | |
|    // P-VTEC takes the form:
 | |
|    // /k.aaa.cccc.pp.s.####.yymmddThhnnZ-yymmddThhnnZ/
 | |
|    // 012345678901234567890123456789012345678901234567
 | |
|    static constexpr size_t pVtecLength_             = 48u;
 | |
|    static constexpr size_t pVtecOffsetStart_        = 0u;
 | |
|    static constexpr size_t pVtecOffsetIdentifier_   = 1u;
 | |
|    static constexpr size_t pVtecOffsetAction_       = 3u;
 | |
|    static constexpr size_t pVtecOffsetOfficeId_     = 7u;
 | |
|    static constexpr size_t pVtecOffsetPhenomenon_   = 12u;
 | |
|    static constexpr size_t pVtecOffsetSignificance_ = 15u;
 | |
|    static constexpr size_t pVtecOffsetEventNumber_  = 17u;
 | |
|    static constexpr size_t pVtecOffsetEventBegin_   = 22u;
 | |
|    static constexpr size_t pVtecOffsetEventEnd_     = 35u;
 | |
|    static constexpr size_t pVtecOffsetEnd_          = 47u;
 | |
| 
 | |
|    bool dataValid = (s.length() >= pVtecLength_ &&     //
 | |
|                      s.at(pVtecOffsetStart_) == '/' && //
 | |
|                      s.at(pVtecOffsetEnd_) == '/');
 | |
| 
 | |
|    if (dataValid)
 | |
|    {
 | |
|       p->pVtecString_ = s.substr(0, pVtecLength_);
 | |
| 
 | |
|       p->fixedIdentifier_ = GetProductType(s.substr(pVtecOffsetIdentifier_, 1));
 | |
|       p->action_          = GetAction(s.substr(pVtecOffsetAction_, 3));
 | |
|       p->officeId_        = s.substr(pVtecOffsetOfficeId_, 4);
 | |
|       p->phenomenon_      = GetPhenomenon(s.substr(pVtecOffsetPhenomenon_, 2));
 | |
|       p->significance_ = GetSignificance(s.substr(pVtecOffsetSignificance_, 1));
 | |
| 
 | |
|       std::string eventNumberString = s.substr(pVtecOffsetEventNumber_, 4);
 | |
| 
 | |
|       try
 | |
|       {
 | |
|          p->eventTrackingNumber_ =
 | |
|             static_cast<int16_t>(std::stoi(eventNumberString));
 | |
|       }
 | |
|       catch (const std::exception& ex)
 | |
|       {
 | |
|          logger_->warn("Error parsing event tracking number: \"{}\" ({})",
 | |
|                        eventNumberString,
 | |
|                        ex.what());
 | |
| 
 | |
|          p->eventTrackingNumber_ = -1;
 | |
|       }
 | |
| 
 | |
|       static const std::string dateTimeFormat {"%y%m%dT%H%MZ"};
 | |
| 
 | |
|       std::string sEventBegin = s.substr(pVtecOffsetEventBegin_, 12);
 | |
|       std::string sEventEnd   = s.substr(pVtecOffsetEventEnd_, 12);
 | |
| 
 | |
|       std::istringstream ssEventBegin {sEventBegin};
 | |
|       std::istringstream ssEventEnd {sEventEnd};
 | |
| 
 | |
|       std::chrono::sys_time<minutes> eventBegin;
 | |
|       std::chrono::sys_time<minutes> eventEnd;
 | |
| 
 | |
|       ssEventBegin >> parse(dateTimeFormat, eventBegin);
 | |
|       ssEventEnd >> parse(dateTimeFormat, eventEnd);
 | |
| 
 | |
|       if (!ssEventBegin.fail())
 | |
|       {
 | |
|          p->eventBegin_ = eventBegin;
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          // Time parsing expected to fail if time is "000000T0000Z"
 | |
|          p->eventBegin_ = {};
 | |
|       }
 | |
| 
 | |
|       if (!ssEventEnd.fail())
 | |
|       {
 | |
|          p->eventEnd_ = eventEnd;
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          // Time parsing expected to fail if time is "000000T0000Z"
 | |
|          p->eventEnd_ = {};
 | |
|       }
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|       logger_->warn("Invalid P-VTEC: \"{}\"", s);
 | |
|    }
 | |
| 
 | |
|    p->valid_ = dataValid;
 | |
| 
 | |
|    return dataValid;
 | |
| }
 | |
| 
 | |
| PVtec::ProductType PVtec::GetProductType(const std::string& code)
 | |
| {
 | |
|    ProductType productType;
 | |
| 
 | |
|    if (productTypeCodes_.right.find(code) != productTypeCodes_.right.end())
 | |
|    {
 | |
|       productType = productTypeCodes_.right.at(code);
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|       productType = ProductType::Unknown;
 | |
| 
 | |
|       logger_->debug("Unrecognized product code: \"{}\"", code);
 | |
|    }
 | |
| 
 | |
|    return productType;
 | |
| }
 | |
| 
 | |
| const std::string& PVtec::GetProductTypeCode(PVtec::ProductType productType)
 | |
| {
 | |
|    return productTypeCodes_.left.at(productType);
 | |
| }
 | |
| 
 | |
| PVtec::Action PVtec::GetAction(const std::string& code)
 | |
| {
 | |
|    Action action;
 | |
| 
 | |
|    if (actionCodes_.right.find(code) != actionCodes_.right.end())
 | |
|    {
 | |
|       action = actionCodes_.right.at(code);
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|       action = Action::Unknown;
 | |
| 
 | |
|       logger_->debug("Unrecognized action code: \"{}\"", code);
 | |
|    }
 | |
| 
 | |
|    return action;
 | |
| }
 | |
| 
 | |
| const std::string& PVtec::GetActionCode(PVtec::Action action)
 | |
| {
 | |
|    return actionCodes_.left.at(action);
 | |
| }
 | |
| 
 | |
| } // namespace awips
 | |
| } // namespace scwx
 | 
