PLEXIL In Apex
8/14/06
This document describes the translation of PLEXIL into PDL, the language
of Apex. It is based on the 9/28/05 PLEXIL specification paper, parts
of the most recent PLEXIL XML schema, and Apex 3.0.12. An example
translation is included at the end. Detailed semantics and
implemenation issues are not explored here -- this is an informal
presentation.
The translation is organized according to the section numbers in the
Plexil paper that cover the language syntax.
2.2. Node description
In general, a Plexil node becomes one or more procedure steps in PDL.
Node Attributes
Identifier
This becomes a step's tag in PDL. Example:
NodeID: DriveToTarget
becomes
(step |DriveToTarget| ...)
NOTE: The vertical bars around DriveToTarget are an artifact of
interning this string to obtain a Lisp symbol. Some details regarding
the conversion of strings in Plexil to symbols in Lisp (where
needed), may need to be worked out.
Conditions
This include: StartCondition, EndCondition, PreCondition,
PostCondition, InvariantCondition, RepeatUntilCondition. Described
below in "2.4.4 Conditions".
Priority
This translates to the priority procedure clause, which has
the same semantics, though broader applicability. Plexil seems to use
it to resolve conflicts between actions (leaf nodes) that do
incompatible things such as assigning to the same variable at the same
time or sending incompatible commands to a device. Apex usage can be
seen as a generalization of this.
Variables
Described below in "2.3.3 Declared Local Variables".
Interface
Described below in "2.3.4 Interfaces".
Node Body
The nodes in a node list are simply steps in a PDL procedure.
Command node
Described below in "2.4.3 Command".
Assignment node
Described below in "2.4.2 Assignment".
2.3.1 External States
World events and states can be obtained by Apex in two ways
- lookup forms (described below in "2.4.1 Lookups")
- commands (described below in "2.4.3 Command")
2.3.2 Internal Variables
An Apex task is the equivalent of an executing Plexil node. A task
has queryable internal state. With respect to the states given in the
Plexil document:
node.state -> (state <task>)
node.state.Timepoint -> obtainable via state history callouts
-> some states timepoints have direct accessors
node.outcome -> (outcome <task>)
node.failureType -> (failure-type <task>)
NOTE: A complete list of Plexil internal variables should be provided and
verified in Apex.
2.3.3 Declared Local Variables
PDL does not support variable declaration or static type checking. In
PDL, variables are created "on the fly" and are scoped to their
enclosing procedure. In translating PLEXIL to PDL, fortunately we can
assume that the source PLEXIL XML is validated (against the PLEXIL XML
Schema). Therefore, we can forego the "declaring" aspect of variable
declaration in PDL because we are assured there is no improper use of
variables in the code (e.g. assigning to an undeclared or in
variable).
Plexil variable declarations also include an initial value, and these
are simply supported as assignments in PDL.
2.3.4 Interfaces
The Plexil style interface declarations are not directly supported in
PDL. As stated in the previous section, if we assume the original
PLEXIL XML is validated, then we can forego interface declarations with
respect to their role in variable usage checking.
The two types of interface variables, in and in-out are
(vacuously) supported correctly within one PDL procedure.
Across PDL procedures, any in variables given to the called
procedure are passed through its index clause. It is TBD how to support
in-out variables given to the called procedure.
2.4.1 Lookups
Condition testing occurs in the context of one of the defined condition
types (see "2.4.4 Conditions") and can be either in the form of PDL
state variable query, or a callout to a Lisp function that obtains the
desired value. Examples of each, respectively:
(start-condition (temperature burner_1 >= 212.0))
(start-condition :callout (>= (get-temperature "burner_1") 212.0))
This will be handled by a Lisp function of the same name, which calls to
the external system (details of communication TBD) to register for
notifications of change in the given variable's value, to the specified
tolerance.
This translates into a step that sets up a repeating poll to the
external world using lookup-now, described next.
This type of lookup is special, because PDL currently lacks a mechanism
to invoke repeated polls inside monitors. While the other two
lookup types can be invoked within a step monitor, this one requires
that the polling/requesting of world state, and the subsequent testing
of this information in a condition (via state variable lookup), must be
done separately. A canonical example is seen in the sample translation
in this document.
A Lisp function lookup-now obtains the given variable's value in the
external world (details TBD).
Another way to look up world state is possible, through a Command with
a return value. See the note and example in "2.4.3 Command".
2.4.2 Assignment
An assignment in PLEXIL:
Assignment: <variable> = <value>;
becomes in PDL a step of the following form:
(step [tag] :blocking-callout <value> => ?<variable>)
Note that the PLEXIL variable name has a '?' prepended when it becomes a
PDL variable. The form following :blocking-callout is
evaluated in Lisp, after any PDL variables withing the form are
evaluated by Apex. So for example, the following boolean conjunction
assignment in PLEXIL:
Assignment: ready = charged AND warm;
becomes
(step :blocking-callout (and ?warm ?charged) => ?ready)
2.4.3 Command
The functional layer is invoked by the Lisp function
(command name arg1 arg2 ...)
which is invoked in a procedure step, in one of two ways. If the
command returns a value, the function will be called synchronously
(blocking). Example:
(step :blocking-callout (command "read_temperature" "stove1")
:returnval ?stove1-temp)
If no return value, it is called asychronously:
(step :non-blocking-callout (command "start_rover"))
Note that the return value form of commands is one way to look up
external states. Example:
(step s1 :blocking-callout (command "read_temperature" "stove1")
:returnval ?stove1-temp)
(step s2 :non-blocking-callout ("turn_up_stove1")
:start-condition (:in-order
?s1
:callout (< ?stove1-temp 200)))
2.4.4 Conditions
All Plexil conditions have a corresponding PDL step level and/or
procedure level clause. Let C be the specified condition.
Gate conditions
StartCondition C -> (start-condition C')
EndCondition C -> (end-condition C')
InvariantCondition C -> (invariant-condition C')
Check conditions
Precondition C -> (precondition C')
Postcondition C -> (postcondition C')
RepeatUntilCondition C -> (repeat-until-condition C')
where C' is the conversion of C into its corresponding PDL form.
Summary
In principle, the current Apex (3.0.12) supports the 9/28/05 description
of PLEXIL. This document needs revision to include newer PLEXIL
features, and the Apex system may require corresponding enhancements.
NOTE: support for in-out interface variables across procedures is
still be worked out.
Example
Here is a translation of the simple rover example given on page 5 of
the Plexil paper. Significant lines have been numbered.
Node: { 0
NodeID: DriveToTarget; 1
Boolean drive_done, timeout; 2
NodeList: { 3
Command: rover_drive(10); 4
When 5
AbsoluteTimeWithin:{10, POSITIVE_INFINITY} 6
Sequence:{ 7
Command: rover_stop(); 8
Assign: timeout=true; 9
}
When 10
LookupWithFrequency{target_in_view,10}==true; 11
Sequence:{ 12
Command: rover_stop(); 13
Assign: drive_done=true; 14
}
When timeout==true 15
Command: take_navcam(); 16
When drive_done==true 17
Command: take_pancam(); 18
Node:{
NodeID: Heater;
StartCondition: LookupOnChange{temperature}<0; 19
EndCondition: LookupOnChange{temperature}>=10; 20
RepeatUntilCondition: false; 21
Command: turn_on_heater(); 22
}
}
}
In PDL, along with supporting functions:
;; 0: A top level node becomes a PDL procedure
;;
(procedure
;; 1: Obtained this procedure's index by "interning" the Node's name.
;; (Interning is a process of obtaining a Lisp symbol from a string).
;;
(index (|DriveToTarget|))
;; 2: Initial values (required according to schema) were missing in
;; the example, so assuming False (nil in Lisp). Declarations are
;; simply translated into assignments of initial values.
(step :blocking-callout nil => ?drive-done)
(step :blocking-callout nil => ?timeout)
;; 3: In general, a NodeList becomes a set of steps. This particular
;; example does not require introducing additional procedures.
;; 4: Invoke the system command "rover_drive" with argument 10 in a
;; non-blocking fashion.
;;
(step s1 :non-blocking-callout (command "rover_drive" 10))
;; 5: Expansion of the WHEN sugar translates into several steps that
;; are sequentially constained.
;; 6-8: AbsoluteTimeWithin becomes a timestamp monitor. This becomes
;; the start condition for a step that does the rover_stop command.
;;
(step s2 :non-blocking-callout (command "rover_stop")
(start-condition (:timestamp (>= 10))))
;; 9: Standard idiom for assignment. This step is constrained to
;; follow the previous one.
;;
(step s3 :blocking-callout t => ?timeout
(start-condition ?s2))
;; 10: Analogous to the WHEN in line 5.
;; 11: LookupWithFrequency requires a separate initial step for setup.
;; Here, we call a to-be-implemented domain interface function
;; LOOKUP-NOW that reads a variable's value in the external world.
;; (NOTE: Not sure if this should actually be non-blocking). This
;; step is repeated every 100ms (assuming 10 in the Plexil means 10
;; times per second), and repeats until we get a true assignment.
;;
(step c1 :non-blocking-callout (lookup-now "target_in_view")
:returnval ?target-in-view
(repeating :min-interval p100ms
:until (:callout (<?target-in-view>))))
;; 11-13
(step s4 :non-blocking-callout (command "rover_stop")
(start-condition (and ?c1 (:callout ?target-in-view))))
;; 14
(step s5 :blocking-callout t => ?drive-done
(start-condition ?s4))
;; 15-16
(step s6 :non-blocking-callout (command "take_navcam")
(start-condition (and ?s3 (:callout ?timeout))))
;; 17-18
(step s7 :non-blocking-callout (command "take_pancam")
(start-condition (and ?s5 (:callout ?drive-done))))
;; 19-20: Assuming 0.0 is the default tolerance, this step calls a
;; to-be-implemented function that registers with the external world a
;; request for notifications of change in temperature (which will be
;; sent as cogevents to Apex). It returns a unique ID used in the
;; subsequent steps to monitor for the right notifications.
;;
(step c2 :blocking-callout
(lookup-on-change "temperature" 0.0)
:returnval ?temperature-watch)
;; 19-22: Assuming the Plexil was in error, and really wanted to say:
;; "turn the heater on whenever temp falls below 0, and turn it off
;; whenever it's >= 10". The temperature is passed in the
;; ?temperature variable via the lookup-on-change registration in the
;; previous step.
(step s8 :non-blocking-callout (command "turn_on_heater")
(start-condition (:in-order ?c2
(changed <?temperature-watch> ?temperature)
(:callout (< ?temperature 0))))
(repeating :enabled nil))
(step s9 :non-blocking-callout (command "turn_off_heater")
(start-condition (:in-order ?c2
(changed <?temperature-watch> ?temperature)
(:callout (> ?temperature 10))))
(repeating :enabled nil))
;; Assuming this end condition (we've taken a picture), not specified
;; in Plexil example
;;
(end (when (:or ?s6 ?s7))))
;;; To-be-implemented support functions:
;;; A system call to invoke the given command
(defun command (name &rest args) ...) ; string * any -> ()
;;; Gets the value of the given var from the external world.
(defun lookup-now (var) ...) ; string -> any
;;; Registers in the external world for notifications of change in
;;; value of the given variable. Returns a unique id as means for
;;; identifying these notifications.
;;;
(defun lookup-on-change (var tolerance) ...) ; string * number -> int
Comments (0)
You don't have permission to comment on this page.