Running SNL Programs

This chapter is about how to run and customize a program, once it has been compiled. It also explains how to get information about a running program instance and how to stop a program.

Our running example is the “demo” program which you find under the path examples/demo in the source tree.

Command Syntax

The commands discussed in this chapter can be invoked in different ways. One way is to call them directly from C code. Prototypes for the corresponding C functions can be found in the header file “seqCom.h” which a successful build installs into the “include” directory.

More typical, however, is to call them from some kind of command shell, such as the EPICS IOC shell, or the VxWorks C shell, or the RTEMS cexp shell. In each case the syntax is slightly different, but this is not the place to discuss all these (sometimes subtle) differences. Thus, for simplicity, I will mostly use the IOC shell syntax, indicated by a leading “epics> ” shell prompt (so this does not belong to the command itself).

Details about the commands are listed in the Shell Command Reference at the end of this chapter.

Starting a Program

To start an SNL program, the seq function is called with three parameters:

  1. The address of the seqProgram structure. This is a value that is generated by the SNL compiler; it has the same name as the identifier after the program keyword.
  2. Optionally a string containing parameter definitions, see next section.
  3. Optionally a stack size. If 0 (zero), a reasonable default for the target platform is used (epicsThreadStackSmall).

For instance, the following command starts the demo program with no parameters and teh default stack size:

epics> seq demo

Note that the IOC shell defaults missing arguments to zero. Also note that the program gets passed as a string when using the IOC shell, whereas in C you would have to call it like:

seq(&demo,0,0);

If parameters are to be specified (see next section), this would instead look like

epics> seq demo "debug=0,prefix=demo"

The sequencer responds with the following output:

Sequencer release 2.1.14, compiled Wed Sep 25 12:18:24 2013
Spawning sequencer program "demo", thread 0x98fe120: "demo"

The most useful information here is the (EPICS) thread ID 0x98fe120, since this can be used later on to identify the running program.

BTW, all shell commands write their output to stdout.

Program Parameters

Parameters are a way to customize an SNL program. A program parameter has a name (a string) and a value (also a string). The parameter name should be a valid SNL identifier, while the value can be anything.

Parameters are specified (or defined) in two places: when invoking a program for execution, using the seq procedure, or from inside a program after the initial program clause. Parameters specified on invocation override those specified in the program. In both cases, the syntax of the actual specification is, roughly:

"name1=value1,name2=value2,..."

Note that the whole specification is embedded in a single string. A single definition consists of the parameter name, followed by an equal sign, followed by the value. Multiple parameter definitions are separated by comma.

The value is not allowed to contain a comma, or spaces; leading or trailing spaces around the value are ignored. Normal C/SNL string character escaping is admissable, e.g. to embed double quotes or line breaks, but cannot be used to circumvent these limitations.

Special Parameters

The following built-in parameters have special meaning to the sequencer.

debug = <level>

This is currently only used by the PV subsystem. A level of 1 or greater turns on debug messages.

name = <thread_name>

Normally the thread names are derived from the program name. This parameter specifies an alternative base name for the state set threads.

priority = <task_priority>

This parameter specifies initial thread priorities. The value should be an integer between 0 (lowest) and 99 (highest) and will be passed epicsThreadCreate when teh state set threads are created.

stack = <stack_size>

This parameter specifies the stack size in bytes. The default is whatever epicsThreadGetStackSize(epicsThreadStackSmall) returns.

Using Parameters

Parameter values are available inside the program via the built-in procedure macValueGet. They are also (automatically) available for parameter expansion when specifying PV names in assign clauses.

Examining a Program

You can get information about your program at runtime by calling one of several shell commands. They all take a thread ID as first argument to uniquely identify the a running program instance. If this argument is zero (i.e. omitted in the IOC shell), they default to displaying a table that lists the thread IDs of all state sets of all running program instances.

Here is what you might get for the demo program:

epics> seqShow
Program Name        Thread ID           Thread Name         SS Name
------------        ---------           -----------         -------
demo                0x88c1da8           demo                light
                    0xb6a1e160          demo_1              ramp
                    0xb6a1e2b8          demo_2              limit

When the seqShow command is called with one of the thread IDs listed in the table it will give you more detailed information about the running program.

You can get detailed information about the process variables associated with a program by calling seqChanShow with a valid thread ID as the first argument. Similarly, seqChanShow displays information about monitor queues. Here are some example invocations:

epics> seqChanShow 0x88c1da8 demo:lightOn
epics> seqChanShow 0x88c1da8 -
epics> seqChanShow 0x88c1da8 +
epics> seqQueueShow 0x88c1da8

The optional second parameter to seqChanShow or seqQueueShow is a channel name, or - to show only those channels which are disconnected, or + to show only those channels which are connected. Both will prompt for input after showing the first (or the specified) channel: hit Enter or a signed number to view more channels or queues; enter q to quit.

Stopping a Program

In order to cleanly shut down a running program, use the seqStop command:

epics> seqStop 0x88c1da8

A program can also terminate itself, see transitions.

Shell Command Reference

These are commands to be issued from the IOC shell or VxWorks shell. They can also be called from C (and therefore SNL) code.

Some of these routines behave slightly different depending on whether run under iocsh or a VxWorks shell. This mostly concerns the threadID argument: under iocsh, this can in fact be a thread name instead of a thread ID. Note, however, that this is unreliable if you have more than one instance of the same program running, since the thread names are identical for all instances. The VxWorks shell version directly takes an epicsThreadID argument and thus does not recognize thread names.

void seq(seqProgram *program, const char *paramdefs, unsigned stacksize)

Start the given program with the given set of parameter definitions and stack size. If `stacksize is zero or is omitted, then use a default (EPICS “small” stack). If paramdefs is zero or the last two arguments are omitted, then no parameters are defined. Otherwise paramdefs should be a string that specifies program parameters as detailed in Program Parameters. See also program_param.

void seqShow()
void seqShow(epicsThreadId threadID)

The first form shows a table of all programs, program instances, and state sets, e.g.

epics> seqShow
Program Name        Thread ID           Thread Name         SS Name
------------        ---------           -----------         -------
demo                0x807e628           demo                light
                    0x809fbc8           demo_1              ramp
                    0x809fcd8           demo_2              limit
------------        ---------           -----------         -------
demo                0x807fd98           demo                light
                    0xb7100470          demo_1              ramp
                    0xb7100580          demo_2              limit
------------        ---------           -----------         -------
demo                0x80814e0           demo                light
                    0x809fe68           demo_1              ramp
                    0x809ff78           demo_2              limit

Note that in this example we have three running instances of a single program named ‘demo’, each of which consists of three state sets running in its own thread.

The second form displays the internal state of a running program instance. The threadID parameter must be one of the program’s state set thread IDs as listed in the above table.

For instance, for the above example we might get

epics> seqShow 0x807e628
State Program: "demo"
  thread priority = 50
  number of state sets = 3
  number of syncQ queues = 0
  number of channels = 6
  number of channels assigned = 6
  number of channels connected = 6
  number of channels monitored = 5
  options: async=0, debug=0, newef=1, reent=1, conn=1, main=0
  user variables: address = 0x807d158, length = 44

  State Set: "light"
  thread name = demo;  Thread id = 0x807e628
  First state = "START"
  Current state = "LIGHT_OFF"
  Previous state = "START"
  Elapsed time since state was entered = 3.0 seconds
  Get in progress = [000000]
  Put in progress = [000000]
  Queued time delays:

  State Set: "ramp"
  thread name = demo_1;  Thread id = 0x809fbc8
  First state = "START"
  Current state = "RAMP_UP"
  Previous state = "RAMP_UP"
  Elapsed time since state was entered = 0.1 seconds
  Get in progress = [000000]
  Put in progress = [000000]
  Queued time delays:
        delay[0]=0.100000

  State Set: "limit"
  thread name = demo_2;  Thread id = 0x809fcd8
  First state = "START"
  Current state = "START"
  Previous state = ""
  Elapsed time since state was entered = 3.0 seconds
  Get in progress = [000000]
  Put in progress = [000000]
  Queued time delays:
void seqChanShow(epicsThreadId threadID)
void seqChanShow(epicsThreadId threadID, const char *)

Display channel information for the program instance specified by the given threadID. If a second argument is given, it is interpreted as part of a channel name. Only channels whose name contains the given string as substring are displayed. The name can be preceded by a single “-” or “+” sign, signifying that only disconnected (“-“) or connected (“+”) channels should be displayed.

The procedure displays one channel at a time, starting with the first matching one, and then asks the user for input. This input can be

a (signed) integer:
increase / decrease current channel number by the given amount, then display the current channel
minus sign (“-“):
same as “-1”
plus sign (“+”), empty string (return):
same as “+1”
anything else:
quit, i.e. back to the shell

If user interaction causes the channel number to leave the range (i.e. less than zero, greater or equal to number of channels), the command quits, too.

void seqQueueShow(epicsThreadId threadID)

Display information about queued channels. For example

epics> seqShow
Program Name        Thread ID           Thread Name         SS Name
------------        ---------           -----------         -------
syncqTest           0x8053e60           syncqTest           get
                    (nil)               (no thread)         get1
                    (nil)               (no thread)         put
                    (nil)               (no thread)         flush
epics> seqQueueShow 0x8053e60
State Program: "syncqTest"
Number of queues = 2
  Queue #0: numElems=5, used=0, elemSize=136
Next? (+/- skip count, q=quit)

  Queue #1: numElems=5, used=0, elemSize=56
Next? (+/- skip count, q=quit)

The command is interactive and accepts the same inputs as seqChanShow.

void seqcar(int level)

The name stands for “sequencer channel access report”. It displays channel connection information. If level <= 1, or no level argument is given, only a summary line is displayed, for example

Total programs=3, channels=18, connected=18, disconnected=0

For level > 1, connection information is displayed for all channels of all running programs. For instance

epics> seqcar 2
  Program "demo"
    Variable "light" connected to PV "demo1:light"
    Variable "lightOn" connected to PV "demo1:lightOn"
    Variable "lightOff" connected to PV "demo1:lightOff"
    Variable "voltage" connected to PV "demo1:voltage"
    Variable "loLimit" connected to PV "demo1:loLimit"
    Variable "hiLimit" connected to PV "demo1:hiLimit"
  Program "demo"
    Variable "light" connected to PV "demo2:light"
    Variable "lightOn" connected to PV "demo2:lightOn"
    Variable "lightOff" connected to PV "demo2:lightOff"
    Variable "voltage" connected to PV "demo2:voltage"
    Variable "loLimit" connected to PV "demo2:loLimit"
    Variable "hiLimit" connected to PV "demo2:hiLimit"
  Program "demo"
    Variable "light" connected to PV "demo3:light"
    Variable "lightOn" connected to PV "demo3:lightOn"
    Variable "lightOff" connected to PV "demo3:lightOff"
    Variable "voltage" connected to PV "demo3:voltage"
    Variable "loLimit" connected to PV "demo3:loLimit"
    Variable "hiLimit" connected to PV "demo3:hiLimit"
Total programs=3, channels=18, connected=18, disconnected=0
void seqStop(epicsThreadId threadID)

Initiate a clean program exit. Running state transitions are completed, then all state set threads exit, all channels are disconnected, and finally allocated resources are freed.