On 4/3/26 10:31 AM, Luca Toniolo wrote:
Somewhat related to the INI parser.
Yesterday I was trying to test parport on RTAI had a look at the
configs that were available, they are a total disaster.
There are probably many more to find ;-)
One interesting thing, you might want to be aware, not sure if you
already took a look when coding your new parser etch-servo has a
weird issues> [TRAJ]
HOME = 0 0
Fails to start, because it's expecting three values, even though the
lather only has two axis Not really part of the ini parser, but
initraj.cc , are you having a look there as well? Or should I?
This is, as you noted, not an issue with the ini-parser, but with how
ini values are parsed. I did notice it to be wrong and very inflexible.
It would only test/split 6 values and only set 3 (XYZ).
But, this particular piece of code has been changed to do:
1. read [TRAJ]HOME as string
2. split on " \t"
3. for each part
- convert to double
- assign to pose XYZABCUVW in sequence
When you only have two pieces, then it will only assign those two to XY.
It does pose a problem with systems that have XZA axes, but you can fake
the Y with a zero, I guess. Double axis systems like XXYYZ will have all
their equally named axes at the same position, but that should be right.
I attached my version of the modified code for you to check out (line
240...), but it is still preliminary and there may be minor adjustments.
--
Greetings Bertho
(disclaimers are disclaimed)
/********************************************************************
* Description: initraj.cc
* INI file initialization for trajectory level
*
* Derived from a work by Fred Proctor & Will Shackleford
*
* Author:
* License: GPL Version 2+
* System: Linux
*
* Copyright (c) 2026 All rights reserved.
********************************************************************/
#include <stdlib.h>
#include <fmt/format.h>
#include "nml_intf/emc.hh"
#include <emcpos.h>
#include "libnml/rcs/rcs_print.hh"
#include "nml_intf/emcglb.h"
#include "inifile.hh"
#include "inihal.hh"
#include "initraj.hh"
using namespace linuxcnc;
extern value_inihal_data old_inihal_data;
static void inline print_dbg_config(const std::string &s)
{
if (emc_debug & EMC_DEBUG_CONFIG) {
rcs_print_error((fmt::format("{}: failed\n", s)).c_str());
}
}
//
// Split string 'str' into tokens using 'delim' as delimiters
//
static std::vector<std::string> split(const std::string &delim, const std::string &str)
{
std::vector<std::string> toks;
size_t start = str.find_first_not_of(delim); // Start-of-token pos (or npos if only delimters)
size_t end = str.find_first_of(delim, start); // End-of-token pos+1 (or npos if last token)
// While start and end positions are available (meaning there is a token)
while (!(std::string::npos == end && std::string::npos == start)) {
toks.push_back(str.substr(start, end - start)); // Copy token into vector
start = str.find_first_not_of(delim, end); // Find new start-of-token from old end
end = str.find_first_of(delim, start); // and end-of-token
}
return toks;
}
static double findLinearUnits(const IniFile &ini, const std::string &var, const std::string &sec, double def)
{
// The const map holds pairs for linear units which are valid under the
// [TRAJ] section. These are of the form {"name", value}.
// If the name "name" is encountered in the INI, the value will be used.
static const std::map<const std::string, const double> linearUnitsMap = {
{ "mm", 1.0 },
{ "metric", 1.0 },
{ "in", 1/25.4 },
{ "inch", 1/25.4 },
{ "imperial", 1/25.4 },
};
if(auto c = ini.findMap(linearUnitsMap, var, sec))
return *c;
return def;
}
static double findAngularUnits(const IniFile &ini, const std::string &var, const std::string &sec, double def)
{
// The const map holds pairs for angular units which are valid under
// the [TRAJ] section. These are of the form {"name", value}.
// If the name "name" is encountered in the INI, the value will be used.
static const std::map<const std::string, const double, IniFile::caseless> angularUnitsMap = {
{ "deg", 1.0 },
{ "degree", 1.0 },
{ "grad", 0.9 },
{ "gon", 0.9 },
{ "rad", M_PI / 180.0 },
{ "radian", M_PI / 180.0 },
};
if(auto c = ini.findMap(angularUnitsMap, var, sec))
return *c;
return def;
}
//
// loadKins()
//
// JOINTS <int> number of joints (DOF) in system
//
static int loadKins(const IniFile &ini)
{
int joints = ini.findIntV("JOINTS", "KINS", 0);
if (0 != emcTrajSetJoints(joints)) {
return -1;
}
return 0;
}
//
// loadTraj()
//
// Load INI file params for traj
//
// COORDINATES <char[]> axes in system
// LINEAR_UNITS <double> units per mm
// ANGULAR_UNITS <double> units per degree
// DEFAULT_LINEAR_VELOCITY <double> default linear velocity
// MAX_LINEAR_VELOCITY <double> max linear velocity
// DEFAULT_LINEAR_ACCELERATION <double> default linear acceleration
// MAX_LINEAR_ACCELERATION <double> max linear acceleration
//
static int loadTraj(const IniFile &ini)
{
int spindles = ini.findIntV("SPINDLES", "TRAJ", 1);
if (0 != emcTrajSetSpindles(spindles)) {
return -1;
}
int axismask = 0;
if (auto coord = ini.findString("COORDINATES", "TRAJ")) {
static const std::string axes{"XYZABCUVW"};
for (auto c : *coord) {
size_t n = axes.find_first_of(std::toupper(c & 0xff));
if (n != std::string::npos)
axismask |= 1 << n;
}
} else {
axismask = (1<<0) | (1<<1) | (1<<2); // X, Y and Z machine
}
if (0 != emcTrajSetAxes(axismask)) {
print_dbg_config("emcTrajSetAxes");
return -1;
}
double linearUnits = findLinearUnits(ini, "LINEAR_UNITS", "TRAJ", 0.0);
double angularUnits = findAngularUnits(ini, "ANGULAR_UNITS", "TRAJ", 0.0);
if (0 != emcTrajSetUnits(linearUnits, angularUnits)) {
rcs_print("emcTrajSetUnits failed to set [TRAJ]LINEAR_UNITS or [TRAJ]ANGULAR_UNITS\n");
return -1;
}
double velocity = ini.findRealV("DEFAULT_LINEAR_VELOCITY", "TRAJ", 1.0);
if (0 != emcTrajSetVelocity(0.0, velocity)) { // Default velocity=0.0 on startup
print_dbg_config("emcTrajSetVelocity");
return -1;
}
old_inihal_data.traj_default_velocity = velocity;
velocity = ini.findRealV("MAX_LINEAR_VELOCITY", "TRAJ", 1e99);
if (0 != emcTrajSetMaxVelocity(velocity)) {
print_dbg_config("emcTrajSetMaxVelocity");
return -1;
}
old_inihal_data.traj_max_velocity = velocity;
double accel = ini.findRealV("DEFAULT_LINEAR_ACCELERATION", "TRAJ", 1e99);
if (0 != emcTrajSetAcceleration(accel)) {
print_dbg_config("emcTrajSetAcceleration");
return -1;
}
old_inihal_data.traj_default_acceleration = accel;
accel = ini.findRealV("MAX_LINEAR_ACCELERATION", "TRAJ", 1e99);
if (0 != emcTrajSetMaxAcceleration(accel)) {
print_dbg_config("emcTrajSetMaxAcceleration");
return -1;
}
old_inihal_data.traj_max_acceleration = accel;
// Set max jerk (default to 1e9 if not specified in INI)
double jerk = ini.findRealV("MAX_LINEAR_JERK", "TRAJ", 1e9);
// Set both current and max jerk
if (0 != emcTrajSetMaxJerk(jerk)) {
print_dbg_config("emcTrajSetMaxJerk");
return -1;
}
old_inihal_data.traj_max_jerk = jerk;
if (0 != emcTrajSetJerk(jerk)) {
print_dbg_config("emcTrajSetJerk");
return -1;
}
// Planner: 0 = trapezoidal, 1 = S-curve
// Default = 0
int planner_type = ini.findIntV("PLANNER_TYPE", "TRAJ", 0, 0, 1);
// Also force planner type 0 if max_jerk < 1 (S-curve needs valid jerk)
if (planner_type == 1 && jerk < 1.0) {
// FIXME: Should write a warning message to the user
planner_type = 0;
}
if (0 != emcTrajPlannerType(planner_type)) {
print_dbg_config("emcTrajPlannerType");
return -1;
}
old_inihal_data.traj_planner_type = planner_type;
int arcBlendEnable = ini.findIntV("ARC_BLEND_ENABLE", "TRAJ", 1);
int arcBlendFallbackEnable = ini.findIntV("ARC_BLEND_FALLBACK_ENABLE", "TRAJ", 0);
int arcBlendOptDepth = ini.findIntV("ARC_BLEND_OPTIMIZATION_DEPTH", "TRAJ", 50);
int arcBlendGapCycles = ini.findIntV("ARC_BLEND_GAP_CYCLES", "TRAJ", 4);
double arcBlendRampFreq = ini.findRealV("ARC_BLEND_RAMP_FREQ", "TRAJ", 100.0);
double arcBlendTangentKinkRatio = ini.findRealV("ARC_BLEND_KINK_RATIO", "TRAJ", 0.1);
if (0 != emcSetupArcBlends(arcBlendEnable, arcBlendFallbackEnable,
arcBlendOptDepth, arcBlendGapCycles, arcBlendRampFreq, arcBlendTangentKinkRatio)) {
print_dbg_config("emcSetupArcBlends");
return -1;
}
old_inihal_data.traj_arc_blend_enable = arcBlendEnable;
old_inihal_data.traj_arc_blend_fallback_enable = arcBlendFallbackEnable;
old_inihal_data.traj_arc_blend_optimization_depth = arcBlendOptDepth;
old_inihal_data.traj_arc_blend_gap_cycles = arcBlendGapCycles;
old_inihal_data.traj_arc_blend_ramp_freq = arcBlendRampFreq;
old_inihal_data.traj_arc_blend_tangent_kink_ratio = arcBlendTangentKinkRatio;
//TODO update inihal
double maxFeedScale = ini.findRealV("MAX_FEED_OVERRIDE", "DISPLAY", 1.0);
if (0 != emcSetMaxFeedOverride(maxFeedScale)) {
print_dbg_config("emcSetMaxFeedOverride");
return -1;
}
bool j_inhibit = ini.findBoolV("NO_PROBE_JOG_ERROR", "TRAJ", false);
bool h_inhibit = ini.findBoolV("NO_PROBE_HOME_ERROR", "TRAJ", false);
if (0 != emcSetProbeErrorInhibit(j_inhibit, h_inhibit)) {
print_dbg_config("emcSetProbeErrorInhibit");
return -1;
}
if (auto inistring = ini.findString("HOME", "TRAJ")) {
std::vector<std::string> toks = split(" \t", *inistring);
// NOTE: originally, this code would only set axes X, Y and Z and
// ignore everything else. Now all axes are set if provided in the
// [TRAJ]HOME position.
EmcPose homePose{};
for (size_t i = 0; i < toks.size() && i <= EMCMOT_MAX_AXIS; i++) {
char *eptr;
errno = 0;
double val = strtod(toks[i].c_str(), &eptr);
if (errno || *eptr || eptr == toks[i].c_str()) {
rcs_print_error("Invalid value '%s' for axis %zu in homePose\n", toks[i].c_str(), i);
return -1;
}
switch(i) {
case 0: homePose.tran.x = val; break;
case 1: homePose.tran.y = val; break;
case 2: homePose.tran.z = val; break;
case 3: homePose.a = val; break;
case 4: homePose.b = val; break;
case 5: homePose.c = val; break;
case 6: homePose.u = val; break;
case 7: homePose.v = val; break;
case 8: homePose.w = val; break;
default:
// Should never trigger because of EMCMOT_MAX_AXIS, but you never know
rcs_print_error("Value for invalid axis number %zu cannot be part of homePose\n", i);
return -1;
}
}
if (0 != emcTrajSetHome(homePose)) {
print_dbg_config("emcTrajSetHome");
return -1;
}
}
return 0;
}
/*
iniTraj(const char *filename)
Loads INI file parameters for trajectory, from [TRAJ] section
*/
int iniTraj(const char *filename)
{
IniFile ini(filename);
if (!ini)
return -1;
if (loadKins(ini)) {
return -1;
}
return loadTraj(ini);
}
_______________________________________________
Emc-developers mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/emc-developers