mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Machine readable progress with -q and --log-format json
This commit is contained in:
+102
-61
@@ -113,7 +113,7 @@ bool rename_file(const std::string& old_filename, const std::string& new_filenam
|
||||
}
|
||||
|
||||
static std::stringstream log_stream;
|
||||
void write_log();
|
||||
void write_log(bool);
|
||||
|
||||
/// @todo make the filters non-global
|
||||
IfcGeom::entity_filter entity_filter; // Entity filter is used always by default.
|
||||
@@ -152,13 +152,16 @@ bool init_input_file(const std::string& filename, IfcParse::IfcFile& ifc_file, b
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::string log_format;
|
||||
po::options_description generic_options("Command line options");
|
||||
generic_options.add_options()
|
||||
("help,h", "display usage information")
|
||||
("version", "display version information")
|
||||
("verbose,v", "more verbose output")
|
||||
("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)")
|
||||
("no-progress", "Suppress possible progress bar type of prints that use carriage return.");
|
||||
("verbose,v", "more verbose log messages")
|
||||
("quiet,q", "less status and progress output")
|
||||
("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)")
|
||||
("no-progress", "suppress possible progress bar type of prints that use carriage return")
|
||||
("log-format", po::value<std::string>(&log_format), "log format: plain or json");
|
||||
|
||||
po::options_description fileio_options;
|
||||
fileio_options.add_options()
|
||||
@@ -174,7 +177,7 @@ int main(int argc, char** argv)
|
||||
inclusion_traverse_filter include_traverse_filter;
|
||||
exclusion_filter exclude_filter;
|
||||
exclusion_traverse_filter exclude_traverse_filter;
|
||||
std::string filter_filename;
|
||||
std::string filter_filename;
|
||||
|
||||
po::options_description geom_options("Geometry options");
|
||||
geom_options.add_options()
|
||||
@@ -336,7 +339,36 @@ int main(int argc, char** argv)
|
||||
|
||||
po::notify(vmap);
|
||||
|
||||
print_version();
|
||||
const bool mmap = vmap.count("mmap") != 0;
|
||||
const bool verbose = vmap.count("verbose") != 0;
|
||||
const bool no_progress = vmap.count("no-progress") != 0;
|
||||
const bool quiet = vmap.count("quiet") != 0;
|
||||
const bool weld_vertices = vmap.count("weld-vertices") != 0;
|
||||
const bool use_world_coords = vmap.count("use-world-coords") != 0;
|
||||
const bool convert_back_units = vmap.count("convert-back-units") != 0;
|
||||
const bool sew_shells = vmap.count("sew-shells") != 0;
|
||||
#if OCC_VERSION_HEX < 0x60900
|
||||
const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0;
|
||||
#endif
|
||||
const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0;
|
||||
const bool include_plan = vmap.count("plan") != 0;
|
||||
const bool include_model = vmap.count("model") != 0 || (!include_plan);
|
||||
const bool enable_layerset_slicing = vmap.count("enable-layerset-slicing") != 0;
|
||||
const bool use_element_names = vmap.count("use-element-names") != 0;
|
||||
const bool use_element_guids = vmap.count("use-element-guids") != 0;
|
||||
const bool use_material_names = vmap.count("use-material-names") != 0;
|
||||
const bool use_element_types = vmap.count("use-element-types") != 0;
|
||||
const bool use_element_hierarchy = vmap.count("use-element-hierarchy") != 0;
|
||||
const bool no_normals = vmap.count("no-normals") != 0;
|
||||
const bool center_model = vmap.count("center-model") != 0;
|
||||
const bool model_offset = vmap.count("model-offset") != 0;
|
||||
const bool site_local_placement = vmap.count("site-local-placement") != 0;
|
||||
const bool building_local_placement = vmap.count("building-local-placement") != 0;
|
||||
const bool generate_uvs = vmap.count("generate-uvs") != 0;
|
||||
|
||||
if (!quiet || vmap.count("version")) {
|
||||
print_version();
|
||||
}
|
||||
|
||||
if (vmap.count("version")) {
|
||||
return EXIT_SUCCESS;
|
||||
@@ -349,31 +381,6 @@ int main(int argc, char** argv)
|
||||
print_usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
const bool mmap = vmap.count("mmap") != 0;
|
||||
const bool verbose = vmap.count("verbose") != 0;
|
||||
const bool no_progress = vmap.count("no-progress") != 0;
|
||||
const bool weld_vertices = vmap.count("weld-vertices") != 0;
|
||||
const bool use_world_coords = vmap.count("use-world-coords") != 0;
|
||||
const bool convert_back_units = vmap.count("convert-back-units") != 0;
|
||||
const bool sew_shells = vmap.count("sew-shells") != 0;
|
||||
#if OCC_VERSION_HEX < 0x60900
|
||||
const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0;
|
||||
#endif
|
||||
const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0;
|
||||
const bool include_plan = vmap.count("plan") != 0;
|
||||
const bool include_model = vmap.count("model") != 0 || (!include_plan);
|
||||
const bool enable_layerset_slicing = vmap.count("enable-layerset-slicing") != 0;
|
||||
const bool use_element_names = vmap.count("use-element-names") != 0;
|
||||
const bool use_element_guids = vmap.count("use-element-guids") != 0 ;
|
||||
const bool use_material_names = vmap.count("use-material-names") != 0;
|
||||
const bool use_element_types = vmap.count("use-element-types") != 0;
|
||||
const bool use_element_hierarchy = vmap.count("use-element-hierarchy") != 0;
|
||||
const bool no_normals = vmap.count("no-normals") != 0 ;
|
||||
const bool center_model = vmap.count("center-model") != 0 ;
|
||||
const bool model_offset = vmap.count("model-offset") != 0 ;
|
||||
const bool site_local_placement = vmap.count("site-local-placement") != 0 ;
|
||||
const bool building_local_placement = vmap.count("building-local-placement") != 0 ;
|
||||
const bool generate_uvs = vmap.count("generate-uvs") != 0 ;
|
||||
|
||||
#ifdef HAVE_ICU
|
||||
if (!unicode_mode.empty()) {
|
||||
@@ -438,12 +445,25 @@ int main(int argc, char** argv)
|
||||
Logger::SetOutput(&std::cout, &log_stream);
|
||||
Logger::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR);
|
||||
|
||||
if (vmap.count("log-format") == 1) {
|
||||
boost::to_lower(log_format);
|
||||
if (log_format == "plain") {
|
||||
Logger::OutputFormat(Logger::FMT_PLAIN);
|
||||
} else if (log_format == "json") {
|
||||
Logger::OutputFormat(Logger::FMT_JSON);
|
||||
} else {
|
||||
std::cerr << "[Error] --log-format should be either plain or json" << std::endl;
|
||||
print_usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
IfcParse::IfcFile ifc_file;
|
||||
|
||||
if (output_extension == ".xml") {
|
||||
int exit_code = EXIT_FAILURE;
|
||||
try {
|
||||
if (init_input_file(input_filename, ifc_file, no_progress, mmap)) {
|
||||
if (init_input_file(input_filename, ifc_file, no_progress || quiet, mmap)) {
|
||||
XmlSerializer s(output_temp_filename);
|
||||
s.setFile(&ifc_file);
|
||||
Logger::Status("Writing XML output...");
|
||||
@@ -455,7 +475,7 @@ int main(int argc, char** argv)
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
}
|
||||
write_log();
|
||||
write_log(!quiet);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
@@ -548,7 +568,7 @@ int main(int argc, char** argv)
|
||||
}
|
||||
} else {
|
||||
std::cerr << "[Error] Unknown output filename extension '" + output_extension + "'\n";
|
||||
write_log();
|
||||
write_log(!quiet);
|
||||
print_usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@@ -557,7 +577,7 @@ int main(int argc, char** argv)
|
||||
|
||||
if (use_element_hierarchy && output_extension != ".dae") {
|
||||
std::cerr << "[Error] --use-element-hierarchy can be used only with .dae output.\n";
|
||||
write_log();
|
||||
write_log(!quiet);
|
||||
print_usage();
|
||||
delete serializer;
|
||||
std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */
|
||||
@@ -582,14 +602,14 @@ int main(int argc, char** argv)
|
||||
if (!serializer->ready()) {
|
||||
delete serializer;
|
||||
std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */
|
||||
write_log();
|
||||
write_log(!quiet);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
time_t start,end;
|
||||
time(&start);
|
||||
|
||||
if (!init_input_file(input_filename, ifc_file, no_progress, mmap)) {
|
||||
if (!init_input_file(input_filename, ifc_file, no_progress || quiet, mmap)) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -600,7 +620,7 @@ int main(int argc, char** argv)
|
||||
Logger::Error("No geometrical entities found");
|
||||
delete serializer;
|
||||
std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */
|
||||
write_log();
|
||||
write_log(!quiet);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -614,7 +634,7 @@ int main(int argc, char** argv)
|
||||
|
||||
serializer->writeHeader();
|
||||
|
||||
int old_progress = -1;
|
||||
int old_progress = quiet ? 0 : -1;
|
||||
|
||||
if (center_model || model_offset) {
|
||||
double* offset = serializer->settings().offset;
|
||||
@@ -643,7 +663,9 @@ int main(int argc, char** argv)
|
||||
Logger::Notice(msg.str());
|
||||
}
|
||||
|
||||
Logger::Status("Creating geometry...");
|
||||
if (!quiet) {
|
||||
Logger::Status("Creating geometry...");
|
||||
}
|
||||
|
||||
// The functions IfcGeom::Iterator::get() and IfcGeom::Iterator::next()
|
||||
// wrap an iterator of all geometrical products in the Ifc file.
|
||||
@@ -670,14 +692,28 @@ int main(int argc, char** argv)
|
||||
}
|
||||
|
||||
if (!no_progress) {
|
||||
const int progress = context_iterator.progress() / 2;
|
||||
if (old_progress != progress) Logger::ProgressBar(progress);
|
||||
old_progress = progress;
|
||||
if (quiet) {
|
||||
const int progress = context_iterator.progress();
|
||||
for (; old_progress < progress; ++old_progress) {
|
||||
std::cout << ".";
|
||||
}
|
||||
std::cout << std::flush;
|
||||
} else {
|
||||
const int progress = context_iterator.progress() / 2;
|
||||
if (old_progress != progress) Logger::ProgressBar(progress);
|
||||
old_progress = progress;
|
||||
}
|
||||
}
|
||||
} while (++num_created, context_iterator.next());
|
||||
|
||||
Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(num_created) +
|
||||
" objects) ");
|
||||
if (!no_progress && quiet) {
|
||||
for (; old_progress < 100; ++old_progress) {
|
||||
std::cout << ".";
|
||||
}
|
||||
} else {
|
||||
Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(num_created) +
|
||||
" objects) ");
|
||||
}
|
||||
|
||||
serializer->finalize();
|
||||
delete serializer;
|
||||
@@ -690,34 +726,39 @@ int main(int argc, char** argv)
|
||||
output_temp_filename + "' for the conversion result.");
|
||||
}
|
||||
|
||||
write_log();
|
||||
write_log(!quiet);
|
||||
|
||||
time(&end);
|
||||
|
||||
int seconds = (int)difftime(end, start);
|
||||
std::stringstream msg;
|
||||
int minutes = seconds / 60;
|
||||
seconds = seconds % 60;
|
||||
msg << "\nConversion took";
|
||||
if (minutes > 0) {
|
||||
msg << " " << minutes << " minute";
|
||||
if (minutes > 1) {
|
||||
if (!quiet) {
|
||||
int seconds = (int)difftime(end, start);
|
||||
std::stringstream msg;
|
||||
int minutes = seconds / 60;
|
||||
seconds = seconds % 60;
|
||||
msg << "\nConversion took";
|
||||
if (minutes > 0) {
|
||||
msg << " " << minutes << " minute";
|
||||
if (minutes > 1) {
|
||||
msg << "s";
|
||||
}
|
||||
}
|
||||
msg << " " << seconds << " second";
|
||||
if (seconds > 1) {
|
||||
msg << "s";
|
||||
}
|
||||
Logger::Status(msg.str());
|
||||
}
|
||||
msg << " " << seconds << " second";
|
||||
if (seconds > 1) {
|
||||
msg << "s";
|
||||
}
|
||||
Logger::Status(msg.str());
|
||||
|
||||
return successful ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
void write_log() {
|
||||
void write_log(bool header) {
|
||||
std::string log = log_stream.str();
|
||||
if (!log.empty()) {
|
||||
std::cout << "\nLog:\n" << log << std::endl;
|
||||
if (header) {
|
||||
std::cout << "\nLog:\n";
|
||||
}
|
||||
std::cout << log << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,13 +23,47 @@
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/version.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
using boost::property_tree::ptree;
|
||||
|
||||
namespace {
|
||||
static const char* severity_strings[] = {"Notice", "Warning", "Error"};
|
||||
|
||||
void plain_text_message(std::ostream& os, const boost::optional<IfcSchema::IfcProduct*>& current_product, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) {
|
||||
os << "[" << severity_strings[type] << "] ";
|
||||
if (current_product) {
|
||||
os << "{" << (*current_product)->GlobalId() << "} ";
|
||||
}
|
||||
os << message << std::endl;
|
||||
if (entity) {
|
||||
os << entity->toString() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void json_message(std::ostream& os, const boost::optional<IfcSchema::IfcProduct*>& current_product, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) {
|
||||
ptree pt;
|
||||
pt.put("level", severity_strings[type]);
|
||||
if (current_product) {
|
||||
pt.put("product", (**current_product).entity->toString());
|
||||
}
|
||||
pt.put("message", message);
|
||||
if (entity) {
|
||||
pt.put("instance", entity);
|
||||
}
|
||||
boost::property_tree::write_json(os, pt, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::SetProduct(boost::optional<IfcSchema::IfcProduct*> product) {
|
||||
current_product = product;
|
||||
}
|
||||
|
||||
void Logger::SetOutput(std::ostream* l1, std::ostream* l2) {
|
||||
log1 = l1;
|
||||
log2 = l2;
|
||||
@@ -39,13 +73,12 @@ void Logger::SetOutput(std::ostream* l1, std::ostream* l2) {
|
||||
}
|
||||
|
||||
void Logger::Message(Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) {
|
||||
if ( log2 && type >= verbosity ) {
|
||||
(*log2) << "[" << severity_strings[type] << "] ";
|
||||
if ( current_product ) {
|
||||
(*log2) << "{" << (*current_product)->GlobalId() << "} ";
|
||||
if (log2 && type >= verbosity) {
|
||||
if (format == FMT_PLAIN) {
|
||||
plain_text_message(*log2, current_product, type, message, entity);
|
||||
} else if (format == FMT_JSON) {
|
||||
json_message(*log2, current_product, type, message, entity);
|
||||
}
|
||||
(*log2) << message << std::endl;
|
||||
if ( entity ) (*log2) << entity->toString() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,26 +87,32 @@ void Logger::Message(Logger::Severity type, const std::exception& exception, Ifc
|
||||
}
|
||||
|
||||
void Logger::Status(const std::string& message, bool new_line) {
|
||||
if ( log1 ) {
|
||||
if (log1) {
|
||||
(*log1) << message;
|
||||
if ( new_line ) (*log1) << std::endl;
|
||||
else (*log1) << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::ProgressBar(int progress) {
|
||||
if ( log1 ) {
|
||||
if (log1) {
|
||||
Status("\r[" + std::string(progress,'#') + std::string(50 - progress,' ') + "]", false);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Logger::GetLog() {
|
||||
return log_stream.str();
|
||||
}
|
||||
|
||||
void Logger::Verbosity(Logger::Severity v) { verbosity = v; }
|
||||
Logger::Severity Logger::Verbosity() { return verbosity; }
|
||||
|
||||
void Logger::OutputFormat(Format f) { format = f; }
|
||||
Logger::Format Logger::OutputFormat() { return format; }
|
||||
|
||||
std::ostream* Logger::log1 = 0;
|
||||
std::ostream* Logger::log2 = 0;
|
||||
std::stringstream Logger::log_stream;
|
||||
Logger::Severity Logger::verbosity = Logger::LOG_NOTICE;
|
||||
const char* Logger::severity_strings[] = { "Notice","Warning","Error" };
|
||||
Logger::Format Logger::format = Logger::FMT_PLAIN;
|
||||
boost::optional<IfcSchema::IfcProduct*> Logger::current_product;
|
||||
@@ -40,20 +40,26 @@
|
||||
class IFC_PARSE_API Logger {
|
||||
public:
|
||||
typedef enum { LOG_NOTICE, LOG_WARNING, LOG_ERROR } Severity;
|
||||
typedef enum { FMT_PLAIN, FMT_JSON } Format;
|
||||
private:
|
||||
static std::ostream* log1;
|
||||
static std::ostream* log2;
|
||||
static std::stringstream log_stream;
|
||||
static Severity verbosity;
|
||||
static const char* severity_strings[];
|
||||
static Format format;
|
||||
static boost::optional<IfcSchema::IfcProduct*> current_product;
|
||||
public:
|
||||
static void SetProduct(boost::optional<IfcSchema::IfcProduct*> product);
|
||||
/// Determines to what stream respectively progress and errors are logged
|
||||
static void SetOutput(std::ostream* l1, std::ostream* l2);
|
||||
|
||||
/// Determines the types of log messages to get logged
|
||||
static void Verbosity(Severity v);
|
||||
static Severity Verbosity();
|
||||
|
||||
/// Determines output format: plain text or sequence of JSON objects
|
||||
static void OutputFormat(Format f);
|
||||
static Format OutputFormat();
|
||||
|
||||
/// Log a message to the output stream
|
||||
static void Message(Severity type, const std::string& message, IfcEntityInstanceData* entity=0);
|
||||
|
||||
Reference in New Issue
Block a user