supercell-wx/wxdata/source/scwx/awips/pvtec.cpp

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