FORUM CTRLX AUTOMATION
ctrlX World Partner Apps for ctrlX AUTOMATION
Dear Community User! We have started the migration process.
This community is now in READ ONLY mode.
Read more: Important
information on the platform change.
09-15-2022 09:08 PM
A customer plans to control a set of axes from an external application via the CORE's REST API. (A sample of the functions that the customer will use are shown in the attached document.)
One of the requirements of the application is shown in the figure below, namely Axis 2 should begin a (buffered) move once Axis 1 reaches a certain critical threshold. Is it possible to realize this functionality using the signaling mechanism (opt-set-signal, wait-for-signal)? The examples given in the Application Manual (R911403791) seem to indicate that signals may be set at the end of a move, not mid-trajectory as required here. Is there other standard functionality that will allow us to realize this requirement?
We have a reasonable workaround if no standard functionality exists - I just don't want to implement it unless I really have to.
Solved! Go to Solution.
09-16-2022 12:25 PM
I created an PLC example using the function of the CXA_MOTION library. Axis "Somo1" will move to position 2.0 and 0.0. Axis "Somo2" will move additive 0.5 when "Somo1" is getting further than 1.0 and again when getting below:
Declaration:
PROGRAM SoMo_WaitForAxisPos
VAR
//Get axes data
Data1: CXA_Motion.ML_AxsGetActValuesData;
Data2: CXA_Motion.ML_AxsGetActValuesData;
Somo1: MB_AXISIF_REF:=(AxisName:='Somo1',AxisNo:=1);
Somo2: MB_AXISIF_REF:=(AxisName:='Somo2',AxisNo:=1);
//Power axes
bPowerSomo1: BOOL;
PowerData1: CXA_Motion.ML_AxsPowerData;
bSwitchOn: BOOL := TRUE;
bPowerSomo2: BOOL;
PowerData2: CXA_Motion.ML_AxsPowerData;
//Check state of signal
GetSignalData: CXA_Motion.ML_GetSignalData;
//Reset signal
udiSyncpoint: UDINT := 42;
ResetSignalData: CXA_Motion.ML_SignalData;
bResetDone: BOOL;
bReset: BOOL;
//Set move command for Somo1
MoveData1: CXA_Motion.ML_AxsPosData;
lrAcc: LREAL := 1;
lrDec: LREAL := 1;
lrJerkAcc: LREAL := 0;
lrJerkDec: LREAL := 0;
lrVel: LREAL := 10;
bMoveSomo1: BOOL;
lrPosEnd: LREAL := 2;
lrPos0: LREAL := 0;
//Set waiting command of Somo2
WaitData: CXA_Motion.ML_WaitForSignalCmdData;
//Set move command for Somo2
MoveData2: CXA_Motion.ML_AxsPosData;
lrPosAdd: LREAL := 0.5;
//Check position of Somo1 and set Signal
fb_R_TRIG: R_TRIG;
SetData: CXA_Motion.ML_SignalCmdData;
SignalData: CXA_Motion.ML_SignalData;
fb_F_TRIG: F_TRIG;
END_VAR
Implementation:
//Get axes data
Data1.In.AxisName := Somo1.AxisName;
ML_AxsGetActValues(Data:= Data1);
Data2.In.AxisName := Somo2.AxisName;
ML_AxsGetActValues(Data:= Data2);
//Power axes
IF bPowerSomo1 THEN
bPowerSomo1 := FALSE;
PowerData1.In.AxisName := Somo1.AxisName;
PowerData1.In.SwitchOn := bSwitchOn;
ML_AxsPower(Data:= PowerData1);
END_IF
IF bPowerSomo2 THEN
bPowerSomo2 := FALSE;
PowerData2.In.AxisName := Somo2.AxisName;
PowerData2.In.SwitchOn := bSwitchOn;
ML_AxsPower(Data:= PowerData2);
END_IF
//Check state of signal
GetSignalData.In.SignalId := 42;
ML_GetSignal(Data:= GetSignalData);
IF bMoveSomo1 THEN
//Reset signal
bMoveSomo1 := FALSE;
ResetSignalData.In.SignalId := udiSyncpoint;
bResetDone := ML_ResetSignal(Data:= ResetSignalData);
ResetSignalData.Out.Error;
//Set move command for Somo1
MoveData1.In.AxisName := Somo1.AxisName;
MoveData1.In.Buffered := TRUE;
MoveData1.In.DynLimits.Acceleration := lrAcc;
MoveData1.In.DynLimits.Deceleration := lrDec;
MoveData1.In.DynLimits.JerkAcc := lrJerkAcc;
MoveData1.In.DynLimits.JerkDec := lrJerkDec;
MoveData1.In.DynLimits.Velocity := lrVel;
MoveData1.In.Position := lrPosEnd;
ML_AxsPosAbs(Data:= MoveData1);
MoveData1.In.Position := lrPos0;
ML_AxsPosAbs(Data:= MoveData1);
//Set waiting command of Somo2
WaitData.In.AutoReset := TRUE;
WaitData.In.ObjName := Somo2.AxisName;
WaitData.In.SignalId := udiSyncpoint;
ML_WaitForSignalCmd(WaitData);
//Set move command for Somo2
MoveData2.In.AxisName := Somo2.AxisName;
MoveData2.In.Buffered := TRUE;
MoveData2.In.DynLimits.Acceleration := lrAcc;
MoveData2.In.DynLimits.Deceleration := lrDec;
MoveData2.In.DynLimits.JerkAcc := lrJerkAcc;
MoveData2.In.DynLimits.JerkDec := lrJerkDec;
MoveData2.In.DynLimits.Velocity := lrVel;
MoveData2.In.Position := lrPosAdd;
ML_AxsPosAdd(Data:= MoveData2);
//Set another waiting and move command of Somo2
ML_WaitForSignalCmd(WaitData);
ML_AxsPosAdd(Data:= MoveData2);
END_IF
//Check position of Somo1 and set Signal
fb_R_TRIG(CLK:= Data1.Out.Position > 1, Q=> );
IF fb_R_TRIG.Q THEN
SignalData.In.SignalId := udiSyncpoint;
ML_SetSignal(Data:= SignalData);
END_IF
fb_F_TRIG(CLK:= Data1.Out.Position > 1, Q=> );
IF fb_F_TRIG.Q THEN
SignalData.In.SignalId := udiSyncpoint;
ML_SetSignal(Data:= SignalData);
END_IF
09-16-2022 02:21 PM
This clarifies the use of the signaling functionalty, but is not really applicable to the customer's use case. The issue is that the customer is commanding the axes from an external application and monitoring the position of axis 1 in his application to trigger the call to ML_SetSignal is not precise enough due to network/application latency.
//Check position of Somo1 and set Signal
fb_R_TRIG(CLK:= Data1.Out.Position > 1, Q=> );
IF fb_R_TRIG.Q THEN
SignalData.In.SignalId := udiSyncpoint;
ML_SetSignal(Data:= SignalData);
END_IF
I was hoping that ML_SetSignal would include functionality similar to the UseSwitchingPosition feature of FlexProfile:
SignalData.In.Axis:= Axis1; //pseudo-code -- do not use
SignalData.In.UseSwitchingPosition:= TRUE;
SignalData.In.SwitchingPosition:= 1.0;
This would allow us to pre-issue the set signal command and the app itself would fire the signal only once the defined threshold had been crossed.
Thanks for your help. We'll proceed with our workaround.
09-16-2022 02:43 PM
I agree that checking the position is the bottleneck of the timing and I am afraid to say that this function is not implemented yet. But also have this on our roadmap but at the moment without actual time schedule.