mirror of https://github.com/sipwise/sems.git
git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@1312 8eb893ce-cfd4-0310-b710-fb5ebe64c474sayer/1.4-spce2.6
parent
4fff0f468d
commit
cc475671c7
@ -0,0 +1,273 @@
|
||||
DSM + monitoring + DI
|
||||
---------------------
|
||||
|
||||
This is a short sample that shows how to use the lastest
|
||||
VoIP services application technologies from SEMS to rapidly
|
||||
create complex converged applications with minimal amount of
|
||||
coding.
|
||||
|
||||
We will use the DSM state machine scripting to create a simple
|
||||
conference application, in which the user is taken to the room
|
||||
by interaction with an external web application. The focus lies
|
||||
here on the media server part, thus we will not create a web
|
||||
interface, but show the functionality through some sample python
|
||||
commands.
|
||||
|
||||
Components used
|
||||
---------------
|
||||
|
||||
Since 1.1 SEMS has a 'dsm' module, which allows to specify and
|
||||
implement applications in a simple state machine language. The DSM
|
||||
interpreter is extensible with modules, which can add conditions
|
||||
and actions for the transitions between states in a DSM.
|
||||
|
||||
Modules in SEMS can export an interface to other modules, so called
|
||||
'DI' ('dynamic invocation') API, which has named functions, that
|
||||
take variant typed arguments. A DI API can be exported to the outside
|
||||
world very easily by using the xmlrpc2di module, which offers an
|
||||
XMLRPC interface. For example, DSM exports a DI method to post events
|
||||
to a DSM session.
|
||||
|
||||
The 'monitoring' module serves for general purpose call monitoring.
|
||||
Applications can add specific attributes to the set which is being
|
||||
logged for every call. Monitoring exports is functions also through
|
||||
a DI interface, which can then be accessed for example from the
|
||||
outside through XMLRPC. On the other hand there is a DSM module
|
||||
mod_monitoring, which allows a DSM to add attributes
|
||||
(monitoring.log()).
|
||||
|
||||
|
||||
|
||||
DSM monitoring
|
||||
+-----------+ +----------+
|
||||
| | | |
|
||||
| O --> O | monitoring.log() | |
|
||||
| | | ======> | |
|
||||
| | | | |
|
||||
| v | | |
|
||||
| O | | |
|
||||
+-----------+ +----------+
|
||||
^ ^
|
||||
| DI: | DI:
|
||||
| postDSMEvent(...) | list(), listByFilter(),
|
||||
+--------+ +---------------+ get()
|
||||
| |
|
||||
+-----------+
|
||||
| |
|
||||
| xmlrpc2di |
|
||||
| |
|
||||
+-----------+
|
||||
^
|
||||
| XMLRPC:
|
||||
| postDSMEvent(...)
|
||||
| list(), listByFilter(), get()
|
||||
|
|
||||
|
||||
+------------+
|
||||
| |
|
||||
| web server |
|
||||
| etc |
|
||||
+------------+
|
||||
|
||||
Application
|
||||
-----------
|
||||
|
||||
Lets have a look at the application (dsm_di_monitoring.dsm):
|
||||
|
||||
First we load the conference and the monitoring modules:
|
||||
| import(mod_conference)
|
||||
| import(mod_monitoring)
|
||||
|
||||
We denote the inital state with 'lobby' and when entering it,
|
||||
we play a welcome message, and save the username (it's actually
|
||||
the callee user) and the position 'lobby' to the session's
|
||||
monitoring status:
|
||||
|
||||
| initial state lobby
|
||||
| enter {
|
||||
| playFile(wav/welcome.wav)
|
||||
| monitoring.log(username, @user)
|
||||
| monitoring.log(position, lobby)
|
||||
| };
|
||||
|
||||
Next, we define a state (now not an initial state) called 'room':
|
||||
| state room;
|
||||
|
||||
Events as sent by the postDSMEvent DI function have event parameters
|
||||
(like other DSM events; for example for key press events, the key
|
||||
pressed is event parameter, or for timer timeout, the timer id is
|
||||
event parameter). Event parameters are accessed by the hash sign.
|
||||
If we receive an event with the action 'take'
|
||||
|
||||
| transition "lobby to room" lobby - eventTest(#action==take) / {
|
||||
|
||||
we first close the playlist to stop the welcome message from playing
|
||||
if it still is:
|
||||
|
||||
| closePlaylist()
|
||||
|
||||
As playout type we set adaptive playout buffering; this is just for
|
||||
conference to sound smoothly in the presence of packet loss and jitter
|
||||
(but we can ignore it for now):
|
||||
|
||||
| conference.setPlayoutType(adaptive)
|
||||
|
||||
play a file to tell the user that we are going to the conference now,
|
||||
and join the conference:
|
||||
| playFile(wav/taken.wav)
|
||||
| conference.join(#roomname)
|
||||
|
||||
Because we may have had empty playlist, we reconnect playing audio:
|
||||
| connectMedia()
|
||||
|
||||
and finally, we update the position of the call to the monitoring log,
|
||||
so that it is visible from the outside:
|
||||
|
||||
| monitoring.log(position, #roomname)
|
||||
|
||||
and we change to the state 'room':
|
||||
| } -> room;
|
||||
|
||||
If we are in the room, and get moved to another room, we do similar things,
|
||||
we just use a different prompt to play to the user:
|
||||
|
||||
| transition "room change" room - eventTest(#action==take) / {
|
||||
| closePlaylist()
|
||||
| playFile(wav/change.wav)
|
||||
| conference.join(#roomname)
|
||||
| connectMedia()
|
||||
| monitoring.log(position, #roomname)
|
||||
| } -> room;
|
||||
|
||||
Note that we still stay in the state 'room', so this can be done several
|
||||
times.
|
||||
|
||||
If we want to stop the call from outside, we send an event with the action 'kick':
|
||||
|
||||
| transition "kickout event" room - eventTest(#action==kick) / stop(true) -> end;
|
||||
|
||||
stop(true) sends a BYE and stops the session. We should not forget to stop the session if we receive a BYE as well:
|
||||
|
||||
| transition "bye recvd" (lobby, room) - hangup / stop(false) -> end;
|
||||
| state end;
|
||||
|
||||
|
||||
So, lets try this out. First we load the necessary modules and set as
|
||||
application to execute when a call comse in the DSM script:
|
||||
sems.conf:
|
||||
load_plugins=wav;l16;ilbc;sipctrl;dsm;xmlrpc2di;monitoring
|
||||
application=dsm_di_monitoring
|
||||
|
||||
We tell dsm to load this dsm script and register it as
|
||||
application in SEMS:
|
||||
dsm.conf:
|
||||
diag_path=../apps/dsm/doc/examples/dsm_di_monit
|
||||
load_diags=dsm_di_monitoring
|
||||
register_apps=dsm_di_monitoring
|
||||
mod_path=../apps/dsm/mods/lib/
|
||||
|
||||
If we start SEMS from core/ directory, we also need to copy the wav
|
||||
files to the core/wav folder, because we have used e.g. wav/welcome.wav
|
||||
in the script:
|
||||
$ cp apps/dsm/doc/examples/dsm_di_monit/wav/*.wav core/wav
|
||||
|
||||
We also tell xmlrpc2di to export the DI APIs for monitoring and dsm, this
|
||||
way we can call the XMLRPC functions directly without the module name:
|
||||
xmlrpc2di.conf:
|
||||
xmlrpc_port=8090
|
||||
export_di=yes
|
||||
direct_export=dsm;monitoring
|
||||
|
||||
Now we go into the core directory, and start sems (If we have not built
|
||||
sems binary yet, we enable monitoring in Makefile.defs:
|
||||
Makefile.defs:
|
||||
USE_MONITORING=yes
|
||||
and make SEMS and necessary applications:
|
||||
make -C core
|
||||
make -C apps/dsm
|
||||
make -C apps/xmlrpc2di
|
||||
make -C apps/monitoring
|
||||
):
|
||||
|
||||
$ cd core
|
||||
$ ./sems -f etc/sems.conf -D 3 -E
|
||||
|
||||
we see SEMS starting up and loading the DSM script.
|
||||
|
||||
Now when we call into SEMS, we hear the welcome message. We will now manipulate the session from a python script:
|
||||
|
||||
$ python
|
||||
Python 2.5.2 (r252:60911, Oct 17 2008, 02:42:54)
|
||||
[GCC 4.1.2 (Gentoo 4.1.2)] on linux2
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> from xmlrpclib import *
|
||||
>>> s = ServerProxy("http://localhost:8090")
|
||||
>>> s.calls()
|
||||
1
|
||||
|
||||
calls() is a built in function that shows the active call count.
|
||||
From monitoring, we can now list the calls:
|
||||
|
||||
>>> s.list()
|
||||
['2ED5BB64-49B7DA860003C9B1-B6E7DB90']
|
||||
|
||||
we can also get all log properties for that call:
|
||||
>>> call_id = s.list()[0]
|
||||
>>> s.get(call_id)
|
||||
[{'username': '35', 'from': '<sip:1@stefan-lap.office.iptego.de>', 'app': 'dsm_di_monitoring', 'to': '<sip:35@stefan-lap.office.iptego.de>', 'position': 'lobby', 'ruri': 'sip:35@stefan-lap.office.iptego.de', 'dir': 'in'}]
|
||||
|
||||
|
||||
We can see that the position of that call is the lobby. Now lets take that one into a room:
|
||||
>>> s.postDSMEvent(call_id, [['action', 'take'],['roomname', 'wonderland']])
|
||||
[200, 'OK']
|
||||
|
||||
We hear the 'taken to the room' message. And, if we get the session's log again:
|
||||
>>> s.get(call_id)
|
||||
[{'username': '35', 'from': '<sip:1@stefan-lap.office.iptego.de>', 'app': 'dsm_di_monitoring', 'to': '<sip:35@stefan-lap.office.iptego.de>', 'position': 'wonderland', 'ruri': 'sip:35@stefan-lap.office.iptego.de', 'dir': 'in'}]
|
||||
|
||||
We are in wonderland... We can also filter the calls by position:
|
||||
>>> s.listByFilter(['position', 'lobby'])
|
||||
''
|
||||
>>> s.listByFilter(['position', 'wonderland'])
|
||||
['2ED5BB64-49B7DA860003C9B1-B6E7DB90']
|
||||
|
||||
And we can take that call into another room:
|
||||
>>> s.postDSMEvent(call_id, [['action', 'take'],['roomname', 'realworld']])
|
||||
[200, 'OK']
|
||||
|
||||
and also end it:
|
||||
>>> s.postDSMEvent(call_id, [['action', 'kick']])
|
||||
[200, 'OK']
|
||||
|
||||
which will make SEMS send a BYE to end the call.
|
||||
|
||||
Conclusion
|
||||
----------
|
||||
|
||||
With the extensibility of DSM through C++ modules, tricky parts can be
|
||||
implemented as actions and conditions, and this implementation separated
|
||||
from the service/application logic.
|
||||
|
||||
Interfacing to VoIP applications through external interfaces is simplified
|
||||
greatly through the combination of monitoring and event passing.
|
||||
|
||||
Converged application development could hardly be easier to write than with this
|
||||
combination of monitoring, dsm, and xmlrpc2di. Of course the real value lies in
|
||||
the application idea, and not in the technology, but hopefully this tutorial
|
||||
has shown a way to quickly implement some ideas.
|
||||
|
||||
|
||||
Further recommended reading:
|
||||
|
||||
doc/Readme.monitoring
|
||||
apps/dsm/doc/Readme.dsm.txt
|
||||
apps/dsm/doc/dsm_syntax.txt
|
||||
apps/dsm/doc/Readme.dsm
|
||||
apps/dsm/mods/mod_sys/Readme.mod_sys.txt
|
||||
apps/dsm/mods/mod_uri/Readme.mod_uri.txt
|
||||
apps/dsm/mods/mod_dlg/Readme.mod_dlg.txt
|
||||
apps/dsm/mods/mod_conference/Readme.mod_conference.txt
|
||||
apps/dsm/mods/mod_monitoring/Readme.mod_monitoring.txt
|
||||
|
||||
|
||||
(C) 2009 IPTEGO GmbH
|
||||
@ -0,0 +1,34 @@
|
||||
import(mod_conference)
|
||||
import(mod_monitoring)
|
||||
|
||||
initial state lobby
|
||||
enter {
|
||||
playFile(wav/welcome.wav)
|
||||
monitoring.log(username, @user)
|
||||
monitoring.log(position, lobby)
|
||||
};
|
||||
|
||||
state room;
|
||||
|
||||
transition "lobby to room" lobby - eventTest(#action==take) / {
|
||||
closePlaylist()
|
||||
conference.setPlayoutType(adaptive)
|
||||
playFile(wav/taken.wav)
|
||||
conference.join(#roomname)
|
||||
connectMedia()
|
||||
monitoring.log(position, #roomname)
|
||||
} -> room;
|
||||
|
||||
transition "room change" room - eventTest(#action==take) / {
|
||||
closePlaylist()
|
||||
playFile(wav/change.wav)
|
||||
conference.join(#roomname)
|
||||
connectMedia()
|
||||
monitoring.log(position, #roomname)
|
||||
} -> room;
|
||||
|
||||
transition "kickout event" room - eventTest(#action==kick) / stop(true) -> end;
|
||||
|
||||
transition "bye recvd" (lobby, room) - hangup / stop(false) -> end;
|
||||
|
||||
state end;
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue