diff --git a/apps/dsm/DSM.cpp b/apps/dsm/DSM.cpp index 2593da44..dd079831 100644 --- a/apps/dsm/DSM.cpp +++ b/apps/dsm/DSM.cpp @@ -31,6 +31,7 @@ #include "log.h" #include "AmConfigReader.h" #include "DSMDialog.h" +#include "DSMChartReader.h" #include #include @@ -54,6 +55,7 @@ DSMFactory* DSMFactory::instance() string DSMFactory::InboundStartDiag; string DSMFactory::OutboundStartDiag; +map DSMFactory::config; DSMFactory::DSMFactory(const string& _app_name) : AmSessionFactory(_app_name), @@ -61,6 +63,12 @@ DSMFactory::DSMFactory(const string& _app_name) { } +DSMFactory::~DSMFactory() { + for (map::iterator it= + prompt_sets.begin(); it != prompt_sets.end(); it++) + delete it->second; +} + int DSMFactory::onLoad() { if (loaded) @@ -95,12 +103,61 @@ int DSMFactory::onLoad() } } + string prompt_sets_path = cfg.getParameter("prompts_sets_path"); + + vector prompt_sets_names = + explode(cfg.getParameter("load_prompts_sets"), ","); + for (vector::iterator it= + prompt_sets_names.begin(); it != prompt_sets_names.end(); it++) { + string fname = prompt_sets_path.empty() ? "": prompt_sets_path + "/"; + fname += *it; + DBG("loading prompts for '%s' (file '%s')\n", it->c_str(), fname.c_str()); + std::ifstream ifs(fname.c_str()); + string s; + if (!ifs.good()) { + WARN("prompts set file '%s' could not be read\n", fname.c_str()); + } + AmPromptCollection* pc = new AmPromptCollection(); + while (ifs.good() && !ifs.eof()) { + getline(ifs, s); + if (s.length() && s.find_first_not_of(" \t")!= string::npos && + s[s.find_first_not_of(" \t")] != '#') { + vector p=explode(s, "="); + if (p.size()==2) { + pc->setPrompt(p[0], p[1], MOD_NAME); + DBG("set '%s' added prompt '%s' as '%s'\n", + it->c_str(), p[0].c_str(), p[1].c_str()); + } + } + } + prompt_sets[*it] = pc; + } + string DiagPath = cfg.getParameter("diag_path"); if (DiagPath.length() && DiagPath[DiagPath.length()-1] != '/') DiagPath += '/'; string ModPath = cfg.getParameter("mod_path"); + string preload_mods = cfg.getParameter("preload_mods"); + vector preload_names = explode(preload_mods, ","); + if (preload_names.size()) { + DSMChartReader reader; + for (vector::iterator it= + preload_names.begin(); it != preload_names.end(); it++) { + DBG("preloading '%s'...\n"); + if (!reader.importModule("import("+*it+")", ModPath)) { + ERROR("importing module '%s' for preload\n", it->c_str()); + return -1; + } + DSMModule* last_loaded = reader.mods.back(); + if (last_loaded) { + last_loaded->preload(); + } + } + } + // TODO: pass preloaded mods to chart reader + string LoadDiags = cfg.getParameter("load_diags"); vector diags_names = explode(LoadDiags, ","); for (vector::iterator it= @@ -121,9 +178,19 @@ int DSMFactory::onLoad() INFO("no 'outbound_start_diag' set in config. outbound calls disabled.\n"); } + for (std::map::const_iterator it = + cfg.begin(); it != cfg.end(); it++) + config[it->first] = it->second; + return 0; } +void DSMFactory::prepareSession(DSMDialog* s) { + s->setPromptSets(prompt_sets); + for (map::iterator it = + config.begin(); it != config.end(); it++) + s->var["config."+it->first] = it->second; +} AmSession* DSMFactory::onInvite(const AmSipRequest& req) { @@ -131,7 +198,9 @@ AmSession* DSMFactory::onInvite(const AmSipRequest& req) ERROR("no inbound calls allowed\n"); throw AmSession::Exception(488, "Not Acceptable Here"); } - return new DSMDialog(prompts, diags, InboundStartDiag, NULL); + DSMDialog* s = new DSMDialog(prompts, diags, InboundStartDiag, NULL); + prepareSession(s); + return s; } AmSession* DSMFactory::onInvite(const AmSipRequest& req, @@ -149,8 +218,9 @@ AmSession* DSMFactory::onInvite(const AmSipRequest& req, cred = dynamic_cast(cred_obj); } - AmSession* s = new DSMDialog(prompts, diags, OutboundStartDiag, cred); - + DSMDialog* s = new DSMDialog(prompts, diags, OutboundStartDiag, cred); + prepareSession(s); + if (NULL == cred) { WARN("discarding unknown session parameters.\n"); } else { diff --git a/apps/dsm/DSM.h b/apps/dsm/DSM.h index deac84b9..dfdad29c 100644 --- a/apps/dsm/DSM.h +++ b/apps/dsm/DSM.h @@ -42,6 +42,8 @@ using std::string; #include +class DSMDialog; + /** \brief Factory for announcement sessions */ class DSMFactory : public AmSessionFactory @@ -54,10 +56,16 @@ class DSMFactory static DSMFactory* _instance; DSMFactory(const string& _app_name); + ~DSMFactory(); bool loaded; + + map prompt_sets; + void prepareSession(DSMDialog* s); + public: static DSMFactory* instance(); + static map config; int onLoad(); AmSession* onInvite(const AmSipRequest& req); diff --git a/apps/dsm/DSMChartReader.cpp b/apps/dsm/DSMChartReader.cpp index 6214a7fe..c05443cc 100644 --- a/apps/dsm/DSMChartReader.cpp +++ b/apps/dsm/DSMChartReader.cpp @@ -171,7 +171,8 @@ bool DSMChartReader::importModule(const string& mod_cmd, const string& mod_path) } bool DSMChartReader::decode(DSMStateDiagram* e, const string& chart, - const string& mod_path, DSMElemContainer* owner) { + const string& mod_path, DSMElemContainer* owner, + vector& out_mods) { vector stack; size_t pos = 0; while (pos < chart.length()) { @@ -389,6 +390,11 @@ bool DSMChartReader::decode(DSMStateDiagram* e, const string& chart, continue; } } + + for (vector::iterator it= + mods.begin(); it != mods.end(); it++) + out_mods.push_back(*it); + return true; } diff --git a/apps/dsm/DSMChartReader.h b/apps/dsm/DSMChartReader.h index 41585476..b56f5ec4 100644 --- a/apps/dsm/DSMChartReader.h +++ b/apps/dsm/DSMChartReader.h @@ -90,7 +90,10 @@ class DSMChartReader { DSMChartReader(); ~DSMChartReader(); bool decode(DSMStateDiagram* e, const string& chart, - const string& mod_path, DSMElemContainer* owner); + const string& mod_path, DSMElemContainer* owner, + vector& out_mods); + + friend class DSMFactory; }; #endif diff --git a/apps/dsm/DSMCoreModule.cpp b/apps/dsm/DSMCoreModule.cpp index fcd1d6f1..c6f54374 100644 --- a/apps/dsm/DSMCoreModule.cpp +++ b/apps/dsm/DSMCoreModule.cpp @@ -73,6 +73,7 @@ DSMAction* DSMCoreModule::getAction(const string& from_str) { DEF_CMD("stop", SCStopAction); DEF_CMD("playPrompt", SCPlayPromptAction); + DEF_CMD("playPromptLooped", SCPlayPromptLoopedAction); DEF_CMD("playFile", SCPlayFileAction); DEF_CMD("recordFile", SCRecordFileAction); DEF_CMD("stopRecord", SCStopRecordAction); @@ -84,6 +85,8 @@ DSMAction* DSMCoreModule::getAction(const string& from_str) { DEF_CMD("setTimer", SCSetTimerAction); + DEF_CMD("setPrompts", SCSetPromptsAction); + if (cmd == "DI") { SCDIAction * a = new SCDIAction(params, false); a->name = from_str; @@ -184,6 +187,23 @@ bool SCPlayPromptAction::execute(AmSession* sess, return false; } + +bool SCSetPromptsAction::execute(AmSession* sess, + DSMCondition::EventType event, + map* event_params) { + GET_SCSESSION(); + sc_sess->setPromptSet(resolveVars(arg, sess, sc_sess, event_params)); + return false; +} + +bool SCPlayPromptLoopedAction::execute(AmSession* sess, + DSMCondition::EventType event, + map* event_params) { + GET_SCSESSION(); + sc_sess->playPrompt(resolveVars(arg, sess, sc_sess, event_params), true); + return false; +} + bool SCPlayFileAction::execute(AmSession* sess, DSMCondition::EventType event, map* event_params) { @@ -282,7 +302,7 @@ bool SCLogAction::execute(AmSession* sess, return false; } string l_line = resolveVars(par2, sess, sc_sess, event_params).c_str(); - _LOG((int)lvl, "FSM: %s %s\n", (par2 != l_line)?par2.c_str():"", + _LOG((int)lvl, "FSM: %s '%s'\n", (par2 != l_line)?par2.c_str():"", l_line.c_str()); return false; } diff --git a/apps/dsm/DSMCoreModule.h b/apps/dsm/DSMCoreModule.h index df1ce502..23b626b7 100644 --- a/apps/dsm/DSMCoreModule.h +++ b/apps/dsm/DSMCoreModule.h @@ -50,10 +50,12 @@ class DSMCoreModule }; DEF_SCStrArgAction(SCPlayPromptAction); +DEF_SCStrArgAction(SCPlayPromptLoopedAction); DEF_SCStrArgAction(SCRecordFileAction); DEF_SCStrArgAction(SCStopRecordAction); DEF_SCStrArgAction(SCClosePlaylistAction); DEF_SCStrArgAction(SCStopAction); +DEF_SCStrArgAction(SCSetPromptsAction); DEF_SCModSEStrArgAction(SCRepostAction); DEF_SCModSEStrArgAction(SCJumpFSMAction); @@ -102,4 +104,6 @@ class TestDSMCondition string resolveVars(const string s, AmSession* sess, DSMSession* sc_sess, map* event_params); +string trim(string const& str,char const* sepSet); + #endif diff --git a/apps/dsm/DSMDialog.cpp b/apps/dsm/DSMDialog.cpp index 97771636..672db329 100644 --- a/apps/dsm/DSMDialog.cpp +++ b/apps/dsm/DSMDialog.cpp @@ -46,6 +46,11 @@ DSMDialog::~DSMDialog() delete *it; } +void DSMDialog::onInvite(const AmSipRequest& req) { + engine.onInvite(req, this); + AmSession::onInvite(req); +} + void DSMDialog::onSessionStart(const AmSipRequest& req) { DBG("DSMDialog::onSessionStart\n"); @@ -113,9 +118,9 @@ inline UACAuthCred* DSMDialog::getCredentials() { return cred.get(); } -void DSMDialog::playPrompt(const string& name) { +void DSMDialog::playPrompt(const string& name, bool loop) { DBG("playing prompt '%s'\n", name.c_str()); - prompts.addToPlaylist(name, (long)this, playlist); + prompts.addToPlaylist(name, (long)this, playlist, /*front =*/ false, loop); } void DSMDialog::closePlaylist(bool notify) { @@ -165,3 +170,32 @@ void DSMDialog::stopRecord() { return; } } + +void DSMDialog::addPromptSet(const string& name, + AmPromptCollection* prompt_set) { + if (prompt_set) { + DBG("adding prompt set '%s'\n", name.c_str()); + prompt_sets[name] = prompt_set; + } else { + ERROR("trying to add NULL prompt set\n"); + } +} + +void DSMDialog::setPromptSets(map& + new_prompt_sets) { + prompt_sets = new_prompt_sets; +} + +void DSMDialog::setPromptSet(const string& name) { + map::iterator it = + prompt_sets.find(name); + + if (it == prompt_sets.end()) { + ERROR("prompt set %s unknown\n", name.c_str()); + return; + } + + DBG("setting prompt set '%s'\n", name.c_str()); + prompts = *it->second; +} + diff --git a/apps/dsm/DSMDialog.h b/apps/dsm/DSMDialog.h index 94fa4568..670cdc9b 100644 --- a/apps/dsm/DSMDialog.h +++ b/apps/dsm/DSMDialog.h @@ -49,6 +49,7 @@ class DSMDialog : public AmSession, vector audiofiles; AmAudioFile* rec_file; + map prompt_sets; public: DSMDialog(AmPromptCollection& prompts, @@ -57,6 +58,7 @@ public: UACAuthCred* credentials = NULL); ~DSMDialog(); + void onInvite(const AmSipRequest& req); void onSessionStart(const AmSipRequest& req); void onSessionStart(const AmSipReply& rep); void startSession(); @@ -67,11 +69,17 @@ public: UACAuthCred* getCredentials(); - void playPrompt(const string& name); + void addPromptSet(const string& name, AmPromptCollection* prompt_set); + void setPromptSets(map& new_prompt_sets); + + // DSMSession interface + void playPrompt(const string& name, bool loop = false); void closePlaylist(bool notify); void playFile(const string& name, bool loop); void recordFile(const string& name); void stopRecord(); + + void setPromptSet(const string& name); }; #endif diff --git a/apps/dsm/DSMModule.h b/apps/dsm/DSMModule.h index bd85d3e5..f6341873 100644 --- a/apps/dsm/DSMModule.h +++ b/apps/dsm/DSMModule.h @@ -27,6 +27,9 @@ #ifndef _DSM_MODULE_H #define _DSM_MODULE_H #include "DSMStateEngine.h" +#include "AmSipMsg.h" +#include "AmArg.h" +class DSMSession; #include using std::string; @@ -41,6 +44,9 @@ class DSMModule { virtual DSMAction* getAction(const string& from_str) = 0; virtual DSMCondition* getCondition(const string& from_str) = 0; + + virtual void preload() { } + virtual void onInvite(const AmSipRequest& req, DSMSession* sess) { } }; typedef void* (*SCFactoryCreate)(); diff --git a/apps/dsm/DSMSession.h b/apps/dsm/DSMSession.h index 55e5530c..408531fd 100644 --- a/apps/dsm/DSMSession.h +++ b/apps/dsm/DSMSession.h @@ -42,11 +42,12 @@ class DSMSession { DSMSession(); virtual ~DSMSession(); - virtual void playPrompt(const string& name) = 0; + virtual void playPrompt(const string& name, bool loop = false) = 0; virtual void playFile(const string& name, bool loop) = 0; virtual void recordFile(const string& name) = 0; virtual void stopRecord() = 0; virtual void closePlaylist(bool notify) = 0; + virtual void setPromptSet(const string& name) = 0; /* holds variables which are accessed by $varname */ map var; diff --git a/apps/dsm/DSMStateDiagramCollection.cpp b/apps/dsm/DSMStateDiagramCollection.cpp index 9d6ad491..6146f5a9 100644 --- a/apps/dsm/DSMStateDiagramCollection.cpp +++ b/apps/dsm/DSMStateDiagramCollection.cpp @@ -62,7 +62,7 @@ bool DSMStateDiagramCollection::loadFile(const string& filename, const string& n s += r + "\n"; } DBG("dsm text\n------------------\n%s\n------------------\n", s.c_str()); - if (!cr.decode(&diags.back(), s, mod_path, this)) { + if (!cr.decode(&diags.back(), s, mod_path, this, mods)) { ERROR("DonkeySM decode script error!\n"); return false; } @@ -74,4 +74,6 @@ void DSMStateDiagramCollection::addToEngine(DSMStateEngine* e) { for (vector ::iterator it = diags.begin(); it != diags.end(); it++) e->addDiagram(&(*it)); + + e->addModules(mods); } diff --git a/apps/dsm/DSMStateDiagramCollection.h b/apps/dsm/DSMStateDiagramCollection.h index 6f3faaa0..e83bedba 100644 --- a/apps/dsm/DSMStateDiagramCollection.h +++ b/apps/dsm/DSMStateDiagramCollection.h @@ -29,13 +29,15 @@ #include "DSMStateEngine.h" #include -using std::string; +using std::string; +class DSMModule; class DSMStateDiagramCollection : public DSMElemContainer { - vector diags; + vector diags; + vector mods; public: DSMStateDiagramCollection(); diff --git a/apps/dsm/DSMStateEngine.cpp b/apps/dsm/DSMStateEngine.cpp index 27a0b4d5..b4ecefe4 100644 --- a/apps/dsm/DSMStateEngine.cpp +++ b/apps/dsm/DSMStateEngine.cpp @@ -26,6 +26,8 @@ */ #include "DSMStateEngine.h" +#include "DSMModule.h" + #include "AmSession.h" #include "log.h" @@ -112,6 +114,12 @@ DSMStateEngine::~DSMStateEngine() { } +void DSMStateEngine::onInvite(const AmSipRequest& req, DSMSession* sess) { + for (vector::iterator it = + mods.begin(); it != mods.end(); it++) + (*it)->onInvite(req, sess); +} + bool DSMStateEngine::runactions(vector::iterator from, vector::iterator to, AmSession* sess, DSMCondition::EventType event, @@ -153,6 +161,12 @@ void DSMStateEngine::addDiagram(DSMStateDiagram* diag) { diags.push_back(diag); } +void DSMStateEngine::addModules(vector modules) { + for (vector::iterator it= + modules.begin(); it != modules.end(); it++) + mods.push_back(*it); +} + bool DSMStateEngine::init(AmSession* sess, const string& startDiagram) { if (!jumpDiag(startDiagram, sess, DSMCondition::Any, NULL)) { @@ -332,3 +346,4 @@ DSMTransition::DSMTransition(){ DSMTransition::~DSMTransition(){ } + diff --git a/apps/dsm/DSMStateEngine.h b/apps/dsm/DSMStateEngine.h index 72db7fdf..8038a813 100644 --- a/apps/dsm/DSMStateEngine.h +++ b/apps/dsm/DSMStateEngine.h @@ -28,8 +28,11 @@ #define _STATE_ENGINE_H #include "DSMElemContainer.h" +#include "AmSipMsg.h" class AmSession; +class DSMSession; + #include using std::map; #include @@ -124,6 +127,8 @@ class DSMTransition string to_state; }; +class DSMModule; + class DSMStateDiagram { vector states; string name; @@ -157,16 +162,23 @@ class DSMStateEngine { vector::iterator to, AmSession* sess, DSMCondition::EventType event, map* event_params, bool& is_consumed); + + vector mods; + public: DSMStateEngine(); ~DSMStateEngine(); void addDiagram(DSMStateDiagram* diag); + void addModules(vector modules); + bool init(AmSession* sess, const string& startDiagram); void runEvent(AmSession* sess, DSMCondition::EventType event, map* event_params); + + void onInvite(const AmSipRequest& req, DSMSession* sess); }; diff --git a/apps/dsm/doc/dsm_syntax.txt b/apps/dsm/doc/dsm_syntax.txt index a7ec1c0b..1171bce4 100644 --- a/apps/dsm/doc/dsm_syntax.txt +++ b/apps/dsm/doc/dsm_syntax.txt @@ -36,7 +36,10 @@ actions: playFile(filename) recordFile(filename) stopRecord() - closePlaylist + closePlaylist() + + setPromptSet() + if more prompt sets are loaded set($var, value) e.g. set($var, "text"); set($var, $var2); set($var, #key) diff --git a/apps/dsm/etc/dsm.conf b/apps/dsm/etc/dsm.conf index 46ed26fc..9a8a9a81 100644 --- a/apps/dsm/etc/dsm.conf +++ b/apps/dsm/etc/dsm.conf @@ -6,9 +6,20 @@ load_diags=inbound_call,outbound_call # for import(mod_name) mod_path=/usr/local/lib/sems/dsm/ +# preload modules which have a preload (init) function +#preload_mods=uri + + # DSM to start for in/outbound call inbound_start_diag=inbound_call outbound_start_diag=outbound_call # prompts files (for prompt collection load_prompts=/usr/local/etc/sems/etc/dsm_in_prompts.conf,/usr/local/etc/sems/etc/dsm_out_prompts.conf + +#load prompt sets config from this path +#prompts_sets_path=/usr/local/etc/sems/etc/ + +# load files with this name (+.conf), e.g. mydomain.conf +#load_prompts_sets=mydomain +