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

Syntax
======
-- comment
# also 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)

 use ## for #  (e.g. set($hash=##); )
     $$ for $  (e.g. set($dol=$$); )
     @@ for @  (e.g. set($at=@@); )

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)   - note that actions after callFSM may have the event & event params of the sub-FSM  
 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.  sets($var="text and some $variable and some #param");
        sets($var="$(variable1)$(variable2)");
        sets($var="this is a single hash: ##")
        sets($var="a single at on a date with a single dollar: @@$$")
 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)

 arrayIndex($array, key)  - find key in $array, set $index to its index, or "nil" if not found
 arrayIndex($array, $var) - find $var in $array, set $var.index to its index, or "nil" if not found

 inc($var)
 clear($var)
 clearStruct($var)
  clears all var.* variables
 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.
 playPromptFront(param) - play a prompt at first item in the playlist
 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

 playSilence(millisec)      - play silence for millisec ms
 playSilenceFront(millisec) - play silence for millisec ms at first position in playlist

 playRingtone(varname, bool front) - play ringtone - parameters in varname
 e.g. set($r.length=10000);
      set($r.on=150);
      set($r.off=550);
      set($r.f=550);
      set($r.f2=850);
      playRingtone($r,true);

 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
 flushPlaylist()
 setInOutPlaylist() 
   set playlist as input and output
 setInputPlaylist() 
   set playlist as input (output untouched)
 setOutputPlaylist() 
   set playlist as output (input untouched)
 addSeparator(id [, bool front])
   fires event when playlist hits it ; front=[true|false]
 connectMedia()     - set playlist as input and output of session,
                      and start processing of RTP and audio 
                     (connect to mediaprocessor)
 disconnectMedia()  - stop processing of RTP and audio (disconnect from mediaprocessor)

 enableReceiving()  - enable processing of received RTP
 disableReceiving() - disable processing of received RTP

 enableForceDTMFReceiving()  - enable/disable RTP DTMF packets processing even 
 disableForceDTMFReceiving()   if received RTP is not processed, e.g. after
                               disableReceiving() or in passive mode
                               (only for RFC2833/4733 DTMF, *no* in-band)
 monitorRTPTimeout(enabled=false)
   set call to monitor RTP timeout (enabled="true" or "false")

 mute()             - set RTP stream to muted (don't send RTP packets)
 unmute()           - set RTP stream to unmuted (do send 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)
    R-URI = remote_uri
    To = remote_party 
    From = $b2b_local_party
    auth = ($b2b_auth_user, $b2b_auth_pwd)
    Call-ID = $b2b_callid
    $b2b_relayed_invite=[true|false] : use relayed INVITE mode

 B2B.terminateOtherLeg
   disconnect second leg of B2B session (see AmB2BSession),
   e.g. to connect to a new destination
   note: don't use this when receiving BYE in an established
         B2BUA call, as the BYE is sent end-to-end

 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.enableEarlyMediaRelay(enable=[true|false])
   enable or disable relaying of early media SDP (180-183) as re-Invite in the first leg

 B2B.addHeader(string header)
  add a header for outgoing INVITE on B leg
 
 B2B.removeHeader(string header_name)
  remove 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)

 B2B.relayEvent(variable_name;variable_name;...)
   relay B2Bevent to related B2B session; variables copied as event parameters
   e.g. B2B.relayEvent(var1;var2;var3)     relay event with var1, var2, var3

 B2B.relayEvent("var")
   relay B2Bevent to related B2B session with all variables of current DSM session

 Logging
 -------

 log(level, text)
   e.g. log(1, $var1)
 logs(level, "text with $(variable_name) and #(paramname) replacements")
 -- 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)
 dbg("some debug with $(replaced_var)") 
 info("some info $(message)")
 warn("warn #(message_parameter)")
 error("error in call @(local_tag)")

 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(monitoring, set, "mytable", @local_tag, (int)1);

       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

 Object memory management/Garbage collector:
  freeObject(varname)     - free object referenced with varname immediately
  trackObject(varname)    - track object referenced with varname, i.e. enable garbage
                            collection with the current call/systemDSM
  releaseObject(varname)  - release object referenced with varname from garbage collector
  
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.

 start
   start of a session (onStart). This event is processed before any other.

beforeDestroy
   end of a session (onBeforeDestroy). This event is processed after any other.

 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

B2Bevent(expr)
  generic event from B2B related call leg
  parameters depend on the ones passed with B2B.relayEvent

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

 remoteDisappeared(expr)
   remote end in an established call is unreachable (408/481 received)
   parameters: see sipReply (below), except #old_dlg_status

   set #processed="true" if you don't want default behaviour (clear call)

 sessionTimeout(expr)
   session expired (SST)
   parameters: none

   set #processed="true" if you don't want default behaviour (clear call)

 rtpTimeout(expr)
   RTP timeout detected

   #type           - "rtp_timeout"
   #timeout_value  - RTP timeout value (as configured)

   set #processed="true" if you don't want default behaviour (clear call)

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

 ringing
   outgoing call: 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
   outgoing call: 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.otherRequest (only sbc)
   Request on other B2B leg received
   parameters:
    #method, #r_uri, #from, #to, #hdrs  - from request

 B2B.otherReply
   Reply on other B2B leg received
   parameters:
    #code    - reply code
    #reason  - reply reason
    #hdrs    - headers
    #trans_method - transaction method (only sbc)
     
 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

subscription - SIP subscription status
   #status   - status: "active", "pending", "failed", "terminated", "timeout"
   #code     - SIP response code (if failed)
   #reason   - SIP response reason (if failed)
   NOTIFY body in avar[DSM_AVAR_SIPSUBSCRIPTION_BODY] (if #status=="active")

 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)

conference events:
  generic events with 
   #type     - "conference_event"
   #id       - event ID

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

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 


   b2b_local_party                - From in outgoing B2B call
   b2b_local_uri                  - URI of From for B2B call, for application internal use
   b2b_auth_user                  - SIP auth user for 2nd leg of B2B call  
   b2b_auth_pwd                   - SIP auth pwd for 2nd leg of B2B call
    
=============================
errors:
   actions set $errno
     #define DSM_ERRNO_OK          ""
     #define DSM_ERRNO_FILE        "1"
     #define DSM_ERRNO_UNKNOWN_ARG "2"
     #define DSM_ERRNO_GENERAL     "99"
     ...
