UserLimitConfigSet() Error

[RMP 8.1.6]

We’re trying to do probing, and we’re using user limits to stop the motors when the probe strikes.

We’ve gotten this very same thing to work for probe 1 on some Yaskawa Sigma-7 drives. When we try it for probe 2, calling UserLimitConfigSet() raises an exception.

Function(RSI::RapidCode::Impl::MotionController::UserLimitConfigSet)
Warning?(No)
Text(Parameter invalid :: {userlimit.c, line 2388} : MPIUserLimitCondition: Specified type (0) is not a valid type. (Error 2) (RSI::RapidCode::Impl::MotionController::UserLimitConfigSet) (Object 0) (File …..\source\motioncontroller.cpp) (Line 4771) (Version 8.1.6 for 04.04.02.RMP))
ShortText(Parameter invalid :: {userlimit.c, line 2388} : MPIUserLimitCondition: Specified type (0) is not a valid type.)

I added some logging to spit out the actual parameters to the function call.

UserLimitConfigSet(0,1,7,0,0)

These look legit to me. The actual code I’m compiling is:

controller->UserLimitConfigSet(user_num,
	:RSIUserLimitTriggerTypeSINGLE_CONDITION,
	:RSIActionE_STOP_MODIFY,
	axis_id,
	duration);

It’s for axis #0.

Any ideas what I’m doing wrong?

@todd_mm

Have you already done the call for UserLimitConditionSet? UserLimitConfigSet is the last UserLimit function you should be using when configuring them. It takes the information from the other calls and set them up.

Yes, we had just called UserLimitConditionSet().

limit # 0
condition # 0
IO addr: 0x0000000018bc044c (FW:0x016a044c)
    mask: 0x00100000
    cmp: NE
    value: 0x00100000

@todd_mm

The case you hit indicates that no known UserLimit Type was specified. UserLimitConditionSet always sets the type to CUSTOM (a valid choice). We don’t even give you the option to pick an invalid one. The code is behaving as if the condition wasn’t set.

Is it possible that you set the condition for a different user limit than the one you are trying to configure when you get the error?

You’ve been using these Limits for a while I believe. Can you tell me anything that has changed recently?

1 Like

In this place, the code hasn’t significantly changed in a while (a year). There have been superficial changes (log messages, etc.) but nothing directly related to these API calls. The same code was working until we tried to use a different probe input on the Yaskawa Sigma-7 drive(s).

We are hardcoding userlimit 0 and condition 0 for the calls. I don’t see how ConditionSet could have been skipped, just looking at the code.

Curiously (to me), Just after I call UserLimitConditionSet(), I try to get the data type via UserLimitConditionDatatTypeGet(), and it returns INVALID. I suppose this isn’t related to the user limit type.

Is there a way to know if something went wrong with the call to ...ConditionSet() other than throwing an exception (which didn’t happen)?

Also, can a single user limit have multiple “configs” set for it? In this case, we want to estop all the motors in the probe move. Can I call ...ConfigSet() for each axis or will a single user limit only ever operate on a single axis?

I’d expect the UserLimitConditionSet function to return an error if it was a problem with the new address.

UserLimitConditionDataTypeGet returns the value that we’ve stored locally. It should match double or uint32 based on which version of condition set you called.

You are limited to 1 motion supervisor action per limit. You can setup multiple user limits with the same conditions but different axis targets for your EStops.

So, after more testing, I’m seeing this problem only the first time I attempt to configure user limit #0. The second time, it works without errors. It also happens regardless of which I/O is being used.

My first thought would be that the initial ConditionSet call isn’t happening, but that doesn’t make sense when I look at my code.
I can’t see in my code how any of the API calls could be getting skipped the first time and not skipped on subsequent attempts. That said, my app is multithreaded and things could be happening in other threads that may have some bearing on what I’m doing.

Would tracing reveal anything useful here? If I ran it for the ConditionSet/ConfigSet pair of calls, would the trace info give me a clue as to what else might be influencing things?

It might. More information is generally good. If you are doing something that invalidates the UserLimits on another thread, it is less likely to yield good information.

You should generally have all calls set immediately following the creation of the motion controller.

  • AxisCountSet
  • MotionCountSet
  • UserLimitCountSet
  • AxisFrameBufferSizeSet
  • SequencerCountSet
  • RecorderCountSet
  • RecoderBufferSizeSet
  • CompensatorCountSet
  • CompensatorPointCountSet

All of them reallocate memory organization which could invalidate anything (other than MotionController:Create) done before them.

Once this and other general initialization is complete, you can kick off the threads that you’ll use in normal operation.

I’m already trying very hard to do what you’ve described.

I’ll see what a trace turns up.

I turned on tracing for function entry/exit and events.

The first difference in the trace output that I see is that the call to UserLimitConditionSet() does some different things.

The first time I call it (when ConfigSet eventually fails), I see these trace messages immediately afterward.

[ArmProbe] Calling (0x1F24D308).UserLimitConditionSet(0,0,1,0x000000002173044c,0x00000200,0x00000200)...
mpiUserLimitCreate(0x1f24d838, 0x21590048, 4)
mpiPlatformAlloc(56) returns 0x2754e110
mpiElementCreate(0x2754e110, 0x2754e110)
mpiElementValidate(0x2754e110) returns 0x3
mpiElementCreate(0x2754e110, 0x2754e110) returns 0x2754e110
mpiControlType(0x21590048, 0x2754e13c)
mpiControlType(0x21590048, 0x2754e13c) returns 0 (7)
mpiControlPlatform(0x21590048, 0x2754e138)
mpiControlPlatform(0x21590048, 0x2754e138) returns 0x0
mpiUserLimitCreate(0x1f24d838, 0x21590048, 4) returns 0x2754e110 (0x0)
mpiUserLimitConfigDefault(0x1f2511e8)
mpiUserLimitConfigDefault(0x1f2511e8) returns 0x0

The second time I call it (when ConfigSet succeeds), the mpiUserLimitCreate stuff doesn’t show up in the trace. Perhaps this is normal because the user limit has never been used in the process before this point. As long as I never restart the Windows process, ConfigSet will never fail.

If I just look for non-zero return values, I don’t see (m)any until after the call to ConfigSet:

[ArmProbe] Calling (0x1F24D308)->UserLimitConfigSet(0,1,7,0,0)...

Here are the failures I see that don’t have an analogous failure in the success log.

probe_arm_fail.log:_mpiUserLimit_MPI_To_MFW_Condition(0x1f2511f8, 0x3d5ad390, ...) returns 0x2
probe_arm_fail.log:_mpiUserLimit_MPI_To_MFW_Trigger(0x1f2511e8, 0x3d5ad2f8, ...) returns 0x2
probe_arm_fail.log:_mpiUserLimitConfigSet_local(0x21590048, 4, 0x1f2511e8) returns 0x2
probe_arm_fail.log:mpiUserLimitConfigSet(0x2754e110, 0x1f2511e8) returns 0x2

I’ll send you the complete trace log files (~300 KiB).

And, as an observation, this happens on all the machines we test. I’m not exactly certain why it didn’t show up before now, but it was probably the result of something we were doing where we weren’t calling ConfigSet the first time, but we were the other times.

Is there something I need to do in order to “create” a user limit (other than setting the count, which we do long before this)?

Consider this code snippet

const int user_num = 0;
const int cond_num = 0;
const r::RSIUserLimitLogic comp = r::RSIUserLimitLogicNE;
const int fw_addr = 0x016a044c; // CTB-D3 #0, T7 X12 (Input #09)
const uint32_t mask = 0x200;
const uint32_t value = mask;
mc->UserLimitConditionSet(user_num,
						  cond_num,
						  comp,
						  mc->HostAddressGet(fw_addr),
						  mask,
						  value);	

auto dt = mc->UserLimitConditionDataTypeGet(user_num,cond_num);

const int axis_id = 0;
const double duration = 0.0;
mc->UserLimitConfigSet(user_num,
					   r::RSIUserLimitTriggerTypeSINGLE_CONDITION,
					   r::RSIActionE_STOP_MODIFY,
					   axis_id,
					   duration);

It seems that calling the UserLimitConditionDataTypeGet() API call will produce the error I’m seeing in my application.

Parameter invalid :: {userlimit.c, line 2388} : MPIUserLimitCondition: Specified type (0) is not a valid type. (Error 2) (RSI::RapidCode::Impl::MotionController::UserLimitConfigSet) (Object 0) (File …..\source\motioncontroller.cpp) (Line 4771) (Version 8.1.6 for 04.04.02.RMP)

If I comment out the DataTypeGet call in this MWE, the user limit calls work as expected.

In my multithreaded app, it’s quite likely that some other thread could make an API call between ConditionSet and ConfigSet, and I’m pretty sure that none of them are touching the specific user limit (it’s not used anywhere else).

Are there API calls that absolutely should not take place between these two steps?

This was resolved in Release 8.1.8.