From ed8c87a29b98752ba26f4d02a0122e7ab9d182ce Mon Sep 17 00:00:00 2001 From: greatEndian Date: Fri, 26 Jun 2026 10:28:10 -0400 Subject: [PATCH] tp: rate-limit Ruckig plan-failure logging to once per segment A segment whose inputs Ruckig can never solve keeps ruckig_planned=0, so tpCalculateSCurveAccel re-enters its failure branch every servo cycle and logs at the full servo rate (e.g. 1kHz). Motion is unaffected (it falls back to trapezoidal), but over a long run this floods the kernel log / journald with GBs of identical records. Add per-segment "already logged" flags to TC_STRUCT (reset by tcInit on every new segment) and log each failure once, tagged with the segment id. Not re-armed on a later success within the same segment, so an oscillating fail/succeed segment also cannot flood. No change to motion behaviour. master's own tpCalculateSCurveAccel already carries a scurve_jerk_warned once-only guard for the adjacent warning. tpCalculateSCurveAccel has five distinct Ruckig failure sites, each retried every servo cycle: - pool acquire: ruckig_planner stays NULL and is re-acquired next cycle for as long as the preallocated pool is exhausted; - velocity control, replan failed: keeps the previous trajectory and falls straight back in on the next cycle; - velocity control, first attempt failed: returns TP_SCURVE_ACCEL_ERROR, which only makes the caller use trapezoidal for that one cycle before calling back in; - position control, replan failed / first attempt failed: the same two cases on the position-control path. Each site gets its own flag rather than one shared flag, so a segment that hits more than one failure mode (e.g. pool-acquire, then a plan failure once a planner frees up) still logs each mode once. A single shared flag would report only whichever fired first. The first-attempt position-control ERR dump also regains plan_result, which was previously only on the replan branch, so the give-up path again shows which limit Ruckig objected to. The velocity-control first-attempt warning carries it too, for symmetry. --- src/emc/tp/tc.c | 5 +++ src/emc/tp/tc_types.h | 10 ++++++ src/emc/tp/tp.c | 75 ++++++++++++++++++++++++++++++++----------- 3 files changed, 71 insertions(+), 19 deletions(-) diff --git a/src/emc/tp/tc.c b/src/emc/tp/tc.c index 1acbd0e7072..098bec84658 100644 --- a/src/emc/tp/tc.c +++ b/src/emc/tp/tc.c @@ -750,6 +750,11 @@ int tcInit(TC_STRUCT * const tc, tc->ruckig_last_use_velocity_control = 0; tc->ruckig_last_req_pos = 0.0; tc->ruckig_last_feed_override = 0.0; + tc->ruckig_fail_logged_pool = 0; + tc->ruckig_fail_logged_vel_replan = 0; + tc->ruckig_fail_logged_vel_first = 0; + tc->ruckig_fail_logged_pos_replan = 0; + tc->ruckig_fail_logged_pos_first = 0; return TP_ERR_OK; } diff --git a/src/emc/tp/tc_types.h b/src/emc/tp/tc_types.h index c1767d56132..7c907e26e28 100644 --- a/src/emc/tp/tc_types.h +++ b/src/emc/tp/tc_types.h @@ -210,6 +210,16 @@ typedef struct { int ruckig_last_use_velocity_control; // control mode used in last planning (1=velocity, 0=position) double ruckig_last_req_pos; // last req_pos value from Ruckig (for velocity control incremental calc) double ruckig_last_feed_override; // feed override value at last planning (for debug and change detection) + // One flag per Ruckig plan-failure site in tpCalculateSCurveAccel. Set + // once a failure of that kind has been logged for this segment, so a + // segment Ruckig can never solve logs ONCE per failure site instead of + // every servo cycle (1kHz) -> avoids a multi-GB log flood on long soak + // runs. Cleared by tcInit on every new segment. + int ruckig_fail_logged_pool; // pool acquire returned NULL + int ruckig_fail_logged_vel_replan; // velocity control, replan failed + int ruckig_fail_logged_vel_first; // velocity control, first attempt failed + int ruckig_fail_logged_pos_replan; // position control, replan failed + int ruckig_fail_logged_pos_first; // position control, first attempt failed } TC_STRUCT; #endif /* TC_TYPES_H */ diff --git a/src/emc/tp/tp.c b/src/emc/tp/tp.c index 4d0d59d9091..73c7bbaba6c 100644 --- a/src/emc/tp/tp.c +++ b/src/emc/tp/tp.c @@ -2827,7 +2827,14 @@ int tpCalculateSCurveAccel(TP_STRUCT const * const tp, TC_STRUCT * const tc, TC_ // Borrow a Ruckig planner from the preallocated pool (no RT-cycle alloc) tc->ruckig_planner = ruckig_pool_acquire(tc->cycle_time); if (!tc->ruckig_planner) { - rtapi_print_msg(RTAPI_MSG_ERR, "tpCalculateSCurveAccel: failed to create Ruckig planner\n"); + // Same rate-limit as the plan-failure paths below: the pool is + // retried every servo cycle for as long as it stays exhausted. + if (!tc->ruckig_fail_logged_pool) { + tc->ruckig_fail_logged_pool = 1; + rtapi_print_msg(RTAPI_MSG_ERR, + "tpCalculateSCurveAccel: failed to acquire a Ruckig planner from the pool" + " (segment %d, reported once)\n", tc->id); + } return TP_SCURVE_ACCEL_ERROR; } tc->ruckig_planned = 0; @@ -2884,10 +2891,27 @@ int tpCalculateSCurveAccel(TP_STRUCT const * const tp, TC_STRUCT * const tc, TC_ maxjerk); // max jerk if (plan_result != 0) { + // Guarded exactly like the position-control paths below: both + // branches are re-entered every servo cycle while the segment + // keeps failing (the caller reverts to trapezoidal for that + // cycle only, then calls back in). Each site has its own flag, + // cleared by tcInit on every new segment, so each distinct + // failure mode still logs once per segment. if (tc->ruckig_planned) { - rtapi_print_msg(RTAPI_MSG_WARN, "tpCalculateSCurveAccel: Ruckig velocity control replanning failed, using previous trajectory\n"); + if (!tc->ruckig_fail_logged_vel_replan) { + tc->ruckig_fail_logged_vel_replan = 1; + rtapi_print_msg(RTAPI_MSG_WARN, + "tpCalculateSCurveAccel: Ruckig velocity control replanning failed," + " using previous trajectory (segment %d, reported once)\n", tc->id); + } } else { - rtapi_print_msg(RTAPI_MSG_WARN, "tpCalculateSCurveAccel: Ruckig velocity control planning failed, falling back to tp 0\n"); + if (!tc->ruckig_fail_logged_vel_first) { + tc->ruckig_fail_logged_vel_first = 1; + rtapi_print_msg(RTAPI_MSG_WARN, + "tpCalculateSCurveAccel: Ruckig velocity control planning failed" + " with result %d, falling back to tp 0 (segment %d, reported once)\n", + plan_result, tc->id); + } return TP_SCURVE_ACCEL_ERROR; } } else { @@ -2950,25 +2974,38 @@ int tpCalculateSCurveAccel(TP_STRUCT const * const tp, TC_STRUCT * const tc, TC_ maxjerk); // max jerk if (plan_result != 0) { - rtapi_print_msg(RTAPI_MSG_INFO, "tpCalculateSCurveAccel: ruckig_plan_position failed with result %d\n", plan_result); if (tc->ruckig_planned) { // Keep using previous trajectory + if (!tc->ruckig_fail_logged_pos_replan) { + tc->ruckig_fail_logged_pos_replan = 1; + rtapi_print_msg(RTAPI_MSG_INFO, "tpCalculateSCurveAccel: ruckig_plan_position failed with result %d (segment %d, reported once)\n", plan_result, tc->id); + } } else { - // First planning attempt failed, fall back - rtapi_print_msg(RTAPI_MSG_ERR, - "Ruckig planning failed (first attempt), Back to tp 0\n" - " feed_override: %.6f \n" - " max_vel: %.6f\n" - " cpos: %.6f, tpos: %.6f, dx: %.6f\n" - " cvel: %.6f, tvel: %.6f\n" - " cacc: %.6f\n" - " maxa: %.6f, maxj: %.6f\n", - emcmotStatus->net_feed_scale, - effective_max_vel, - replan_pos, target_pos, dx, - replan_vel, effective_target_vel, - replan_acc, - maxaccel, maxjerk); + // First planning attempt failed, fall back to trapezoidal. + // Guarded so a segment Ruckig can never solve logs ONCE, not + // every servo cycle (the retry loop re-enters here each cycle + // until ruckig_planned flips). The flag is cleared by tcInit + // on every new segment, so each distinct failure still logs. + if (!tc->ruckig_fail_logged_pos_first) { + tc->ruckig_fail_logged_pos_first = 1; + rtapi_print_msg(RTAPI_MSG_ERR, + "Ruckig planning failed (first attempt), Back to tp 0 (segment %d, reported once)\n" + " plan_result: %d\n" + " feed_override: %.6f \n" + " max_vel: %.6f\n" + " cpos: %.6f, tpos: %.6f, dx: %.6f\n" + " cvel: %.6f, tvel: %.6f\n" + " cacc: %.6f\n" + " maxa: %.6f, maxj: %.6f\n", + tc->id, + plan_result, + emcmotStatus->net_feed_scale, + effective_max_vel, + replan_pos, target_pos, dx, + replan_vel, effective_target_vel, + replan_acc, + maxaccel, maxjerk); + } return TP_SCURVE_ACCEL_ERROR; } } else {