Author: sayer
Date: 2009-10-22 02:53:24 +0200 (Thu, 22 Oct 2009)
New Revision: 1554
Added:
trunk/apps/dsm/doc/examples/test_exception.dsm
Modified:
trunk/apps/dsm/DSMChartReader.cpp
trunk/apps/dsm/DSMChartReader.h
trunk/apps/dsm/DSMCoreModule.cpp
trunk/apps/dsm/DSMCoreModule.h
trunk/apps/dsm/DSMStateEngine.cpp
trunk/apps/dsm/DSMStateEngine.h
trunk/apps/dsm/doc/Readme.dsm.txt
Log:
DSM exceptions. SEMS-59.
actions and conditions should be able to throw exceptions. if an exceptions
happens, execution of the current actions is interrupted, and special exception
transitions are executed.
Modified: trunk/apps/dsm/DSMChartReader.cpp
===================================================================
--- trunk/apps/dsm/DSMChartReader.cpp 2009-10-21 23:33:09 UTC (rev 1553)
+++ trunk/apps/dsm/DSMChartReader.cpp 2009-10-22 00:53:24 UTC (rev 1554)
@@ -340,6 +340,7 @@
}
tr->precond = cl->conditions;
+ tr->is_exception = cl->is_exception;
delete cl;
// start AL_trans action list
@@ -354,6 +355,11 @@
continue;
}
+ if (token == "exception") {
+ cl->is_exception = true;
+ continue;
+ }
+
// DBG("new condition: '%s'\n", token.c_str());
DSMCondition* c = conditionFromToken(token, cl->invert_next);
cl->invert_next = false;
Modified: trunk/apps/dsm/DSMChartReader.h
===================================================================
--- trunk/apps/dsm/DSMChartReader.h 2009-10-21 23:33:09 UTC (rev 1553)
+++ trunk/apps/dsm/DSMChartReader.h 2009-10-22 00:53:24 UTC (rev 1554)
@@ -69,9 +69,10 @@
};
struct DSMConditionList : public DSMElement {
- DSMConditionList() : invert_next(false) { }
+ DSMConditionList() : invert_next(false), is_exception(false) { }
vector<DSMCondition*> conditions;
bool invert_next;
+ bool is_exception;
};
class DSMChartReader {
Modified: trunk/apps/dsm/DSMCoreModule.cpp
===================================================================
--- trunk/apps/dsm/DSMCoreModule.cpp 2009-10-21 23:33:09 UTC (rev 1553)
+++ trunk/apps/dsm/DSMCoreModule.cpp 2009-10-22 00:53:24 UTC (rev 1554)
@@ -44,6 +44,8 @@
DEF_CMD("callFSM", SCCallFSMAction);
DEF_CMD("returnFSM", SCReturnFSMAction);
+ DEF_CMD("throw", SCThrowAction);
+
DEF_CMD("stop", SCStopAction);
DEF_CMD("playPrompt", SCPlayPromptAction);
@@ -257,6 +259,29 @@
sess->setDtmfDetectionEnabled(false);
} EXEC_ACTION_END;
+CONST_ACTION_2P(SCThrowAction, ',', true);
+EXEC_ACTION_START(SCThrowAction) {
+ map<string, string> e_args;
+ e_args["type"] = resolveVars(par1, sess, sc_sess, event_params);
+ DBG("throwing DSMException type '%s'\n", e_args["type"].c_str());
+
+ string e_params = resolveVars(par2, sess, sc_sess, event_params);
+
+ // inefficient param-split
+ vector<string> params = explode(e_params, ";");
+ for (vector<string>::iterator it=
+ params.begin(); it != params.end(); it++) {
+ vector<string> n = explode(*it, "=");
+ if (n.size()==2) {
+ e_args[n[0]]=n[1];
+ }
+ }
+
+ throw DSMException(e_args);
+
+} EXEC_ACTION_END;
+
+
EXEC_ACTION_START(SCStopAction) {
if (resolveVars(arg, sess, sc_sess, event_params) == "true") {
DBG("sending bye\n");
Modified: trunk/apps/dsm/DSMCoreModule.h
===================================================================
--- trunk/apps/dsm/DSMCoreModule.h 2009-10-21 23:33:09 UTC (rev 1553)
+++ trunk/apps/dsm/DSMCoreModule.h 2009-10-22 00:53:24 UTC (rev 1554)
@@ -70,6 +70,7 @@
DEF_SCModSEStrArgAction(SCCallFSMAction);
DEF_SCModSEStrArgAction(SCReturnFSMAction);
+DEF_ACTION_2P(SCThrowAction);
DEF_ACTION_2P(SCSetAction);
DEF_ACTION_2P(SCAppendAction);
Modified: trunk/apps/dsm/DSMStateEngine.cpp
===================================================================
--- trunk/apps/dsm/DSMStateEngine.cpp 2009-10-21 23:33:09 UTC (rev 1553)
+++ trunk/apps/dsm/DSMStateEngine.cpp 2009-10-22 00:53:24 UTC (rev 1554)
@@ -156,9 +156,9 @@
}
bool DSMStateEngine::runactions(vector<DSMAction*>::iterator from,
- vector<DSMAction*>::iterator to,
- AmSession* sess, DSMCondition::EventType event,
- map<string,string>* event_params, bool&
is_consumed) {
+ vector<DSMAction*>::iterator to,
+ AmSession* sess, DSMCondition::EventType event,
+ map<string,string>* event_params, bool&
is_consumed) {
// DBG("running %zd actions\n", to - from);
for (vector<DSMAction*>::iterator it=from; it != to; it++) {
DBG("executing '%s'\n", (*it)->name.c_str());
@@ -205,11 +205,19 @@
bool DSMStateEngine::init(AmSession* sess, const string& startDiagram,
DSMCondition::EventType init_event) {
- if (!jumpDiag(startDiagram, sess, init_event, NULL)) {
- ERROR("initializing with start diag '%s'\n",
- startDiagram.c_str());
- return false;
- }
+ try {
+ if (!jumpDiag(startDiagram, sess, init_event, NULL)) {
+ ERROR("initializing with start diag '%s'\n",
+ startDiagram.c_str());
+ return false;
+ }
+ } catch (DSMException& e) {
+ DBG("Exception type '%s' occured while initializing! Run init event as
exception...\n",
+ e.params["type"].c_str());
+ runEvent(sess, init_event, &e.params, true);
+ return true;
+
+ }
DBG("run init event...\n");
runEvent(sess, init_event, NULL);
@@ -245,98 +253,119 @@
}
void DSMStateEngine::runEvent(AmSession* sess,
- DSMCondition::EventType event,
- map<string,string>* event_params) {
+ DSMCondition::EventType event,
+ map<string,string>* event_params,
+ bool run_exception) {
if (!current || !current_diag)
return;
+
+ DSMCondition::EventType active_event = event;
+ map<string,string>* active_params = event_params;
+ map<string,string> exception_params;
+ bool is_exception = run_exception;
bool is_consumed = true;
do {
- is_consumed = true;
+ try {
+ is_consumed = true;
- for (vector<DSMTransition>::iterator tr = current->transitions.begin();
- tr != current->transitions.end();tr++) {
- DBG("checking transition '%s'\n", tr->name.c_str());
-
- vector<DSMCondition*>::iterator con=tr->precond.begin();
- while (con!=tr->precond.end()) {
- if (!(*con)->_match(sess, event, event_params))
- break;
- con++;
- }
- if (con == tr->precond.end()) {
- DBG("transition '%s' matched.\n", tr->name.c_str());
+ for (vector<DSMTransition>::iterator tr = current->transitions.begin();
+ tr != current->transitions.end();tr++) {
+ if (tr->is_exception != is_exception)
+ continue;
- // matched all preconditions
- // find target state
- State* target_st = current_diag->getState(tr->to_state);
- if (!target_st) {
- ERROR("script writer error: transition '%s' from "
- "state '%s' to unknown state '%s'\n",
- tr->name.c_str(),
- current->name.c_str(),
- tr->to_state.c_str());
- }
+ DBG("checking transition '%s'\n", tr->name.c_str());
- // run post-actions
- if (current->post_actions.size()) {
- DBG("running %zd post_actions of state '%s'\n",
- current->post_actions.size(), current->name.c_str());
- if (runactions(current->post_actions.begin(),
- current->post_actions.end(),
- sess, event, event_params, is_consumed)) {
+ vector<DSMCondition*>::iterator con=tr->precond.begin();
+ while (con!=tr->precond.end()) {
+ if (!(*con)->_match(sess, active_event, active_params))
break;
+ con++;
+ }
+ if (con == tr->precond.end()) {
+ DBG("transition '%s' matched.\n", tr->name.c_str());
+
+ // matched all preconditions
+ // find target state
+ State* target_st = current_diag->getState(tr->to_state);
+ if (!target_st) {
+ ERROR("script writer error: transition '%s' from "
+ "state '%s' to unknown state '%s'\n",
+ tr->name.c_str(),
+ current->name.c_str(),
+ tr->to_state.c_str());
}
- }
-
- // run transition actions
- if (tr->actions.size()) {
- DBG("running %zd actions of transition '%s'\n",
- tr->actions.size(), tr->name.c_str());
- if (runactions(tr->actions.begin(),
- tr->actions.end(),
- sess, event, event_params, is_consumed)) {
+
+ // run post-actions
+ if (current->post_actions.size()) {
+ DBG("running %zd post_actions of state '%s'\n",
+ current->post_actions.size(), current->name.c_str());
+ if (runactions(current->post_actions.begin(),
+ current->post_actions.end(),
+ sess, active_event, active_params, is_consumed)) {
+ break;
+ }
+ }
+
+ // run transition actions
+ if (tr->actions.size()) {
+ DBG("running %zd actions of transition '%s'\n",
+ tr->actions.size(), tr->name.c_str());
+ if (runactions(tr->actions.begin(),
+ tr->actions.end(),
+ sess, active_event, active_params, is_consumed)) {
+ break;
+ }
+ }
+
+ // go into new state
+ if (!target_st) {
break;
}
- }
-
- // go into new state
- if (!target_st) {
- break;
- }
- DBG("changing to new state '%s'\n", target_st->name.c_str());
- MONITORING_LOG(sess->getLocalTag().c_str(), "dsm_state",
target_st->name.c_str());
-
+ DBG("changing to new state '%s'\n", target_st->name.c_str());
+
#ifdef USE_MONITORING
- if (DSMFactory::MonitoringFullTransitions) {
- MONITORING_LOG_ADD(sess->getLocalTag().c_str(),
- "dsm_stategraph",
- ("> "+ tr->name + " >").c_str());
- }
-
- if (DSMFactory::MonitoringFullCallgraph) {
- MONITORING_LOG_ADD(sess->getLocalTag().c_str(),
- "dsm_stategraph",
- (current_diag->getName() +"/"+
target_st->name).c_str());
- }
+ MONITORING_LOG(sess->getLocalTag().c_str(), "dsm_state",
target_st->name.c_str());
+
+ if (DSMFactory::MonitoringFullTransitions) {
+ MONITORING_LOG_ADD(sess->getLocalTag().c_str(),
+ "dsm_stategraph",
+ ("> "+ tr->name + " >").c_str());
+ }
+
+ if (DSMFactory::MonitoringFullCallgraph) {
+ MONITORING_LOG_ADD(sess->getLocalTag().c_str(),
+ "dsm_stategraph",
+ (current_diag->getName() +"/"+
target_st->name).c_str());
+ }
#endif
-
- current = target_st;
-
- // execute pre-actions
- if (current->pre_actions.size()) {
- DBG("running %zd pre_actions of state '%s'\n",
- current->pre_actions.size(), current->name.c_str());
- if (runactions(current->pre_actions.begin(),
- current->pre_actions.end(),
- sess, event, event_params, is_consumed)) {
- break;
+
+ current = target_st;
+
+ // execute pre-actions
+ if (current->pre_actions.size()) {
+ DBG("running %zd pre_actions of state '%s'\n",
+ current->pre_actions.size(), current->name.c_str());
+ if (runactions(current->pre_actions.begin(),
+ current->pre_actions.end(),
+ sess, active_event, active_params, is_consumed)) {
+ break;
+ }
}
+
+ break;
}
+ }
+ } catch (DSMException& e) {
+ DBG("DSMException occured, type = %s\n", e.params["type"].c_str());
+ is_consumed = false;
- break;
- }
+ is_exception = true; // continue to process as exception event
+ exception_params = e.params;
+ active_params = &exception_params;
+ active_event = DSMCondition::DSMException;
}
+
} while (!is_consumed);
}
Modified: trunk/apps/dsm/DSMStateEngine.h
===================================================================
--- trunk/apps/dsm/DSMStateEngine.h 2009-10-21 23:33:09 UTC (rev 1553)
+++ trunk/apps/dsm/DSMStateEngine.h 2009-10-22 00:53:24 UTC (rev 1554)
@@ -73,7 +73,9 @@
PlaylistSeparator,
B2BOtherReply,
- B2BOtherBye
+ B2BOtherBye,
+
+ DSMException
};
bool invert;
@@ -137,6 +139,8 @@
vector<DSMAction*> actions;
string from_state;
string to_state;
+
+ bool is_exception;
};
class DSMModule;
@@ -158,6 +162,19 @@
const string& getName() { return name; }
};
+class DSMException {
+ public:
+ DSMException(const string& e_type)
+ { params["type"] = e_type; }
+
+ DSMException(map<string, string>& params)
+ : params(params) { }
+
+ ~DSMException() { }
+
+ map<string, string> params;
+};
+
class DSMStateEngine {
State* current;
DSMStateDiagram* current_diag;
@@ -189,7 +206,8 @@
void runEvent(AmSession* sess,
DSMCondition::EventType event,
- map<string,string>* event_params);
+ map<string,string>* event_params,
+ bool run_exception = false);
/** @return whether call should be accepted */
bool onInvite(const AmSipRequest& req, DSMSession* sess);
Modified: trunk/apps/dsm/doc/Readme.dsm.txt
===================================================================
--- trunk/apps/dsm/doc/Readme.dsm.txt 2009-10-21 23:33:09 UTC (rev 1553)
+++ trunk/apps/dsm/doc/Readme.dsm.txt 2009-10-22 00:53:24 UTC (rev 1554)
@@ -26,8 +26,9 @@
The variables may be used as parameter to most conditions and
actions, by prepending the variable name with a dollar sign. The
parameters of an event (e.g. the key on key press) may be accessed
-by prepending the name with a hash. There are also 'selects' with
-which a set of dialog properties can be accessed (e.g. @local_tag).
+by prepending the name with a hash (e.g. #key). There are also
+'selects' with which a set of dialog properties can be accessed
+(e.g. @local_tag).
The DonkeySM can be extended by modules, which add new conditions
and actions to the language. This way, menuing system etc can be
@@ -37,7 +38,14 @@
DonkeySM also has built in actions to call
DI methods from other modules.
-It can cache a set of prompts, configured at start, in memory
+Actions (and conditions) can throw exceptions. Once an exception occurs,
+execution of the current actions is interrupted. Exceptions are handled
+this way that special "exception" transitions are executed. Exception
+transitions are marked with "exception" in the conditions list. Once the
+FSM is in exception handling, only exception transitions are followed.
+DSMs may throw exceptions with the throw(<type>) action.
+
+DSM can cache a set of prompts, configured at start, in memory
using PromptCollection.
A patch for fmsc 1.0.4 from the graphical FSM editor fsme
@@ -47,6 +55,8 @@
DI commands
===========
+DI commands allow interaction with DSM calls, and DSM script reload:
+
postDSMEvent(string call_id, [ [[param0,val0],[param1,val1],...] ]
post a DSM event into a call. can be used to interact with running
calls in DSM. See DSM + monitoring + DI example in
@@ -95,7 +105,7 @@
the DSMStateDiagramCollection from text file and interpreted by
the DSMChartReader, a simple stack based tokenizing compiler.
-DSMDialogs, which implement the DSMSession interface (additionally
+DSMCall, which implement the DSMSession interface (additionally
to being an AmSession), run DSMStateEngine::runEvent for every event
that occurs that should be processed by the engine (e.g. Audio event,
onBye, ...).
@@ -107,7 +117,7 @@
get the event parameters and the session as parameters, so that they
can operate on variables, implement selects etc.
-The DSMDialog implementation is very simple, it uses a playlist and
+The DSMCall implementation is very simple, it uses a playlist and
has PromptCollection to simply play prompts etc.
DSMCoreModule is a 'built in' module that implements the basic
@@ -123,9 +133,6 @@
functionality be exposed to the DSM interpreter by custom actions and
conditions that interact with that specific session type.
-Another direction is python/lua/... interpreter module, so that
-conditions and actions can be expressed in a more powerful language.
-
A set of modules exposing more of the core functionality.
As the call state representation is nicely encapsulated here, this can
@@ -171,7 +178,9 @@
SEMS has a dynamically typed type (AmArg), why not use that one for
variables? That would also make DI simpler.
a patch is very welcome, best to semsdev list: [email protected] or
- the tracker: http://tracker.iptel.org
+ the tracker: http://tracker.iptel.org.
+ There is also the avar array ("AmArg-Var"), which can hold AmArg
+ variables.
some performance numbers?
unfortunately not yet for running DSMs. DSM processing is actually fast:
Added: trunk/apps/dsm/doc/examples/test_exception.dsm
===================================================================
--- trunk/apps/dsm/doc/examples/test_exception.dsm 2009-10-21 23:33:09 UTC
(rev 1553)
+++ trunk/apps/dsm/doc/examples/test_exception.dsm 2009-10-22 00:53:24 UTC
(rev 1554)
@@ -0,0 +1,10 @@
+
+initial state lobby
+ enter {
+ playFile(wav/default_en.wav)
+ throw(bla,blub=blu;i=somevalue);
+ };
+transition "thrown error" lobby - exception; test(#blub=="blu"); /
+ log(1, #i); stop(true) -> end;
+
+transition "file error" lobby - exception; test(#type="file") / stop(true) ->
end;
_______________________________________________
Semsdev mailing list
[email protected]
http://lists.iptel.org/mailman/listinfo/semsdev