Examples of SNL Programs¶
Entry and exit action example¶
The following program illustrates entry and exit actions.
program snctest
float v;
assign v to "grw:xxxExample"; monitor v;
ss ss1 {
state low {
entry {
printf("Will do this on entry");
printf("Another thing to do on entry");
}
when (v>5.0) {
printf("now changing to high\n");
} state high
when (delay(.1)) { } state low
exit {
printf("Something to do on exit");
}
}
state high {
when (v<=5.0) {
printf("changing to low\n");
} state low
when(delay(.1)) { } state high
}
}
Dynamic assignment example¶
The following segment of a program illustrates dynamic assignment of database variables to database channels. We have left out error checking for simplicity.
program dynamic
option -c; /* don't wait for db connections */
string sysName;
assign sysName to "";
long setpoint[5];
assign setpoint to {}; /* don't need all five strings */
int i;
char str[30];
ss dyn {
state init {
when () {
sprintf (str, "MySys:%s", "name");
pvAssign (sysName, str);
for (i = 0; i < 5; i++) {
sprintf (str, "MySys:SP%d\n", i);
pvAssign (setpoint[i], str);
pvMonitor (setpoint[i]);
}
} state process
}
state process {
...
}
}
Complex example¶
The following program contains most of the concepts presented
in the previous sections. It consists of four state sets: (1)
level_det
, (2) generate_voltage
, (3) test_status
, and
(4) periodic_read
. The state set level_det
is similar to the
example in A Complete Program from the Tutorial.
It generates a triangle waveform in one state set and detects the
level in another. Other state sets detect and print alarm status
and demonstrate asynchronous pvGet
and pvPut
operation. The
program demonstrates several other concepts, including access to
run-time parameters with macro substitution and macValueGet
, use
of arrays, escaped C code, and VxWorks input-output.
Preamble¶
program example ("unit=ajk, stack=11000")
/*=================== declarations =========================*/
float ao1;
assign ao1 to "{unit}:ao1";
monitor ao1;
float ao2;
assign ao2 to "{unit}:ao1";
float wf1[2000];
assign wf1 to "{unit}:wf1.FVAL";
short bi1;
assign bi1 to "{unit}:bi1";
float delta;
short prev_status;
short ch_status;
evflag ef1;
evflag ef2;
option +r;
int fd; /* file descriptor for logging */
char *pmac; /* used to access program macros */
level_det state set¶
/*=================== State Sets ===========================*/
/* State set level_det detects level > 5v & < 3v */
ss level_det {
state start {
when() {
fd = -1;
/* Use parameter to define logging file */
pmac = macValueGet("output");
if (pmac == 0 || pmac[0] == 0) {
printf("No macro defined for \"output\"\n");
fd = 1;
}
else {
fd = open(pmac, (O_CREAT | O_WRONLY), 0664);
if (fd == ERROR) {
printf("Can't open %s\n", pmac);
exit (-1);
}
}
fdprintf(fd, "Starting program\n");
} state init
}
state init {
/* Initialize */
when (pvConnectCount() == pvChannelCount() ) {
fdprintf(fd, "All channels connectedly");
bi1 = FALSE;
ao2 = -1.0;
pvPut(bi1);
pvPut(ao2);
efClear(ef2);
efSet(ef1);
} state low
when (delay(5.0)) {
fdprintf(fd, "...waiting\n");
} state init
}
state low {
when (ao1 > 5.0) {
fdprintf(fd, "High\n");
bi1 = TRUE;
pvPut(bi1);
} state high
when (pvConnectCount() < pvChannelCount() ) {
fdprintf(fd, "Connection lost\n");
efClear(ef1);
efSet(ef2);
} state init
}
state high {
when (ao1 < 3.0) {
fdprintf(fd, "Low\n");
bi1 = FALSE;
pvPut(bi1);
} state low
when (pvConnectCount() < pvChannelCount() ) {
efSet(ef2);
} state init
}
}
generate_voltage state set¶
/* Generate a ramp up/down */
ss generate_voltage {
state init {
when (efTestAndClear(ef1)) {
printf("start ramp\n");
fdprintf(fd, "start ramp\n");
delta = 0.2;
} state ramp
}
state ramp {
when (delay(0.1)) {
if ( (delta > 0.0 && ao2 >= 11.0) ||
(delta < 0.0 && ao2 <= -11.0) ) {
delta = -delta;
}
ao2 += delta;
pvPut(ao2);
} state ramp
when (efTestAndClear(ef2)) {
} state init
}
}
test_status state set¶
/* Check for channel status; print exceptions */
ss test_status {
state init {
when (efTestAndClear(ef1)) {
printf("start test_status\n");
fdprintf(fd, "start test_status\n");
prev_status = pvStatus(ao1);
} state status_check
}
state status_check {
when ((ch_status = pvStatus(ao1)) != prev_status) {
print_status(fd, ao1, ch_status, pvSeverity(ao1));
prev_status = ch_status;
} state status_check
}
}
periodic_read state set¶
/* Periodically write/read a waveform channel. This uses
pvGetComplete() to allow asynchronous pvGet().
*/
ss periodic_read {
state init {
when (efTestAndClear(ef1)) {
wf1[0] = 2.5;
wf1[1] = -2.5;
pvPut(wf1);
} state read_chan
}
state read_chan {
when (delay(5.)) {
wf1[0] += 2.5;
wf1[1] += -2.5;
pvPut(wf1);
pvGet(wf1);
} state wait_read
}
state wait_read {
when (pvGetComplete(wf1)) {
fdprintf(fd, "periodic read: ");
print_status(fd, wf1[0], pvStatus(wf1), pvSeverity(wf1));
} state read_chan
}
}
exit procedure¶
/* Exit procedure - close the log file */
exit {
printf("close fd=%d\n", fd);
if ((fd > 0) && (fd != ioGlobalStdGet(1)) )
close(fd);
fd = -1;
}
C functions¶
/*==================== End of state sets =====================*/
%{
/* This C function prints out the status, severity,
and value for a channel. Note: fd is passed as a
parameter to allow reentrant code to be generated */
print_status(int fd, float value, int status, int severity)
{
char *pstr;
switch (status)
{
case NO_ALARM: pstr = "no alarm"; break;
case HIHI_ALARM: pstr = "high-high alarm"; break;
case HIGH_ALARM: pstr = "high alarm"; break;
case LOLO_ALARM: pstr = "low-low alarm"; break;
case LOW_ALARM: pstr = "low alarm"; break;
case STATE_ALARM: pstr = "state alarm"; break;
case COS_ALARM: pstr = "cos alarm"; break;
case READ_ALARM: pstr = "read alarm"; break;
case WRITE_ALARM: pstr = "write alarm"; break;
default: pstr = "other alarm"; break;
}
fprintf (fd, "Alarm condition: \"%s\"", pstr);
if (severity == MINOR_ALARM)
pstr = "minor";
else if (severity == MAJOR_ALARM)
pstr = "major";
else
pstr = "none";
fdprintf (fd, ", severity: \"%s\", value=%g\n", pstr, value);
}
}%