From 606b22a9ad1eb57e01c50646407ec4ec346433e3 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 25 Jul 2021 01:17:25 +0200 Subject: [PATCH 01/12] Make Motion::Acceleration const --- src/modules/motion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/motion.h b/src/modules/motion.h index 2c0b311..2cc67d8 100644 --- a/src/modules/motion.h +++ b/src/modules/motion.h @@ -140,7 +140,7 @@ public: /// Get current acceleration for the selected axis /// @param axis axis affected /// @returns acceleration - steps_t Acceleration(Axis axis) { + steps_t Acceleration(Axis axis) const { return axisData[axis].ctrl.Acceleration(); } From 5e04d4ccafe19b5b8b53e9e145c9e8fd0228bfa6 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 25 Jul 2021 01:24:40 +0200 Subject: [PATCH 02/12] Add getters/setters for Jerk in Motion/PulseGen This allows us to make tests that expect jerk to be the same across two axes, even if they're statically configured differently. --- src/modules/motion.h | 14 ++++++++++++++ src/modules/pulse_gen.h | 6 ++++++ tests/unit/modules/motion/test_motion.cpp | 16 ++++++++-------- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/modules/motion.h b/src/modules/motion.h index 2cc67d8..98cc23b 100644 --- a/src/modules/motion.h +++ b/src/modules/motion.h @@ -151,6 +151,20 @@ public: axisData[axis].ctrl.SetAcceleration(accel); } + /// Get current jerk for the selected axis + /// @param axis axis affected + /// @returns jerk + steps_t Jerk(Axis axis) const { + return axisData[axis].ctrl.Jerk(); + } + + /// Set maximum jerk for the selected axis + /// @param axis axis affected + /// @param max_jerk maximum jerk + void SetJerk(Axis axis, steps_t max_jerk) { + return axisData[axis].ctrl.SetJerk(max_jerk); + } + /// State machine doing all the planning and stepping. Called by the stepping ISR. /// @returns the interval for the next tick st_timer_t Step(); diff --git a/src/modules/pulse_gen.h b/src/modules/pulse_gen.h index ecf9fbc..9c10a38 100644 --- a/src/modules/pulse_gen.h +++ b/src/modules/pulse_gen.h @@ -24,6 +24,12 @@ class PulseGen { public: PulseGen(steps_t max_jerk, steps_t acceleration); + /// @returns the jerk for the axis + steps_t Jerk() const { return max_jerk; }; + + /// Set maximum jerk for the axis + void SetJerk(steps_t max_jerk) { this->max_jerk = max_jerk; }; + /// @returns the acceleration for the axis steps_t Acceleration() const { return acceleration; }; diff --git a/tests/unit/modules/motion/test_motion.cpp b/tests/unit/modules/motion/test_motion.cpp index d241b83..f7d8d14 100644 --- a/tests/unit/modules/motion/test_motion.cpp +++ b/tests/unit/modules/motion/test_motion.cpp @@ -32,13 +32,13 @@ TEST_CASE("motion::basic", "[motion]") { } TEST_CASE("motion::dual_move_fwd", "[motion]") { - // check for configuration values that we cannot change but should match for this test - // to function as expected (maybe this should be a static_assert?) - REQUIRE(config::idler.jerk == config::selector.jerk); - // enqueue moves on two axes REQUIRE(motion.QueueEmpty()); + // ensure the same jerk is set on both + motion.SetJerk(Idler, motion.Jerk(Selector)); + REQUIRE(motion.Jerk(Idler) == motion.Jerk(Selector)); + // ensure the same acceleration is set on both motion.SetAcceleration(Idler, motion.Acceleration(Selector)); REQUIRE(motion.Acceleration(Idler) == motion.Acceleration(Selector)); @@ -57,13 +57,13 @@ TEST_CASE("motion::dual_move_fwd", "[motion]") { } TEST_CASE("motion::dual_move_inv", "[motion]") { - // check for configuration values that we cannot change but should match for this test - // to function as expected (maybe this should be a static_assert?) - REQUIRE(config::idler.jerk == config::selector.jerk); - // enqueue moves on two axes REQUIRE(motion.QueueEmpty()); + // ensure the same jerk is set on both + motion.SetJerk(Idler, motion.Jerk(Selector)); + REQUIRE(motion.Jerk(Idler) == motion.Jerk(Selector)); + // ensure the same acceleration is set on both motion.SetAcceleration(Idler, motion.Acceleration(Selector)); REQUIRE(motion.Acceleration(Idler) == motion.Acceleration(Selector)); From 888cdf7cd5af4857c3418c1626ff84f80f2a1853 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 25 Jul 2021 16:39:54 +0200 Subject: [PATCH 03/12] Introduce compile-time axis unit type checks and conversions Introduces: - config::Unit: base class for physical quantities - motion::AxisUnit: type-checked steps type "config/unit.h" defines basic physical quantities, which are not normally used elsewhere besides config.h. "modules/axisunit.h" extends the modules::motion namespace with Axis-aware units, with one type per axis per unit. P_pos_t defines step positions for the pulley, I_pos_t for the idler, etc. These are defined through the literar operators which are similarly named and automatically convert a physical quantity to an AxisUnit at compile time: P_pos_t pulley_pos = 10.0_P_mm; Besides type-checking, AxisUnit are otherwise identical to raw step counts and are intended to be used along with the updated Motion API. PlanMove/PlanMoveTo has been extended to support moves using these units or physical quantities. Again, conversion is performed at compile time. --- src/config/axis.h | 30 +++++- src/config/config.h | 63 +++++++----- src/config/unit.h | 89 ++++++++++++++++ src/modules/axisunit.h | 119 ++++++++++++++++++++++ src/modules/motion.h | 81 +++++++++++---- tests/unit/modules/motion/test_motion.cpp | 45 ++++++++ 6 files changed, 382 insertions(+), 45 deletions(-) create mode 100644 src/config/unit.h create mode 100644 src/modules/axisunit.h diff --git a/src/config/axis.h b/src/config/axis.h index 6ca96a8..6d5acd0 100644 --- a/src/config/axis.h +++ b/src/config/axis.h @@ -1,18 +1,42 @@ #pragma once #include +#include "unit.h" namespace config { /// Axis configuration data struct AxisConfig { bool dirOn; ///< direction ON state (for inversion) - uint8_t uSteps; ///< microstepping [1-32] bool vSense; ///< vSense scaling uint8_t iRun; ///< running current uint8_t iHold; ///< holding current - uint16_t accel; ///< Acceleration (unit/s^2) - uint16_t jerk; ///< Jerk (unit/s) bool stealth; ///< Default to Stealth mode + uint8_t uSteps; ///< microstepping [1-256] + long double stepsPerUnit; ///< steps per unit }; +/// List of available axes +enum Axis : uint8_t { + Pulley, + Selector, + Idler, + _Axis_Last = Idler +}; + +/// Number of available axes +static constexpr uint8_t NUM_AXIS = Axis::_Axis_Last + 1; + +/// Phisical limits for an axis +template +struct AxisLimits { + static constexpr UnitBase base = B; + Unit lenght; ///< Longest move that can be performed by the axis + Unit jerk; ///< Maximum jerk for the axis + Unit accel; ///< Maximum acceleration for the axis +}; + +typedef AxisLimits PulleyLimits; ///< Pulley axis limits +typedef AxisLimits SelectorLimits; ///< Selector axis limits +typedef AxisLimits IdlerLimits; ///< Idler axis limits + } // namespace config diff --git a/src/config/config.h b/src/config/config.h index 3875d94..ece5230 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -27,11 +27,6 @@ static constexpr const uint16_t buttonsDebounceMs = 100; static constexpr const uint16_t buttonADCLimits[buttonCount][2] = { { 0, 50 }, { 80, 100 }, { 160, 180 } }; static constexpr const uint8_t buttonsADCIndex = 5; ///< ADC index of buttons input -/// Maximum microstepping resolution. This defines the effective unit of -/// the step intevals on the motion API, independently of the selected -/// microstepping interval. -static constexpr uint8_t uStepMaxRes = 32; - /// Do not plan moves equal or shorter than the requested steps static constexpr uint8_t dropSegments = 0; @@ -51,40 +46,58 @@ static constexpr uint8_t stepTimerFrequencyDivider = 8; /// 16 = 8us (25us is the max frequency interval per maxStepFrequency) static constexpr uint8_t stepTimerQuantum = 16; -/// Idler configuration -static constexpr AxisConfig idler = { - .dirOn = true, - .uSteps = 16, - .vSense = false, - .iRun = 20, - .iHold = 20, - .accel = 100, - .jerk = 10, - .stealth = false, -}; - -/// Pulley configuration +/// Pulley axis configuration static constexpr AxisConfig pulley = { .dirOn = true, - .uSteps = 16, .vSense = false, .iRun = 20, .iHold = 20, - .accel = 100, - .jerk = 10, .stealth = false, + .uSteps = 16, + .stepsPerUnit = 100, +}; + +/// Pulley motion limits +static constexpr PulleyLimits pulleyLimits = { + .lenght = 100.0_mm, + .jerk = 10.0_mm_s, + .accel = 1000.0_mm_s2, }; /// Selector configuration static constexpr AxisConfig selector = { .dirOn = true, - .uSteps = 16, .vSense = false, .iRun = 20, .iHold = 20, - .accel = 100, - .jerk = 10, - .stealth = false + .stealth = false, + .uSteps = 16, + .stepsPerUnit = 100, +}; + +/// Selector motion limits +static constexpr SelectorLimits selectorLimits = { + .lenght = 100.0_mm, + .jerk = 10.0_mm_s, + .accel = 1000.0_mm_s2, +}; + +/// Idler configuration +static constexpr AxisConfig idler = { + .dirOn = true, + .vSense = false, + .iRun = 20, + .iHold = 20, + .stealth = false, + .uSteps = 16, + .stepsPerUnit = 100, +}; + +/// Idler motion limits +static constexpr IdlerLimits idlerLimits = { + .lenght = 360.0_deg, + .jerk = 10.0_deg_s, + .accel = 1000.0_deg_s2, }; } // namespace config diff --git a/src/config/unit.h b/src/config/unit.h new file mode 100644 index 0000000..c855934 --- /dev/null +++ b/src/config/unit.h @@ -0,0 +1,89 @@ +#pragma once +#include + +// In this header we introduce a minimal Unit class that can be used for conformability, +// type checking and conversion at compile time. Template parameters are abused to create +// unique types, which then can go through (explicit) overload and conversion. Despite +// looking daunting, usage is quite straightforward once the appropriate aliases and +// inline operators are defined: +// +// U_mm distance = 10.0_mm; +// auto another = 20.5_mm; +// auto sum = distance + another; +// +// auto angle = 15.0_deg; +// auto test = distance + angle; // compile time error +// +// Template parameters are only used for type checking. The Unit contains a single value +// Unit::v and is thus well suited for parameter passing and inline initialization. +// +// Conversion to physical steps is done in modules::motion through the sister class +// AxisUnit, which also ensures quantities from different axes are not mixed together. +// AxisUnit are the normal units that should be used at runtime, which is why physical +// units and operators are not exported into the global namespace by default. + +namespace config { + +/// Base units for conformability testing +enum UnitBase : uint8_t { + Millimeter, + Degree, +}; + +/// Unit types for conformability testing +enum UnitType : uint8_t { + Lenght, + Speed, + Accel, +}; + +/// Generic unit type for compile-time conformability testing +template +struct Unit { + T v; + + typedef T type_t; + typedef Unit self_t; + + constexpr self_t operator+(const self_t r) { return { v + r.v }; } + constexpr self_t operator-(const self_t r) { return { v - r.v }; } + constexpr self_t operator-() { return { -v }; } + constexpr self_t operator*(const self_t r) { return { v * r.v }; } + constexpr self_t operator/(const self_t r) { return { v / r.v }; } +}; + +// Millimiters +typedef Unit U_mm; +typedef Unit U_mm_s; +typedef Unit U_mm_s2; + +static constexpr U_mm operator"" _mm(long double mm) { + return { mm }; +} + +static constexpr U_mm_s operator"" _mm_s(long double mm_s) { + return { mm_s }; +} + +static constexpr U_mm_s2 operator"" _mm_s2(long double mm_s2) { + return { mm_s2 }; +} + +// Degrees +typedef Unit U_deg; +typedef Unit U_deg_s; +typedef Unit U_deg_s2; + +static constexpr U_deg operator"" _deg(long double deg) { + return { deg }; +} + +static constexpr U_deg_s operator"" _deg_s(long double deg_s) { + return { deg_s }; +} + +static constexpr U_deg_s2 operator"" _deg_s2(long double deg_s2) { + return { deg_s2 }; +} + +} // namespace config diff --git a/src/modules/axisunit.h b/src/modules/axisunit.h new file mode 100644 index 0000000..3d0eba9 --- /dev/null +++ b/src/modules/axisunit.h @@ -0,0 +1,119 @@ +#pragma once +#include "../config/axis.h" +#include "pulse_gen.h" + +namespace modules { +namespace motion { + +// Import required types +using config::Axis; +using config::Idler; +using config::Pulley; +using config::Selector; + +using config::Accel; +using config::Lenght; +using config::Speed; + +using pulse_gen::pos_t; +using pulse_gen::steps_t; + +/// Specialized axis unit type for compile-time conformability testing. Like config::Unit +/// this is done ensure unit quantities are not mixed between types, while also providing +/// convenience methods to convert from physical units to AxisUnits directly at compile. +/// +/// Each axis unit type is separate for each axis, since the low-level count is not +/// directly comparable across axes. Quantities are normally defined through the +/// literar operators. Types and base axes are prefixed with a single letter identifier +/// for the axis: P=pulley, S=selector, I=idler. +/// +/// P_pos_t pulley_position = 10.0_P_mm; +/// auto pulley_zero = 0.0_P_mm; // implicit type +/// P_speed_ pulley_feedrate = 30.0_P_mm_s; +/// I_pos_t idler_position = 15.0_I_deg; +/// pulley_position + idler_position; // compile time error +/// +/// modules::motion::Motion.PlanMove (and related functions) support AxisUnit natively. +/// The low-level step count can be accessed when necessary through AxisUnit::v, which +/// should be avoided as it bypasses type checks. +template +struct AxisUnit { + T v; + + typedef T type_t; + typedef AxisUnit self_t; + + constexpr self_t operator+(const self_t r) { return { v + r.v }; } + constexpr self_t operator-(const self_t r) { return { v - r.v }; } + constexpr self_t operator-() { return { -v }; } + constexpr self_t operator*(const self_t r) { return { v * r.v }; } + constexpr self_t operator/(const self_t r) { return { v / r.v }; } +}; + +typedef AxisUnit P_pos_t; ///< Pulley position type (steps) +typedef AxisUnit P_speed_t; ///< Pulley speed type (steps/s) +typedef AxisUnit P_accel_t; ///< Pulley acceleration type (steps/s2) + +/// Convert a Unit to AxisUnit +template +static constexpr T unitToAxisUnit(const long double stepsPerUnit, U v) { + return { (typename T::type_t)(v.v * stepsPerUnit) }; +} + +static constexpr P_pos_t operator"" _P_mm(long double mm) { + return { unitToAxisUnit(config::pulley.stepsPerUnit, config::U_mm { mm }) }; +} + +static constexpr P_speed_t operator"" _P_mm_s(long double mm_s) { + return { unitToAxisUnit(config::pulley.stepsPerUnit, config::U_mm { mm_s }) }; +} + +static constexpr P_accel_t operator"" _P_mm_s2(long double mm_s2) { + return { unitToAxisUnit(config::pulley.stepsPerUnit, config::U_mm_s2 { mm_s2 }) }; +} + +typedef AxisUnit S_pos_t; ///< Selector position type (steps) +typedef AxisUnit S_speed_t; ///< Selector speed type (steps/s) +typedef AxisUnit S_accel_t; ///< Selector acceleration type (steps/s2) + +static constexpr S_pos_t operator"" _S_mm(long double mm) { + return { unitToAxisUnit(config::selector.stepsPerUnit, config::U_mm { mm }) }; +} + +static constexpr S_speed_t operator"" _S_mm_s(long double mm_s) { + return { unitToAxisUnit(config::selector.stepsPerUnit, config::U_mm_s { mm_s }) }; +} + +static constexpr S_accel_t operator"" _S_mm_s2(long double mm_s2) { + return { unitToAxisUnit(config::selector.stepsPerUnit, config::U_mm_s2 { mm_s2 }) }; +} + +typedef AxisUnit I_pos_t; ///< Idler position type (steps) +typedef AxisUnit I_speed_t; ///< Idler speed type (steps/s) +typedef AxisUnit I_accel_t; ///< Idler acceleration type (steps/s2) + +static constexpr I_pos_t operator"" _I_deg(long double deg) { + return { unitToAxisUnit(config::idler.stepsPerUnit, config::U_deg { deg }) }; +} + +static constexpr I_speed_t operator"" _I_deg_s(long double deg_s) { + return { unitToAxisUnit(config::idler.stepsPerUnit, config::U_deg_s { deg_s }) }; +} + +static constexpr I_accel_t operator"" _I_deg_s2(long double deg_s2) { + return { unitToAxisUnit(config::idler.stepsPerUnit, config::U_deg_s2 { deg_s2 }) }; +} + +} // namespace motion +} // namespace modules + +// Inject literal operators into the global namespace for convenience +using modules::motion::operator"" _P_mm; +using modules::motion::operator"" _P_mm_s; +using modules::motion::operator"" _P_mm_s2; +using modules::motion::operator"" _S_mm; +using modules::motion::operator"" _S_mm_s; +using modules::motion::operator"" _S_mm_s2; +using modules::motion::operator"" _I_deg; +using modules::motion::operator"" _I_deg_s; +using modules::motion::operator"" _I_deg_s2; diff --git a/src/modules/motion.h b/src/modules/motion.h index 98cc23b..109bc96 100644 --- a/src/modules/motion.h +++ b/src/modules/motion.h @@ -1,6 +1,7 @@ #pragma once #include "../pins.h" #include "pulse_gen.h" +#include "axisunit.h" namespace modules { @@ -9,10 +10,11 @@ namespace modules { /// Ideally enable stepping of motors under ISR (all timers have higher priority than serial) namespace motion { +// Import axes definitions +using config::NUM_AXIS; + using namespace hal::tmc2130; -using pulse_gen::pos_t; using pulse_gen::st_timer_t; -using pulse_gen::steps_t; // Check for configuration invariants static_assert( @@ -21,20 +23,13 @@ static_assert( "stepTimerQuantum must be smaller than the maximal stepping frequency interval"); /// Main axis enumeration -enum Axis : uint8_t { - Pulley, - Selector, - Idler, - _Axis_Last = Idler -}; - -static constexpr uint8_t NUM_AXIS = _Axis_Last + 1; - struct AxisParams { char name; MotorParams params; MotorCurrents currents; MotorMode mode; + long double stepsPerUnit; + config::UnitBase unitBase; steps_t jerk; steps_t accel; }; @@ -44,6 +39,12 @@ static constexpr MotorMode DefaultMotorMode(const config::AxisConfig &axis) { return axis.stealth ? MotorMode::Stealth : MotorMode::Normal; } +/// Convert an AxisUnit to a steps type (pos_t or steps_t) +template +static constexpr typename AU::type_t unitToSteps(const long double stepsPerUnit, U v) { + return unitToAxisUnit(stepsPerUnit, v).v; +} + /// Static axis configuration static constexpr AxisParams axisParams[NUM_AXIS] = { // Pulley @@ -52,8 +53,10 @@ static constexpr AxisParams axisParams[NUM_AXIS] = { .params = { .idx = Pulley, .dirOn = config::pulley.dirOn, .csPin = PULLEY_CS_PIN, .stepPin = PULLEY_STEP_PIN, .sgPin = PULLEY_SG_PIN, .uSteps = config::pulley.uSteps }, .currents = { .vSense = config::pulley.vSense, .iRun = config::pulley.iRun, .iHold = config::pulley.iHold }, .mode = DefaultMotorMode(config::pulley), - .jerk = config::pulley.jerk, - .accel = config::pulley.accel, + .stepsPerUnit = config::pulley.stepsPerUnit, + .unitBase = config::PulleyLimits::base, + .jerk = unitToSteps(config::pulley.stepsPerUnit, config::pulleyLimits.jerk), + .accel = unitToSteps(config::pulley.stepsPerUnit, config::pulleyLimits.accel), }, // Selector { @@ -61,8 +64,10 @@ static constexpr AxisParams axisParams[NUM_AXIS] = { .params = { .idx = Selector, .dirOn = config::selector.dirOn, .csPin = SELECTOR_CS_PIN, .stepPin = SELECTOR_STEP_PIN, .sgPin = SELECTOR_SG_PIN, .uSteps = config::selector.uSteps }, .currents = { .vSense = config::selector.vSense, .iRun = config::selector.iRun, .iHold = config::selector.iHold }, .mode = DefaultMotorMode(config::selector), - .jerk = config::selector.jerk, - .accel = config::selector.accel, + .stepsPerUnit = config::selector.stepsPerUnit, + .unitBase = config::SelectorLimits::base, + .jerk = unitToSteps(config::selector.stepsPerUnit, config::selectorLimits.jerk), + .accel = unitToSteps(config::selector.stepsPerUnit, config::selectorLimits.accel), }, // Idler { @@ -70,8 +75,10 @@ static constexpr AxisParams axisParams[NUM_AXIS] = { .params = { .idx = Idler, .dirOn = config::idler.dirOn, .csPin = IDLER_CS_PIN, .stepPin = IDLER_STEP_PIN, .sgPin = IDLER_SG_PIN, .uSteps = config::idler.uSteps }, .currents = { .vSense = config::idler.vSense, .iRun = config::idler.iRun, .iHold = config::idler.iHold }, .mode = DefaultMotorMode(config::idler), - .jerk = config::idler.jerk, - .accel = config::idler.accel, + .stepsPerUnit = config::idler.stepsPerUnit, + .unitBase = config::IdlerLimits::base, + .jerk = unitToSteps(config::idler.stepsPerUnit, config::idlerLimits.jerk), + .accel = unitToSteps(config::idler.stepsPerUnit, config::idlerLimits.accel), }, }; @@ -112,6 +119,26 @@ public: /// @param feedrate maximum feedrate void PlanMoveTo(Axis axis, pos_t pos, steps_t feedrate); + /// Enqueue a single axis move using PlanMoveTo, but using AxisUnit. The Axis needs to + /// be supplied as the first template argument: PlanMoveTo(pos, rate). + /// @see PlanMoveTo, unitToSteps + template + void PlanMoveTo(AxisUnit pos, AxisUnit feedrate) { + PlanMoveTo(A, pos.v, feedrate.v); + } + + /// Enqueue a single axis move using PlanMoveTo, but using physical units. The Axis + /// needs to be supplied as the first template argument: PlanMoveTo(pos, rate). + /// @see PlanMoveTo, unitToSteps + template + void PlanMoveTo(config::Unit pos, + config::Unit feedrate) { + static_assert(B == axisParams[A].unitBase, "incorrect unit base"); + PlanMoveTo( + unitToAxisUnit>(axisParams[A].stepsPerUnit, pos), + unitToAxisUnit>(axisParams[A].stepsPerUnit, feedrate)); + } + /// Enqueue a single axis move in steps starting and ending at zero speed with maximum /// feedrate. Moves can only be enqueued if the axis is not Full(). /// @param axis axis affected @@ -121,6 +148,26 @@ public: PlanMoveTo(axis, Position(axis) + delta, feedrate); } + /// Enqueue a single axis move using PlanMove, but using AxisUnit. The Axis needs to + /// be supplied as the first template argument: PlanMove(pos, rate). + /// @see PlanMove, unitToSteps + template + void PlanMove(AxisUnit delta, AxisUnit feedrate) { + PlanMove(A, delta.v, feedrate.v); + } + + /// Enqueue a single axis move using PlanMove, but using physical units. The Axis needs to + /// be supplied as the first template argument: PlanMove(pos, rate). + /// @see PlanMove, unitToSteps + template + void PlanMove(config::Unit delta, + config::Unit feedrate) { + static_assert(B == axisParams[A].unitBase, "incorrect unit base"); + PlanMove( + unitToAxisUnit>(axisParams[A].stepsPerUnit, delta), + unitToAxisUnit>(axisParams[A].stepsPerUnit, feedrate)); + } + /// @returns head position of an axis (last enqueued position) /// @param axis axis affected pos_t Position(Axis axis) const; diff --git a/tests/unit/modules/motion/test_motion.cpp b/tests/unit/modules/motion/test_motion.cpp index f7d8d14..426fa7d 100644 --- a/tests/unit/modules/motion/test_motion.cpp +++ b/tests/unit/modules/motion/test_motion.cpp @@ -31,6 +31,51 @@ TEST_CASE("motion::basic", "[motion]") { REQUIRE(motion.Position(Idler) == 10); } +TEST_CASE("motion::unit", "[motion]") { + // test AxisUnit conversion in the PlanMove and PlanMoveTo. + using config::operator"" _mm; + using config::operator"" _mm_s; + using config::operator"" _deg; + using config::operator"" _deg_s; + + REQUIRE(motion.QueueEmpty()); + REQUIRE(motion.Position(Pulley) == 0); + + // move with AxisUnit + pos_t target = config::pulley.stepsPerUnit * 10; + motion.PlanMoveTo(10.0_P_mm, 100.0_P_mm_s); + CHECK(stepUntilDone()); + REQUIRE(motion.Position(Pulley) == target); + + // move directly with physical units + motion.PlanMoveTo(10.0_mm, 100.0_mm_s); + REQUIRE(stepUntilDone() == 0); + REQUIRE(motion.Position(Pulley) == target); + + // relative move with AxisUnit + motion.PlanMove(-5.0_P_mm, 100.0_P_mm_s); + CHECK(stepUntilDone()); + REQUIRE(motion.Position(Pulley) == target / 2); + + // relative move with physical unit + motion.PlanMove(-5.0_mm, 100.0_mm_s); + CHECK(stepUntilDone()); + REQUIRE(motion.Position(Pulley) == 0); + + // now test remaining axes + target = config::selector.stepsPerUnit * 10; + motion.PlanMoveTo(10.0_S_mm, 100.0_S_mm_s); + motion.PlanMove(10.0_mm, 100.0_mm_s); + CHECK(stepUntilDone()); + REQUIRE(motion.Position(Selector) == target * 2); + + target = config::idler.stepsPerUnit * 10; + motion.PlanMoveTo(10.0_I_deg, 100.0_I_deg_s); + motion.PlanMove(10.0_deg, 100.0_deg_s); + CHECK(stepUntilDone()); + REQUIRE(motion.Position(Idler) == target * 2); +} + TEST_CASE("motion::dual_move_fwd", "[motion]") { // enqueue moves on two axes REQUIRE(motion.QueueEmpty()); From 402a2b91cebeb0363dd0fb782d9e84f01be48b6b Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 25 Jul 2021 17:08:15 +0200 Subject: [PATCH 04/12] Define Motion::PlanMove/PlanMoveTo as constexpr --- src/modules/motion.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/motion.h b/src/modules/motion.h index 109bc96..e33ed60 100644 --- a/src/modules/motion.h +++ b/src/modules/motion.h @@ -123,7 +123,7 @@ public: /// be supplied as the first template argument: PlanMoveTo(pos, rate). /// @see PlanMoveTo, unitToSteps template - void PlanMoveTo(AxisUnit pos, AxisUnit feedrate) { + constexpr void PlanMoveTo(AxisUnit pos, AxisUnit feedrate) { PlanMoveTo(A, pos.v, feedrate.v); } @@ -131,7 +131,7 @@ public: /// needs to be supplied as the first template argument: PlanMoveTo(pos, rate). /// @see PlanMoveTo, unitToSteps template - void PlanMoveTo(config::Unit pos, + constexpr void PlanMoveTo(config::Unit pos, config::Unit feedrate) { static_assert(B == axisParams[A].unitBase, "incorrect unit base"); PlanMoveTo( @@ -152,7 +152,7 @@ public: /// be supplied as the first template argument: PlanMove(pos, rate). /// @see PlanMove, unitToSteps template - void PlanMove(AxisUnit delta, AxisUnit feedrate) { + constexpr void PlanMove(AxisUnit delta, AxisUnit feedrate) { PlanMove(A, delta.v, feedrate.v); } @@ -160,7 +160,7 @@ public: /// be supplied as the first template argument: PlanMove(pos, rate). /// @see PlanMove, unitToSteps template - void PlanMove(config::Unit delta, + constexpr void PlanMove(config::Unit delta, config::Unit feedrate) { static_assert(B == axisParams[A].unitBase, "incorrect unit base"); PlanMove( From 49275b2cb2a7ef245d0011c47ca8fbe4db27fee3 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 25 Jul 2021 17:24:49 +0200 Subject: [PATCH 05/12] Update eject_steps with the new unit API for demonstration --- src/logic/eject_filament.cpp | 2 +- src/logic/eject_filament.h | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/logic/eject_filament.cpp b/src/logic/eject_filament.cpp index 82e7dbd..4417320 100644 --- a/src/logic/eject_filament.cpp +++ b/src/logic/eject_filament.cpp @@ -50,7 +50,7 @@ bool EjectFilament::Step() { if (mm::motion.QueueEmpty()) { // selector parked aside state = ProgressCode::EjectingFilament; mm::motion.InitAxis(mm::Pulley); - mm::motion.PlanMove(mm::Pulley, ejectSteps, 1500); + mm::motion.PlanMove(ejectLenght, ejectSpeed); } break; case ProgressCode::EjectingFilament: diff --git a/src/logic/eject_filament.h b/src/logic/eject_filament.h index 9012ac9..96ba418 100644 --- a/src/logic/eject_filament.h +++ b/src/logic/eject_filament.h @@ -2,9 +2,14 @@ #include #include "command_base.h" #include "unload_filament.h" +#include "../modules/axisunit.h" namespace logic { +// These cannot be class memebers without definition until c++17 +static constexpr modules::motion::P_pos_t ejectLenght = 50.0_P_mm; //@@TODO +static constexpr modules::motion::P_speed_t ejectSpeed = 1000.0_P_mm_s; //@@TODO + /// @brief A high-level command state machine - handles the complex logic of ejecting filament /// /// The eject operation consists of: @@ -31,7 +36,6 @@ public: ErrorCode Error() const override; private: - constexpr static const uint16_t ejectSteps = 500; //@@TODO UnloadFilament unl; ///< a high-level command/operation may be used as a building block of other operations as well uint8_t slot; void MoveSelectorAside(); From 187858d2281503ae47cafb508832661a9e472565 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 25 Jul 2021 22:14:56 +0200 Subject: [PATCH 06/12] Move config/unit.h to unit.h and it's own namespace --- src/config/axis.h | 4 +++- src/{config => }/unit.h | 53 +++++++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 24 deletions(-) rename src/{config => }/unit.h (54%) diff --git a/src/config/axis.h b/src/config/axis.h index 6d5acd0..c55afbe 100644 --- a/src/config/axis.h +++ b/src/config/axis.h @@ -1,9 +1,11 @@ #pragma once #include -#include "unit.h" +#include "../unit.h" namespace config { +using namespace unit; + /// Axis configuration data struct AxisConfig { bool dirOn; ///< direction ON state (for inversion) diff --git a/src/config/unit.h b/src/unit.h similarity index 54% rename from src/config/unit.h rename to src/unit.h index c855934..f6a8f92 100644 --- a/src/config/unit.h +++ b/src/unit.h @@ -1,28 +1,27 @@ #pragma once #include -// In this header we introduce a minimal Unit class that can be used for conformability, -// type checking and conversion at compile time. Template parameters are abused to create -// unique types, which then can go through (explicit) overload and conversion. Despite -// looking daunting, usage is quite straightforward once the appropriate aliases and -// inline operators are defined: -// -// U_mm distance = 10.0_mm; -// auto another = 20.5_mm; -// auto sum = distance + another; -// -// auto angle = 15.0_deg; -// auto test = distance + angle; // compile time error -// -// Template parameters are only used for type checking. The Unit contains a single value -// Unit::v and is thus well suited for parameter passing and inline initialization. -// -// Conversion to physical steps is done in modules::motion through the sister class -// AxisUnit, which also ensures quantities from different axes are not mixed together. -// AxisUnit are the normal units that should be used at runtime, which is why physical -// units and operators are not exported into the global namespace by default. - -namespace config { +/// Introduce a minimal Unit class that can be used for conformability, type checking and +/// conversion at compile time. Template parameters are abused to create unique types, +/// which then can go through (explicit) overload and conversion. Despite looking +/// daunting, usage is quite straightforward once the appropriate aliases and inline +/// operators are defined: +/// +/// U_mm distance = 10.0_mm; +/// auto another = 20.5_mm; +/// auto sum = distance + another; +/// +/// auto angle = 15.0_deg; +/// auto test = distance + angle; // compile time error +/// +/// Template parameters are only used for type checking. The Unit contains a single value +/// Unit::v and is thus well suited for parameter passing and inline initialization. +/// +/// Conversion to physical steps is done in modules::motion through the sister class +/// AxisUnit, which also ensures quantities from different axes are not mixed together. +/// AxisUnit are the normal units that should be used at runtime, which is why physical +/// units and operators are not exported into the global namespace by default. +namespace unit { /// Base units for conformability testing enum UnitBase : uint8_t { @@ -86,4 +85,12 @@ static constexpr U_deg_s2 operator"" _deg_s2(long double deg_s2) { return { deg_s2 }; } -} // namespace config +} // namespace unit + +// Inject literal operators into the global namespace for convenience +using unit::operator"" _mm; +using unit::operator"" _mm_s; +using unit::operator"" _mm_s2; +using unit::operator"" _deg; +using unit::operator"" _deg_s; +using unit::operator"" _deg_s2; From b133c8b975b9305959965091354651f40c837d7f Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 25 Jul 2021 22:25:48 +0200 Subject: [PATCH 07/12] Simplify and enhance unit conversion - Move unit/step conversion to modules/axisunit.h - Unify motion::unitToAxisUnit<> and motion::unitToSteps<>, making conversion in other modules just as easy as motion. - Improve the documentation --- src/modules/axisunit.h | 97 +++++++++++++++++++++++++++++++----------- src/modules/motion.h | 38 +++++------------ src/unit.h | 3 ++ 3 files changed, 87 insertions(+), 51 deletions(-) diff --git a/src/modules/axisunit.h b/src/modules/axisunit.h index 3d0eba9..736a6fc 100644 --- a/src/modules/axisunit.h +++ b/src/modules/axisunit.h @@ -20,26 +20,52 @@ using pulse_gen::steps_t; /// Specialized axis unit type for compile-time conformability testing. Like config::Unit /// this is done ensure unit quantities are not mixed between types, while also providing -/// convenience methods to convert from physical units to AxisUnits directly at compile. +/// convenience methods to convert from physical units to AxisUnits directly at compile +/// time. AxisUnits are just as efficient as non-checked pos_t and steps_t. /// /// Each axis unit type is separate for each axis, since the low-level count is not /// directly comparable across axes. Quantities are normally defined through the -/// literar operators. Types and base axes are prefixed with a single letter identifier +/// literal operators. Types and base axes are prefixed with a single letter identifier /// for the axis: P=pulley, S=selector, I=idler. /// -/// P_pos_t pulley_position = 10.0_P_mm; -/// auto pulley_zero = 0.0_P_mm; // implicit type -/// P_speed_ pulley_feedrate = 30.0_P_mm_s; -/// I_pos_t idler_position = 15.0_I_deg; -/// pulley_position + idler_position; // compile time error +/// P_pos_t pulley_position = 10.0_P_mm; +/// auto pulley_zero = 0.0_P_mm; // implicit type +/// P_speed_ pulley_feedrate = 30.0_P_mm_s; +/// I_pos_t idler_position = 15.0_I_deg; +/// pulley_position + idler_position; // compile time error +/// +/// modules::motion::Motion.PlanMove (and related functions) support both physical and +/// AxisUnit natively. This is done by specifying the axis through the first template +/// parameter, which ensures related units are also conforming: +/// +/// motion.PlanMoveTo(10.0_mm, 100._mm_s); // using physical units +/// motion.PlanMoveTo(10.0_P_mm, 100._P_mm_s); // using AxisUnit +/// +/// Physical units are always represented with the largest floating point type, so they +/// should only be used at compile-time (constants, fixed-lenght moves). +/// +/// If runtime manipulation is necessary, use AxisUnit should always be used instead. +/// Conversion from physical to AxisUnit can be done through unitToAxisUnit: +/// +/// unitToAxisUnit(physical_type) +/// +/// Examples: +/// +/// P_pos_t pulley_pos = unitToAxisUnit(10.0_mm); +/// P_speed_t pulley_speed = unitToAxisUnit(100.0_mm_s); +/// +/// Conversion to pos_t or steps_t works the same using unitToSteps instead. /// -/// modules::motion::Motion.PlanMove (and related functions) support AxisUnit natively. /// The low-level step count can be accessed when necessary through AxisUnit::v, which -/// should be avoided as it bypasses type checks. +/// should be avoided as it bypasses type checks. AxisUnit can also be constructed by +/// providing a counter as the first initializer. template struct AxisUnit { T v; + static constexpr Axis axis = A; + static constexpr config::UnitType unit = U; + typedef T type_t; typedef AxisUnit self_t; @@ -50,58 +76,81 @@ struct AxisUnit { constexpr self_t operator/(const self_t r) { return { v / r.v }; } }; +/// Axis type conversion table for template expansion +struct AxisScale { + unit::UnitBase base; + long double stepsPerUnit; +}; + +static constexpr AxisScale axisScale[config::NUM_AXIS] = { + { config::pulleyLimits.base, config::pulley.stepsPerUnit }, + { config::selectorLimits.base, config::selector.stepsPerUnit }, + { config::idlerLimits.base, config::idler.stepsPerUnit }, +}; + +/// Convert a Unit to AxisUnit +template +static constexpr T unitToAxisUnit(U v) { + static_assert(T::unit == U::unit, "incorrect unit type conversion"); + static_assert(U::base == axisScale[T::axis].base, "incorrect unit base conversion"); + return { (typename T::type_t)(v.v * axisScale[T::axis].stepsPerUnit) }; +} + +/// Convert an Unit to a steps type (pos_t or steps_t) +template +static constexpr typename AU::type_t unitToSteps(U v) { + return unitToAxisUnit(v).v; +} + +// Pulley typedef AxisUnit P_pos_t; ///< Pulley position type (steps) typedef AxisUnit P_speed_t; ///< Pulley speed type (steps/s) typedef AxisUnit P_accel_t; ///< Pulley acceleration type (steps/s2) -/// Convert a Unit to AxisUnit -template -static constexpr T unitToAxisUnit(const long double stepsPerUnit, U v) { - return { (typename T::type_t)(v.v * stepsPerUnit) }; -} - static constexpr P_pos_t operator"" _P_mm(long double mm) { - return { unitToAxisUnit(config::pulley.stepsPerUnit, config::U_mm { mm }) }; + return { unitToAxisUnit(config::U_mm { mm }) }; } static constexpr P_speed_t operator"" _P_mm_s(long double mm_s) { - return { unitToAxisUnit(config::pulley.stepsPerUnit, config::U_mm { mm_s }) }; + return { unitToAxisUnit(config::U_mm_s { mm_s }) }; } static constexpr P_accel_t operator"" _P_mm_s2(long double mm_s2) { - return { unitToAxisUnit(config::pulley.stepsPerUnit, config::U_mm_s2 { mm_s2 }) }; + return { unitToAxisUnit(config::U_mm_s2 { mm_s2 }) }; } +// Selector typedef AxisUnit S_pos_t; ///< Selector position type (steps) typedef AxisUnit S_speed_t; ///< Selector speed type (steps/s) typedef AxisUnit S_accel_t; ///< Selector acceleration type (steps/s2) static constexpr S_pos_t operator"" _S_mm(long double mm) { - return { unitToAxisUnit(config::selector.stepsPerUnit, config::U_mm { mm }) }; + return { unitToAxisUnit(config::U_mm { mm }) }; } static constexpr S_speed_t operator"" _S_mm_s(long double mm_s) { - return { unitToAxisUnit(config::selector.stepsPerUnit, config::U_mm_s { mm_s }) }; + return { unitToAxisUnit(config::U_mm_s { mm_s }) }; } static constexpr S_accel_t operator"" _S_mm_s2(long double mm_s2) { - return { unitToAxisUnit(config::selector.stepsPerUnit, config::U_mm_s2 { mm_s2 }) }; + return { unitToAxisUnit(config::U_mm_s2 { mm_s2 }) }; } +// Idler typedef AxisUnit I_pos_t; ///< Idler position type (steps) typedef AxisUnit I_speed_t; ///< Idler speed type (steps/s) typedef AxisUnit I_accel_t; ///< Idler acceleration type (steps/s2) static constexpr I_pos_t operator"" _I_deg(long double deg) { - return { unitToAxisUnit(config::idler.stepsPerUnit, config::U_deg { deg }) }; + return { unitToAxisUnit(config::U_deg { deg }) }; } static constexpr I_speed_t operator"" _I_deg_s(long double deg_s) { - return { unitToAxisUnit(config::idler.stepsPerUnit, config::U_deg_s { deg_s }) }; + return { unitToAxisUnit(config::U_deg_s { deg_s }) }; } static constexpr I_accel_t operator"" _I_deg_s2(long double deg_s2) { - return { unitToAxisUnit(config::idler.stepsPerUnit, config::U_deg_s2 { deg_s2 }) }; + return { unitToAxisUnit(config::U_deg_s2 { deg_s2 }) }; } } // namespace motion diff --git a/src/modules/motion.h b/src/modules/motion.h index e33ed60..c8c377e 100644 --- a/src/modules/motion.h +++ b/src/modules/motion.h @@ -28,8 +28,6 @@ struct AxisParams { MotorParams params; MotorCurrents currents; MotorMode mode; - long double stepsPerUnit; - config::UnitBase unitBase; steps_t jerk; steps_t accel; }; @@ -39,12 +37,6 @@ static constexpr MotorMode DefaultMotorMode(const config::AxisConfig &axis) { return axis.stealth ? MotorMode::Stealth : MotorMode::Normal; } -/// Convert an AxisUnit to a steps type (pos_t or steps_t) -template -static constexpr typename AU::type_t unitToSteps(const long double stepsPerUnit, U v) { - return unitToAxisUnit(stepsPerUnit, v).v; -} - /// Static axis configuration static constexpr AxisParams axisParams[NUM_AXIS] = { // Pulley @@ -53,10 +45,8 @@ static constexpr AxisParams axisParams[NUM_AXIS] = { .params = { .idx = Pulley, .dirOn = config::pulley.dirOn, .csPin = PULLEY_CS_PIN, .stepPin = PULLEY_STEP_PIN, .sgPin = PULLEY_SG_PIN, .uSteps = config::pulley.uSteps }, .currents = { .vSense = config::pulley.vSense, .iRun = config::pulley.iRun, .iHold = config::pulley.iHold }, .mode = DefaultMotorMode(config::pulley), - .stepsPerUnit = config::pulley.stepsPerUnit, - .unitBase = config::PulleyLimits::base, - .jerk = unitToSteps(config::pulley.stepsPerUnit, config::pulleyLimits.jerk), - .accel = unitToSteps(config::pulley.stepsPerUnit, config::pulleyLimits.accel), + .jerk = unitToSteps(config::pulleyLimits.jerk), + .accel = unitToSteps(config::pulleyLimits.accel), }, // Selector { @@ -64,10 +54,8 @@ static constexpr AxisParams axisParams[NUM_AXIS] = { .params = { .idx = Selector, .dirOn = config::selector.dirOn, .csPin = SELECTOR_CS_PIN, .stepPin = SELECTOR_STEP_PIN, .sgPin = SELECTOR_SG_PIN, .uSteps = config::selector.uSteps }, .currents = { .vSense = config::selector.vSense, .iRun = config::selector.iRun, .iHold = config::selector.iHold }, .mode = DefaultMotorMode(config::selector), - .stepsPerUnit = config::selector.stepsPerUnit, - .unitBase = config::SelectorLimits::base, - .jerk = unitToSteps(config::selector.stepsPerUnit, config::selectorLimits.jerk), - .accel = unitToSteps(config::selector.stepsPerUnit, config::selectorLimits.accel), + .jerk = unitToSteps(config::selectorLimits.jerk), + .accel = unitToSteps(config::selectorLimits.accel), }, // Idler { @@ -75,10 +63,8 @@ static constexpr AxisParams axisParams[NUM_AXIS] = { .params = { .idx = Idler, .dirOn = config::idler.dirOn, .csPin = IDLER_CS_PIN, .stepPin = IDLER_STEP_PIN, .sgPin = IDLER_SG_PIN, .uSteps = config::idler.uSteps }, .currents = { .vSense = config::idler.vSense, .iRun = config::idler.iRun, .iHold = config::idler.iHold }, .mode = DefaultMotorMode(config::idler), - .stepsPerUnit = config::idler.stepsPerUnit, - .unitBase = config::IdlerLimits::base, - .jerk = unitToSteps(config::idler.stepsPerUnit, config::idlerLimits.jerk), - .accel = unitToSteps(config::idler.stepsPerUnit, config::idlerLimits.accel), + .jerk = unitToSteps(config::idlerLimits.jerk), + .accel = unitToSteps(config::idlerLimits.accel), }, }; @@ -133,10 +119,9 @@ public: template constexpr void PlanMoveTo(config::Unit pos, config::Unit feedrate) { - static_assert(B == axisParams[A].unitBase, "incorrect unit base"); PlanMoveTo( - unitToAxisUnit>(axisParams[A].stepsPerUnit, pos), - unitToAxisUnit>(axisParams[A].stepsPerUnit, feedrate)); + unitToAxisUnit>(pos), + unitToAxisUnit>(feedrate)); } /// Enqueue a single axis move in steps starting and ending at zero speed with maximum @@ -162,10 +147,9 @@ public: template constexpr void PlanMove(config::Unit delta, config::Unit feedrate) { - static_assert(B == axisParams[A].unitBase, "incorrect unit base"); PlanMove( - unitToAxisUnit>(axisParams[A].stepsPerUnit, delta), - unitToAxisUnit>(axisParams[A].stepsPerUnit, feedrate)); + unitToAxisUnit>(delta), + unitToAxisUnit>(feedrate)); } /// @returns head position of an axis (last enqueued position) @@ -264,7 +248,7 @@ private: }; /// ISR stepping routine -extern void ISR(); +//extern void ISR(); extern Motion motion; diff --git a/src/unit.h b/src/unit.h index f6a8f92..52525fb 100644 --- a/src/unit.h +++ b/src/unit.h @@ -41,6 +41,9 @@ template struct Unit { T v; + static constexpr UnitBase base = B; + static constexpr UnitType unit = U; + typedef T type_t; typedef Unit self_t; From 6daf7fd060f5df7095b0c8be50a3d27ebcb3cc30 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 25 Jul 2021 22:39:18 +0200 Subject: [PATCH 08/12] Convert config::idlerSlotPositions to physical units --- src/config/config.h | 4 +++- src/modules/idler.cpp | 4 ++-- src/modules/idler.h | 8 ++++++-- .../unit/logic/eject_filament/test_eject_filament.cpp | 2 +- .../logic/feed_to_bondtech/test_feed_to_bondtech.cpp | 4 ++-- tests/unit/logic/feed_to_finda/test_feed_to_finda.cpp | 10 +++++----- tests/unit/logic/helpers/helpers.ipp | 4 ++-- .../logic/unload_to_finda/test_unload_to_finda.cpp | 4 ++-- 8 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/config/config.h b/src/config/config.h index ece5230..cdc9ce6 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -8,7 +8,9 @@ namespace config { static constexpr const uint8_t toolCount = 5U; ///< Max number of extruders/tools/slots // Idler's setup -static constexpr uint16_t idlerSlotPositions[toolCount + 1] = { 1, 2, 3, 4, 5, 0 }; ///< slots 0-4 are the real ones, the 5th is the idle position +static constexpr U_deg idlerSlotPositions[toolCount + 1] = { + 1.0_deg, 2.0_deg, 3.0_deg, 4.0_deg, 5.0_deg, 0 +}; ///< slots 0-4 are the real ones, the 5th is the idle position // Selector's setup static constexpr uint16_t selectorSlotPositions[toolCount + 1] = { 1, 2, 3, 4, 5, 6 }; ///< slots 0-4 are the real ones, the 5th is the farthest parking positions diff --git a/src/modules/idler.cpp b/src/modules/idler.cpp index b04dd4b..ca7618d 100644 --- a/src/modules/idler.cpp +++ b/src/modules/idler.cpp @@ -22,7 +22,7 @@ bool Idler::Disengage() { mm::motion.InitAxis(mm::Idler); // plan move to idle position - mm::motion.PlanMove(mm::Idler, config::idlerSlotPositions[IdleSlotIndex()] - mm::motion.Position(mm::Idler), 1000); // @@TODO + mm::motion.PlanMoveTo(SlotPosition(IdleSlotIndex()), 1000._I_deg_s); // @@TODO state = Moving; return true; } @@ -38,7 +38,7 @@ bool Idler::Engage(uint8_t slot) { return true; mm::motion.InitAxis(mm::Idler); - mm::motion.PlanMove(mm::Idler, config::idlerSlotPositions[slot] - mm::motion.Position(mm::Idler), 1000); // @@TODO + mm::motion.PlanMoveTo(SlotPosition(slot), 1000._I_deg_s); // @@TODO state = Moving; return true; } diff --git a/src/modules/idler.h b/src/modules/idler.h index 93e7e54..79946cf 100644 --- a/src/modules/idler.h +++ b/src/modules/idler.h @@ -1,12 +1,14 @@ #pragma once #include "../config/config.h" -#include +#include "../modules/axisunit.h" namespace modules { /// The idler namespace provides all necessary facilities related to the logical model of the idler device of the MMU unit. namespace idler { +namespace mm = modules::motion; + /// The Idler model handles asynchronnous Engaging / Disengaging operations and keeps track of idler's current state. class Idler { public: @@ -50,7 +52,9 @@ public: inline uint8_t Slot() const { return currentSlot; } /// @returns predefined positions of individual slots - inline static uint16_t SlotPosition(uint8_t slot) { return config::idlerSlotPositions[slot]; } + static constexpr mm::I_pos_t SlotPosition(uint8_t slot) { + return mm::unitToAxisUnit(config::idlerSlotPositions[slot]); + } /// @returns the index of idle position of the idler, usually 5 in case of 0-4 valid indices of filament slots inline static constexpr uint8_t IdleSlotIndex() { return config::toolCount; } diff --git a/tests/unit/logic/eject_filament/test_eject_filament.cpp b/tests/unit/logic/eject_filament/test_eject_filament.cpp index ab608b7..7fed93d 100644 --- a/tests/unit/logic/eject_filament/test_eject_filament.cpp +++ b/tests/unit/logic/eject_filament/test_eject_filament.cpp @@ -41,7 +41,7 @@ TEST_CASE("eject_filament::eject0", "[eject_filament][.]") { // it should have instructed the selector and idler to move to slot 1 // check if the idler and selector have the right command - CHECK(modules::motion::axes[modules::motion::Idler].targetPos == mi::Idler::SlotPosition(0)); + CHECK(modules::motion::axes[modules::motion::Idler].targetPos == mi::Idler::SlotPosition(0).v); CHECK(modules::motion::axes[modules::motion::Selector].targetPos == ms::Selector::SlotPosition(4)); // now cycle at most some number of cycles (to be determined yet) and then verify, that the idler and selector reached their target positions diff --git a/tests/unit/logic/feed_to_bondtech/test_feed_to_bondtech.cpp b/tests/unit/logic/feed_to_bondtech/test_feed_to_bondtech.cpp index f96fd81..cdc63ee 100644 --- a/tests/unit/logic/feed_to_bondtech/test_feed_to_bondtech.cpp +++ b/tests/unit/logic/feed_to_bondtech/test_feed_to_bondtech.cpp @@ -44,7 +44,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") { // it should have instructed the selector and idler to move to slot 0 // check if the idler and selector have the right command - CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0)); + CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0).v); CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0)); CHECK(mm::axes[mm::Idler].enabled == true); @@ -54,7 +54,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") { [&](int) { return !mi::idler.Engaged(); }, 5000)); - CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0)); + CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0).v); CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0)); // idler engaged, selector in position, we'll start pushing filament diff --git a/tests/unit/logic/feed_to_finda/test_feed_to_finda.cpp b/tests/unit/logic/feed_to_finda/test_feed_to_finda.cpp index 6d7fa0d..19184bf 100644 --- a/tests/unit/logic/feed_to_finda/test_feed_to_finda.cpp +++ b/tests/unit/logic/feed_to_finda/test_feed_to_finda.cpp @@ -44,7 +44,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") { // it should have instructed the selector and idler to move to slot 1 // check if the idler and selector have the right command - CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0)); + CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0).v); CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0)); CHECK(mm::axes[mm::Idler].enabled == true); @@ -54,7 +54,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") { [&](int) { return !mi::idler.Engaged(); }, 5000)); - CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0)); + CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0).v); CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0)); // idler engaged, selector in position, we'll start pushing filament @@ -86,7 +86,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") { // [&](int) { return mi::idler.Engaged(); }, // 5000)); - CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0)); // @@TODO constants + CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0).v); // @@TODO constants CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0)); // state machine finished ok, the green LED should be on @@ -111,7 +111,7 @@ TEST_CASE("feed_to_finda::FINDA_failed", "[feed_to_finda]") { // it should have instructed the selector and idler to move to slot 1 // check if the idler and selector have the right command - CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0)); + CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0).v); CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0)); // engaging idler @@ -120,7 +120,7 @@ TEST_CASE("feed_to_finda::FINDA_failed", "[feed_to_finda]") { [&](int) { return !mi::idler.Engaged(); }, 5000)); - CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0)); + CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0).v); CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0)); // idler engaged, we'll start pushing filament diff --git a/tests/unit/logic/helpers/helpers.ipp b/tests/unit/logic/helpers/helpers.ipp index 20d0d51..711f75e 100644 --- a/tests/unit/logic/helpers/helpers.ipp +++ b/tests/unit/logic/helpers/helpers.ipp @@ -3,7 +3,7 @@ template bool VerifyState(SM &uf, bool filamentLoaded, uint8_t idlerSlotIndex, uint8_t selectorSlotIndex, bool findaPressed, ml::Mode greenLEDMode, ml::Mode redLEDMode, ErrorCode err, ProgressCode topLevelProgress) { CHECKED_ELSE(mg::globals.FilamentLoaded() == filamentLoaded) { return false; } - CHECKED_ELSE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(idlerSlotIndex)) { return false; } + CHECKED_ELSE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(idlerSlotIndex).v) { return false; } CHECKED_ELSE(mi::idler.Engaged() == (idlerSlotIndex < config::toolCount)) { return false; } CHECKED_ELSE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(selectorSlotIndex)) { return false; } CHECKED_ELSE(ms::selector.Slot() == selectorSlotIndex) { return false; } @@ -30,7 +30,7 @@ template bool VerifyState2(SM &uf, bool filamentLoaded, uint8_t idlerSlotIndex, uint8_t selectorSlotIndex, bool findaPressed, uint8_t ledCheckIndex, ml::Mode greenLEDMode, ml::Mode redLEDMode, ErrorCode err, ProgressCode topLevelProgress) { CHECKED_ELSE(mg::globals.FilamentLoaded() == filamentLoaded) { return false; } - CHECKED_ELSE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(idlerSlotIndex)) { return false; } + CHECKED_ELSE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(idlerSlotIndex).v) { return false; } CHECKED_ELSE(mi::idler.Engaged() == (idlerSlotIndex < config::toolCount)) { return false; } CHECKED_ELSE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(selectorSlotIndex)) { return false; } CHECKED_ELSE(ms::selector.Slot() == selectorSlotIndex) { return false; } diff --git a/tests/unit/logic/unload_to_finda/test_unload_to_finda.cpp b/tests/unit/logic/unload_to_finda/test_unload_to_finda.cpp index 25c816a..a05973b 100644 --- a/tests/unit/logic/unload_to_finda/test_unload_to_finda.cpp +++ b/tests/unit/logic/unload_to_finda/test_unload_to_finda.cpp @@ -44,7 +44,7 @@ TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") { // it should have instructed the selector and idler to move to slot 1 // check if the idler and selector have the right command - CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0)); + CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0).v); CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0)); CHECK(mm::axes[mm::Idler].enabled == true); @@ -93,7 +93,7 @@ TEST_CASE("unload_to_finda::unload_without_FINDA_trigger", "[unload_to_finda]") // it should have instructed the selector and idler to move to slot 1 // check if the idler and selector have the right command - CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0)); + CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0).v); CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0)); CHECK(mm::axes[mm::Idler].enabled == true); From d95589782957ed305fedfd9d715c7695c26e217b Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 25 Jul 2021 23:13:28 +0200 Subject: [PATCH 09/12] Convert config::selectorSlotPositions to physical units --- src/config/config.h | 4 +++- src/modules/selector.cpp | 2 +- src/modules/selector.h | 8 ++++++-- .../unit/logic/eject_filament/test_eject_filament.cpp | 2 +- .../logic/feed_to_bondtech/test_feed_to_bondtech.cpp | 6 +++--- tests/unit/logic/feed_to_finda/test_feed_to_finda.cpp | 10 +++++----- tests/unit/logic/helpers/helpers.ipp | 4 ++-- tests/unit/logic/stubs/stub_motion.cpp | 4 +++- .../logic/unload_to_finda/test_unload_to_finda.cpp | 4 ++-- 9 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/config/config.h b/src/config/config.h index cdc9ce6..11cfdd7 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -13,7 +13,9 @@ static constexpr U_deg idlerSlotPositions[toolCount + 1] = { }; ///< slots 0-4 are the real ones, the 5th is the idle position // Selector's setup -static constexpr uint16_t selectorSlotPositions[toolCount + 1] = { 1, 2, 3, 4, 5, 6 }; ///< slots 0-4 are the real ones, the 5th is the farthest parking positions +static constexpr U_mm selectorSlotPositions[toolCount + 1] = { + 1.0_mm, 2.0_mm, 3.0_mm, 4.0_mm, 5.0_mm, 6.0_mm +}; ///< slots 0-4 are the real ones, the 5th is the farthest parking positions // Printer's filament sensor setup static constexpr const uint16_t fsensorDebounceMs = 10; diff --git a/src/modules/selector.cpp b/src/modules/selector.cpp index 67605e0..6ad25b1 100644 --- a/src/modules/selector.cpp +++ b/src/modules/selector.cpp @@ -21,7 +21,7 @@ bool Selector::MoveToSlot(uint8_t slot) { return true; mm::motion.InitAxis(mm::Selector); - mm::motion.PlanMove(mm::Selector, config::selectorSlotPositions[slot] - mm::motion.Position(mm::Selector), 1000); // @@TODO + mm::motion.PlanMoveTo(SlotPosition(slot), 1000.0_S_mm_s); // @@TODO state = Moving; return true; } diff --git a/src/modules/selector.h b/src/modules/selector.h index 61bc807..8f6b347 100644 --- a/src/modules/selector.h +++ b/src/modules/selector.h @@ -1,12 +1,14 @@ #pragma once #include "../config/config.h" -#include +#include "../modules/axisunit.h" namespace modules { /// The selector namespace provides all necessary facilities related to the logical model of the selector device of the MMU unit. namespace selector { +namespace mm = modules::motion; + /// The selector model - handles asynchronnous move operations between filament individual slots and keeps track of selector's current state. class Selector { public: @@ -41,7 +43,9 @@ public: inline uint8_t Slot() const { return currentSlot; } /// @returns predefined positions of individual slots - inline static uint16_t SlotPosition(uint8_t slot) { return config::selectorSlotPositions[slot]; } + static constexpr mm::S_pos_t SlotPosition(uint8_t slot) { + return mm::unitToAxisUnit(config::selectorSlotPositions[slot]); + } /// @returns the index of idle position of the selector, usually 5 in case of 0-4 valid indices of filament slots inline static constexpr uint8_t IdleSlotIndex() { return config::toolCount; } diff --git a/tests/unit/logic/eject_filament/test_eject_filament.cpp b/tests/unit/logic/eject_filament/test_eject_filament.cpp index 7fed93d..54f0f43 100644 --- a/tests/unit/logic/eject_filament/test_eject_filament.cpp +++ b/tests/unit/logic/eject_filament/test_eject_filament.cpp @@ -42,7 +42,7 @@ TEST_CASE("eject_filament::eject0", "[eject_filament][.]") { // it should have instructed the selector and idler to move to slot 1 // check if the idler and selector have the right command CHECK(modules::motion::axes[modules::motion::Idler].targetPos == mi::Idler::SlotPosition(0).v); - CHECK(modules::motion::axes[modules::motion::Selector].targetPos == ms::Selector::SlotPosition(4)); + CHECK(modules::motion::axes[modules::motion::Selector].targetPos == ms::Selector::SlotPosition(4).v); // now cycle at most some number of cycles (to be determined yet) and then verify, that the idler and selector reached their target positions REQUIRE(WhileTopState(ef, ProgressCode::SelectingFilamentSlot, 5000)); diff --git a/tests/unit/logic/feed_to_bondtech/test_feed_to_bondtech.cpp b/tests/unit/logic/feed_to_bondtech/test_feed_to_bondtech.cpp index cdc63ee..a57ac4b 100644 --- a/tests/unit/logic/feed_to_bondtech/test_feed_to_bondtech.cpp +++ b/tests/unit/logic/feed_to_bondtech/test_feed_to_bondtech.cpp @@ -45,7 +45,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") { // it should have instructed the selector and idler to move to slot 0 // check if the idler and selector have the right command CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0).v); - CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0)); + CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0).v); CHECK(mm::axes[mm::Idler].enabled == true); // engaging idler @@ -55,7 +55,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") { 5000)); CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0).v); - CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0)); + CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0).v); // idler engaged, selector in position, we'll start pushing filament REQUIRE(fb.State() == FeedToBondtech::PushingFilament); @@ -81,7 +81,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") { // 5000)); // CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); // @@TODO constants - CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0)); + CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0).v); // state machine finished ok, the green LED should be on REQUIRE(fb.State() == FeedToBondtech::OK); diff --git a/tests/unit/logic/feed_to_finda/test_feed_to_finda.cpp b/tests/unit/logic/feed_to_finda/test_feed_to_finda.cpp index 19184bf..b379035 100644 --- a/tests/unit/logic/feed_to_finda/test_feed_to_finda.cpp +++ b/tests/unit/logic/feed_to_finda/test_feed_to_finda.cpp @@ -45,7 +45,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") { // it should have instructed the selector and idler to move to slot 1 // check if the idler and selector have the right command CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0).v); - CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0)); + CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0).v); CHECK(mm::axes[mm::Idler].enabled == true); // engaging idler @@ -55,7 +55,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") { 5000)); CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0).v); - CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0)); + CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0).v); // idler engaged, selector in position, we'll start pushing filament REQUIRE(ff.State() == FeedToFinda::PushingFilament); @@ -87,7 +87,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") { // 5000)); CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0).v); // @@TODO constants - CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0)); + CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0).v); // state machine finished ok, the green LED should be on REQUIRE(ff.State() == FeedToFinda::OK); @@ -112,7 +112,7 @@ TEST_CASE("feed_to_finda::FINDA_failed", "[feed_to_finda]") { // it should have instructed the selector and idler to move to slot 1 // check if the idler and selector have the right command CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0).v); - CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0)); + CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0).v); // engaging idler REQUIRE(WhileCondition( @@ -121,7 +121,7 @@ TEST_CASE("feed_to_finda::FINDA_failed", "[feed_to_finda]") { 5000)); CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0).v); - CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0)); + CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0).v); // idler engaged, we'll start pushing filament REQUIRE(ff.State() == FeedToFinda::PushingFilament); diff --git a/tests/unit/logic/helpers/helpers.ipp b/tests/unit/logic/helpers/helpers.ipp index 711f75e..9ab5d4e 100644 --- a/tests/unit/logic/helpers/helpers.ipp +++ b/tests/unit/logic/helpers/helpers.ipp @@ -5,7 +5,7 @@ bool VerifyState(SM &uf, bool filamentLoaded, uint8_t idlerSlotIndex, uint8_t se CHECKED_ELSE(mg::globals.FilamentLoaded() == filamentLoaded) { return false; } CHECKED_ELSE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(idlerSlotIndex).v) { return false; } CHECKED_ELSE(mi::idler.Engaged() == (idlerSlotIndex < config::toolCount)) { return false; } - CHECKED_ELSE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(selectorSlotIndex)) { return false; } + CHECKED_ELSE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(selectorSlotIndex).v) { return false; } CHECKED_ELSE(ms::selector.Slot() == selectorSlotIndex) { return false; } CHECKED_ELSE(mf::finda.Pressed() == findaPressed) { return false; } @@ -32,7 +32,7 @@ bool VerifyState2(SM &uf, bool filamentLoaded, uint8_t idlerSlotIndex, uint8_t s CHECKED_ELSE(mg::globals.FilamentLoaded() == filamentLoaded) { return false; } CHECKED_ELSE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(idlerSlotIndex).v) { return false; } CHECKED_ELSE(mi::idler.Engaged() == (idlerSlotIndex < config::toolCount)) { return false; } - CHECKED_ELSE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(selectorSlotIndex)) { return false; } + CHECKED_ELSE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(selectorSlotIndex).v) { return false; } CHECKED_ELSE(ms::selector.Slot() == selectorSlotIndex) { return false; } CHECKED_ELSE(mf::finda.Pressed() == findaPressed) { return false; } diff --git a/tests/unit/logic/stubs/stub_motion.cpp b/tests/unit/logic/stubs/stub_motion.cpp index b22ec88..de6bb0b 100644 --- a/tests/unit/logic/stubs/stub_motion.cpp +++ b/tests/unit/logic/stubs/stub_motion.cpp @@ -70,9 +70,11 @@ void Motion::AbortPlannedMoves() { } void ReinitMotion() { + const pos_t selector_pos = unitToSteps(config::selectorSlotPositions[0]); + // reset the simulation data to defaults axes[0] = AxisSim({ 0, 0, false, false, false }); // pulley - axes[1] = AxisSim({ 1, 1, false, false, false }); // selector //@@TODO proper selector positions once defined + axes[1] = AxisSim({ selector_pos, selector_pos, false, false, false }); // selector //@@TODO proper selector positions once defined axes[2] = AxisSim({ 0, 0, false, false, false }); // idler } diff --git a/tests/unit/logic/unload_to_finda/test_unload_to_finda.cpp b/tests/unit/logic/unload_to_finda/test_unload_to_finda.cpp index a05973b..82cf86d 100644 --- a/tests/unit/logic/unload_to_finda/test_unload_to_finda.cpp +++ b/tests/unit/logic/unload_to_finda/test_unload_to_finda.cpp @@ -45,7 +45,7 @@ TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") { // it should have instructed the selector and idler to move to slot 1 // check if the idler and selector have the right command CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0).v); - CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0)); + CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0).v); CHECK(mm::axes[mm::Idler].enabled == true); // engaging idler @@ -94,7 +94,7 @@ TEST_CASE("unload_to_finda::unload_without_FINDA_trigger", "[unload_to_finda]") // it should have instructed the selector and idler to move to slot 1 // check if the idler and selector have the right command CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0).v); - CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0)); + CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0).v); CHECK(mm::axes[mm::Idler].enabled == true); // engaging idler From 051bce90980da035b4649b7caef29c4d9f0608e0 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 25 Jul 2021 23:40:04 +0200 Subject: [PATCH 10/12] Nicely format doxygen documentation --- src/modules/axisunit.h | 65 +++++++++++++++++++++++------------------- src/unit.h | 18 ++++++------ 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/modules/axisunit.h b/src/modules/axisunit.h index 736a6fc..5a08fde 100644 --- a/src/modules/axisunit.h +++ b/src/modules/axisunit.h @@ -18,47 +18,51 @@ using config::Speed; using pulse_gen::pos_t; using pulse_gen::steps_t; -/// Specialized axis unit type for compile-time conformability testing. Like config::Unit -/// this is done ensure unit quantities are not mixed between types, while also providing -/// convenience methods to convert from physical units to AxisUnits directly at compile -/// time. AxisUnits are just as efficient as non-checked pos_t and steps_t. +/// Specialized axis unit type for compile-time conformability testing. Like for +/// unit::Unit this is done ensure quantities are not mixed between types, while also +/// providing convenience methods to convert from physical units to AxisUnits directly at +/// compile time. AxisUnits are just as efficient as the non-checked pulse_gen::pos_t and +/// pulse_gen::steps_t. /// -/// Each axis unit type is separate for each axis, since the low-level count is not -/// directly comparable across axes. Quantities are normally defined through the -/// literal operators. Types and base axes are prefixed with a single letter identifier -/// for the axis: P=pulley, S=selector, I=idler. +/// Each axis provides separate types for each quantity, since the low-level count is also +/// not directly comparable across each (depending on the configuration settings). +/// Quantities are normally defined through the literal operators. Types and base axes are +/// prefixed with a single letter identifier for the axis: P=pulley, S=selector, I=idler. /// -/// P_pos_t pulley_position = 10.0_P_mm; -/// auto pulley_zero = 0.0_P_mm; // implicit type -/// P_speed_ pulley_feedrate = 30.0_P_mm_s; -/// I_pos_t idler_position = 15.0_I_deg; -/// pulley_position + idler_position; // compile time error +/// P_pos_t pulley_position = 10.0_P_mm; +/// auto pulley_zero = 0.0_P_mm; // implicit type +/// P_speed_ pulley_feedrate = 30.0_P_mm_s; +/// I_pos_t idler_position = 15.0_I_deg; +/// pulley_position + idler_position; // compile time error /// -/// modules::motion::Motion.PlanMove (and related functions) support both physical and -/// AxisUnit natively. This is done by specifying the axis through the first template -/// parameter, which ensures related units are also conforming: +/// motion::Motion.PlanMove (and related functions) support both physical and AxisUnit +/// natively. This is done by specifying the axis through the first template parameter, +/// which ensures related units are also conforming: /// -/// motion.PlanMoveTo(10.0_mm, 100._mm_s); // using physical units -/// motion.PlanMoveTo(10.0_P_mm, 100._P_mm_s); // using AxisUnit +/// motion.PlanMoveTo(10.0_mm, 100._mm_s); // using physical units +/// motion.PlanMoveTo(10.0_P_mm, 100._P_mm_s); // using AxisUnit /// /// Physical units are always represented with the largest floating point type, so they -/// should only be used at compile-time (constants, fixed-lenght moves). +/// should only preferably be used at compile-time only. /// -/// If runtime manipulation is necessary, use AxisUnit should always be used instead. -/// Conversion from physical to AxisUnit can be done through unitToAxisUnit: +/// If runtime manipulation is necessary, AxisUnit should be used instead. Conversion from +/// physical to AxisUnit can be done through motion::unitToAxisUnit: /// -/// unitToAxisUnit(physical_type) +/// unitToAxisUnit(physical_type) /// /// Examples: /// -/// P_pos_t pulley_pos = unitToAxisUnit(10.0_mm); -/// P_speed_t pulley_speed = unitToAxisUnit(100.0_mm_s); +/// P_pos_t pulley_pos = unitToAxisUnit(10.0_mm); +/// P_speed_t pulley_speed = unitToAxisUnit(100.0_mm_s); /// -/// Conversion to pos_t or steps_t works the same using unitToSteps instead. +/// Conversion to pos_t or steps_t works the same using motion::unitToSteps instead. /// /// The low-level step count can be accessed when necessary through AxisUnit::v, which -/// should be avoided as it bypasses type checks. AxisUnit can also be constructed by -/// providing a counter as the first initializer. +/// should be avoided as it bypasses all type checks. AxisUnit can also be constructed +/// without checks by providing a counter as the first initializer. +/// +/// The scaling factor is stored with the pair config::AxisConfig::uSteps and +/// config::AxisConfig::stepsPerUnit. template struct AxisUnit { T v; @@ -88,7 +92,9 @@ static constexpr AxisScale axisScale[config::NUM_AXIS] = { { config::idlerLimits.base, config::idler.stepsPerUnit }, }; -/// Convert a Unit to AxisUnit +/// Convert a unit::Unit to AxisUnit. +/// The scaling factor is stored with the pair config::AxisConfig::uSteps and +/// config::AxisConfig::stepsPerUnit (one per-axis). template static constexpr T unitToAxisUnit(U v) { static_assert(T::unit == U::unit, "incorrect unit type conversion"); @@ -96,7 +102,8 @@ static constexpr T unitToAxisUnit(U v) { return { (typename T::type_t)(v.v * axisScale[T::axis].stepsPerUnit) }; } -/// Convert an Unit to a steps type (pos_t or steps_t) +/// Convert an unit::Unit to a steps type (pos_t or steps_t). +/// Extract the raw step count from an AxisUnit with type checking. template static constexpr typename AU::type_t unitToSteps(U v) { return unitToAxisUnit(v).v; diff --git a/src/unit.h b/src/unit.h index 52525fb..9364d08 100644 --- a/src/unit.h +++ b/src/unit.h @@ -7,20 +7,20 @@ /// daunting, usage is quite straightforward once the appropriate aliases and inline /// operators are defined: /// -/// U_mm distance = 10.0_mm; -/// auto another = 20.5_mm; -/// auto sum = distance + another; +/// U_mm distance = 10.0_mm; +/// auto another = 20.5_mm; +/// auto sum = distance + another; /// -/// auto angle = 15.0_deg; -/// auto test = distance + angle; // compile time error +/// auto angle = 15.0_deg; +/// auto test = distance + angle; // compile time error /// -/// Template parameters are only used for type checking. The Unit contains a single value +/// Template parameters are only used for type checking. Unit contains a single value /// Unit::v and is thus well suited for parameter passing and inline initialization. /// /// Conversion to physical steps is done in modules::motion through the sister class -/// AxisUnit, which also ensures quantities from different axes are not mixed together. -/// AxisUnit are the normal units that should be used at runtime, which is why physical -/// units and operators are not exported into the global namespace by default. +/// modules::motion::AxisUnit, modules::motion::unitToAxisUnit and +/// modules::motion::unitToSteps, which also ensures quantities from different axes are +/// not mixed together. AxisUnit are the normal type that *should* be used at runtime. namespace unit { /// Base units for conformability testing From 5928ade6bef281a9a4f45085889a28432365960f Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 26 Jul 2021 01:28:29 +0200 Subject: [PATCH 11/12] Improve motion::unitToAxisUnit template parameter names --- src/modules/axisunit.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/axisunit.h b/src/modules/axisunit.h index 5a08fde..fbe91e4 100644 --- a/src/modules/axisunit.h +++ b/src/modules/axisunit.h @@ -95,11 +95,11 @@ static constexpr AxisScale axisScale[config::NUM_AXIS] = { /// Convert a unit::Unit to AxisUnit. /// The scaling factor is stored with the pair config::AxisConfig::uSteps and /// config::AxisConfig::stepsPerUnit (one per-axis). -template -static constexpr T unitToAxisUnit(U v) { - static_assert(T::unit == U::unit, "incorrect unit type conversion"); - static_assert(U::base == axisScale[T::axis].base, "incorrect unit base conversion"); - return { (typename T::type_t)(v.v * axisScale[T::axis].stepsPerUnit) }; +template +static constexpr AU unitToAxisUnit(U v) { + static_assert(AU::unit == U::unit, "incorrect unit type conversion"); + static_assert(U::base == axisScale[AU::axis].base, "incorrect unit base conversion"); + return { (typename AU::type_t)(v.v * axisScale[AU::axis].stepsPerUnit) }; } /// Convert an unit::Unit to a steps type (pos_t or steps_t). From 1abc8713bb31e82e46fdf5961d4f24ee6fcd83d4 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 26 Jul 2021 11:20:51 +0200 Subject: [PATCH 12/12] Fix ejectLength typo --- src/logic/eject_filament.cpp | 2 +- src/logic/eject_filament.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/logic/eject_filament.cpp b/src/logic/eject_filament.cpp index 4417320..50f2e18 100644 --- a/src/logic/eject_filament.cpp +++ b/src/logic/eject_filament.cpp @@ -50,7 +50,7 @@ bool EjectFilament::Step() { if (mm::motion.QueueEmpty()) { // selector parked aside state = ProgressCode::EjectingFilament; mm::motion.InitAxis(mm::Pulley); - mm::motion.PlanMove(ejectLenght, ejectSpeed); + mm::motion.PlanMove(ejectLength, ejectSpeed); } break; case ProgressCode::EjectingFilament: diff --git a/src/logic/eject_filament.h b/src/logic/eject_filament.h index 96ba418..32ef524 100644 --- a/src/logic/eject_filament.h +++ b/src/logic/eject_filament.h @@ -7,7 +7,7 @@ namespace logic { // These cannot be class memebers without definition until c++17 -static constexpr modules::motion::P_pos_t ejectLenght = 50.0_P_mm; //@@TODO +static constexpr modules::motion::P_pos_t ejectLength = 50.0_P_mm; //@@TODO static constexpr modules::motion::P_speed_t ejectSpeed = 1000.0_P_mm_s; //@@TODO /// @brief A high-level command state machine - handles the complex logic of ejecting filament