Skip to content

switchkins: allow more than three kinematics types - #4372

Open
grandixximo wants to merge 3 commits into
LinuxCNC:masterfrom
grandixximo:switchkins-n
Open

switchkins: allow more than three kinematics types#4372
grandixximo wants to merge 3 commits into
LinuxCNC:masterfrom
grandixximo:switchkins-n

Conversation

@grandixximo

Copy link
Copy Markdown
Contributor

switchkins.c dispatched on switchkins_type with a three way switch statement and created a fixed set of kinstype.is-0/1/2 pins, so a kinematics module could never offer more than three kinematics. This changes the dispatch to arrays indexed by type, and adds a way for a module to provide more.

Nothing changes for any existing module or configuration.

What a module sees

switchkinsSetup() keeps its exact prototype and still provides types 0, 1 and 2. None of the eight in-tree kinematics modules is touched, and out of tree modules keep compiling unchanged.

A module that wants more calls the new export from inside its own switchkinsSetup(), once per additional type:

int switchkinsRegister(int ktype, KS kset, KF kfwd, KI kinv);

ktype runs from 3 to SWITCHKINS_MAX_TYPES - 1, and every type below the highest one registered has to be provided. SWITCHKINS_MAX_TYPES goes from 3 to 9. It is now only a ceiling on the array sizes rather than the number of types in use, so a module still provides exactly three unless it registers more, and the extra headroom costs a few unused array entries.

HAL pins

The kinstype.is-N pins are created in a loop, one per provided type. For a module providing three types that produces kinstype.is-0, kinstype.is-1 and kinstype.is-2, the same names as before, so nothing that nets those pins breaks. A module providing five gets kinstype.is-0 through kinstype.is-4.

A setup routine now runs once per type it is registered for, so a routine used for two types must not create the same pin twice. Noted in the docs.

The first commit is a separate bug fix

kinematicsSwitch() stored the requested type in switchkins_type and only then ran the switch statement that validates it. The switch returned -1 and motion raised its error flag, but switchkins_type kept the bad value, so the module was left pointing at a kinematics that does not exist. Every kinematicsForward() and kinematicsInverse() call after that failed as well, printing

switchkins: Forward BAD switchkins_type </7>

once per servo cycle for as long as the machine stayed up. On the scara sim, M68 E3 Q7 reproduces it on master.

The fix validates the request first and returns without touching switchkins_type, which leaves the running kinematics in place. It stands on its own and does not depend on the rest, which is why it is a separate commit.

Testing

All eight switchable modules (scarakins, genhexkins, genserkins, pumakins, three21kins, 5axiskins, xyzac-trt-kins, xyzbc-trt-kins) load under halrun and create exactly the same three pins, with the same names, as they do on master.

On configs/sim/axis/vismach/scara/scara.ini:

  • M68 E3 Q1 selects kinematics 1, M68 E3 Q0 selects 0.
  • M68 E3 Q7 is refused, kinematics 1 keeps running, and there is no per cycle log spam.

With scarakins temporarily registering two extra types (a local test edit, not part of this PR), the same config gives kinstype.is-0 through kinstype.is-4 and switches to type 4 and back.

Why

I have a machine that needs more than three kinematics for the same config. There is no in-tree module that needs a fourth type today, so the second commit is enabling work rather than something with a visible user in this repository. I would rather add the capability in a shape you are happy with than carry a fork of switchkins.c.

I looked at doing this with a second array based switchkinsSetup() prototype selected by #if, but that duplicates every dispatch site and every module's setup function for something that is decided at compile time anyway. A registration call seemed like much less to maintain.

kinematicsSwitch() stored the requested type in switchkins_type and only
then ran the switch statement that validates it, so an out of range
request left the module pointing at a kinematics that does not exist.
The switch itself returned -1 and motion raised its error flag, but
switchkins_type kept the bad value, so every kinematicsForward() and
kinematicsInverse() call after that failed too and printed

  switchkins: Forward BAD switchkins_type </7>

once per servo cycle for as long as the machine stayed up.

Validate the request first and return without touching switchkins_type,
which leaves the running kinematics in place.  With the range checked up
front the default arm of the switch is unreachable, so it goes away.
switchkins.c dispatched on switchkins_type with a three way switch and
created a fixed kinstype.is-0/1/2, so a module could never provide more
than three kinematics.

Hold the setup, forward and inverse functions in arrays and dispatch by
index.  switchkinsSetup() still provides types 0,1,2 exactly as before,
so no kinematics module changes and out of tree modules keep compiling.
A module wanting more calls the new switchkinsRegister() from within
switchkinsSetup(), once per additional type.  The kinstype.is-N pins are
created in a loop, which leaves the names of the first three unchanged.
Comment thread src/emc/kinematics/switchkins.c
Comment on lines +219 to +227
int switchkinsRegister(int ktype, KS kset, KF kfwd, KI kinv)
{
if (ktype < 3 || ktype >= SWITCHKINS_MAX_TYPES) {
rtapi_print_msg(RTAPI_MSG_ERR,
"switchkinsRegister: BAD switchkins_type <%d>"
" (must be 3..%d)\n",
ktype, SWITCHKINS_MAX_TYPES - 1);
return -1;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be more consistent to allow switchkinsRegister to supplant the old switchkinsSetup?

The real issue is to deal with is empty/skipping slots. That problem will be present also for any entry larger than 3.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Supplanting switchkinsSetup(): agreed the split is inconsistent. It cannot be replaced without making it optional, since every module defines it and rtapi_app_main() calls it unconditionally. Either convert the eight in-tree modules and drop it, which breaks out of tree modules at compile time, or declare it weak and call it only if present, which I can only test on uspace. Cheap step either way is dropping the ktype < 3 restriction so registration covers 0 to 2 as well. Which end state do you want?

Gaps: already refused at module load, not at switch time. scarakins registering type 5 without 3 and 4 gives

Switchkins FAIL scarakins:<Missing setup function>

The message is the problem, it names neither the type nor the gap. Same area: a double registration silently overwrites, and nobody checks the return of switchkinsRegister(). I will name the missing type, reject double registration, and fail the load on a rejected registration.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path of least resistance would be to drop the ktype < 3 condition.

In the long run, we should make it generic. I'm also confused a bit because there are examples in hal/components that do their own switch. Maybe this is the reason why the current construct feels messy and inconsistent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, in a separate commit.

ktype runs from 0 now, and the count comes from the highest type filled by either route, so a module can register all of them and leave the switchkinsSetup() arguments alone. A type has to come from one route or the other. Registering one that switchkinsSetup() already filled, leaving a gap, and an out of range type are all refused at load time, naming the type at fault.

On the components with their own switch: millturn, xyzab_tdr_kins, xyzacb_trsrn and xyzbca_trsrn in src/objects/hal/components implement kinematicsSwitch(), forward, inverse and their own kinstype.is-N pins without linking switchkins.o, so there are two implementations of the same thing in the tree. They also all have the assign before validate bug the first commit fixes. Folding them onto switchkins.c looks doable now the type count is not fixed. Do you want that in this PR or a separate one?

For context on where I am going with this: I have G12.1 and G13.1 working locally to select the kinematics from G-code, with the interpreter tracking which one is active and exposing it as #<_kins_type>. Selecting the kinematics through a HAL pin and then forcing a queue buster with M66 E0 L0 is the wrong shape: it is a state change the interpreter has to know about, and today it does not, so #<_kins_type> and anything built on it can disagree with what motion is running. The intention is to make G-code the proper way to switch and deprecate the HAL route, which is also what makes more than three types worth having.

Registration was restricted to types 3 and up, which left types 0,1,2
arriving one way and the rest another.  Allow any type from 0, so
registration is the general mechanism and the switchkinsSetup()
arguments are a shorthand for the first three.

A type has to come from one route or the other.  Registering one that
switchkinsSetup() already filled in is refused, and a rejected
registration now fails the module load instead of only printing, since
no caller checks the return value.

The count of provided types is taken from the highest one filled by
either route rather than assumed to be three, so a module can register
all of them.  A type left out below that is a gap, and the load time
message now names the type and which of the three functions is missing
instead of saying only "Missing setup function".
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants