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
---------------

.. todo:: This example needs updating.

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 :ref:`A Complete Program`.
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
^^^^^^^^

::

  /* File example.st: State program example. */
  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);
    }
  }%
