cancel
Showing results for 
Search instead for 
Did you mean: 
SOLVED

Usage of opt-set-signal, wait-for-signal

Usage of opt-set-signal, wait-for-signal

bostroemc
New Contributor

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.

Untitled.png

3 REPLIES 3

CodeShepherd
Community Moderator
Community Moderator

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

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.

 

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.

Icon--AD-black-48x48Icon--address-consumer-data-black-48x48Icon--appointment-black-48x48Icon--back-left-black-48x48Icon--calendar-black-48x48Icon--center-alignedIcon--Checkbox-checkIcon--clock-black-48x48Icon--close-black-48x48Icon--compare-black-48x48Icon--confirmation-black-48x48Icon--dealer-details-black-48x48Icon--delete-black-48x48Icon--delivery-black-48x48Icon--down-black-48x48Icon--download-black-48x48Ic-OverlayAlertIcon--externallink-black-48x48Icon-Filledforward-right_adjustedIcon--grid-view-black-48x48IC_gd_Check-Circle170821_Icons_Community170823_Bosch_Icons170823_Bosch_Icons170821_Icons_CommunityIC-logout170821_Icons_Community170825_Bosch_Icons170821_Icons_CommunityIC-shopping-cart2170821_Icons_CommunityIC-upIC_UserIcon--imageIcon--info-i-black-48x48Icon--left-alignedIcon--Less-minimize-black-48x48Icon-FilledIcon--List-Check-grennIcon--List-Check-blackIcon--List-Cross-blackIcon--list-view-mobile-black-48x48Icon--list-view-black-48x48Icon--More-Maximize-black-48x48Icon--my-product-black-48x48Icon--newsletter-black-48x48Icon--payment-black-48x48Icon--print-black-48x48Icon--promotion-black-48x48Icon--registration-black-48x48Icon--Reset-black-48x48Icon--right-alignedshare-circle1Icon--share-black-48x48Icon--shopping-bag-black-48x48Icon-shopping-cartIcon--start-play-black-48x48Icon--store-locator-black-48x48Ic-OverlayAlertIcon--summary-black-48x48tumblrIcon-FilledvineIc-OverlayAlertwhishlist