Refactoring to unique product message types, in order to support more products

This commit is contained in:
Dan Paulat 2022-01-13 02:05:56 -06:00
parent 235c060b17
commit dc545f19e9
11 changed files with 742 additions and 177 deletions

View file

@ -1,20 +1,12 @@
#include <scwx/wsr88d/level3_file.hpp>
#include <scwx/wsr88d/rpg/ccb_header.hpp>
#include <scwx/wsr88d/rpg/graphic_alphanumeric_block.hpp>
#include <scwx/wsr88d/rpg/level3_message_header.hpp>
#include <scwx/wsr88d/rpg/product_description_block.hpp>
#include <scwx/wsr88d/rpg/product_symbology_block.hpp>
#include <scwx/wsr88d/rpg/tabular_alphanumeric_block.hpp>
#include <scwx/wsr88d/rpg/level3_message_factory.hpp>
#include <scwx/wsr88d/rpg/wmo_header.hpp>
#include <scwx/util/rangebuf.hpp>
#include <scwx/util/time.hpp>
#include <fstream>
#include <set>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/filter/bzip2.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <boost/log/trivial.hpp>
@ -25,33 +17,20 @@ namespace wsr88d
static const std::string logPrefix_ = "[scwx::wsr88d::level3_file] ";
static const std::set<int16_t> 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<rpg::WmoHeader> wmoHeader_;
std::shared_ptr<rpg::CcbHeader> ccbHeader_;
std::shared_ptr<rpg::WmoHeader> innerHeader_;
std::shared_ptr<rpg::Level3MessageHeader> messageHeader_;
std::shared_ptr<rpg::ProductDescriptionBlock> descriptionBlock_;
std::shared_ptr<rpg::ProductSymbologyBlock> symbologyBlock_;
std::shared_ptr<rpg::GraphicAlphanumericBlock> graphicBlock_;
std::shared_ptr<rpg::TabularAlphanumericBlock> tabularBlock_;
std::shared_ptr<rpg::WmoHeader> wmoHeader_;
std::shared_ptr<rpg::CcbHeader> ccbHeader_;
std::shared_ptr<rpg::WmoHeader> innerHeader_;
std::shared_ptr<rpg::Level3Message> message_;
};
Level3File::Level3File() : p(std::make_unique<Level3FileImpl>()) {}
@ -60,15 +39,9 @@ Level3File::~Level3File() = default;
Level3File::Level3File(Level3File&&) noexcept = default;
Level3File& Level3File::operator=(Level3File&&) noexcept = default;
std::shared_ptr<rpg::Level3MessageHeader> Level3File::message_header() const
std::shared_ptr<rpg::Level3Message> Level3File::message() const
{
return p->messageHeader_;
}
std::shared_ptr<rpg::ProductSymbologyBlock>
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<rpg::Level3MessageHeader>();
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<rpg::ProductDescriptionBlock>();
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<boost::iostreams::input> 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<rpg::ProductSymbologyBlock>();
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<rpg::GraphicAlphanumericBlock>();
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<rpg::TabularAlphanumericBlock>();
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

View file

@ -0,0 +1,223 @@
#include <scwx/wsr88d/rpg/graphic_product_message.hpp>
#include <scwx/util/rangebuf.hpp>
#include <istream>
#include <string>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/filter/bzip2.hpp>
#include <boost/log/trivial.hpp>
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<ProductDescriptionBlock> descriptionBlock_;
std::shared_ptr<ProductSymbologyBlock> symbologyBlock_;
std::shared_ptr<GraphicAlphanumericBlock> graphicBlock_;
std::shared_ptr<TabularAlphanumericBlock> tabularBlock_;
};
GraphicProductMessage::GraphicProductMessage() :
p(std::make_unique<GraphicProductMessageImpl>())
{
}
GraphicProductMessage::~GraphicProductMessage() = default;
GraphicProductMessage::GraphicProductMessage(GraphicProductMessage&&) noexcept =
default;
GraphicProductMessage&
GraphicProductMessage::operator=(GraphicProductMessage&&) noexcept = default;
std::shared_ptr<ProductDescriptionBlock>
GraphicProductMessage::description_block() const
{
return p->descriptionBlock_;
}
std::shared_ptr<ProductSymbologyBlock>
GraphicProductMessage::symbology_block() const
{
return p->symbologyBlock_;
}
std::shared_ptr<GraphicAlphanumericBlock>
GraphicProductMessage::graphic_block() const
{
return p->graphicBlock_;
}
std::shared_ptr<TabularAlphanumericBlock>
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<ProductDescriptionBlock>();
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<boost::iostreams::input> 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<ProductSymbologyBlock>();
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<GraphicAlphanumericBlock>();
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<TabularAlphanumericBlock>();
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>
GraphicProductMessage::Create(Level3MessageHeader&& header, std::istream& is)
{
std::shared_ptr<GraphicProductMessage> message =
std::make_shared<GraphicProductMessage>();
message->set_header(std::move(header));
if (!message->Parse(is))
{
message.reset();
}
return message;
}
} // namespace rpg
} // namespace wsr88d
} // namespace scwx

View file

@ -0,0 +1,49 @@
#include <scwx/wsr88d/rpg/level3_message.hpp>
#include <boost/log/trivial.hpp>
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<Level3MessageImpl>())
{
}
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

View file

@ -0,0 +1,154 @@
#include <scwx/wsr88d/rpg/level3_message_factory.hpp>
#include <scwx/util/vectorbuf.hpp>
#include <scwx/wsr88d/rpg/graphic_product_message.hpp>
#include <scwx/wsr88d/rpg/tabular_product_message.hpp>
#include <unordered_map>
#include <vector>
#include <boost/log/trivial.hpp>
namespace scwx
{
namespace wsr88d
{
namespace rpg
{
static const std::string logPrefix_ =
"[scwx::wsr88d::rpg::level3_message_factory] ";
typedef std::function<std::shared_ptr<Level3Message>(Level3MessageHeader&&,
std::istream&)>
CreateLevel3MessageFunction;
static const std::unordered_map<int16_t, CreateLevel3MessageFunction> //
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<Level3Message> Level3MessageFactory::Create(std::istream& is)
{
Level3MessageHeader header;
std::shared_ptr<Level3Message> 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

View file

@ -0,0 +1,131 @@
#include <scwx/wsr88d/rpg/tabular_product_message.hpp>
#include <istream>
#include <string>
#include <boost/log/trivial.hpp>
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<ProductDescriptionBlock> descriptionBlock_;
std::shared_ptr<TabularAlphanumericBlock> tabularBlock_;
};
TabularProductMessage::TabularProductMessage() :
p(std::make_unique<TabularProductMessageImpl>())
{
}
TabularProductMessage::~TabularProductMessage() = default;
TabularProductMessage::TabularProductMessage(TabularProductMessage&&) noexcept =
default;
TabularProductMessage&
TabularProductMessage::operator=(TabularProductMessage&&) noexcept = default;
std::shared_ptr<ProductDescriptionBlock>
TabularProductMessage::description_block() const
{
return p->descriptionBlock_;
}
std::shared_ptr<TabularAlphanumericBlock>
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<ProductDescriptionBlock>();
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<TabularAlphanumericBlock>();
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>
TabularProductMessage::Create(Level3MessageHeader&& header, std::istream& is)
{
std::shared_ptr<TabularProductMessage> message =
std::make_shared<TabularProductMessage>();
message->set_header(std::move(header));
if (!message->Parse(is))
{
message.reset();
}
return message;
}
} // namespace rpg
} // namespace wsr88d
} // namespace scwx