V4 Replacement for SNL

From EPICSWIKI

Warning: The following is work in progress.

Scope

This document is concerned mainly with features of SNL as a programming language, especially for the control of complex devices. Other people have gathered requirements that concentrate more on the interaction between the runtime system (the sequencer) and the rest of the IOC, in order to support features like redundant IOC replication, better debugging etc. I hope we will be able to bring all those requirements together, somehow.

Deficiencies of current SNL

The current version of SNL (State Notation Language) has a number of severe deficiencies that should be addressed in a new version for EPICS R4.

No Abstraction Facilities
SNL doesn't support factoring common functionality. Functions cannot be defined in SNL, only by escaping to C, loosing all the features SNL adds (for instance easy access to PVs via 'assigned' variables). A typical workaround is to create additional state sets as a kind of subroutine. However, parameter passing and start/done communication must be simulated via PV-assigned global variables.
State Change is Determined by When Clause
In SNL the next state is already determined by the when-condition. This means that a condition for when to *do* something is coupled to a commitment for what to do next. This is often very inconvenient and to work around this limitation leads to a proliferation of artificial states that have nothing to do with the original state machine model.
Weak Support for Introspection
SNL supports querying internal state via the iocShell, but this is rather inconvenient and doesn't cover continous monitoring of the current state of a state set. In order to achieve this, the programmer has to (1) create a record to contain the value representing the current state, and (2) manually pvPut a value corresponding to the state into the PV on entry to the state, which clutters the code with extremely repetitive code, maintained via cut & paste.

Remedies

We propose a redesign of SNL, building on good and bad experiences with the current version. The goal is to remedy the deficiencies mentioned above, without throwing away what is good and proven to work. The syntax and semantics should be similar, although we adopt a somewhat different viewpoint. This viewpoint naturally leads to a number of new features that, taken together, solve the above mentioned problems. In particular:

Abstraction
Instead of adding ordinary procedures to the language, we propose a new way to instantiate state sets. A state set can be called like a procedure. It accepts parameters and can return a value. This necessitates thinking about the lifetime of state set objects.
State Change
Going to another state becomes a statement. It can be executed from anywhere in the code.
Introspection
The current state of all state sets, as well as global diagnostics (like the number of connected PVs) are exported using Data Access. The whole SNL programm will be represented as a propertyCatalog, with sub-properties for each named state set instance.

A Preliminary Observation

It is widely known that closures (procedure + environment) can be implemented as objects in a language that natively supports objects. It is less well known, that objects can be implemented as closures in languages where closures are a native feature. The two concepts are quite similar. In what follows we will often switch from viewing things as objects to viewing them as closures and vice versa.

Overview

The new SNL will still be a programming language in its own right. It will still be based on the notions of "state" and "state set"; it will still support a subset of C for "normal" imperative programming (statements, if/then/else, assignment, etc.).

However, we introduce a new view on the concepts of "state set" and "state". A state set is regarded as a (special form of) closure resp. object.

  • State sets can be asynchronously spawned to run as an independent task. But they can also be synchronously called, like a normal procedure, where the caller stops and waits for the state set to return. In both cases, the caller can specify a name (string) for the state set that will be used to represent the state set to the outside.
  • In contrast to the current SNL, all state sets must be instantiated explicitly, even those that are meant to "live forever". This has the additional advantage that initialization can be better controlled by the programmer.
  • State sets can receive arguments on creation. In the OO view, this corresponds to constructor arguments. The arguments are visible throughout the state set, but not to any outside entity.
  • State sets can also return a value. Returning from a state set always destroys the underlying object. If a value was returned and the state set was created with a call, the caller receives this value. Otherwise the returned value is lost.
  • State sets can have local variables. These are not visible from the outside.
  • Individual states in a state set can also have local variables, as well as parameters (but no return value). Variables and parameters are not visible outside the state. The general syntax for a state change is state state_name(arg,...);.
  • The lifetime of asynchronously (via spawn) created state sets is always limited to the enclosing block. For state sets created at the top-level, this means forever (or rather as long as the underlying task doesn't get killed).
  • Waiting on a condition to become true is done with the traditional when-clause. However, the clause does not determine a new state. Instead 'state x(args...);' is treated like an ordinary SNL action statement and can be used freely inside a when-block.

Details

Creation and Execution of State Sets

There are two ways to specify instance creation and execution of a state set: synchronous

(result_1,...,result_n) = call stateSetName(arg_1,...,arg_n) as instanceName;

and asynchronous

spawn stateSetName(arg_1,...,arg_n) as instanceName;

In both cases an instance of the state set 'stateSetName' is created with the given parameters and started. The 'as instanceName' is optional. If given, 'instanceName' must be a constant string that gives a name to the state set instance. The state set will then be visible from outside of teh SNL program via the Data Access interface to the program.

call
The caller waits (blocks execution) until the called state set returns, at which point the return values get assigned to the result variables. Assignment to result variables is optional.
spawn
The caller does not block execution and cannot receive any return values.

In the old SNL, a state set declaration implied the creation and execution of a global top-level state set object. This is no longer the case. Instead, state set instances must be explicitly created at the top-level. If a top-level state set is supposed to run permanently, it must be created with a spawn statement. This gives the programmer full control over the sequence of initialization.

Return from a State Set

A state set can stop its own execution at any time, returning any number of values, with a

return(ret_1,...,ret_n);

statement. The list of return values (including parenthesis) can be omitted. If the state set instance was created with a spawn, then any return values are lost.

State Set Declaration

Synopsis

ss MyStateSet(int int_arg, double double_arg) {
    string local_string_var = "a string";
    /* more local variable declarations */
    state aState {...}
    /* more state declarations */
}

A state set declaration is introduced by the reserved word 'ss', followed by the name of the state set, followed by an optional parenthesized list of argument declarations, followed by the state set body. The body consist of any number of local variable declarations, followed by any number of state declarations.

Two things are new here:

  • a state set may have parameters
  • a state set may have local variables

These behave the same as parameters and local variables in a C procedure definition. That is, both are visible only inside the state set, and both live as long as the state set instance lives.

State Declaration

Synopsis

state myStateName(string string_arg,...) {
    int state_local_var = 1;
    /* ...initial action statements... */
    when (condition) {
        /* ...action statements... */
        if (another_condition) {
            state anotherState(42);
        }
        state yetAnotherState;
    }
    when (...) {...}
    ...
}

A state declaration can only appear inside a state set declaration. Similarly to a state set, a single state can have parameters and local variables. The main difference to the current version are:

  • no state NextState; after the when block, instead
  • state change can appear anywhere a normal action statement is allowed

It is not yet clear what best to do if code falls off a when clause without specifying a state change. Possibilities are:

  • compiler warns about possibly missing state change
    • similar to C compiler warning about missing return
    • generated code adds "abort state set" action as guard at the end
  • compiler enforces that last statement in a when block is a state change
  • keep the old syntax as expressing a default state change if execution reaches the end of a when block

Main Program

Synopsis

program MyProgram;

int x = 0;
pv<double> y("mydevice:setpoint");
/* ...more variable declarations... */

ss MyStateSet(int n) {
    ...
}
/* ...more state set declarations... */

spawn MyStateSet(0) as "MyStateSetInstance0";
spawn MyStateSet(1) as "MyStateSetInstance1";

The main program starts with the word 'program', followed by the program name. After this follow variable declarations, then state set declarations, and then action statements. The action statements are executed immediately after program start.

PVs

PV Data Type

It might be useful to introduce a special data type to represent references to PVs. The idea is to make it possible to pass PV references as arguments to state sets (and states). With the way PVs are handled at the moment, i.e. declaring a variable and assigning it to a PV name, passing the variable would pass only the (current) value, but not the PV itself.

We could mimick C++ template syntax and declare PVs like this:

pv<double> var("pv_name");

Now, var would be a PV object. The main point here is that it now has a type different from a plain double. Implicit conversion can be applied, so that assignment (from a double value) and usage within an expression (expecting a double) can be done as before:

double x = var;
var += 1.0;

Setting up a monitor and synchronizing with an event flag can be done exactly like before. However, we could now use var as a parameter to a state set instantiation:

call MyStateSet(var);

where the state set was declared as taking a PV as argument:

ss MyStateSet(pv<double> arg) {...}

An example could be a state set that generically handles setting or releasing brakes on a motor. On instantiation of the state set, it gets passed the motor (and possibly other) PVs as the actual parameters to work on. Without a special PV type this would only be possible by declaring an array of PVs and passing an index into this array to the state set.

Anonymous PVs

Another useful feature would be to let the SNL machinery create anonymous PVs for variables that are not assigned. A typical example:

pv<int> myFlag;
ss ...{
    state ...{
        when (myFlag) {
            ...
        }
    }
}

Currently, you would have to externally create a record (for instance, a bo) and connect a variable 'myFlag' to it, even though nothing besides the SNL program is interested in the value of this flag. Automatically creating PVs for un-assigned PV variables is only possible in V4, where records can be added online. In principle, a full-blown record for such a variable is overkill; there may be a way for the sequencer to create the PV in some other way.

External Access

Each SNL program will be presented to the outside world as a Data Access property catalog. Available properties include diagnostics such as the number of connected/available member variables (if assigned to a PV), a list of all global variables assigned to PVs, and all existing (life) state set objects as subproperties. Properties of a state set include the current state (including the state's parameters), and member variables.

Implementation

We believe that our proposals can be implemented efficiently and without significantly changing the way SNL code is compiled currently. Of course, this remains to be proven.