Skip to content

Commit

Permalink
Add command line option to parse json's
Browse files Browse the repository at this point in the history
NP-205
  • Loading branch information
casperlamboo committed May 23, 2024
1 parent 0b16185 commit f1ad784
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 0 deletions.
15 changes: 15 additions & 0 deletions include/communication/CommandLine.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#define COMMANDLINE_H

#include <filesystem>
#include <optional>
#include <rapidjson/document.h> //Loading JSON documents to get settings from them.
#include <string> //To store the command line arguments.
#include <vector> //To store the command line arguments.
Expand Down Expand Up @@ -212,6 +213,20 @@ class CommandLine : public Communication
* \return The first definition file that matches the definition ID.
*/
static std::string findDefinitionFile(const std::string& definition_id, const std::vector<std::filesystem::path>& search_directories);

/*
* \brief Read the resolved JSON values from a file.
* \param element The path to the file to read the JSON values from.
* \return The resolved JSON values.
*/
static std::optional<std::unordered_map<std::string, std::unordered_map<std::string, std::string>>> readResolvedJsonValues(const std::filesystem::path& element);

/*
* \brief Read the resolved JSON values from a document.
* \param document The document to read the JSON values from.
* \return The resolved JSON values.
*/
static std::optional<std::unordered_map<std::string, std::unordered_map<std::string, std::string>>> readResolvedJsonValues(const rapidjson::Document& document);
};

} // namespace cura
Expand Down
1 change: 1 addition & 0 deletions src/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ void Application::printHelp() const
fmt::print(" -p\n\tLog progress information.\n");
fmt::print(" -d Add definition search paths seperated by a `:` (Unix) or `;` (Windows)\n");
fmt::print(" -j\n\tLoad settings.def.json file to register all settings and their defaults.\n");
fmt::print(" -r\n\tLoad a json file containing resolved setting values.\n");
fmt::print(" -s <setting>=<value>\n\tSet a setting to a value for the last supplied object, \n\textruder train, or general settings.\n");
fmt::print(" -l <model_file>\n\tLoad an STL model. \n");
fmt::print(" -g\n\tSwitch setting focus to the current mesh group only.\n\tUsed for one-at-a-time printing.\n");
Expand Down
5 changes: 5 additions & 0 deletions src/MeshGroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,11 @@ bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const Mat
spdlog::info("loading '{}' took {:03.3f} seconds", filename, load_timer.restart());
return true;
}
else
{
spdlog::warn("loading '{}' failed", filename);
return false;
}
}
spdlog::warn("Unable to recognize the extension of the file. Currently only .stl and .STL are supported.");
return false;
Expand Down
155 changes: 155 additions & 0 deletions src/communication/CommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
#include <filesystem>
#include <fstream> //To check if files exist.
#include <numeric> //For std::accumulate.
#include <optional>
#include <rapidjson/error/en.h> //Loading JSON documents to get settings from them.
#include <rapidjson/rapidjson.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include <utility>

#include <range/v3/all.hpp>
#include <spdlog/details/os.h>
Expand All @@ -22,6 +26,7 @@
#include "Application.h" //To get the extruders for material estimates.
#include "ExtruderTrain.h"
#include "FffProcessor.h" //To start a slice and get time estimates.
#include "MeshGroup.h"
#include "Slice.h"
#include "utils/Matrix4x3D.h" //For the mesh_rotation_matrix setting.
#include "utils/format/filesystem_path.h"
Expand Down Expand Up @@ -354,6 +359,107 @@ void CommandLine::sliceNext()
last_settings->add(key, value);
break;
}
case 'r':
{
// read in resolved values

argument_index++;
if (argument_index >= arguments_.size())
{
spdlog::error("Missing setting name and value with -s argument.");
exit(1);
}
argument = arguments_[argument_index];
const auto settings = readResolvedJsonValues(std::filesystem::path{ argument });

if (! settings.has_value())
{
spdlog::error("Failed to load JSON file: {}", argument);
exit(1);
}

constexpr std::string_view global_identifier = "global";
constexpr std::string_view extruder_identifier = "extruder.";
constexpr std::string_view model_identifier = "model.";
constexpr std::string_view limit_to_extruder_identifier = "limit_to_extruder";

// Split the settings into global, extruder and model settings. This is needed since the order in which the settings are applied is important.
// first global settings, then extruder settings, then model settings. The order of these stacks is not enforced in the JSON files.
std::unordered_map<std::string, std::string> global_settings;
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> extruder_settings;
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> model_settings;
std::unordered_map<std::string, std::string> limit_to_extruder;

for (const auto& [key, values] : settings.value())
{
if (key == global_identifier)
{
global_settings = values;
}
else if (key.starts_with(extruder_identifier))
{
extruder_settings[key] = values;
}
else if (key == limit_to_extruder_identifier)
{
limit_to_extruder = values;
}
else
{
model_settings[key] = values;
}
}

for (const auto& [setting_key, setting_value] : global_settings)
{
slice.scene.settings.add(setting_key, setting_value);
}

for (const auto& [key, values] : extruder_settings)
{
const auto extruder_nr = std::stoi(key.substr(extruder_identifier.size()));
while (slice.scene.extruders.size() <= static_cast<size_t>(extruder_nr))
{
slice.scene.extruders.emplace_back(extruder_nr, &slice.scene.settings);
}
for (const auto& [setting_key, setting_value] : values)
{
slice.scene.extruders[extruder_nr].settings_.add(setting_key, setting_value);
}
}

for (const auto& [key, values] : model_settings)
{
const auto model_name = key;

cura::MeshGroup mesh_group;
for (const auto& [setting_key, setting_value] : values)
{
mesh_group.settings.add(setting_key, setting_value);
}

const auto transformation = mesh_group.settings.get<Matrix4x3D>("mesh_rotation_matrix");
const auto extruder_nr = mesh_group.settings.get<size_t>("extruder_nr");

if (! loadMeshIntoMeshGroup(&mesh_group, model_name.c_str(), transformation, slice.scene.extruders[extruder_nr].settings_))
{
spdlog::error("Failed to load model: {}. (error number {})", model_name, errno);
exit(1);
}

slice.scene.mesh_groups.push_back(std::move(mesh_group));
}
for (const auto& [key, value] : limit_to_extruder)
{
const auto extruder_nr = std::stoi(value.substr(extruder_identifier.size()));
if (extruder_nr >= 0)
{
slice.scene.limit_to_extruder[key] = &slice.scene.extruders[extruder_nr];
}
}

break;
}
default:
{
spdlog::error("Unknown option: -{}", argument[1]);
Expand Down Expand Up @@ -586,6 +692,55 @@ void CommandLine::loadJSONSettings(const rapidjson::Value& element, Settings& se
}
}

std::optional<std::unordered_map<std::string, std::unordered_map<std::string, std::string>>> CommandLine::readResolvedJsonValues(const std::filesystem::path& json_filename)
{
std::ifstream file(json_filename, std::ios::binary);
if (! file)
{
spdlog::error("Couldn't open JSON file: {}", json_filename);
return std::nullopt;
}

std::vector<char> read_buffer(std::istreambuf_iterator<char>(file), {});
rapidjson::MemoryStream memory_stream(read_buffer.data(), read_buffer.size());

rapidjson::Document json_document;
json_document.ParseStream(memory_stream);
if (json_document.HasParseError())
{
spdlog::error("Error parsing JSON (offset {}): {}", json_document.GetErrorOffset(), GetParseError_En(json_document.GetParseError()));
return std::nullopt;
}

return readResolvedJsonValues(json_document);
}

std::optional<std::unordered_map<std::string, std::unordered_map<std::string, std::string>>> CommandLine::readResolvedJsonValues(const rapidjson::Document& document)
{
if (! document.IsObject())
{
return std::nullopt;
}

std::unordered_map<std::string, std::unordered_map<std::string, std::string>> result;
for (rapidjson::Value::ConstMemberIterator resolved_key = document.MemberBegin(); resolved_key != document.MemberEnd(); resolved_key++)
{
std::unordered_map<std::string, std::string> values;
for (rapidjson::Value::ConstMemberIterator resolved_value = resolved_key->value.MemberBegin(); resolved_value != resolved_key->value.MemberEnd(); resolved_value++)
{
std::string value_string;
if (! jsonValue2Str(resolved_value->value, value_string))
{
spdlog::warn("Unrecognized data type in JSON setting {}", resolved_value->name.GetString());
continue;
}
values.emplace(resolved_value->name.GetString(), value_string);
}
result.emplace(resolved_key->name.GetString(), std::move(values));
}
return result;
}

std::string CommandLine::findDefinitionFile(const std::string& definition_id, const std::vector<std::filesystem::path>& search_directories)
{
for (const auto& search_directory : search_directories)
Expand Down

0 comments on commit f1ad784

Please sign in to comment.