Every once in a while I get asked about FANUC’s multitasking capabilities:
“Can I do multitasking with a FANUC robot?”
“How do I run a background task with FANUC?”
“How do I run an aux axis independently while the robot is doing other stuff?”
The short answer is yes, but there are a number of options. Which one’s appropriate depends on your use-case.
Option | Environment |
---|---|
Background Logic | Setup (can run TP |
The RUN statement |
TP (can run TP or KAREL programs) |
AUTOEXEC programs |
System Config (can run TP or KAREL programs) |
Condition Monitors | TP |
Condition Handlers | KAREL |
PMC (Programmable Machine Control)* | Ladder logic |
*NOTE: PMC requires a robot software option and the FANUC Ladder PC software. Only recommended for budget systems that don’t have a separate PLC.
I use Background Logic most often, and I’ve written about it before with an Intro to FANUC Background Logic and How to use BG Logic to to simplify your TP programs. If you don’t need motion, you can probably do it with Background Logic. For almost all other cases, I will generally use the RUN
statement to spawn another task.
NOTE: I almost never use
AUTOEXEC
programs, Condition Monitors or PMC, so let’s skip over those for now. Let me know if you want to know more about them.
Today I want to talk about the RUN
statement — the basics, potential use-cases and some things to watch for. I’ll wrap things up with a simple KAREL utility that should help with one of the most annoying issues you might see when using the RUN
statement.
RUN statement basics
You can add a RUN
statement in the TP editor via INST > Multiple Control > RUN
.
On the surface it looks just like a program CALL
. You simply provide the program name and any (optional) parameters:
CALL FOO ;
RUN FOO ;
CALL FOO_WITH_ARGS(1,2,3) ;
RUN FOO_WITH_ARGS(1,2,3) ;
You’re probably aware that process control is immediately given to a called program and returned to the calling program when the called program ends.
! main task ;
CALL FOO ;
! we are in FOO now ;
! FOO ends ;
! back to main task ;
RUN
statements work differently. The main program proceeds immediately after the RUN
statement is executed, and both programs will now execute concurrently.
! main task ;
RUN FOO ;
! both the main task AND foo are both running now ;
NOTE: The details of how the controller handles task-scheduling are outside the scope of this post, but scheduling itself is a really interesting computer science problem. I learned a lot by taking this free course on operating systems, and I would highly recommend it if you are interested in learning more about how computers and operating systems (like the FANUC robot controller) actually work.
Some potential use-cases
I’ve generally only RUN
a concurrent task when I am controlling an external device or independent axis.
You may also want to spin off a separate task when you need to signal something that takes a little while, but you don’t want the robot to wait for a response.
I generally use Flags (F[]
) to communicate between tasks. Flags are just boolean I/O bits that are completely local to the robot — perfect for interprocess communication.
Say you wanted to spin off a concurrent task, let the robot do some other stuff without waiting but then you wanted the robot to make sure the task completed before moving on:
! main task ;
F[1:TASK DONE]=(OFF) ;
RUN ASYNC_TASK ;
! do some stuff ;
WAIT (F[1:TASK DONE]) ;
! async_task ;
! do some concurrent stuff ;
F[1:TASK DONE]=(ON) ;
Sometimes it’s safe to just fire these tasks off and forget, but it’s usually helpful to have some sort of signaling to make sure your concurrent tasks don’t hang or create race conditions.
Some things to watch out for
Motion control
Only one task can have motion control for any group at one time. Have you ever noticed that GROUP_MASK
header in your TP programs? That specifies which, if any, motion groups your program will lock when it is executed.
A FANUC robot can have up to 8 motion groups (a group of axes or a single axis), but most robots are probably just a single group. That’s why the GROUP_MASK
header is usually set to 1,*,*,*,*,*,*,*
. This indicates that the program will lock motion for group 1 and no other groups.
BGLOGIC programs must not lock any motion groups, so that GROUP_MASK
header will be *,*,*,*,*,*,*,*
.
In general, it may be a good idea to only lock motion when necessary (e.g. use a GROUP_MASK of *,*,*,*,*,*,*,*
for your gripper macros, etc.)
Setting the IGNORE PAUSE
header
Sometimes you want your concurrent task to continue running even if a fault occurs. If this is the case, set the IGNORE PAUSE
header from the program DETAIL
screen.
Debugging and monitoring concurrent tasks
When your robot is running, the TP editor will typically show the main task’s status.
The best way to see what your other tasks are doing is to bring up the SELECT
screen and then press the F4 (MONITOR)
softkey. Press ENTER
to view the task in an editor, and you can also PAUSE
and ABORT
each one individually from this screen.
NOTE: You can also see the program status (but not the source code) from the
Menu > Status > Program
menu. HitNEXT
to cycle through the current running tasks.
Making sure all tasks abort
You can configure the UOP cycle stop bit to abort all tasks by going to Menu > System > Config
and setting CSTOPI for ABORT
and Abort all Programs by CSTOPI
to true. (You could also just set the $SHELL_CFG.$USE_ABORT
and $SHELL_CFG.$CSTOPI_ALL
system variables.)
I generally always set these bits. I can’t think of any instances where I wouldn’t want a single bit to be able to abort everything in an error condition. It’s like giving the PLC access to the TP’s Function > ABORT ALL
button.
Attempting to RUN
the task when it’s already running
Ah the dreaded INTP-267 RUN stmt failed
error caused by PROG-007 Program is already running
. This error is always annoying, typically requires an abort, and I’ve seen a lot of people (including myself) throw some nasty hacks in to try and avoid this. (I’ve even seen this occur while running FANUC’s PalletTool and iRPickTool… maybe it’s fixed now!)
This could simply be a programming bug (e.g. you did not have appropriate checks in place to ensure that your task completed before running it again), but it could also be a case where your concurrent task hung up for some reason (e.g. IGNORE PAUSE header was not set) and did not get resumed properly. Maybe you are not using the Abort all Program by CSTOPI
option.
Wouldn’t it be nice if there was a way to check if a task is running before you attempt to RUN
it? I’m looking for something a bit more reliable than a F[1:TASK RUNNING]
flag.
TASK_STATUS KAREL utility
I should have written this years ago, but here’s a little KAREL utility that provides just that. You give it the name of a program and a status register id, and it will tell you what the task is doing.
Example:
LBL[1] ;
CALL TASK_STATUS(‘ASYNC_TASK’,1) ;
IF R[1:TASK STATUS]<>2,JMP LBL[501] ;
! task is aborted ;
RUN ASYNC_TASK ;
END ;
;
LBL[501] ;
! task is still doing something or hung up ;
JMP LBL[1] ;
The possible return values are:
-2 | Run request has been accepted |
-1 | Abortrequest has been accepted |
0 | Task is running |
1 | Task is paused |
2 | Task is aborted |
Other | Refer to the Error Code manual… probably a usage issue (e.g. misspelled program name) |
I’ll write another article about the KAREL internals of TASK_STATUS
, but for now you can check out the source code and download the latest binary release on GitHub.
NOTE: You’ll need the R632 KAREL software option on your robot in order to load and use this utility.
As a parting note (and I actually did not know this until I was working on writing this post) you can actually use the RUN
statement to resume a paused task by name. This pairs nicely with the TASK_STATUS
KAREL program.
Here’s a contrived example:
Parent Task
RUN BG ;
WAIT 2.00(sec) ;
CALL TASK_STATUS(‘BG’,1) ;
IF R[1:TASK_STATUS]<>1,JMP LBL[500] ;
! bg is paused ;
RUN BG ;
CALL TASK_STATUS(‘BG’,1) ;
IF R[1:TASK_STATUS]<>0,JMP LBL[500] ;
BG Task
PAUSE ;
LBL[1] ;
JMP LBL[1] ;
Traditionally I’ve avoided pausing concurrent tasks, preferring to use WAIT
statement to keep things in sync instead, but maybe there are cases when you would legitimately want to use the PAUSE
statement. Paired with TASK_STATUS
, you can be sure to avoid those pesky INTP-267
and PROG-007
errors.