DSM: add system DSMs, DSMs unrelated to calls

sayer/1.4-spce2.6
Stefan Sayer 16 years ago
parent e68122bb4e
commit 40af0d1777

@ -37,6 +37,7 @@
#include "DSMChartReader.h"
#include "AmSipHeaders.h"
#include "AmEventDispatcher.h"
#include "SystemDSM.h"
#include <string>
#include <fstream>
@ -164,6 +165,17 @@ int DSMFactory::onLoad()
MainScriptConfig.SetParamVariables = cfg.getParameter("set_param_variables")=="yes";
vector<string> system_dsms = explode(cfg.getParameter("run_system_dsms"), ",");
for (vector<string>::iterator it=system_dsms.begin(); it != system_dsms.end(); it++) {
string status;
if (createSystemDSM("main", *it, false /* reload */, status)) {
DBG("created SystemDSM '%s'\n", it->c_str());
} else {
ERROR("creating system DSM '%s': '%s'\n", it->c_str(), status.c_str());
return -1;
}
}
#ifdef USE_MONITORING
string monitoring_full_callgraph = cfg.getParameter("monitoring_full_stategraph");
MonitoringFullCallgraph = monitoring_full_callgraph == "yes";
@ -420,6 +432,7 @@ bool DSMFactory::loadConfig(const string& conf_file_name, const string& conf_nam
ScriptConfigs_mut.lock();
try {
Name2ScriptConfig[script_name] = script_config;
// set ScriptConfig to this for all registered apps' names
for (vector<string>::iterator reg_app_it=
registered_apps.begin(); reg_app_it != registered_apps.end(); reg_app_it++) {
@ -442,7 +455,19 @@ bool DSMFactory::loadConfig(const string& conf_file_name, const string& conf_nam
}
ScriptConfigs_mut.unlock();
return true;
bool res = true;
vector<string> system_dsms = explode(cfg.getParameter("run_system_dsms"), ",");
for (vector<string>::iterator it=system_dsms.begin(); it != system_dsms.end(); it++) {
string status;
if (createSystemDSM(script_name, *it, live_reload, status)) {
} else {
ERROR("creating system DSM '%s': '%s'\n", it->c_str(), status.c_str());
res = false;
}
}
return res;
}
@ -726,6 +751,39 @@ AmSession* DSMFactory::onInvite(const AmSipRequest& req,
return s;
}
bool DSMFactory::createSystemDSM(const string& config_name, const string& start_diag, bool reload, string& status) {
bool res = true;
DSMScriptConfig* script_config = NULL;
ScriptConfigs_mut.lock();
if (config_name == "main")
script_config = &MainScriptConfig;
else {
map<string, DSMScriptConfig>::iterator it = Name2ScriptConfig.find(config_name);
if (it != Name2ScriptConfig.end())
script_config = &it->second;
}
if (script_config==NULL) {
status = "Error: Script config '"+config_name+"' not found, in [";
for (map<string, DSMScriptConfig>::iterator it =
Name2ScriptConfig.begin(); it != Name2ScriptConfig.end(); it++) {
if (it != Name2ScriptConfig.begin())
status+=", ";
status += it->first;
}
status += "]";
res = false;
} else {
SystemDSM* s = new SystemDSM(*script_config, start_diag, reload);
s->start();
// add to garbage collector
AmThreadWatcher::instance()->add(s);
status = "OK";
}
ScriptConfigs_mut.unlock();
return res;
}
void DSMFactory::reloadDSMs(const AmArg& args, AmArg& ret) {
DSMStateDiagramCollection* new_diags = new DSMStateDiagramCollection();
@ -1035,6 +1093,16 @@ void DSMFactory::invoke(const string& method, const AmArg& args,
} else if (method == "loadConfig"){
args.assertArrayFmt("ss");
loadConfig(args,ret);
} else if (method == "runSystemDSM"){
args.assertArrayFmt("ss");
string status;
if (createSystemDSM(args.get(0).asCStr(), args.get(1).asCStr(), false, status)) {
ret.push(200);
ret.push(status);
} else {
ret.push(500);
ret.push(status);
}
} else if(method == "_list"){
ret.push(AmArg("postDSMEvent"));
ret.push(AmArg("reloadDSMs"));
@ -1046,6 +1114,8 @@ void DSMFactory::invoke(const string& method, const AmArg& args,
ret.push(AmArg("hasDSM"));
ret.push(AmArg("listDSMs"));
ret.push(AmArg("registerApplication"));
ret.push(AmArg("runSystemDSM"));
} else
throw AmDynInvoke::NotImplemented(method);
}

@ -73,7 +73,10 @@ class DSMFactory
static string OutboundStartDiag;
DSMScriptConfig MainScriptConfig;
// script name -> config
map<string, DSMScriptConfig> ScriptConfigs;
// config name -> config
map<string, DSMScriptConfig> Name2ScriptConfig;
AmMutex ScriptConfigs_mut;
#ifdef USE_MONITORING
@ -108,6 +111,8 @@ class DSMFactory
DSMChartReader preload_reader;
bool createSystemDSM(const string& config_name, const string& start_diag, bool reload, string& status);
void listDSMs(const AmArg& args, AmArg& ret);
void hasDSM(const AmArg& args, AmArg& ret);
void reloadDSMs(const AmArg& args, AmArg& ret);

@ -340,8 +340,7 @@ void DSMCall::process(AmEvent* event)
if (dsm_event) {
engine.runEvent(this, this, DSMCondition::DSMEvent, &dsm_event->params);
return;
}
}
}
AmAudioEvent* audio_event = dynamic_cast<AmAudioEvent*>(event);

@ -31,6 +31,7 @@
#include "AmSession.h"
#include "AmSessionContainer.h"
#include "AmUtils.h"
#include "AmEventDispatcher.h"
#include "jsonArg.h"
@ -94,6 +95,9 @@ DSMAction* DSMCoreModule::getAction(const string& from_str) {
DEF_CMD("postEvent", SCPostEventAction);
DEF_CMD("registerEventQueue", SCRegisterEventQueueAction);
DEF_CMD("unregisterEventQueue", SCUnregisterEventQueueAction);
if (cmd == "DI") {
SCDIAction * a = new SCDIAction(params, false);
a->name = from_str;
@ -183,6 +187,15 @@ DSMCondition* DSMCoreModule::getCondition(const string& from_str) {
if (cmd == "jsonRpcResponse")
return new TestDSMCondition(params, DSMCondition::JsonRpcResponse);
if (cmd == "startup")
return new TestDSMCondition(params, DSMCondition::Startup);
if (cmd == "reload")
return new TestDSMCondition(params, DSMCondition::Reload);
if (cmd == "system")
return new TestDSMCondition(params, DSMCondition::System);
return NULL;
}
@ -843,7 +856,7 @@ TestDSMCondition::TestDSMCondition(const string& expr, DSMCondition::EventType e
name = expr;
}
bool TestDSMCondition::match(AmSession* sess, DSMCondition::EventType event,
bool TestDSMCondition::match(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event,
map<string,string>* event_params) {
if (ttype == None || (type != DSMCondition::Any && type != event))
return false;
@ -851,7 +864,6 @@ bool TestDSMCondition::match(AmSession* sess, DSMCondition::EventType event,
if (ttype == Always)
return true;
DSMSession* sc_sess = dynamic_cast<DSMSession*>(sess);
if (!sc_sess) {
ERROR("wrong session type\n");
return false;
@ -1189,3 +1201,21 @@ EXEC_ACTION_START(SCSendDTMFAction) {
sess->sendDtmf(event_i, duration_i);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCRegisterEventQueueAction) {
string q_name = resolveVars(arg, sess, sc_sess, event_params);
DBG("Registering event queue '%s'\n", q_name.c_str());
if (q_name.empty()) {
WARN("Registering empty event queue name!\n");
}
AmEventDispatcher::instance()->addEventQueue(q_name, sess);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCUnregisterEventQueueAction) {
string q_name = resolveVars(arg, sess, sc_sess, event_params);
DBG("Unregistering event queue '%s'\n", q_name.c_str());
if (q_name.empty()) {
WARN("Unregistering empty event queue name!\n");
}
AmEventDispatcher::instance()->delEventQueue(q_name);
} EXEC_ACTION_END;

@ -104,6 +104,9 @@ DEF_ACTION_1P(SCB2BAddHeaderAction);
DEF_ACTION_1P(SCB2BClearHeadersAction);
DEF_ACTION_2P(SCB2BSetHeadersAction);
DEF_ACTION_1P(SCRegisterEventQueueAction);
DEF_ACTION_1P(SCUnregisterEventQueueAction);
class SCDIAction
: public DSMAction {
vector<string> params;
@ -132,7 +135,7 @@ class TestDSMCondition
public:
TestDSMCondition(const string& expr, DSMCondition::EventType e);
bool match(AmSession* sess, DSMCondition::EventType event,
bool match(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event,
map<string,string>* event_params);
};

@ -290,15 +290,15 @@ bool DSMStateEngine::init(AmSession* sess, DSMSession* sc_sess,
}
bool DSMCondition::_match(AmSession* sess, DSMSession* sc_sess,
DSMCondition::EventType event,
map<string,string>* event_params) {
DSMCondition::EventType event,
map<string,string>* event_params) {
// or xor
return invert?(!match(sess,sc_sess,event,event_params)):match(sess, sc_sess, event, event_params);
return invert? (!match(sess,sc_sess,event,event_params)) : match(sess, sc_sess, event, event_params);
}
bool DSMCondition::match(AmSession* sess, DSMSession* sc_sess,
DSMCondition::EventType event,
map<string,string>* event_params) {
DSMCondition::EventType event,
map<string,string>* event_params) {
if ((type != Any) && (event != type))
return false;

@ -86,8 +86,11 @@ class DSMCondition
XmlrpcResponse,
JsonRpcResponse,
JsonRpcRequest
JsonRpcRequest,
Startup,
Reload,
System
};
bool invert;

@ -0,0 +1,181 @@
#include "SystemDSM.h"
#include "log.h"
#include "AmUtils.h"
#include "AmEventDispatcher.h"
#include "DSMStateDiagramCollection.h"
#include "../apps/jsonrpc/JsonRPCEvents.h" // todo!
SystemDSM::SystemDSM(const DSMScriptConfig& config,
const string& startDiagName,
bool reload)
: stop_requested(false), AmEventQueue(this),
startDiagName(startDiagName), dummy_session(this),
reload(reload)
{
config.diags->addToEngine(&engine);
for (map<string, string>::const_iterator it =
config.config_vars.begin(); it != config.config_vars.end(); it++)
var["config."+it->first] = it->second;
// register our event queue
string our_id = "SystemDSM_"+AmSession::getNewId();
dummy_session.setLocalTag(our_id);
AmEventDispatcher::instance()->addEventQueue(our_id, this);
}
SystemDSM::~SystemDSM() {
}
void SystemDSM::run() {
DBG("SystemDSM thread starting...\n");
DBG("Running init of SystemDSM...\n");
if (!engine.init(&dummy_session, this, startDiagName,
reload ? DSMCondition::Reload : DSMCondition::Startup)) {
WARN("Initialization failed for SystemDSM\n");
AmEventDispatcher::instance()->delEventQueue(dummy_session.getLocalTag());
return;
}
while (!stop_requested.get() && !dummy_session.getStopped()) {
waitForEvent();
processEvents();
}
AmEventDispatcher::instance()->delEventQueue(dummy_session.getLocalTag());
DBG("SystemDSM thread finished.\n");
}
void SystemDSM::on_stop() {
DBG("requesting stop of SystemDSM\n");
stop_requested.set(true);
}
void SystemDSM::process(AmEvent* event) {
AmPluginEvent* plugin_event = dynamic_cast<AmPluginEvent*>(event);
if(plugin_event && plugin_event->name == "timer_timeout") {
int timer_id = plugin_event->data.get(0).asInt();
map<string, string> params;
params["id"] = int2str(timer_id);
engine.runEvent(&dummy_session, this, DSMCondition::Timer, &params);
}
if (event->event_id == DSM_EVENT_ID) {
DSMEvent* dsm_event = dynamic_cast<DSMEvent*>(event);
if (dsm_event) {
engine.runEvent(&dummy_session, this, DSMCondition::DSMEvent, &dsm_event->params);
return;
}
}
// todo: give modules the possibility to define/process events
JsonRpcEvent* jsonrpc_ev = dynamic_cast<JsonRpcEvent*>(event);
if (jsonrpc_ev) {
DBG("received jsonrpc event\n");
JsonRpcResponseEvent* resp_ev =
dynamic_cast<JsonRpcResponseEvent*>(jsonrpc_ev);
if (resp_ev) {
map<string, string> params;
params["ev_type"] = "JsonRpcResponse";
params["id"] = resp_ev->response.id;
params["is_error"] = resp_ev->response.is_error ?
"true":"false";
// decode result for easy use from script
varPrintArg(resp_ev->response.data, params, resp_ev->response.is_error ? "error": "result");
// save reference to full parameters
avar[DSM_AVAR_JSONRPCRESPONEDATA] = AmArg(&resp_ev->response.data);
engine.runEvent(&dummy_session, this, DSMCondition::JsonRpcResponse, &params);
avar.erase(DSM_AVAR_JSONRPCRESPONEDATA);
return;
}
JsonRpcRequestEvent* req_ev =
dynamic_cast<JsonRpcRequestEvent*>(jsonrpc_ev);
if (req_ev) {
map<string, string> params;
params["ev_type"] = "JsonRpcRequest";
params["is_notify"] = req_ev->isNotification() ?
"true" : "false";
params["method"] = req_ev->method;
if (!req_ev->id.empty())
params["id"] = req_ev->id;
// decode request params result for easy use from script
varPrintArg(req_ev->params, params, "params");
// save reference to full parameters
avar[DSM_AVAR_JSONRPCREQUESTDATA] = AmArg(&req_ev->params);
engine.runEvent(&dummy_session, this, DSMCondition::JsonRpcRequest, &params);
avar.erase(DSM_AVAR_JSONRPCREQUESTDATA);
return;
}
}
if (event->event_id == E_SYSTEM) {
AmSystemEvent* sys_ev = dynamic_cast<AmSystemEvent*>(event);
if(sys_ev){
DBG("SystemDSM received system Event\n");
map<string, string> params;
params["type"] = AmSystemEvent::getDescription(sys_ev->sys_event);
engine.runEvent(&dummy_session, this, DSMCondition::System, &params);
// stop_requested.set(true);
return;
}
}
}
#define NOT_IMPLEMENTED(_func) \
void SystemDSM::_func { \
throw DSMException("core", "cause", "not implemented"); \
}
#define NOT_IMPLEMENTED_UINT(_func) \
unsigned int SystemDSM::_func { \
throw DSMException("core", "cause", "not implemented"); \
}
NOT_IMPLEMENTED(playPrompt(const string& name, bool loop));
NOT_IMPLEMENTED(playFile(const string& name, bool loop, bool front));
NOT_IMPLEMENTED(recordFile(const string& name));
NOT_IMPLEMENTED_UINT(getRecordLength());
NOT_IMPLEMENTED_UINT(getRecordDataSize());
NOT_IMPLEMENTED(stopRecord());
NOT_IMPLEMENTED(setInOutPlaylist());
NOT_IMPLEMENTED(setInputPlaylist());
NOT_IMPLEMENTED(setOutputPlaylist());
NOT_IMPLEMENTED(addToPlaylist(AmPlaylistItem* item));
NOT_IMPLEMENTED(closePlaylist(bool notify));
NOT_IMPLEMENTED(setPromptSet(const string& name));
NOT_IMPLEMENTED(addSeparator(const string& name, bool front));
NOT_IMPLEMENTED(connectMedia());
NOT_IMPLEMENTED(disconnectMedia());
NOT_IMPLEMENTED(mute());
NOT_IMPLEMENTED(unmute());
/** B2BUA functions */
NOT_IMPLEMENTED(B2BconnectCallee(const string& remote_party,
const string& remote_uri,
bool relayed_invite));
NOT_IMPLEMENTED(B2BterminateOtherLeg());
NOT_IMPLEMENTED(B2BaddReceivedRequest(const AmSipRequest& req));
NOT_IMPLEMENTED(B2BsetHeaders(const string& hdr, bool replaceCRLF));
NOT_IMPLEMENTED(B2BclearHeaders());
NOT_IMPLEMENTED(B2BaddHeader(const string& hdr));
NOT_IMPLEMENTED(transferOwnership(DSMDisposable* d));
NOT_IMPLEMENTED(releaseOwnership(DSMDisposable* d));
#undef NOT_IMPLEMENTED
#undef NOT_IMPLEMENTED_UINT

@ -0,0 +1,100 @@
#ifndef _SystemDSM_h_
#define _SystemDSM_h_
#include "AmThread.h"
#include "AmEventQueue.h"
#include "DSMSession.h"
#include "AmSession.h"
#include "DSMStateEngine.h"
#include <string>
using std::string;
class EventProxySession
: public AmSession
{
AmEventQueueInterface* e;
public:
EventProxySession(AmEventQueueInterface* e)
: e(e) { assert(e); }
void postEvent(AmEvent* event) { e->postEvent(event); }
};
class SystemDSM
: public AmThread,
public AmEventQueue,
public AmEventHandler,
public DSMSession
{
EventProxySession dummy_session;
AmSharedVar<bool> stop_requested;
DSMStateEngine engine;
string startDiagName;
bool reload;
public:
SystemDSM(const DSMScriptConfig& config,
const string& startDiagName,
bool reload);
~SystemDSM();
void run();
void on_stop();
// AmEventHandler interface
void process(AmEvent* event);
// DSMSession interface
void playPrompt(const string& name, bool loop = false);
void playFile(const string& name, bool loop, bool front = false);
void recordFile(const string& name);
unsigned int getRecordLength();
unsigned int getRecordDataSize();
void stopRecord();
void setInOutPlaylist();
void setInputPlaylist();
void setOutputPlaylist();
void addToPlaylist(AmPlaylistItem* item);
void closePlaylist(bool notify);
void setPromptSet(const string& name);
void addSeparator(const string& name, bool front = false);
void connectMedia();
void disconnectMedia();
void mute();
void unmute();
/** B2BUA functions */
void B2BconnectCallee(const string& remote_party,
const string& remote_uri,
bool relayed_invite = false);
void B2BterminateOtherLeg();
/** insert request in list of received ones */
void B2BaddReceivedRequest(const AmSipRequest& req);
/** set headers of outgoing INVITE */
void B2BsetHeaders(const string& hdr, bool replaceCRLF);
/** set headers of outgoing INVITE */
void B2BclearHeaders();
/** add a header to the headers of outgoing INVITE */
void B2BaddHeader(const string& hdr);
/** transfer ownership of object to this session instance */
void transferOwnership(DSMDisposable* d);
/** release ownership of object from this session instance */
void releaseOwnership(DSMDisposable* d);
};
#endif

@ -55,6 +55,10 @@ load_prompts=/usr/local/etc/sems/dsm_in_prompts.conf,/usr/local/etc/sems/dsm_out
#
#set_param_variables=yes
# run these system DSMs on startup (system DSMs are DSMs executed without a call)
#
#run_system_dsms=system_dsm1,system_dsm2
# monitoring_full_stategraph=[yes|no]
#
# Controls whether to log the full call graph (all states visited)
@ -118,7 +122,8 @@ load_prompts=/usr/local/etc/sems/dsm_in_prompts.conf,/usr/local/etc/sems/dsm_out
# preload_mods=
# run_invite_event=
# set_param_variables=
#
# run_system_dsms=
# and additional configuration variables (as script variables)
#
# conf_dir=/usr/local/etc/sems/dsm/

@ -53,6 +53,27 @@ A patch for fmsc 1.0.4 from the graphical FSM editor fsme
(http://fsme.sf.net) is available, so DSMs can be defined in
click-n-drag fashion and compiled to SEMS DSM diagrams.
SystemDSMs
==========
A system DSM is executed without a corresponding call. This can be useful
e.g. to execute something periodically, to make a call generator etc.
Obviously, only limited functionality is available in System DSMs, all
call and media related functionality is not available (and will throw
exceptions with type 'core').
A system DSM receives the "startup" event on start of the server, or if
it is created via runSystemDSM DI call. It gets a "reload" event if the
system DSM is created by a live config reload.
On server shutdown, a system DSM receives a "system" event with
"ServerShutdown" as type.
See test_system_event.dsm example for an example how to handle server
start and reload.
DI commands
===========
@ -103,6 +124,10 @@ loadConfig(string conf_file_name, string conf_name)
(re)load application configuration and script
like a file in conf_dir
runSystemDSM(string conf_name, string start_diag)
run a system DSM (i.e. a DSM thread not connected to a session)
using scripts/configuration from conf_name.
conf_name=='main' for main scripts/main config (from dsm.conf)
More info
=========

@ -187,6 +187,13 @@ sendDTMF(key [, duration_ms])
throwOnError()
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:
@ -206,13 +213,13 @@ conditions:
test(len($var) < len(@user))
-- like test(expr), but only on key press
keyTest(expr)
timerTest(expr)
noAudioTest(expr)
separatorTest(expr)
key or keyTest(expr)
timer or timerTest(expr)
noAudio or noAudioTest(expr)
separator or separatorTest(expr)
e.g. separatorTest(#id == 5)
eventTest(expr)
event or eventTest(expr)
keyPress(no)
-- bye received:
@ -229,6 +236,15 @@ conditions:
-- event is start of session (with run_invite_event, otherwise its always true):
sessionStart
-- event is startup
startup
-- event is reload
startup
-- event is system event
system
B2B.otherReply
Reply on other leg received (see AmB2BSession)
#code - reply code

@ -0,0 +1,37 @@
-- script to show how SystemDSM can be used
--
-- on server startup (or via runSystemDSM), the "startup" event is processed.
-- on config reload, the "reload" event is processed; here we only send an
-- event to the main SystemDSM thread
initial state START;
transition "startup" START - startup / {
log(2, "Here we are!");
logAll(2);
setTimer(1, 20);
-- register a friendlier name
registerEventQueue(system_dsm);
} -> RUNNING;
-- this gets called when script config is reloaded
transition "reload" START - reload / {
set($cmd="reload");
postEvent(system_dsm, cmd);
-- or: postEvent(system_dsm, var) to post all variables,
-- including changed config (#config.*)
stop(false);
} -> END;
state RUNNING;
transition "timer hit" RUNNING - timer / log(2, "still there!"); setTimer(1, 20); -> RUNNING;
transition "shutdown" RUNNING - system / unregisterEventQueue(system_dsm); stop(false) -> END;
transition "stop cmd" RUNNING - eventTest(#cmd=="stop") / logAll(2); unregisterEventQueue(system_dsm); stop(false) } -> END;
transition "reload cmd" RUNNING - eventTest(#cmd=="reload") / log(2, "got refresh"); logParams(2); -> RUNNING;
transition "some other event" RUNNING - event / logAll(2) -> RUNNING;
state END;
Loading…
Cancel
Save