Run trajectories on a MotoLogix system, using a dynamic program where the trajectory is defined by data, not by the program code.
Usage
McePosTable
instance.
There can be up to 4 motion devices on a robot controller.The McePosTable
function block relies on several global constants that must
be defined.
VAR CONSTANT
TOOLS_UBOUND : INT := 9; (*Tools array; upper boundary*)
USERFRAMES_UBOUND : USINT := 9; (*User frames; upper boundary*)
ENTRIES_UBOUND : INT := 19; (*PosTable array; upper boundary*)
NR_OF_INSTANCES : USINT := 3; (*Number of motion command instances*)
END_VAR
Constant | Lower limit | Upper limit | Recommended value |
---|---|---|---|
TOOLS_UBOUND | 0 | 63* | 9 |
USERFRAMES_UBOUND | 0 | 62* | 9 |
ENTRIES_UBOUND | 1 | N/A | 19 |
NR_OF_INSTANCES | 2 | 20 | 3** |
* These numbers are limited by the robot controller.
** The robot controller’s path comment: \L$1r can calculate blending nicely when there are three motion commands in its buffer. Having more commands in the buffer (by increasing the number of motion command instances) is seldom useful and consumes more PLC resources.
NR_OF_INSTANCES
you need to pay close attention on
not overfilling the MotoLogix command buffer (see MLxxInternalData
).We start by creating the (global) variables for the interface data. These variables are also used for connecting buttons or an HMI.
stPosTable : ARRAY [0..GVL.MOTION_DEVICES_UBOUND] OF McePosTableIO; // data to handle a robot trajectory
Now we create the instances:
fbPosTable : ARRAY [0..GVL.MOTION_DEVICES_UBOUND] OF McePosTable;
Map all relevant inputs (see McePosTableIO
)
to your HMI and/or to higher level state machines.
// this is just a portion of the relevant signals
GVL.stPosTable[0].nRobotNumber := ...;
GVL.stPosTable[0].bSystemReady := ...;
GVL.stPosTable[0].bRun := ...;
GVL.stPosTable[0].bStep := ...;
GVL.stPosTable[0].nMode := ...;
GVL.stPosTable[0].nNrOfCycles := ...;
For the function call, make sure to link it to the right MLX data structure.
// function call
fbPosTable[0](
io := GVL.stPosTable[0],
MLX := GVL.stMLX[0],
PosTable := myPosTable,
UserFrames := GVL.stUserFrames[0],
Tools := GVL.stTools[0]);
Map all relevant outputs (see McePosTableIO
)
to your HMI and/or to higher level state machines.
// this is just a portion of the relevant signals
... := GVL.stPosTable[0].bIdle;
... := GVL.stPosTable[0].bBusy;
... := GVL.stPosTable[0].bWaitForStep;
... := GVL.stPosTable[0].bDone;
... := GVL.stPosTable[0].bError;
... := GVL.stPosTable[0].nErrorCode;
... := GVL.stPosTable[0].nPercentComplete;
... := GVL.stPosTable[0].nStateTime;
... := GVL.stPosTable[0].nIndex;
... := GVL.stPosTable[0].nCycleNr;
System state mapping
To run a trajectory, the controller needs to be in SERVO ON state.
This state must be input to the bSystemReady
variable
(see McePosTableIO
).
If you use the MceStartStop
function block, this status is available
as the bSystemReady
output.
Simply link them as below.
GVL.stPosTable[0].bSystemReady := GVL.stStartStop[0].bSystemReady;
Use ActionID
ActionID are actions run at the end of a motion.
ActionID | Type of action |
---|---|
0 | No action |
1-19 | Default action |
>19 | Custom action |
For ActionID = 0
, no action is performed at the end of the motion.
The trajectory is blended depending on the selected nMode
.
The default ActionID are listed below.
They are handled inside McePosTable
.
ActionID | Action |
---|---|
1 | Wait one PLC task scan |
2 | Wait a bStep rising edge |
Custom ActionID number are greater or equal to 20
.
You can create as many as you want (max 32767
).
McePosTableIO
has several I/O dedicated for Custom ActionID.
// Input
GVL.stPosTable[0].bCustomActionDone := ...; // The custom ActionID process is completed
//Output
... := GVL.stPosTable[0].bActionStart; // An ActionID has started (default or custom)
... := GVL.stPosTable[0].nActionID; // Current ActionID number
The example below add 2 Custom ActionID: close and open gripper.
IF GVL.stPosTable[0].bActionStart THEN
CASE GVL.stPosTable[0].nActionID OF
20:
// Close gripper
GVL.stPosTable[0].bCustomActionDone := GripperCloseTime.Q;
21:
// Open gripper
GVL.stPosTable[0].bCustomActionDone := GripperOpenTime.Q;
END_CASE;
ELSE
GVL.stPosTable[0].bCustomActionDone := FALSE;
END_IF
McePosTable controlled by a higher-level state machine
McePosTable
is typically controlled by a higher-level state machine
that prepares the data for the desired trajectory and commands McePosTable
to
execute that trajectory.
Below pseudo code gives an example of a such a higher-level state machine.
// Process to run 2 different trajectories
stPosTable[0].bRun := False; // init cyclically
CASE
...
// State 40 - generate trajectory 1
40:
GenerateTrajectory(trajType := 1, posTable:= myPosTable); // Write data for trajectory 1
// Go to next state
// State 50 - Run trajectory 1
50:
stPosTable[0].bRun := True; // Run McePosTable
IF stPosTable[0].bDone THEN // McePosTable done
// Go to next state
END_IF;
// State 60 - generate trajectory 2
60:
GenerateTrajectory(trajType := 2, posTable:= myPosTable); // Write data for trajectory 2
// Go to next state
// State 70 - Run trajectory 2
70:
stPosTable[0].bRun := True; // Run McePosTable
IF stPosTable[0].bDone THEN // McePosTable done
// Go to next state
END_IF;
...
END_CASE;
Version history
0.2.0
Changes
- Selection of the number of motion instances
- Selection of aborting mode
- Variable renamed
- Resetting with
bReset
input replaced by settingbRun=False
show full history
0.1.0-beta
Changes
- initial version
Overview
kind | name | type | default | comment |
---|---|---|---|---|
in_out | io | McePosTableIO | McePosTable IO variables | |
in_out | MLX | MLxData | MotoLogix shared memory variable | |
in_out | posTable | McePosTableData | trajectory to be processed by PosTable | |
in_out | userFrames | MceUserFrames | user frame data | |
in_out | tools | MceToolData | tool data |
Details
Interface data for this function.
Check the data type for more information.
MLX
MLxData
The MotoLogix variable which acts as the shared memory for a MotoLogix system.
Check the YaskawaMLx library for more information.
posTable
McePosTableData
Trajectory to be processed by PosTable.
userFrames
MceUserFrames
A collection of User Frames used by the PosTable.
tools
MceToolData
A collection of Tools used by the PosTable.
Source code
Declarations
(*Performed a trajectory stored in a PosTable*)
FUNCTION_BLOCK McePosTable
(*
* -----------------------------------------------------------------------------
* Name : McePosTable
* Version : 0.2.0
* Date : 2024-03-01
* Author : Rioual
* Family : YaskawaMce
* Organisation : github.com/YaskawaEurope/mlx-examples
*
* -----------------------------------------------------------------------------
* Performed a trajectory stored in a PosTable
* -----------------------------------------------------------------------------
*)
VAR_IN_OUT
io : McePosTableIO; (*McePosTable IO variables*)
MLX : MLxData; (*MotoLogix shared memory variable*)
posTable : McePosTableData; (*trajectory to be processed by PosTable*)
userFrames : MceUserFrames; (*user frame data*)
tools : MceToolData; (*tool data*)
END_VAR
VAR
aMoveCmd : ARRAY [0..QA_MAX] OF MceCmdStatus; (*status signals of mapped move instruction*)
fbRobotMoveAxisAbsolute : ARRAY [0..QA_MAX] OF MLxRobotMoveAxisAbsolute; (*move to target absolute axis position using PTP motion*)
fbRobotMoveLinearAbsolute : ARRAY [0..QA_MAX] OF MLxRobotMoveLinearAbsolute; (*move to target TCP position in straight-line motion*)
fbRobotSelectTool : ARRAY [0..QA_MAX] OF MLxRobotSelectTool; (*select the tool*)
fbRobotSetUserFrame : ARRAY [0..QA_MAX] OF MLxRobotSetUserFrame; (*set the active User Frame for the Robot*)
stStateMonitoringData : MceStateMonitoringData; (*monitor the elapsed time of a state*)
aUseMoveType : ARRAY [0..QA_MAX] OF SINT; (*use linear motion for this mapped move*)
nInstanceNumber : ARRAY [0..QA_MAX] OF INT; (*mapping of the instances; value in nInstanceNumber[0] tells the instance of the active motion.*)
nQA : INT; (*allowed Queueing Amount; number of motions to queue (for smooth motion)*)
nUserFrameNr : DINT;
nToolNr : DINT;
nPosTableSize : INT; (*number of entries*)
aEnable : ARRAY [0..QA_MAX] OF BOOL; (*enable mapped move instruction*)
aChangeTool : ARRAY [0..QA_MAX] OF BOOL; (*change the tool for this mapped move*)
aChangeUserFrame : ARRAY [0..QA_MAX] OF BOOL; (*change the user frame for this mapped move*)
aOneShots : ARRAY [0..QA_MAX+6] OF BOOL; (*rising edge signals*)
aOsrEnable : ARRAY [0..QA_MAX] OF BOOL; (*rising edge*)
bLastEntry : BOOL; (*last entry in process*)
bFirstMove : BOOL; (*calc first move after system was reset*)
bDefaultActionDone : BOOL; (*default action at standstill done*)
bOsrRun : BOOL; (*rising edge*)
bOsfRun : BOOL; (*falling edge*)
bOsrStep : BOOL; (*rising edge*)
bOsrSystemReady : BOOL; (*rising edge*)
bOsrReset : BOOL; (*rising edge*)
bWaiting : BOOL; (*wait for bStep*)
bStopEntry : BOOL; (*entry with robot standstill (Action > 0) in process*)
bOsrRecalcQA : BOOL; (*rising edge*)
bError : BOOL; (*active when any MotoLogix FB returned an error*)
bLastEntryInProcess : BOOL; (*last motion of the PosTable is running*)
bOsfLastMoveInProcess : BOOL; (*falling edge*)
bAllowQueuing : BOOL; (*allow following motions to be queued*)
bStopAfterLastEntry : BOOL; (*stop PosTable after the last entry*)
END_VAR
VAR_TEMP
i : DINT; (*index for FOR loops*)
bCustomActionDone : BOOL; (*check if input bCustomActionDone correspond to the right custom ActionID*)
bUserFrameDone : BOOL; (*user frame has been loaded by MLxRobotSetUserFrame*)
bToolDone : BOOL; (*tool has been loaded by MLxRobotSelectTool*)
END_VAR
VAR CONSTANT
QA_MAX : INT := LIMIT(1, GVL.NR_OF_INSTANCES - 1, 20); (*max Queueing Amount; max number of motions which can be queued*)
RESERVED_ACTIONID_UBOUND : SINT := 19; (*actionID lower or equal to this value are reserved for default ActionID*)
PT_MODE_0 : SINT := 0; (*PosTable in continuous mode*)
PT_MODE_2 : SINT := 2; (*PosTable in step-by-step mode*)
ACTIONID_NO_ACTION : SINT := 0; (*no action*)
ACTIONID_WAIT_ONE_SCAN : SINT := 1; (*wait one PLC scan*)
ACTIONID_WAIT_STEP : SINT := 2; (*wait for rising edge*)
END_VAR
Logic
// -----------------------------------------------------------------------------
// common
// -----------------------------------------------------------------------------
bOsrStep := io.bStep AND NOT aOneShots[0];
aOneShots[0] := io.bStep;
bOsrSystemReady := io.bSystemReady AND NOT aOneShots[1];
aOneShots[1] := io.bSystemReady;
bOsrRecalcQA := io.bRecalcQA AND NOT aOneShots[2];
aOneShots[2] := io.bRecalcQA;
bOsfRun := NOT io.bRun AND aOneShots[3];
aOneShots[3] := io.bRun;
nPosTableSize := UINT_TO_INT(SIZEOF(posTable) / SIZEOF(posTable.stEntry[0]));
bLastEntryInProcess := posTable.stEntry[io.nIndex].bLastEntry OR (io.nIndex = (nPosTableSize - 1));
bOsfLastMoveInProcess := NOT bLastEntryInProcess AND aOneShots[4];
aOneShots[4] := bLastEntryInProcess;
bOsrRun := io.bRun AND NOT aOneShots[5];
aOneShots[5] := io.bRun;
// Count the number of cycles
IF bOsfLastMoveInProcess AND io.bBusy THEN
io.nCycleNr := io.nCycleNr + 1;
END_IF;
io.nIndex := LIMIT(0, io.nIndex, nPosTableSize - 1);
io.nLoadIndex := LIMIT(0, io.nLoadIndex, nPosTableSize - 1);
bWaiting := FALSE;
bAllowQueuing := (io.nMode = PT_MODE_0) AND io.bRun;
bStopAfterLastEntry := ((io.nCycleNr + 1) >= io.nNrOfCycles) AND NOT (io.nNrOfCycles = 0);
// clear Enable signals
MEMUtils.MemSet(pbyBuffer := ADR(aEnable), byValue := 0, dwSize := SIZEOF(aEnable));
io.bError := FALSE;
// Update QA
IF bOsrRecalcQA OR bOsrSystemReady THEN
bFirstMove := bOsrSystemReady OR bOsrRun; // only if first move after standstill
McePosTableRecalcQA(allowQueuing := bAllowQueuing,
firstMove := bFirstMove,
stopAfterLastEntry := bStopAfterLastEntry,
posTable := posTable,
QA := nQA,
lastEntry := bLastEntry,
stopEntry := bStopEntry,
index := io.nIndex
);
END_IF;
// -----------------------------------------------------------------------------
// state machine 1: Motion
// -----------------------------------------------------------------------------
CASE io.nSmPosTable OF
// -------------------------------------
// state 0 - idle, not ready for start
// -------------------------------------
0:
IF bOsfRun THEN
io.nSmPosTable := 60;
ELSIF io.bSystemReady THEN
io.nErrorCode := 0;
io.nSmPosTable := 1;
END_IF;
// -------------------------------------
// state 1 - idle, ready for start
// -------------------------------------
1:
IF NOT io.bSystemReady THEN
io.nSmPosTable := 0;
ELSIF bOsfRun THEN
io.nSmPosTable := 60;
ELSIF io.bSystemReady THEN
bWaiting := (io.nMode = PT_MODE_2);
bFirstMove := TRUE;
IF ((io.nMode <> PT_MODE_2 AND io.bRun) OR (io.nMode = PT_MODE_2 AND bOsrStep AND io.bRun)) THEN
McePosTableUpdateIndex( allowQueuing := bAllowQueuing,
firstMove := bFirstMove,
stopAfterLastEntry := bStopAfterLastEntry,
index := io.nIndex,
QA := nQA,
lastEntry := bLastEntry,
stopEntry := bStopEntry,
posTable := posTable);
IF io.nIndex < 0 THEN
io.nSmPosTable := 50;
ELSE
io.nSmPosTable := 10;
END_IF;
END_IF;
END_IF;
// -------------------------------------
// state 10..29 - handle motion commands
// -------------------------------------
10..29:
FOR i := 0 TO QA_MAX DO
nInstanceNumber[i] := io.nSmPosTable + DINT_TO_INT(i) - 10;
IF nInstanceNumber[i] > QA_MAX THEN
nInstanceNumber[i] := nInstanceNumber[i] - (QA_MAX + 1);
END_IF;
END_FOR;
IF NOT io.bRun AND ((io.nAbortingMode = 1) OR NOT io.bSystemReady) THEN
io.nSmPosTable := 50;
ELSIF io.bSystemReady THEN
// start this motion
aEnable[nInstanceNumber[0]] := TRUE;
// queue next motions
FOR i := 1 TO QA_MAX DO
aEnable[nInstanceNumber[i]] := (aMoveCmd[nInstanceNumber[i-1]].bSts_EN AND aMoveCmd[nInstanceNumber[i-1]].bSts_DN) AND (nQA >= i);
END_FOR;
// motion completed
IF (aMoveCmd[nInstanceNumber[0]].bSts_EN AND aMoveCmd[nInstanceNumber[0]].bSts_PC) THEN
IF NOT io.bRun AND (nQA = 0) THEN
io.nSmPosTable := 50;
ELSIF bStopEntry THEN
io.nSmPosTable := 30;
ELSIF bLastEntry THEN
io.nSmPosTable := 40;
ELSE
bWaiting := (io.nMode = PT_MODE_2);
IF (io.nMode <> PT_MODE_2 AND io.bRun) OR (bOsrStep OR (nQA > 0)) THEN
aEnable[nInstanceNumber[0]] := FALSE;
McePosTableUpdateIndex( allowQueuing := bAllowQueuing,
firstMove := bFirstMove,
stopAfterLastEntry := bStopAfterLastEntry,
index := io.nIndex,
QA := nQA,
lastEntry := bLastEntry,
stopEntry := bStopEntry,
posTable := posTable);
io.nSmPosTable := nInstanceNumber[1] + 10;
END_IF;
END_IF;
END_IF;
END_IF;
// -------------------------------------
// state 30 - waiting before performing action at standstill
// -------------------------------------
30:
IF NOT io.bRun THEN
io.nSmPosTable := 50;
ELSIF io.bSystemReady THEN
bWaiting := (io.nMode = PT_MODE_2);
IF (io.nMode <> PT_MODE_2) OR bOsrStep THEN
io.nSmPosTable := 31;
END_IF;
END_IF;
// -------------------------------------
// state 31 - perform action at standstill
// -------------------------------------
31:
IF NOT io.bRun THEN
io.nSmPosTable := 50;
ELSIF io.bSystemReady THEN
bWaiting := (bDefaultActionDone OR io.bCustomActionDone) AND (io.nMode = PT_MODE_2);
bCustomActionDone := io.bCustomActionDone AND (io.nActionID > RESERVED_ACTIONID_UBOUND);
IF (bDefaultActionDone OR bCustomActionDone) AND ((io.nMode <> PT_MODE_2) OR bOsrStep) THEN
IF bLastEntry AND (io.nNrOfCycles - io.nCycleNr <= 1) THEN
io.nSmPosTable := 40;
ELSE
McePosTableUpdateIndex( allowQueuing := bAllowQueuing,
firstMove := bFirstMove,
stopAfterLastEntry := bStopAfterLastEntry,
index := io.nIndex,
QA := nQA,
lastEntry := bLastEntry,
stopEntry := bStopEntry,
posTable := posTable);
io.nSmPosTable := 10;
END_IF;
END_IF;
END_IF;
// -------------------------------------
// state 40 - cycle completed
// -------------------------------------
40:
io.nCycleNr := io.nCycleNr + 1;
IF NOT io.bRun THEN
io.nSmPosTable := 50;
ELSIF ((io.nCycleNr < io.nNrOfCycles) OR (io.nNrOfCycles = 0)) AND (io.nMode <> PT_MODE_2) THEN
McePosTableUpdateIndex( allowQueuing := bAllowQueuing,
firstMove := bFirstMove,
stopAfterLastEntry := FALSE,
index := io.nIndex,
QA := nQA,
lastEntry := bLastEntry,
stopEntry := bStopEntry,
posTable := posTable);
io.nSmPosTable := 10;
ELSE
io.nSmPosTable := 50;
END_IF;
// -------------------------------------
// state 50 - wait at the end of cycle
// -------------------------------------
50:
IF NOT io.bRun THEN
io.nSmPosTable := 60;
END_IF;
// -------------------------------------
// state 60 - reset cycle
// -------------------------------------
60:
io.nIndex := 0;
io.nLoadIndex := 0;
io.nCycleNr := 0;
io.nSmPosTable := 0;
// -------------------------------------
// state 99 - state machine error
// -------------------------------------
99:
io.bError := TRUE;
IF NOT io.bRun THEN
io.nSmPosTable := 60;
END_IF;
END_CASE;
// -----------------------------------------------------------------------------
// actions
// -----------------------------------------------------------------------------
io.bActionStart := (io.nSmPosTable = 31);
io.nActionID := posTable.stEntry[io.nIndex].nActionID;
IF io.bActionStart THEN
// map PosTable actions
CASE io.nActionID OF
ACTIONID_NO_ACTION:
bDefaultActionDone := TRUE;
ACTIONID_WAIT_ONE_SCAN:
bDefaultActionDone := TRUE;
ACTIONID_WAIT_STEP:
bWaiting := TRUE;
bDefaultActionDone := bOsrStep;
ELSE
bDefaultActionDone := io.nActionID <= RESERVED_ACTIONID_UBOUND;
END_CASE;
ELSE
bDefaultActionDone := FALSE;
END_IF;
// -----------------------------------------------------------------------------
// state monitoring
// Waiting condition is used to exclude the time where it is waiting for the Step button
// -----------------------------------------------------------------------------
io.nStateTime := MceStateMonitoring(nState := io.nSmPosTable,
bFreezeTimer := bWaiting OR NOT io.bSystemReady OR (io.nSmPosTable >= 40) OR (io.nSmPosTable < 10),
stateData := stStateMonitoringData);
// -----------------------------------------------------------------------------
// process move commands
// -----------------------------------------------------------------------------
// Rising edge signals of Enable
FOR i := 0 TO QA_MAX DO
aOsrEnable[i] := aEnable[i] AND NOT aOneShots[i+6];
aOneShots[i+6] := aEnable[i];
END_FOR;
// map parameterdata to move FB's
FOR i := 0 TO QA_MAX DO
IF aOsrEnable[i] THEN
McePosTableUpdateLoadIndex( allowQueuing := bAllowQueuing,
firstMove := bFirstMove,
stopAfterLastEntry := ((io.nCycleNr + 1) >= io.nNrOfCycles) AND NOT (io.nNrOfCycles = 0),
index := io.nIndex,
loadIndex := io.nLoadIndex,
QA := nQA,
posTable := posTable);
aUseMoveType[i] := posTable.stEntry[io.nLoadIndex].bMoveType;
IF aUseMoveType[i] = 1 THEN
// -------------------------------------
// mapping linear move FB'S
// -------------------------------------
fbRobotMoveLinearAbsolute[i].RobotNumber := io.nRobotNumber;
fbRobotMoveLinearAbsolute[i].TargetType := posTable.stEntry[io.nLoadIndex].bTargetType;
// target position
MEMUtils.MemSet(pbyBuffer := ADR(fbRobotMoveLinearAbsolute[i].TargetPosition), byValue := 0, dwSize := SIZEOF(fbRobotMoveLinearAbsolute[i].TargetPosition));
IF posTable.stEntry[io.nLoadIndex].bTargetType THEN
// as TCP position
MEMUtils.MemCpy(pbyDest := ADR(fbRobotMoveLinearAbsolute[i].TargetPosition.TCPPosition.TCPPosition), pbySrc := ADR(posTable.stEntry[io.nLoadIndex].aPosition), dwSize := SIZEOF(posTable.stEntry[io.nLoadIndex].aPosition));
ELSE
// as axis positions
MEMUtils.MemCpy(pbyDest := ADR(fbRobotMoveLinearAbsolute[i].TargetPosition.AxisPosition), pbySrc := ADR(posTable.stEntry[io.nLoadIndex].aPosition), dwSize := SIZEOF(posTable.stEntry[io.nLoadIndex].aPosition));
END_IF;
// blending between motions
fbRobotMoveLinearAbsolute[i].BlendFactor := SINT_TO_DINT(posTable.stEntry[io.nLoadIndex].nBlendFactor);
fbRobotMoveLinearAbsolute[i].BlendType := 0;
fbRobotMoveLinearAbsolute[i].Speed := posTable.stEntry[io.nLoadIndex].fSpeed;
fbRobotMoveLinearAbsolute[i].UseRotationalSpeed := posTable.stEntry[io.nLoadIndex].bUseRotationalSpeed;
fbRobotMoveLinearAbsolute[i].SpeedUnits := posTable.stEntry[io.nLoadIndex].nSpeedUnits;
fbRobotMoveLinearAbsolute[i].Acceleration := SINT_TO_REAL(posTable.stEntry[io.nLoadIndex].nAcc);
fbRobotMoveLinearAbsolute[i].Deceleration := SINT_TO_REAL(posTable.stEntry[io.nLoadIndex].nDec);
ELSE
// -------------------------------------
// mapping PtP move FB's
// -------------------------------------
fbRobotMoveAxisAbsolute[i].RobotNumber := io.nRobotNumber;
fbRobotMoveAxisAbsolute[i].TargetType := posTable.stEntry[io.nLoadIndex].bTargetType;
// target position
MEMUtils.MemSet(pbyBuffer := ADR(fbRobotMoveAxisAbsolute[i].TargetPosition), byValue := 0, dwSize := SIZEOF(fbRobotMoveAxisAbsolute[i].TargetPosition));
IF posTable.stEntry[io.nLoadIndex].bTargetType THEN
// as TCP position
MEMUtils.MemCpy(pbyDest := ADR(fbRobotMoveAxisAbsolute[i].TargetPosition.TCPPosition.TCPPosition), pbySrc := ADR(posTable.stEntry[io.nLoadIndex].aPosition), dwSize := SIZEOF(posTable.stEntry[io.nLoadIndex].aPosition));
ELSE
// as axis positions
MEMUtils.MemCpy(pbyDest := ADR(fbRobotMoveAxisAbsolute[i].TargetPosition.AxisPosition), pbySrc := ADR(posTable.stEntry[io.nLoadIndex].aPosition), dwSize := SIZEOF(posTable.stEntry[io.nLoadIndex].aPosition));
END_IF;
// blending between motions
fbRobotMoveAxisAbsolute[i].BlendFactor := SINT_TO_DINT(posTable.stEntry[io.nLoadIndex].nBlendFactor);
fbRobotMoveAxisAbsolute[i].BlendType := 0;
fbRobotMoveAxisAbsolute[i].Speed := posTable.stEntry[io.nLoadIndex].fSpeed;
fbRobotMoveAxisAbsolute[i].Acceleration := SINT_TO_REAL(posTable.stEntry[io.nLoadIndex].nAcc);
fbRobotMoveAxisAbsolute[i].Deceleration := SINT_TO_REAL(posTable.stEntry[io.nLoadIndex].nDec);
END_IF;
// -------------------------------------
// mapping SetUserFrame FB's
// -------------------------------------
fbRobotSetUserFrame[i].RobotNumber := io.nRobotNumber;
nUserFrameNr := SINT_TO_DINT(posTable.stEntry[io.nLoadIndex].nUserFrameNumber);
fbRobotSetUserFrame[i].UserFrameNumber := nUserFrameNr;
fbRobotSetUserFrame[i].UserFrameData.CoordFrame := userFrames.aFrames[nUserFrameNr].aVector;
// change user frame; if the user frame number differs from the previous stEntry a MLxRobotSetUserFrame will be triggered just before the move
aChangeUserFrame[i] := bFirstMove OR (nUserFrameNr <> INT_TO_DINT(MLX.Robot[io.nRobotNumber].ActiveUserFrameNumber));
// -------------------------------------
// mapping ToolSelect FB's
// -------------------------------------
fbRobotSelectTool[i].RobotNumber := io.nRobotNumber;
nToolNr := SINT_TO_DINT(posTable.stEntry[io.nLoadIndex].nToolNumber);
fbRobotSelectTool[i].ToolNumber := nToolNr;
fbRobotSelectTool[i].ToolData := tools.aTools[nToolNr];
// change (select) tool; if the tool number differs from the previous stEntry a MLxRobotSelectTool will be triggered just before the move
aChangeTool[i] := bFirstMove OR (nToolNr <> INT_TO_DINT(MLX.Robot[io.nRobotNumber].ActiveToolNumber));
bFirstMove := FALSE;
END_IF;
END_FOR;
// map Enable signals to move FB's
FOR i := 0 TO QA_MAX DO
bUserFrameDone := NOT aChangeUserFrame[i] OR (fbRobotSetUserFrame[i].Sts_EN AND fbRobotSetUserFrame[i].Sts_DN AND NOT fbRobotSetUserFrame[i].Sts_ER);
bToolDone := NOT aChangeTool[i] OR (fbRobotSelectTool[i].Sts_EN AND fbRobotSelectTool[i].Sts_DN AND NOT fbRobotSelectTool[i].Sts_ER);
fbRobotMoveAxisAbsolute[i].Enable := aEnable[i] AND NOT (aUseMoveType[i] = 1) AND bUserFrameDone AND bToolDone;
fbRobotMoveLinearAbsolute[i].Enable := aEnable[i] AND (aUseMoveType[i] = 1) AND bUserFrameDone AND bToolDone;
fbRobotSetUserFrame[i].Enable := aEnable[i] AND aChangeUserFrame[i];
fbRobotSelectTool[i].Enable := aEnable[i] AND aChangeTool[i];
END_FOR;
// -----------------------------------------------------------------------------
// FB calls (order is important!)
// -----------------------------------------------------------------------------
FOR i := 0 TO QA_MAX DO
fbRobotSelectTool[i](MLX := MLX);
fbRobotSetUserFrame[i](MLX := MLX);
fbRobotMoveAxisAbsolute[i](MLX := MLX);
fbRobotMoveLinearAbsolute[i](MLX := MLX);
END_FOR;
// -----------------------------------------------------------------------------
// map status signals from move FB's
// -----------------------------------------------------------------------------
FOR i := 0 TO QA_MAX DO
IF aUseMoveType[i] = 1 THEN
aMoveCmd[i].bSts_EN := fbRobotMoveLinearAbsolute[i].Sts_EN;
aMoveCmd[i].bSts_DN := fbRobotMoveLinearAbsolute[i].Sts_DN;
aMoveCmd[i].bSts_IP := fbRobotMoveLinearAbsolute[i].Sts_IP;
aMoveCmd[i].bSts_AC := fbRobotMoveLinearAbsolute[i].Sts_AC;
aMoveCmd[i].bSts_PC := fbRobotMoveLinearAbsolute[i].Sts_PC;
aMoveCmd[i].bSts_ER := fbRobotMoveLinearAbsolute[i].Sts_ER;
aMoveCmd[i].nPercentComplete := fbRobotMoveLinearAbsolute[i].PercentComplete;
ELSE
aMoveCmd[i].bSts_EN := fbRobotMoveAxisAbsolute[i].Sts_EN;
aMoveCmd[i].bSts_DN := fbRobotMoveAxisAbsolute[i].Sts_DN;
aMoveCmd[i].bSts_IP := fbRobotMoveAxisAbsolute[i].Sts_IP;
aMoveCmd[i].bSts_AC := fbRobotMoveAxisAbsolute[i].Sts_AC;
aMoveCmd[i].bSts_PC := fbRobotMoveAxisAbsolute[i].Sts_PC;
aMoveCmd[i].bSts_ER := fbRobotMoveAxisAbsolute[i].Sts_ER;
aMoveCmd[i].nPercentComplete := fbRobotMoveAxisAbsolute[i].PercentComplete;
END_IF;
END_FOR;
// -----------------------------------------------------------------------------
// map nPercentComplete
// -----------------------------------------------------------------------------
CASE io.nSmPosTable OF
10..29:
io.nPercentComplete := aMoveCmd[nInstanceNumber[0]].nPercentComplete;
30..50:
io.nPercentComplete := 100;
ELSE
io.nPercentComplete := 0;
END_CASE;
// -----------------------------------------------------------------------------
// PosTable status
// -----------------------------------------------------------------------------
io.bWaitForStep := bWaiting;
io.bDone := (io.nSmPosTable = 50);
io.bIdle := (io.nSmPosTable < 10);
io.bBusy := (io.nSmPosTable >= 10 AND io.nSmPosTable < 50);
bError := MLX.JoggingMode;
FOR i := 0 TO QA_MAX DO
bError := bError OR (aMoveCmd[i].bSts_EN AND aMoveCmd[i].bSts_ER)
OR (fbRobotSelectTool[i].Sts_EN AND fbRobotSelectTool[i].Sts_ER)
OR (fbRobotSetUserFrame[i].Sts_EN AND fbRobotSetUserFrame[i].Sts_ER);
END_FOR;
IF bError THEN
io.nErrorCode := 1000 + INT_TO_UDINT(io.nSmPosTable);
io.nSmPosTable := 99;
END_IF;
Implementation
fbPosTable : ARRAY[0..0] OF McePosTable;
fbPosTable[0]( io := dummy, MLX := dummy, posTable := dummy, userFrames := dummy, tools := dummy );