diff --git a/wxdata/include/scwx/wsr88d/level3_file.hpp b/wxdata/include/scwx/wsr88d/level3_file.hpp index 3d730a49..90ef23ab 100644 --- a/wxdata/include/scwx/wsr88d/level3_file.hpp +++ b/wxdata/include/scwx/wsr88d/level3_file.hpp @@ -1,7 +1,6 @@ #pragma once -#include -#include +#include #include #include @@ -25,8 +24,7 @@ public: Level3File(Level3File&&) noexcept; Level3File& operator=(Level3File&&) noexcept; - std::shared_ptr message_header() const; - std::shared_ptr product_symbology_block() const; + std::shared_ptr message() const; bool LoadFile(const std::string& filename); bool LoadData(std::istream& is); diff --git a/wxdata/include/scwx/wsr88d/rpg/graphic_product_message.hpp b/wxdata/include/scwx/wsr88d/rpg/graphic_product_message.hpp new file mode 100644 index 00000000..a14df940 --- /dev/null +++ b/wxdata/include/scwx/wsr88d/rpg/graphic_product_message.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rpg +{ + +class GraphicProductMessageImpl; + +class GraphicProductMessage : public Level3Message +{ +public: + explicit GraphicProductMessage(); + ~GraphicProductMessage(); + + GraphicProductMessage(const GraphicProductMessage&) = delete; + GraphicProductMessage& operator=(const GraphicProductMessage&) = delete; + + GraphicProductMessage(GraphicProductMessage&&) noexcept; + GraphicProductMessage& operator=(GraphicProductMessage&&) noexcept; + + std::shared_ptr description_block() const; + std::shared_ptr symbology_block() const; + std::shared_ptr graphic_block() const; + std::shared_ptr tabular_block() const; + + bool Parse(std::istream& is) override; + + static std::shared_ptr + Create(Level3MessageHeader&& header, std::istream& is); + +private: + std::unique_ptr p; +}; + +} // namespace rpg +} // namespace wsr88d +} // namespace scwx diff --git a/wxdata/include/scwx/wsr88d/rpg/level3_message.hpp b/wxdata/include/scwx/wsr88d/rpg/level3_message.hpp new file mode 100644 index 00000000..ffa157ce --- /dev/null +++ b/wxdata/include/scwx/wsr88d/rpg/level3_message.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rpg +{ + +class Level3MessageImpl; + +class Level3Message : public Message +{ +protected: + explicit Level3Message(); + + Level3Message(const Level3Message&) = delete; + Level3Message& operator=(const Level3Message&) = delete; + + Level3Message(Level3Message&&) noexcept; + Level3Message& operator=(Level3Message&&) noexcept; + +public: + virtual ~Level3Message(); + + size_t data_size() const override; + + const Level3MessageHeader& header() const; + + void set_header(Level3MessageHeader&& header); + +private: + std::unique_ptr p; +}; + +} // namespace rpg +} // namespace wsr88d +} // namespace scwx diff --git a/wxdata/include/scwx/wsr88d/rpg/level3_message_factory.hpp b/wxdata/include/scwx/wsr88d/rpg/level3_message_factory.hpp new file mode 100644 index 00000000..c2556434 --- /dev/null +++ b/wxdata/include/scwx/wsr88d/rpg/level3_message_factory.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rpg +{ + +class Level3MessageFactory +{ +private: + explicit Level3MessageFactory() = delete; + ~Level3MessageFactory() = delete; + + Level3MessageFactory(const Level3MessageFactory&) = delete; + Level3MessageFactory& operator=(const Level3MessageFactory&) = delete; + + Level3MessageFactory(Level3MessageFactory&&) noexcept = delete; + Level3MessageFactory& operator=(Level3MessageFactory&&) noexcept = delete; + +public: + static std::shared_ptr Create(std::istream& is); +}; + +} // namespace rpg +} // namespace wsr88d +} // namespace scwx diff --git a/wxdata/include/scwx/wsr88d/rpg/tabular_product_message.hpp b/wxdata/include/scwx/wsr88d/rpg/tabular_product_message.hpp new file mode 100644 index 00000000..006d5e82 --- /dev/null +++ b/wxdata/include/scwx/wsr88d/rpg/tabular_product_message.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rpg +{ + +class TabularProductMessageImpl; + +class TabularProductMessage : public Level3Message +{ +public: + explicit TabularProductMessage(); + ~TabularProductMessage(); + + TabularProductMessage(const TabularProductMessage&) = delete; + TabularProductMessage& operator=(const TabularProductMessage&) = delete; + + TabularProductMessage(TabularProductMessage&&) noexcept; + TabularProductMessage& operator=(TabularProductMessage&&) noexcept; + + std::shared_ptr description_block() const; + std::shared_ptr tabular_block() const; + + bool Parse(std::istream& is) override; + + static std::shared_ptr + Create(Level3MessageHeader&& header, std::istream& is); + +private: + std::unique_ptr p; +}; + +} // namespace rpg +} // namespace wsr88d +} // namespace scwx diff --git a/wxdata/source/scwx/wsr88d/level3_file.cpp b/wxdata/source/scwx/wsr88d/level3_file.cpp index 4f53ea49..c77f253f 100644 --- a/wxdata/source/scwx/wsr88d/level3_file.cpp +++ b/wxdata/source/scwx/wsr88d/level3_file.cpp @@ -1,20 +1,12 @@ #include #include -#include -#include -#include -#include -#include +#include #include -#include -#include #include -#include #include #include -#include #include #include @@ -25,33 +17,20 @@ namespace wsr88d static const std::string logPrefix_ = "[scwx::wsr88d::level3_file] "; -static const std::set standaloneTabularProducts_ = {62, 75, 77, 82}; - class Level3FileImpl { public: explicit Level3FileImpl() : - wmoHeader_ {}, - ccbHeader_ {}, - messageHeader_ {}, - descriptionBlock_ {}, - symbologyBlock_ {}, - graphicBlock_ {}, - tabularBlock_ {} {}; + wmoHeader_ {}, ccbHeader_ {}, innerHeader_ {}, message_ {} {}; ~Level3FileImpl() = default; bool DecompressFile(std::istream& is, std::stringstream& ss); bool LoadFileData(std::istream& is); - bool LoadBlocks(std::istream& is); - std::shared_ptr wmoHeader_; - std::shared_ptr ccbHeader_; - std::shared_ptr innerHeader_; - std::shared_ptr messageHeader_; - std::shared_ptr descriptionBlock_; - std::shared_ptr symbologyBlock_; - std::shared_ptr graphicBlock_; - std::shared_ptr tabularBlock_; + std::shared_ptr wmoHeader_; + std::shared_ptr ccbHeader_; + std::shared_ptr innerHeader_; + std::shared_ptr message_; }; Level3File::Level3File() : p(std::make_unique()) {} @@ -60,15 +39,9 @@ Level3File::~Level3File() = default; Level3File::Level3File(Level3File&&) noexcept = default; Level3File& Level3File::operator=(Level3File&&) noexcept = default; -std::shared_ptr Level3File::message_header() const +std::shared_ptr Level3File::message() const { - return p->messageHeader_; -} - -std::shared_ptr -Level3File::product_symbology_block() const -{ - return p->symbologyBlock_; + return p->message_; } bool Level3File::LoadFile(const std::string& filename) @@ -197,145 +170,9 @@ bool Level3FileImpl::DecompressFile(std::istream& is, std::stringstream& ss) bool Level3FileImpl::LoadFileData(std::istream& is) { - messageHeader_ = std::make_shared(); + message_ = rpg::Level3MessageFactory::Create(is); - bool dataValid = messageHeader_->Parse(is); - - if (dataValid) - { - BOOST_LOG_TRIVIAL(debug) - << logPrefix_ << "Code: " << messageHeader_->message_code(); - - descriptionBlock_ = std::make_shared(); - - dataValid = descriptionBlock_->Parse(is); - } - - if (dataValid) - { - if (descriptionBlock_->IsCompressionEnabled()) - { - size_t messageLength = messageHeader_->length_of_message(); - size_t prefixLength = - rpg::Level3MessageHeader::SIZE + rpg::ProductDescriptionBlock::SIZE; - size_t recordSize = - (messageLength > prefixLength) ? messageLength - prefixLength : 0; - - boost::iostreams::filtering_streambuf in; - util::rangebuf r(is.rdbuf(), recordSize); - in.push(boost::iostreams::bzip2_decompressor()); - in.push(r); - - try - { - std::stringstream ss; - std::streamsize bytesCopied = boost::iostreams::copy(in, ss); - BOOST_LOG_TRIVIAL(trace) - << logPrefix_ << "Decompressed data size = " << bytesCopied - << " bytes"; - - dataValid = LoadBlocks(ss); - } - catch (const boost::iostreams::bzip2_error& ex) - { - int error = ex.error(); - BOOST_LOG_TRIVIAL(warning) - << logPrefix_ << "Error decompressing data: " << ex.what(); - - dataValid = false; - } - } - else - { - dataValid = LoadBlocks(is); - } - } - - return dataValid; -} - -bool Level3FileImpl::LoadBlocks(std::istream& is) -{ - bool symbologyValid = true; - bool graphicValid = true; - bool tabularValid = true; - - BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "Loading Blocks"; - - bool skipTabularHeader = false; - - std::streampos offsetBasePos = is.tellg(); - - constexpr size_t offsetBase = - rpg::Level3MessageHeader::SIZE + rpg::ProductDescriptionBlock::SIZE; - - size_t offsetToSymbology = descriptionBlock_->offset_to_symbology() * 2u; - size_t offsetToGraphic = descriptionBlock_->offset_to_graphic() * 2u; - size_t offsetToTabular = descriptionBlock_->offset_to_tabular() * 2u; - - if (standaloneTabularProducts_.contains(messageHeader_->message_code())) - { - // These products are completely alphanumeric, and do not contain a - // symbology block. - offsetToTabular = offsetToSymbology; - offsetToSymbology = 0; - offsetToGraphic = 0; - - skipTabularHeader = true; - } - - if (offsetToSymbology >= offsetBase) - { - symbologyBlock_ = std::make_shared(); - - is.seekg(offsetToSymbology - offsetBase, std::ios_base::cur); - symbologyValid = symbologyBlock_->Parse(is); - is.seekg(offsetBasePos, std::ios_base::beg); - - BOOST_LOG_TRIVIAL(debug) - << logPrefix_ << "Product symbology block valid: " << symbologyValid; - - if (!symbologyValid) - { - symbologyBlock_ = nullptr; - } - } - - if (offsetToGraphic >= offsetBase) - { - graphicBlock_ = std::make_shared(); - - is.seekg(offsetToGraphic - offsetBase, std::ios_base::cur); - graphicValid = graphicBlock_->Parse(is); - is.seekg(offsetBasePos, std::ios_base::beg); - - BOOST_LOG_TRIVIAL(debug) - << logPrefix_ << "Graphic alphanumeric block valid: " << graphicValid; - - if (!graphicValid) - { - graphicBlock_ = nullptr; - } - } - - if (offsetToTabular >= offsetBase) - { - tabularBlock_ = std::make_shared(); - - is.seekg(offsetToTabular - offsetBase, std::ios_base::cur); - tabularValid = tabularBlock_->Parse(is, skipTabularHeader); - is.seekg(offsetBasePos, std::ios_base::beg); - - BOOST_LOG_TRIVIAL(debug) - << logPrefix_ << "Tabular alphanumeric block valid: " << tabularValid; - - if (!tabularValid) - { - tabularBlock_ = nullptr; - } - } - - return (symbologyValid && graphicValid && tabularValid); + return (message_ != nullptr); } } // namespace wsr88d diff --git a/wxdata/source/scwx/wsr88d/rpg/graphic_product_message.cpp b/wxdata/source/scwx/wsr88d/rpg/graphic_product_message.cpp new file mode 100644 index 00000000..6f2b99f7 --- /dev/null +++ b/wxdata/source/scwx/wsr88d/rpg/graphic_product_message.cpp @@ -0,0 +1,223 @@ +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rpg +{ + +static const std::string logPrefix_ = + "[scwx::wsr88d::rpg::graphic_product_message] "; + +class GraphicProductMessageImpl +{ +public: + explicit GraphicProductMessageImpl() : + descriptionBlock_ {0}, + symbologyBlock_ {0}, + graphicBlock_ {0}, + tabularBlock_ {0} + { + } + ~GraphicProductMessageImpl() = default; + + bool LoadBlocks(std::istream& is); + + std::shared_ptr descriptionBlock_; + std::shared_ptr symbologyBlock_; + std::shared_ptr graphicBlock_; + std::shared_ptr tabularBlock_; +}; + +GraphicProductMessage::GraphicProductMessage() : + p(std::make_unique()) +{ +} +GraphicProductMessage::~GraphicProductMessage() = default; + +GraphicProductMessage::GraphicProductMessage(GraphicProductMessage&&) noexcept = + default; +GraphicProductMessage& +GraphicProductMessage::operator=(GraphicProductMessage&&) noexcept = default; + +std::shared_ptr +GraphicProductMessage::description_block() const +{ + return p->descriptionBlock_; +} + +std::shared_ptr +GraphicProductMessage::symbology_block() const +{ + return p->symbologyBlock_; +} + +std::shared_ptr +GraphicProductMessage::graphic_block() const +{ + return p->graphicBlock_; +} + +std::shared_ptr +GraphicProductMessage::tabular_block() const +{ + return p->tabularBlock_; +} + +bool GraphicProductMessage::Parse(std::istream& is) +{ + bool dataValid = true; + + const std::streampos dataStart = is.tellg(); + + p->descriptionBlock_ = std::make_shared(); + dataValid = p->descriptionBlock_->Parse(is); + + if (dataValid) + { + if (p->descriptionBlock_->IsCompressionEnabled()) + { + size_t messageLength = header().length_of_message(); + size_t prefixLength = + Level3MessageHeader::SIZE + ProductDescriptionBlock::SIZE; + size_t recordSize = + (messageLength > prefixLength) ? messageLength - prefixLength : 0; + + boost::iostreams::filtering_streambuf in; + util::rangebuf r(is.rdbuf(), recordSize); + in.push(boost::iostreams::bzip2_decompressor()); + in.push(r); + + try + { + std::stringstream ss; + std::streamsize bytesCopied = boost::iostreams::copy(in, ss); + BOOST_LOG_TRIVIAL(trace) + << logPrefix_ << "Decompressed data size = " << bytesCopied + << " bytes"; + + dataValid = p->LoadBlocks(ss); + } + catch (const boost::iostreams::bzip2_error& ex) + { + int error = ex.error(); + BOOST_LOG_TRIVIAL(warning) + << logPrefix_ << "Error decompressing data: " << ex.what(); + + dataValid = false; + } + } + else + { + dataValid = p->LoadBlocks(is); + } + } + + const std::streampos dataEnd = is.tellg(); + if (!ValidateMessage(is, dataEnd - dataStart)) + { + dataValid = false; + } + + return dataValid; +} + +bool GraphicProductMessageImpl::LoadBlocks(std::istream& is) +{ + bool symbologyValid = true; + bool graphicValid = true; + bool tabularValid = true; + + BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "Loading Blocks"; + + std::streampos offsetBasePos = is.tellg(); + + constexpr size_t offsetBase = + Level3MessageHeader::SIZE + ProductDescriptionBlock::SIZE; + + size_t offsetToSymbology = descriptionBlock_->offset_to_symbology() * 2u; + size_t offsetToGraphic = descriptionBlock_->offset_to_graphic() * 2u; + size_t offsetToTabular = descriptionBlock_->offset_to_tabular() * 2u; + + if (offsetToSymbology >= offsetBase) + { + symbologyBlock_ = std::make_shared(); + + is.seekg(offsetToSymbology - offsetBase, std::ios_base::cur); + symbologyValid = symbologyBlock_->Parse(is); + is.seekg(offsetBasePos, std::ios_base::beg); + + BOOST_LOG_TRIVIAL(debug) + << logPrefix_ << "Product symbology block valid: " << symbologyValid; + + if (!symbologyValid) + { + symbologyBlock_ = nullptr; + } + } + + if (offsetToGraphic >= offsetBase) + { + graphicBlock_ = std::make_shared(); + + is.seekg(offsetToGraphic - offsetBase, std::ios_base::cur); + graphicValid = graphicBlock_->Parse(is); + is.seekg(offsetBasePos, std::ios_base::beg); + + BOOST_LOG_TRIVIAL(debug) + << logPrefix_ << "Graphic alphanumeric block valid: " << graphicValid; + + if (!graphicValid) + { + graphicBlock_ = nullptr; + } + } + + if (offsetToTabular >= offsetBase) + { + tabularBlock_ = std::make_shared(); + + is.seekg(offsetToTabular - offsetBase, std::ios_base::cur); + tabularValid = tabularBlock_->Parse(is); + is.seekg(offsetBasePos, std::ios_base::beg); + + BOOST_LOG_TRIVIAL(debug) + << logPrefix_ << "Tabular alphanumeric block valid: " << tabularValid; + + if (!tabularValid) + { + tabularBlock_ = nullptr; + } + } + + return (symbologyValid && graphicValid && tabularValid); +} + +std::shared_ptr +GraphicProductMessage::Create(Level3MessageHeader&& header, std::istream& is) +{ + std::shared_ptr message = + std::make_shared(); + message->set_header(std::move(header)); + + if (!message->Parse(is)) + { + message.reset(); + } + + return message; +} + +} // namespace rpg +} // namespace wsr88d +} // namespace scwx diff --git a/wxdata/source/scwx/wsr88d/rpg/level3_message.cpp b/wxdata/source/scwx/wsr88d/rpg/level3_message.cpp new file mode 100644 index 00000000..482f21e8 --- /dev/null +++ b/wxdata/source/scwx/wsr88d/rpg/level3_message.cpp @@ -0,0 +1,49 @@ +#include + +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rpg +{ + +static const std::string logPrefix_ = "[scwx::wsr88d::rpg::level3_message] "; + +class Level3MessageImpl +{ +public: + explicit Level3MessageImpl() : header_() {}; + ~Level3MessageImpl() = default; + + Level3MessageHeader header_; +}; + +Level3Message::Level3Message() : + Message(), p(std::make_unique()) +{ +} +Level3Message::~Level3Message() = default; + +Level3Message::Level3Message(Level3Message&&) noexcept = default; +Level3Message& Level3Message::operator=(Level3Message&&) noexcept = default; + +size_t Level3Message::data_size() const +{ + return (header().length_of_message() - header().SIZE); +} + +const Level3MessageHeader& Level3Message::header() const +{ + return p->header_; +} + +void Level3Message::set_header(Level3MessageHeader&& header) +{ + p->header_ = std::move(header); +} + +} // namespace rpg +} // namespace wsr88d +} // namespace scwx diff --git a/wxdata/source/scwx/wsr88d/rpg/level3_message_factory.cpp b/wxdata/source/scwx/wsr88d/rpg/level3_message_factory.cpp new file mode 100644 index 00000000..3c120184 --- /dev/null +++ b/wxdata/source/scwx/wsr88d/rpg/level3_message_factory.cpp @@ -0,0 +1,154 @@ +#include + +#include +#include +#include + +#include +#include + +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rpg +{ + +static const std::string logPrefix_ = + "[scwx::wsr88d::rpg::level3_message_factory] "; + +typedef std::function(Level3MessageHeader&&, + std::istream&)> + CreateLevel3MessageFunction; + +static const std::unordered_map // + create_ {{19, GraphicProductMessage::Create}, + {20, GraphicProductMessage::Create}, + {27, GraphicProductMessage::Create}, + {30, GraphicProductMessage::Create}, + {31, GraphicProductMessage::Create}, + {32, GraphicProductMessage::Create}, + {37, GraphicProductMessage::Create}, + {38, GraphicProductMessage::Create}, + {41, GraphicProductMessage::Create}, + {48, GraphicProductMessage::Create}, + {49, GraphicProductMessage::Create}, + {50, GraphicProductMessage::Create}, + {51, GraphicProductMessage::Create}, + {56, GraphicProductMessage::Create}, + {57, GraphicProductMessage::Create}, + {58, GraphicProductMessage::Create}, + {59, GraphicProductMessage::Create}, + {61, GraphicProductMessage::Create}, + {62, TabularProductMessage::Create}, + {65, GraphicProductMessage::Create}, + {66, GraphicProductMessage::Create}, + {67, GraphicProductMessage::Create}, + {75, TabularProductMessage::Create}, + {77, TabularProductMessage::Create}, + {78, GraphicProductMessage::Create}, + {79, GraphicProductMessage::Create}, + {80, GraphicProductMessage::Create}, + {81, GraphicProductMessage::Create}, + {82, TabularProductMessage::Create}, + {84, GraphicProductMessage::Create}, + {86, GraphicProductMessage::Create}, + {90, GraphicProductMessage::Create}, + {93, GraphicProductMessage::Create}, + {94, GraphicProductMessage::Create}, + {97, GraphicProductMessage::Create}, + {98, GraphicProductMessage::Create}, + {99, GraphicProductMessage::Create}, + {100, GraphicProductMessage::Create}, + {101, GraphicProductMessage::Create}, + {102, GraphicProductMessage::Create}, + {104, GraphicProductMessage::Create}, + {105, GraphicProductMessage::Create}, + {107, GraphicProductMessage::Create}, + {108, GraphicProductMessage::Create}, + {109, GraphicProductMessage::Create}, + {110, GraphicProductMessage::Create}, + {111, GraphicProductMessage::Create}, + {113, GraphicProductMessage::Create}, + {132, GraphicProductMessage::Create}, + {133, GraphicProductMessage::Create}, + {134, GraphicProductMessage::Create}, + {135, GraphicProductMessage::Create}, + {137, GraphicProductMessage::Create}, + {138, GraphicProductMessage::Create}, + {140, GraphicProductMessage::Create}, + {141, GraphicProductMessage::Create}, + {143, GraphicProductMessage::Create}, + {144, GraphicProductMessage::Create}, + {145, GraphicProductMessage::Create}, + {146, GraphicProductMessage::Create}, + {147, GraphicProductMessage::Create}, + {149, GraphicProductMessage::Create}, + {150, GraphicProductMessage::Create}, + {151, GraphicProductMessage::Create}, + {152, GraphicProductMessage::Create}, + {153, GraphicProductMessage::Create}, + {154, GraphicProductMessage::Create}, + {155, GraphicProductMessage::Create}, + {159, GraphicProductMessage::Create}, + {161, GraphicProductMessage::Create}, + {163, GraphicProductMessage::Create}, + {165, GraphicProductMessage::Create}, + {166, GraphicProductMessage::Create}, + {167, GraphicProductMessage::Create}, + {168, GraphicProductMessage::Create}, + {169, GraphicProductMessage::Create}, + {170, GraphicProductMessage::Create}, + {171, GraphicProductMessage::Create}, + {172, GraphicProductMessage::Create}, + {173, GraphicProductMessage::Create}, + {174, GraphicProductMessage::Create}, + {175, GraphicProductMessage::Create}, + {176, GraphicProductMessage::Create}, + {177, GraphicProductMessage::Create}, + {178, GraphicProductMessage::Create}, + {179, GraphicProductMessage::Create}, + {193, GraphicProductMessage::Create}, + {195, GraphicProductMessage::Create}, + {196, GraphicProductMessage::Create}, + {202, GraphicProductMessage::Create}}; + +std::shared_ptr Level3MessageFactory::Create(std::istream& is) +{ + Level3MessageHeader header; + std::shared_ptr message; + + bool headerValid = header.Parse(is); + bool messageValid = headerValid; + + if (headerValid && create_.find(header.message_code()) == create_.end()) + { + BOOST_LOG_TRIVIAL(warning) + << logPrefix_ << "Unknown message type: " << header.message_code(); + messageValid = false; + } + + if (messageValid) + { + int16_t messageCode = header.message_code(); + size_t dataSize = header.length_of_message() - Level3MessageHeader::SIZE; + + BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "Found Message " << messageCode; + + message = create_.at(messageCode)(std::move(header), is); + } + else if (headerValid) + { + // Seek to the end of the current message + is.seekg(header.length_of_message() - Level3MessageHeader::SIZE, + std::ios_base::cur); + } + + return message; +} + +} // namespace rpg +} // namespace wsr88d +} // namespace scwx diff --git a/wxdata/source/scwx/wsr88d/rpg/tabular_product_message.cpp b/wxdata/source/scwx/wsr88d/rpg/tabular_product_message.cpp new file mode 100644 index 00000000..ca59e561 --- /dev/null +++ b/wxdata/source/scwx/wsr88d/rpg/tabular_product_message.cpp @@ -0,0 +1,131 @@ +#include + +#include +#include + +#include + +namespace scwx +{ +namespace wsr88d +{ +namespace rpg +{ + +static const std::string logPrefix_ = + "[scwx::wsr88d::rpg::tabular_product_message] "; + +class TabularProductMessageImpl +{ +public: + explicit TabularProductMessageImpl() : + descriptionBlock_ {0}, tabularBlock_ {0} + { + } + ~TabularProductMessageImpl() = default; + + bool LoadBlocks(std::istream& is); + + std::shared_ptr descriptionBlock_; + std::shared_ptr tabularBlock_; +}; + +TabularProductMessage::TabularProductMessage() : + p(std::make_unique()) +{ +} +TabularProductMessage::~TabularProductMessage() = default; + +TabularProductMessage::TabularProductMessage(TabularProductMessage&&) noexcept = + default; +TabularProductMessage& +TabularProductMessage::operator=(TabularProductMessage&&) noexcept = default; + +std::shared_ptr +TabularProductMessage::description_block() const +{ + return p->descriptionBlock_; +} + +std::shared_ptr +TabularProductMessage::tabular_block() const +{ + return p->tabularBlock_; +} + +bool TabularProductMessage::Parse(std::istream& is) +{ + bool dataValid = true; + + const std::streampos dataStart = is.tellg(); + + p->descriptionBlock_ = std::make_shared(); + dataValid = p->descriptionBlock_->Parse(is); + + if (dataValid) + { + dataValid = p->LoadBlocks(is); + } + + const std::streampos dataEnd = is.tellg(); + if (!ValidateMessage(is, dataEnd - dataStart)) + { + dataValid = false; + } + + return dataValid; +} + +bool TabularProductMessageImpl::LoadBlocks(std::istream& is) +{ + constexpr bool skipTabularHeader = true; + + bool tabularValid = true; + + BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "Loading Blocks"; + + std::streampos offsetBasePos = is.tellg(); + + constexpr size_t offsetBase = + Level3MessageHeader::SIZE + ProductDescriptionBlock::SIZE; + + size_t offsetToTabular = descriptionBlock_->offset_to_symbology() * 2u; + + if (offsetToTabular >= offsetBase) + { + tabularBlock_ = std::make_shared(); + + is.seekg(offsetToTabular - offsetBase, std::ios_base::cur); + tabularValid = tabularBlock_->Parse(is, skipTabularHeader); + is.seekg(offsetBasePos, std::ios_base::beg); + + BOOST_LOG_TRIVIAL(debug) + << logPrefix_ << "Tabular alphanumeric block valid: " << tabularValid; + + if (!tabularValid) + { + tabularBlock_ = nullptr; + } + } + + return tabularValid; +} + +std::shared_ptr +TabularProductMessage::Create(Level3MessageHeader&& header, std::istream& is) +{ + std::shared_ptr message = + std::make_shared(); + message->set_header(std::move(header)); + + if (!message->Parse(is)) + { + message.reset(); + } + + return message; +} + +} // namespace rpg +} // namespace wsr88d +} // namespace scwx diff --git a/wxdata/wxdata.cmake b/wxdata/wxdata.cmake index 87ba8afe..a48604bb 100644 --- a/wxdata/wxdata.cmake +++ b/wxdata/wxdata.cmake @@ -53,7 +53,10 @@ set(HDR_WSR88D_RPG include/scwx/wsr88d/rpg/ccb_header.hpp include/scwx/wsr88d/rpg/digital_radial_data_array_packet.hpp include/scwx/wsr88d/rpg/generic_data_packet.hpp include/scwx/wsr88d/rpg/graphic_alphanumeric_block.hpp + include/scwx/wsr88d/rpg/graphic_product_message.hpp include/scwx/wsr88d/rpg/hda_hail_symbol_packet.hpp + include/scwx/wsr88d/rpg/level3_message.hpp + include/scwx/wsr88d/rpg/level3_message_factory.hpp include/scwx/wsr88d/rpg/level3_message_header.hpp include/scwx/wsr88d/rpg/linked_contour_vector_packet.hpp include/scwx/wsr88d/rpg/linked_vector_packet.hpp @@ -73,6 +76,7 @@ set(HDR_WSR88D_RPG include/scwx/wsr88d/rpg/ccb_header.hpp include/scwx/wsr88d/rpg/sti_circle_symbol_packet.hpp include/scwx/wsr88d/rpg/storm_id_symbol_packet.hpp include/scwx/wsr88d/rpg/tabular_alphanumeric_block.hpp + include/scwx/wsr88d/rpg/tabular_product_message.hpp include/scwx/wsr88d/rpg/text_and_special_symbol_packet.hpp include/scwx/wsr88d/rpg/unlinked_contour_vector_packet.hpp include/scwx/wsr88d/rpg/unlinked_vector_packet.hpp @@ -86,7 +90,10 @@ set(SRC_WSR88D_RPG source/scwx/wsr88d/rpg/ccb_header.cpp source/scwx/wsr88d/rpg/digital_radial_data_array_packet.cpp source/scwx/wsr88d/rpg/generic_data_packet.cpp source/scwx/wsr88d/rpg/graphic_alphanumeric_block.cpp + source/scwx/wsr88d/rpg/graphic_product_message.cpp source/scwx/wsr88d/rpg/hda_hail_symbol_packet.cpp + source/scwx/wsr88d/rpg/level3_message.cpp + source/scwx/wsr88d/rpg/level3_message_factory.cpp source/scwx/wsr88d/rpg/level3_message_header.cpp source/scwx/wsr88d/rpg/linked_contour_vector_packet.cpp source/scwx/wsr88d/rpg/linked_vector_packet.cpp @@ -106,6 +113,7 @@ set(SRC_WSR88D_RPG source/scwx/wsr88d/rpg/ccb_header.cpp source/scwx/wsr88d/rpg/sti_circle_symbol_packet.cpp source/scwx/wsr88d/rpg/storm_id_symbol_packet.cpp source/scwx/wsr88d/rpg/tabular_alphanumeric_block.cpp + source/scwx/wsr88d/rpg/tabular_product_message.cpp source/scwx/wsr88d/rpg/text_and_special_symbol_packet.cpp source/scwx/wsr88d/rpg/unlinked_contour_vector_packet.cpp source/scwx/wsr88d/rpg/unlinked_vector_packet.cpp