Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
main.cpp 17.66 KiB
#include <iostream>
#include <filesystem>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <cmath>

// Function to remove a substring from the end of a string
std::string substractStringFromEnd(const std::string& string, const std::string& string_to_substract) {
    if (string.size() >= string_to_substract.size() &&
        string.compare(string.size() - string_to_substract.size(), string_to_substract.size(), string_to_substract) == 0) {
        // If the string ends with the substring, remove it
        return string.substr(0, string.size() - string_to_substract.size());
    }
    // Otherwise, return the original string unchanged
    return string;
}

void printPtree(const boost::property_tree::ptree &tree, const std::string &prefix = "") {
    for (const auto &node : tree) {
        // Print current node name
        std::cout << prefix << node.first;

        // Print node value only if not empty
        if (!node.second.data().empty()) {
            std::cout << ": " << node.second.data();
        }
        std::cout << std::endl;

        // Recursively print child nodes
        printPtree(node.second, prefix + "  ");
    }
}

int containsSymbol(const std::string& my_string, const std::string& my_symbol) {
    if (my_string.find(my_symbol) != std::string::npos) {
        return 1;
    } else {
        return 0;
    }
}

boost::property_tree::ptree getChildTreeFromParentTree(const boost::property_tree::ptree& parent_tree, const std::string& node_name_to_search) {
    boost::property_tree::ptree child_tree{};

    for (const auto& node : parent_tree) {
        if (node.first == node_name_to_search) {
            child_tree.add_child(node.first, node.second);
        } else {
            boost::property_tree::ptree myPtree{};
            myPtree = getChildTreeFromParentTree(node.second, node_name_to_search);
            for (const auto& child_node : myPtree) {
                child_tree.add_child(child_node.first, child_node.second);
            }
        }
    }
    return child_tree;
}

unsigned int getNumberOfNodes(const boost::property_tree::ptree& ptree, const std::string& node_name_to_search) {
    unsigned int counter{0};

    for (const auto& node : ptree) {
        if (node.first == node_name_to_search) {
            ++counter;
        }
    }

    return counter;
}
boost::property_tree::ptree GetSubtreeByAttributevalue(const boost::property_tree::ptree& ptree, 
                                                        const std::string& node_path, 
                                                        const std::string& attribute_for_which_to_filter, 
                                                        const std::string& attribute_value) {
    boost::property_tree::ptree subtree{};
    boost::property_tree::ptree temp_tree{};
    std::string parent_path{node_path.substr(0, node_path.find_last_of('/'))};
    std::string child_name{node_path.substr(node_path.find_last_of('/')+1)};

    if (!containsSymbol(node_path, "/")) {
        parent_path = "";
        child_name = node_path;
    }

    try
    {
        temp_tree = ptree.get_child(boost::property_tree::ptree::path_type(parent_path, '/'));
    }
    catch(const std::exception& e)
    {
        std::cerr << "Error occured at: " << __FILE__ << ", ";
        std::cerr << "in function: " << __func__ << "(), ";
        std::cerr << "in line: " << __LINE__ << std::endl;
        std::cerr << "Error creating xml tree: " << e.what() << std::endl;
        std::cerr << "Aborting Programm!" << std::endl;
        std::exit(1);
    }

    for (const auto& node : temp_tree) {
        bool bool1{node.first == child_name};
        bool bool2{node.second.get_optional<std::string>("<xmlattr>." + attribute_for_which_to_filter) == attribute_value};

        if (bool1 && bool2) {
            subtree = node.second;
        }
    }

    if (subtree.empty()) {
        std::string myMessage{"Could not find node '" + child_name + "' with attribute '" + attribute_for_which_to_filter + "'=" + attribute_value};
        throw boost::property_tree::ptree_bad_data(myMessage, subtree);
    }

    return subtree;
}

enum State { 
    LoadingConfigFile,
    LoadingAircraftDesignFilePaths,
    LoadingFilePathsIntoPtrees,
    ExtractingConfigTreeInforamtion,
    SubstractingNodeValues,
    WritingOutput,
    ProgrammFinished
};

int main () {
    std::filesystem::path config_file_path{"design_evaluator_conf.xml"};
    std::vector<std::filesystem::path> aircraft_design_file_paths{};

    boost::property_tree::ptree config_tree{}, program_settings_tree{}, subpaths_tree{};
    std::vector<boost::property_tree::ptree> aircraft_design_trees{};
    std::vector<boost::property_tree::ptree> result_tree{};

    State state{State::LoadingConfigFile};

    while (true) {  
        switch (state) {
            case State::LoadingConfigFile: {
                std::cout << "Current State: " << state << std::endl;
                // Check if path is valid
                if (!std::filesystem::exists(config_file_path)) {
                    std::cerr << "Error occured at: " << __FILE__ << ", ";
                    std::cerr << "in function: " << __func__ << "(), ";
                    std::cerr << "in line: " << __LINE__ << std::endl;
                    std::cerr << "File or Path does not exist: " << config_file_path << std::endl;
                    std::cerr << "Aborting Programm!" << std::endl;
                    return 1;
                }

                // If path is valid, store cofig.xml as ptree (Property Tree) object
                try
                {
                    boost::property_tree::read_xml(config_file_path.string(),
                                                config_tree,
                                                boost::property_tree::xml_parser::no_comments |
                                                boost::property_tree::xml_parser::trim_whitespace);
                }
                catch(const std::exception& e)
                {
                    std::cerr << "Error occured at: " << __FILE__ << ", ";
                    std::cerr << "in function: " << __func__ << "(), ";
                    std::cerr << "in line: " << __LINE__ << std::endl;
                    std::cerr << "Error creating xml tree: " << e.what() << std::endl;
                    std::cerr << "Aborting Programm!" << std::endl;
                    return 1;
                }
                state = State::LoadingAircraftDesignFilePaths;
                break;
            }


            case State::LoadingAircraftDesignFilePaths: {
                std::cout << "Current State: " << state << std::endl;

                program_settings_tree = getChildTreeFromParentTree(config_tree, "aircraft_design");

                for (const auto& node : program_settings_tree) {
                    // if (node.second.get<int>("<xmlattr>.ID") == 0) {
                    //     aircraft_design_file_paths.insert(aircraft_design_file_paths.begin(), node.second.get<std::string>("design_directory") + node.second.get<std::string>("file_name"));
                    // } else {
                    //     aircraft_design_file_paths.push_back(node.second.get<std::string>("design_directory") + node.second.get<std::string>("file_name"));
                    // }
                    aircraft_design_file_paths.push_back(node.second.get<std::string>("design_directory") + node.second.get<std::string>("file_name"));
                }

                for (const auto& element : aircraft_design_file_paths) {
                    if (!std::filesystem::exists(element)) {
                        std::cerr << "Error occured at: " << __FILE__ << ", ";
                        std::cerr << "in function: " << __func__ << "(), ";
                        std::cerr << "in line: " << __LINE__ << std::endl;
                        std::cerr << "File or Path does not exist: " << element << std::endl;
                    }
                }
                for (const auto& element : aircraft_design_file_paths) {
                    if (!std::filesystem::exists(element)) {
                        std::cerr << "Aborting Programm!" << std::endl;
                        return 1;
                    }
                }

                state = State::LoadingFilePathsIntoPtrees;
                break;
            }
                
            case State::LoadingFilePathsIntoPtrees: {
                std::cout << "Current State: " << state << std::endl;

                try
                {
                    for (const auto& element : aircraft_design_file_paths) {
                        boost::property_tree::ptree temp_tree{};

                        boost::property_tree::read_xml(element.string(), 
                                                        temp_tree, 
                                                        boost::property_tree::xml_parser::no_comments | 
                                                        boost::property_tree::xml_parser::trim_whitespace);
                        aircraft_design_trees.push_back(temp_tree);
                    }
                }
                catch(const std::exception& e)
                {
                    std::cerr << "Error occured at: " << __FILE__ << ", ";
                    std::cerr << "in function: " << __func__ << "(), ";
                    std::cerr << "in line: " << __LINE__ << std::endl;
                    std::cerr << "Error creating xml tree: " << e.what() << std::endl;
                    std::cerr << "Aborting Programm!" << std::endl;
                    return 1;
                }
                state = State::ExtractingConfigTreeInforamtion;
                break;
            }
            
            case State::ExtractingConfigTreeInforamtion: {
                std::cout << "Current State: " << state << std::endl;

                subpaths_tree = getChildTreeFromParentTree(config_tree, "sub_path");
                state = State::SubstractingNodeValues;
                break;
            }
            
            case State::SubstractingNodeValues: {
                std::cout << "Current State: " << state << std::endl;
                
                try
                {
                    for (const auto& tree : std::ranges::subrange(aircraft_design_trees.begin() + 1, aircraft_design_trees.end())) {
                        boost::property_tree::ptree temp_tree{};
                        for (const auto& node : subpaths_tree) {
                            // double upstream_value{std::nan("")};
                            double upstream_value{NAN};
                            double local_value{NAN};
                            std::string unit{"?"};

                            std::string node_path = node.second.get_value<std::string>();
                            if (containsSymbol(node_path, "@")) {
                                std::string pre{node_path.substr(0, node_path.find_first_of('@'))};
                                std::string temp_string{node_path.substr(node_path.find_first_of('@'))};
                                std::string attribute_value{temp_string.substr(temp_string.find_first_of('@')+1, temp_string.find_first_of('/')-1)};
                                std::string post{temp_string.substr(temp_string.find_first_of('/')+1)};
                                // TODO(Oli): try to fit this into own function, with multiple '@' seperations

                                boost::property_tree::ptree temp_upstream_tree{GetSubtreeByAttributevalue(aircraft_design_trees.at(0), pre, "ID", attribute_value)};
                                boost::property_tree::ptree temp_local_tree{GetSubtreeByAttributevalue(tree, pre, "ID", attribute_value)};
                                boost::property_tree::ptree temp_unit_tree{GetSubtreeByAttributevalue(tree, pre, "ID", attribute_value)};

                                upstream_value = temp_upstream_tree.get<double>(boost::property_tree::ptree::path_type(post, '/'));
                                local_value = temp_local_tree.get<double>(boost::property_tree::ptree::path_type(post, '/'));
                                unit = temp_unit_tree.get<std::string>(boost::property_tree::ptree::path_type(substractStringFromEnd(post, "value") + "unit", '/'));

                            } else {
                                upstream_value = aircraft_design_trees.at(0).get<double>(boost::property_tree::ptree::path_type(node_path, '/'));
                                local_value = tree.get<double>(boost::property_tree::ptree::path_type(node_path, '/'));
                                unit = tree.get<std::string>(boost::property_tree::ptree::path_type(substractStringFromEnd(node_path, "value") + "unit", '/'));
                            }
                            temp_tree.put(node.second.get<std::string>("<xmlattr>.Name"), local_value - upstream_value);
                            temp_tree.put(node.second.get<std::string>("<xmlattr>.Name") + ".<xmlattr>.unit", unit);
                        }
                        result_tree.push_back(temp_tree);
                    }                      
                }
                catch(const std::exception& e)
                {
                    std::cerr << "Error occured at: " << __FILE__ << ", ";
                    std::cerr << "in function: " << __func__ << "(), ";
                    std::cerr << "in line: " << __LINE__ << std::endl;
                    std::cerr << "Error substracting node values: " << e.what() << std::endl;
                    std::cerr << "Aborting Programm!" << std::endl;
                    return 1;
                }
                state = State::WritingOutput;
                break;
            }

            case State::WritingOutput: {
                std::cout << "Current State: " << state << std::endl;

                // Write Output
                // Create header
                boost::property_tree::ptree output_tree{};
                output_tree.put("html.<xmlattr>.lang", "en");
                output_tree.put("html.head.meta.<xmlattr>.content", "width=device-width, initial-scale=1.0");
                output_tree.put("html.head.meta.<xmlattr>.name", "viewport");
                output_tree.put("html.head.meta.<xmlattr>.charset", "utf-8");
                output_tree.put("html.head.title", "<probably same as report name>");
                output_tree.put("html.head.link.<xmlattr>.href", "style.css");
                output_tree.put("html.head.link.<xmlattr>.rel", "stylesheet");

                // Create body
                boost::property_tree::ptree temp_tree{};
                temp_tree.put("<xmlattr>.class", "content");
                temp_tree.put("h1", "Design evaluation report");
                temp_tree.put("div.<xmlattr>.class", "container");
                // Create data tables
                unsigned int aircraft_counter{0};
                for (const auto& tree : result_tree) {
                    ++aircraft_counter;
                    boost::property_tree::ptree data_tree{};
                    data_tree.put("<xmlattr>.class", "box data");
                        boost::property_tree::ptree my_tree{GetSubtreeByAttributevalue(program_settings_tree, "aircraft_design", "ID", std::to_string(aircraft_counter))};
                        std::string myString{my_tree.get<std::string>("name")};
                    data_tree.put("h2", myString);
                    data_tree.put("table.<xmlattr>.class", "content-table");
                    data_tree.put("table.caption", "Residual Values");
                    data_tree.put("table.thead", "");
                    data_tree.put("table.tbody", "");
                        boost::property_tree::ptree thead{};
                        thead.add_child("th", boost::property_tree::ptree{"paramter"});
                        // thead.add_child("th", boost::property_tree::ptree{"symbol"});
                        thead.add_child("th", boost::property_tree::ptree{"value"});
                        thead.add_child("th", boost::property_tree::ptree{"unit"});
                    data_tree.add_child("table.thead.tr", thead);
                    for (const auto& node : tree) {
                        boost::property_tree::ptree tbody{};
                        tbody.add_child("td", boost::property_tree::ptree{node.first});
                        tbody.add_child("td", boost::property_tree::ptree{node.second.data()});
                        tbody.add_child("td", boost::property_tree::ptree{node.second.get<std::string>("<xmlattr>.unit")});
                        data_tree.add_child("table.tbody.tr", tbody);
                    }
                    temp_tree.add_child("div.div", data_tree);
                }
                output_tree.add_child("html.body.div", temp_tree);

                // Write .html file
                boost::property_tree::xml_writer_settings<std::string> settings(' ', 4); // 4 spaces for indentation
                boost::property_tree::write_xml("ReportXYZ.html", 
                                                output_tree, 
                                                std::locale(), 
                                                settings);
                
                state = State::ProgrammFinished;
                break;
            }
            
            case State::ProgrammFinished: {
                std::cout << "Programm terminated successfully." << std::endl;
                return 0;
                break;
            }
            
            default: {
                // TODO(Oli): check why when nodes have the same name only one is printed in the result_tree

                // Questions:
                // where are filepath of output.html and config.xml specified?

                break;
            }
        }
    }
}

/*
    for (auto element : result_tree) {
        std::cout << "TREE: " << std::endl;
        printPtree(element);
        std::cout << std::endl;
    }
*/