DSM quick reference
===================

Syntax
======
-- comment
#include "script.dsm"
#include "/path/to/anotherscript.dsm"
import(mod_name);

[initial] state name 
	  [ enter { 
	     <actions>
	    } ] 
	  [ exit {
	     <actions>
	    } ]
      ;

transition name s1 - [ { [not] condition; [not] condition; ... } ] [/ { <actions>} ]  -> s2;
 or 
transition name (s1a, s1b[, s1c, ...])  - [ { [not] condition; [not] condition; ... } ] [/ { <actions> } ]  -> s2;
 or (exception transition)
transition name s1 - exception [ { [not] condition;  ... } ] [/ { <actions>} ]  -> s2;


function func_name() {
  <actions>
};

<actions> ::=
  action;
  action(param, param, ...);

  if condition; condition; {
   <actions>
  } else {
   <actions>
  }

  func_name();
  ...


Variables, event parameters, selects
====================================

#paramname uses the event parameter 'paramname' (from current event)
$varname uses the variable varname (from session's variable)
@selectname uses the "select" 'selectname' (from the session's dialog)

Variable arrays:
 $myarray[0]
 $myarray[1]
 ...

Variable structs:
 $mystruct.member1
 $mystruct.member2

Core actions
============

DSM flow
--------


 -- call/jump/return sub-FSM
 jumpFSM(name)
 callFSM(name)
 returnFSM()

 stop(<send bye>)
   e.g. stop(false), stop(true)

 -- reprocess the current event after transition:
 repost()

 Variable manipulation
 ---------------------

 set($var=value)
  e.g.  set($var="text"); set($var=$var2); set($var=#key)
 sets($var=value)
  e.g.  set($var="text and some $variable and some #param");
 var($dstvar=srcvarname)
  e.g.  var($dstvar=$var_counter)
 param($dstvar=srcparamname)
  e.g. param($dstvar=$myparam) (like: #($myparam) )
 eval($var=value) 
  evaluate expression (only simple binary + and - supported)
  e.g.  set($var=1+5); set($var=$var2); set($var=#key)
 append($var, value)
 e.g. append($var, "text"); append($var, #key);
      append($var, @select); append($var, $var2);
 substr($var, pos)
  e.g. substr($myvar, 5);
 
 size($arrayname, $dst);
  set variable $dst to size of array 
  (e.g. $arrayname[0], $arrayname[1] set, $dst set to 2)

 inc($var)
 clear($var)
 clearArray($var)
  clears all var.* variables

Playing prompts and file I/O
----------------------------

  playPrompt(param)
   from promptCollection, e.g. playPrompt("hello");
   if $prompts.default_fallback=yes, default prompt set is tried if
   prompt not found in current prompt set
   Throws "prompt" exeption with #name if prompt not found.

 playPromptLooped(param)

 setPromptSet(name)
   select active prompt set if more prompt sets are loaded
   Throws "prompt" exeption with #name if prompt set not found

 playFile(filename [, loop=true])
   e.g. playFile($myfile, true); will play the file looped.
   Throws "file" exeption with #path if file can not be opened

 playFileFront(filename [, loop=true])
   e.g. playFileFront($myfile, true); will play the file at first
   position in the playlist, and looped.
   Throws "file" exeption with #path if file can not be opened

 recordFile(filename)
   Throws "file" exeption with #path if file can not be opened for recording

 stopRecord()
 getRecordLength([dst_varname])   -- only while recording! default dst var: record_length
 getRecordDataSize([dst_varname]) -- only while recording! default dst var: record_data_size
 closePlaylist(notify=true)
   if notify=true, send an event
 setInOutPlaylist() 
   set playlist as input and output
 addSeparator(id [, bool front])
   fires event when playlist hits it ; front=[true|false]
 connectMedia() 
   set playlist as input and output of session, and connect to mediaprocessor 
 disconnectMedia() 
   disconnect from mediaprocessor

 mute() 
   set RTP stream to muted (don't send and receive RTP packets)
 unmute() 
   set RTP stream to unmuted (send and receive RTP packets)

DTMF
----

 enableDTMFDetection() 
 disableDTMFDetection()

sendDTMF(key [, duration_ms])
  send a DTMF event (RFC4733 event)
  duration_ms defaults to 500ms

sendDTMFSequence(sequence [, duration_ms])
  send a sequence of DTMF events (RFC4733 event), e.g. 123#45*1
  duration_ms defaults to 500ms

B2B call control
----------------
 B2B.connectCallee(remote_party, remote_uri)
   connect second leg of B2B session (see AmB2BSession)

 B2B.terminateOtherLeg
   disconnect second leg of B2B session (see AmB2BSession)

 B2B.sendReinvite(generate_sdp [, string extra_headers])
   send a reinvite in caller leg (first leg), e.g. to 
   reconnect first leg after B2B.otherBye received. 
   generate_sdp can be 'true' or 'false'
   (B2B.sendReinvite(true) recommended)

 B2B.clearHeaders()
   clear the headers used for outgoing INVITE on B leg

 B2B.addHeader(string header)
  add a header for outgoing INVITE on B leg
 
 B2B.setHeaders(string headers [, replace_crlf=true|false])
  set headers for outgoing INVITE on B leg
  replace_crlf=true for replacing \r\n with CRLF
  e.g. 
  B2B.setHeaders("P-One: value\r\nP-Two: anothervalue", true)

 Logging
 -------

 log(level, text)
   e.g. log(1, $var1)
 -- log all variables:
 logVars(level) 
 -- log all selects:
 logSelects(level) 
 -- log all Params (only in actions of a transition):
 logParams(level) 
 -- log everything:
 logAll(level) 

 Timers
 ------

 setTimer(timer_id, timeout)
   e.g. setTimer(1, $timeout)
   * sets $errno (arg,config)
 removeTimer(timer_id)
   * sets $errno (arg,config)
 removeTimers()
   * sets $errno (config)

 DI functions
 ------------
  DI(factory, function [, params...])
    execute DI function
    e.g. DI(factory, function, $var_param, (int)int_param, "str param", @select_par, (array)arrayname, (struct)structname, (json)json_object...)
       DI(user_timer, setTimer, (int)1, (int)5, @local_tag);

       set($sweets.candy="nice");
       set($sweets.fruit="superb");
       set($sweets.cake.tahini="great");
       DI(myfactory, myfunc, (struct)sweets);

       set($bi[0]="ba");
       set($bi[1]="butzki");
       DI(myfactory, myfunc, (array)bi);
        
       set($js="{"x":"y", "a": 1}");
       DI(myfactory, myfunc, (json)$js);

   * sets $errno (arg,config)

  DIgetResult(factory, function, param,...)
    saves result from DI call to DI_res or DI_res0, DI_res1, ...
   * sets $errno (arg,config)

  Exception handling
  ------------------
  throw(<type>[,arg1=val1;arg2=val2;...])
   e.g. throw(bad_command),  throw(bad_command,cmd=help;reason=whynot)

  throwOnError()

 Events
 ------
 postEvent(sess_id[, variable_name;variable_name;...])
   post dsm event to session sess_id; variables copied as event parameters
   e.g. postEvent(@local_tag, PAI) : post event to ourselves
        postEvent($some_call, var1;var2;var3)     post event with var1, var2, var3
   * sets $errno (arg)

 postEvent(sess_id, var)
   all local variables copied as event variables
   * sets $errno (arg)

  registerEventQueue(queue_name)
    register session to receive events under the name queue_name
    WARNING: make sure to unregister the event queue before ending the session!

  unregisterEventQueue(queue_name)
    unregister events queue queue_name

Conditions
==========
 
Conditions are combined as AND, i.e. a transition is executed if all conditions match.

Generic conditions (regardless of the type of the event):
 test(#key == 1)
 test(#key == prefix*)
 test(#key != 1)
 test(#key < 1)
 test(#key > 1)

 test($var == 1)
 test($var == prefix*)
 test($var != 1)
 test($var < 1)
 test($var < 1)

 test(len($var) < len(#key))
 test(len($var) < len(@user))

Other conditions only match on the type of event and when the expression expr in the
brackets match.

 hangup
   bye/cancel received
   parameters:
      #headers - headers of the BYE/CANCEL request

 key(expr) or keyTest(expr)
   parameters:
      #key - Key pressed (0 - 11, * is 10, # is 11)
      #duration - duration of key press

 timer(expr) or timerTest(expr)
   parameters:
      #id - Timer ID

 noAudio(expr) or noAudioTest(expr)
   parameters:
      #type - "cleared" (audio cleared) or "noAudio" (playlist empty)

 separator(expr) or separatorTest(expr)
   parameters:
      #id - Separator ID
   e.g. separatorTest(#id == 5)

 event(expr) or eventTest(expr)
  generic event, e.g. passed from DI or another call with postEvent
  parameters depend on the DI call/the ones passed with postEvent

 keyPress(key)
  alias to key(#key==key)

 invite
   invite received/sent (only with run_invite_event):
   parameters: none

 ringing
   ringing reply, 180 (only with run_invite_event):
   parameters:
      #code     - SIP response code, e.g. 180
      #reason   - SIP reason string, e.g. "Ringing"
      #has_body - "true" or "false" 

 early
   early session, 183 (only with run_invite_event):
   parameters: none

 failed
   outgoing call failed (only with run_invite_event):
   parameters:
      #code     - SIP response code, e.g. 404
      #reason   - SIP reason string, e.g. "Not found"

 sessionStart
   start of session (with run_invite_event):
   parameters: none

 startup
   startup of a system DSM
   parameters: none

 reload
   reload (system DSM)

 system
   system event - shutdown or SIGNAL sent (kill <pid>)
   parameters:
      #type     - system event type, e.g. ServerShutdown, User1, User2

 B2B.otherReply
   Reply on other B2B leg received
   parameters:
    #code    - reply code
    #reason  - reply reason
    #hdrs    - headers
     
 B2B.otherBye
   BYE on other leg received
    #hdrs    - headers

 sipRequest
   SIP request received - only executed if enable_request_events=="true"
   parameters:
    #method        - SIP method
    #r_uri         - request URI
    #from          - From
    #to            - To
    #hdrs          - Headers (apart from dialog-IDs)
    #content_type  - Content-Type
    #body          - body of message
    #cseq          - CSeq

 sipReply
   SIP reply received - only executed if enable_reply_events=="true"
   parameters:
    #code          - response code
    #reason        - reason string
    #hdrs          - Headers (apart from dialog-IDs)
    #content_type  - Content-Type
    #body          - body of message
    #cseq          - CSeq

    #dlg_status     - SIP dialog status (Disconnected, Trying, ...)
    #old_dlg_status - old SIP dialog status (before this reply)


 jsonRpcRequest - json-rpc request received
   parameters:
   #ev_type   - JsonRpcRequest
   #method    - RPC method
   #is_notify - "true" or "false"
   #id        - request ID (if present)

   #params.*  - parameters array

 jsonRpcResponse - json-rpc response received
   #ev_type  - JsonRpcResponse
   #id       - response ID
   #is_error - "true" or "false"
   #udata    - user data that was saved when sending the request

   #result.* or #error.* - response data array (or error data)

Selects
=======
selects :
 @local_tag
 @user
 @domain
 @remote_tag
 @callid
 @local_uri
 @remote_uri

Importing modules
=================

 module imported with import(mod_name); loads mod_name.so in 
 module load path. modules provide conditions and actions.
 modules' actions/conditions-factory is checked first 
 (modules can override core conditions/actions)

Variables controlling call flow
===============================
special variables:
    connect_session     "0" -> after the start event (initial transition):
                                  do not connect session to 
                                  media processor on start

                            -> after the invite event:
                                  do not reply with 200 OK and do not 
                                  connect session to media processor on start
   enable_request_events  "true"  - run events on receiving a request
                          "false" - don't run events on receiveing request 

    
=============================
errors:
   actions set $errno
     #define DSM_ERRNO_OK          ""
     #define DSM_ERRNO_FILE        "1"
     #define DSM_ERRNO_UNKNOWN_ARG "2"
     #define DSM_ERRNO_GENERAL     "99"
     ...
