| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <cinttypes> |
| | #include <iomanip> |
| | #include <boost/algorithm/string.hpp> |
| |
|
| | #include <Base/Exception.h> |
| | #include <Base/Reader.h> |
| | #include <Base/Rotation.h> |
| | #include <Base/Vector3D.h> |
| | #include <Base/Writer.h> |
| |
|
| | #include "Command.h" |
| |
|
| |
|
| | using namespace Base; |
| | using namespace Path; |
| |
|
| | TYPESYSTEM_SOURCE(Path::Command, Base::Persistence) |
| |
|
| | |
| |
|
| | Command::Command(const char* name, const std::map<std::string, double>& parameters) |
| | : Name(name) |
| | , Parameters(parameters) |
| | , Annotations() |
| | {} |
| |
|
| | Command::Command( |
| | const char* name, |
| | const std::map<std::string, double>& parameters, |
| | const std::map<std::string, std::variant<std::string, double>>& annotations |
| | ) |
| | : Name(name) |
| | , Parameters(parameters) |
| | , Annotations(annotations) |
| | {} |
| |
|
| | Command::Command() |
| | : Annotations() |
| | {} |
| |
|
| | Command::~Command() |
| | {} |
| |
|
| | |
| |
|
| | Placement Command::getPlacement(const Base::Vector3d pos) const |
| | { |
| | static const std::string x = "X"; |
| | static const std::string y = "Y"; |
| | static const std::string z = "Z"; |
| | static const std::string a = "A"; |
| | static const std::string b = "B"; |
| | static const std::string c = "C"; |
| | Vector3d vec(getParam(x, pos.x), getParam(y, pos.y), getParam(z, pos.z)); |
| | Rotation rot; |
| | rot.setYawPitchRoll(getParam(a), getParam(b), getParam(c)); |
| | Placement plac(vec, rot); |
| | return plac; |
| | } |
| |
|
| | Vector3d Command::getCenter() const |
| | { |
| | static const std::string i = "I"; |
| | static const std::string j = "J"; |
| | static const std::string k = "K"; |
| | Vector3d vec(getParam(i), getParam(j), getParam(k)); |
| | return vec; |
| | } |
| |
|
| | double Command::getValue(const std::string& attr) const |
| | { |
| | std::string a(attr); |
| | boost::to_upper(a); |
| | return getParam(a); |
| | } |
| |
|
| | bool Command::has(const std::string& attr) const |
| | { |
| | std::string a(attr); |
| | boost::to_upper(a); |
| | return Parameters.contains(a); |
| | } |
| |
|
| | std::string Command::toGCode(int precision, bool padzero) const |
| | { |
| | std::stringstream str; |
| | str.fill('0'); |
| | str << Name; |
| | if (precision < 0) { |
| | precision = 0; |
| | } |
| | double scale = std::pow(10.0, precision + 1); |
| | std::int64_t iscale = static_cast<std::int64_t>(scale) / 10; |
| | for (std::map<std::string, double>::const_iterator i = Parameters.begin(); i != Parameters.end(); |
| | ++i) { |
| | if (i->first == "N") { |
| | continue; |
| | } |
| |
|
| | str << " " << i->first; |
| |
|
| | std::int64_t v = static_cast<std::int64_t>(i->second * scale); |
| | if (v < 0) { |
| | v = -v; |
| | str << '-'; |
| | } |
| | v += 5; |
| | v /= 10; |
| | str << (v / iscale); |
| | if (!precision) { |
| | continue; |
| | } |
| |
|
| | int width = precision; |
| | std::int64_t digits = v % iscale; |
| | if (!padzero) { |
| | if (!digits) { |
| | continue; |
| | } |
| | while (digits % 10 == 0) { |
| | digits /= 10; |
| | --width; |
| | } |
| | } |
| | str << '.' << std::setw(width) << std::right << digits; |
| | } |
| |
|
| | |
| | if (!Annotations.empty()) { |
| | str << " ; "; |
| | bool first = true; |
| | for (const auto& pair : Annotations) { |
| | if (!first) { |
| | str << " "; |
| | } |
| | first = false; |
| | str << pair.first << ":"; |
| | if (std::holds_alternative<std::string>(pair.second)) { |
| | str << "'" << std::get<std::string>(pair.second) << "'"; |
| | } |
| | else if (std::holds_alternative<double>(pair.second)) { |
| | std::ostringstream oss; |
| | oss << std::fixed << std::setprecision(6) << std::get<double>(pair.second); |
| | str << oss.str(); |
| | } |
| | } |
| | } |
| |
|
| | return str.str(); |
| | } |
| |
|
| | void Command::setFromGCode(const std::string& str) |
| | { |
| | Parameters.clear(); |
| | Annotations.clear(); |
| |
|
| | |
| | std::string gcode_part = str; |
| | std::string annotation_part; |
| |
|
| | auto comment_pos = str.find("; "); |
| | if (comment_pos != std::string::npos) { |
| | gcode_part = str.substr(0, comment_pos); |
| | annotation_part = str.substr(comment_pos + 1); |
| | } |
| |
|
| | std::string mode = "none"; |
| | std::string key; |
| | std::string value; |
| | for (unsigned int i = 0; i < gcode_part.size(); i++) { |
| | if ((isdigit(gcode_part[i])) || (gcode_part[i] == '-') || (gcode_part[i] == '.')) { |
| | value += gcode_part[i]; |
| | } |
| | else if (isalpha(gcode_part[i])) { |
| | if (mode == "command") { |
| | if (!key.empty() && !value.empty()) { |
| | std::string cmd = key + value; |
| | boost::to_upper(cmd); |
| | Name = cmd; |
| | key = ""; |
| | value = ""; |
| | mode = "argument"; |
| | } |
| | else { |
| | throw Base::BadFormatError("Badly formatted GCode command"); |
| | } |
| | mode = "argument"; |
| | } |
| | else if (mode == "none") { |
| | mode = "command"; |
| | } |
| | else if (mode == "argument") { |
| | if (!key.empty() && !value.empty()) { |
| | double val = std::atof(value.c_str()); |
| | boost::to_upper(key); |
| | Parameters[key] = val; |
| | key = ""; |
| | value = ""; |
| | } |
| | else { |
| | throw Base::BadFormatError("Badly formatted GCode argument"); |
| | } |
| | } |
| | else if (mode == "comment") { |
| | value += gcode_part[i]; |
| | } |
| | key = gcode_part[i]; |
| | } |
| | else if (gcode_part[i] == '(') { |
| | mode = "comment"; |
| | } |
| | else if (gcode_part[i] == ')') { |
| | key = "("; |
| | value += ")"; |
| | } |
| | else { |
| | |
| | if (mode == "comment") { |
| | value += gcode_part[i]; |
| | } |
| | } |
| | } |
| |
|
| | |
| | if (!annotation_part.empty()) { |
| | setAnnotations(annotation_part); |
| | } |
| |
|
| | if (!key.empty() && !value.empty()) { |
| | if ((mode == "command") || (mode == "comment")) { |
| | std::string cmd = key + value; |
| | if (mode == "command") { |
| | boost::to_upper(cmd); |
| | } |
| | Name = cmd; |
| | } |
| | else { |
| | double val = std::atof(value.c_str()); |
| | boost::to_upper(key); |
| | Parameters[key] = val; |
| | } |
| | } |
| | else { |
| | throw Base::BadFormatError("Badly formatted GCode argument"); |
| | } |
| | } |
| |
|
| | void Command::setFromPlacement(const Base::Placement& plac) |
| | { |
| | Name = "G1"; |
| | Parameters.clear(); |
| | static const std::string x = "X"; |
| | static const std::string y = "Y"; |
| | static const std::string z = "Z"; |
| | static const std::string a = "A"; |
| | static const std::string b = "B"; |
| | static const std::string c = "C"; |
| | double xval, yval, zval, aval, bval, cval; |
| | xval = plac.getPosition().x; |
| | yval = plac.getPosition().y; |
| | zval = plac.getPosition().z; |
| | plac.getRotation().getYawPitchRoll(aval, bval, cval); |
| | if (xval != 0.0) { |
| | Parameters[x] = xval; |
| | } |
| | if (yval != 0.0) { |
| | Parameters[y] = yval; |
| | } |
| | if (zval != 0.0) { |
| | Parameters[z] = zval; |
| | } |
| | if (aval != 0.0) { |
| | Parameters[a] = aval; |
| | } |
| | if (bval != 0.0) { |
| | Parameters[b] = bval; |
| | } |
| | if (cval != 0.0) { |
| | Parameters[c] = cval; |
| | } |
| | } |
| |
|
| | void Command::setCenter(const Base::Vector3d& pos, bool clockwise) |
| | { |
| | if (clockwise) { |
| | Name = "G2"; |
| | } |
| | else { |
| | Name = "G3"; |
| | } |
| | static const std::string i = "I"; |
| | static const std::string j = "J"; |
| | static const std::string k = "K"; |
| | double ival, jval, kval; |
| | ival = pos.x; |
| | jval = pos.y; |
| | kval = pos.z; |
| | Parameters[i] = ival; |
| | Parameters[j] = jval; |
| | Parameters[k] = kval; |
| | } |
| |
|
| | Command Command::transform(const Base::Placement& other) |
| | { |
| | Base::Placement plac = getPlacement(); |
| | plac *= other; |
| | double xval, yval, zval, aval, bval, cval; |
| | xval = plac.getPosition().x; |
| | yval = plac.getPosition().y; |
| | zval = plac.getPosition().z; |
| | plac.getRotation().getYawPitchRoll(aval, bval, cval); |
| | Command c = Command(); |
| | c.Name = Name; |
| | for (std::map<std::string, double>::const_iterator i = Parameters.begin(); i != Parameters.end(); |
| | ++i) { |
| | std::string k = i->first; |
| | double v = i->second; |
| | if (k == "X") { |
| | v = xval; |
| | } |
| | if (k == "Y") { |
| | v = yval; |
| | } |
| | if (k == "Z") { |
| | v = zval; |
| | } |
| | if (k == "A") { |
| | v = aval; |
| | } |
| | if (k == "B") { |
| | v = bval; |
| | } |
| | if (k == "C") { |
| | v = cval; |
| | } |
| | c.Parameters[k] = v; |
| | } |
| | return c; |
| | } |
| |
|
| | void Command::scaleBy(double factor) |
| | { |
| | for (std::map<std::string, double>::const_iterator i = Parameters.begin(); i != Parameters.end(); |
| | ++i) { |
| | switch (i->first[0]) { |
| | case 'X': |
| | case 'Y': |
| | case 'Z': |
| | case 'I': |
| | case 'J': |
| | case 'R': |
| | case 'Q': |
| | case 'F': |
| | Parameters[i->first] = i->second * factor; |
| | break; |
| | } |
| | } |
| | } |
| |
|
| | void Command::setAnnotation(const std::string& key, const std::string& value) |
| | { |
| | Annotations[key] = value; |
| | } |
| |
|
| | void Command::setAnnotation(const std::string& key, double value) |
| | { |
| | Annotations[key] = value; |
| | } |
| |
|
| | std::string Command::getAnnotation(const std::string& key) const |
| | { |
| | auto it = Annotations.find(key); |
| | if (it != Annotations.end()) { |
| | if (std::holds_alternative<std::string>(it->second)) { |
| | return std::get<std::string>(it->second); |
| | } |
| | else if (std::holds_alternative<double>(it->second)) { |
| | return std::to_string(std::get<double>(it->second)); |
| | } |
| | } |
| | return ""; |
| | } |
| |
|
| | std::string Command::getAnnotationString(const std::string& key) const |
| | { |
| | auto it = Annotations.find(key); |
| | if (it != Annotations.end() && std::holds_alternative<std::string>(it->second)) { |
| | return std::get<std::string>(it->second); |
| | } |
| | return ""; |
| | } |
| |
|
| | double Command::getAnnotationDouble(const std::string& key, double fallback) const |
| | { |
| | auto it = Annotations.find(key); |
| | if (it != Annotations.end() && std::holds_alternative<double>(it->second)) { |
| | return std::get<double>(it->second); |
| | } |
| | return fallback; |
| | } |
| |
|
| | std::variant<std::string, double> Command::getAnnotationValue(const std::string& key) const |
| | { |
| | auto it = Annotations.find(key); |
| | if (it != Annotations.end()) { |
| | return it->second; |
| | } |
| | return std::string(""); |
| | } |
| |
|
| | bool Command::hasAnnotation(const std::string& key) const |
| | { |
| | return Annotations.find(key) != Annotations.end(); |
| | } |
| |
|
| | Command& Command::setAnnotations(const std::string& annotationString) |
| | { |
| | std::istringstream iss(annotationString); |
| | std::string token; |
| | while (iss >> token) { |
| | auto pos = token.find(':'); |
| | if (pos != std::string::npos) { |
| | std::string key = token.substr(0, pos); |
| | std::string value = token.substr(pos + 1); |
| |
|
| | |
| | if (value.size() >= 2 && value.front() == '\'' && value.back() == '\'') { |
| | Annotations[key] = value.substr(1, value.size() - 2); |
| | } |
| | else { |
| | |
| | try { |
| | size_t processed = 0; |
| | double numValue = std::stod(value, &processed); |
| | if (processed == value.length()) { |
| | Annotations[key] = numValue; |
| | } |
| | else { |
| | Annotations[key] = value; |
| | } |
| | } |
| | catch (const std::exception&) { |
| | Annotations[key] = value; |
| | } |
| | } |
| | } |
| | } |
| | return *this; |
| | } |
| |
|
| | unsigned int Command::getMemSize() const |
| | { |
| | return toGCode().size(); |
| | } |
| |
|
| | void Command::Save(Base::Writer& writer) const |
| | { |
| | |
| | writer.Stream() << writer.ind() << "<Command gcode=\"" << toGCode() << "\""; |
| |
|
| | if (!Annotations.empty()) { |
| | writer.Stream() << " annotations=\""; |
| | bool first = true; |
| | for (const auto& pair : Annotations) { |
| | if (!first) { |
| | writer.Stream() << " "; |
| | } |
| | first = false; |
| | writer.Stream() << pair.first << ":"; |
| | if (std::holds_alternative<std::string>(pair.second)) { |
| | writer.Stream() << "'" << std::get<std::string>(pair.second) << "'"; |
| | } |
| | else if (std::holds_alternative<double>(pair.second)) { |
| | writer.Stream() << std::fixed << std::setprecision(6) |
| | << std::get<double>(pair.second); |
| | } |
| | } |
| | writer.Stream() << "\""; |
| | } |
| |
|
| | writer.Stream() << " />" << std::endl; |
| | } |
| |
|
| | void Command::Restore(Base::XMLReader& reader) |
| | { |
| | reader.readElement("Command"); |
| | std::string gcode = reader.getAttribute<const char*>("gcode"); |
| | setFromGCode(gcode); |
| |
|
| | Annotations.clear(); |
| |
|
| | std::string attr; |
| | try { |
| | attr = reader.getAttribute<const char*>("annotations"); |
| | } |
| | catch (...) { |
| | return; |
| | } |
| |
|
| | std::istringstream iss(attr); |
| | std::string token; |
| | while (iss >> token) { |
| | auto pos = token.find(':'); |
| | if (pos != std::string::npos) { |
| | std::string key = token.substr(0, pos); |
| | std::string value = token.substr(pos + 1); |
| |
|
| | |
| | if (value.size() >= 2 && value.front() == '\'' && value.back() == '\'') { |
| | Annotations[key] = value.substr(1, value.size() - 2); |
| | } |
| | else { |
| | try { |
| | double d = std::stod(value); |
| | Annotations[key] = d; |
| | } |
| | catch (...) { |
| | Annotations[key] = value; |
| | } |
| | } |
| | } |
| | } |
| | } |
| |
|