/* * $Id: CallGen.cpp 288 2007-03-28 16:32:02Z sayer $ * * Copyright (C) 2007 iptego GmbH * * This file is part of SEMS, a free SIP media server. * * SEMS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * For a license to use the sems software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * sems is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "CallGen.h" #include "AmConferenceStatus.h" #include "AmUtils.h" #include "log.h" #include "AmUAC.h" #include "AmPlugIn.h" #include "AmSessionContainer.h" #include "AmMediaProcessor.h" #include #include #define APP_NAME "callgen" #define PLAY_FILE "play_file" #define CALL_TIMER 1 #include using std::vector; EXPORT_SESSION_FACTORY(CallGenFactory,APP_NAME); EXPORT_PLUGIN_CLASS_FACTORY(CallGenFactory,APP_NAME); string CallGenFactory::DigitsDir; AmFileCache CallGenFactory::play_file; CallGenFactory::CallGenFactory(const string& _app_name) : AmSessionFactory(_app_name), AmDynInvokeFactory(_app_name), configured(false), target_args(NULL), scheduled(0) { if (NULL == _instance) { _instance = this; } } CallGenFactory* CallGenFactory::_instance=0; int CallGenFactory::onLoad() { return getInstance()->load(); } int CallGenFactory::load() { // only execute this once if (configured) return 0; configured = true; AmConfigReader cfg; if(cfg.loadFile(AmConfig::ModConfigPath + string(APP_NAME)+ ".conf")) return -1; // get application specific global parameters configureModule(cfg); string play_fname = cfg.getParameter(PLAY_FILE, "default.wav"); if (play_file.load(play_fname)) { ERROR("file %s could not be loaded.\n", play_fname.c_str()); return -1; } // get prompts AM_PROMPT_START; AM_PROMPT_END(prompts, cfg, APP_NAME); DigitsDir = cfg.getParameter("digits_dir"); if (DigitsDir.length() && DigitsDir[DigitsDir.length()-1]!='/') DigitsDir+='/'; if (!DigitsDir.length()) { WARN("No digits_dir specified in configuration.\n"); } for (int i=0;i<10;i++) prompts.setPrompt(int2str(i), DigitsDir+int2str(i)+".wav", APP_NAME); prompts.setPrompt("*", DigitsDir+"s.wav", APP_NAME); prompts.setPrompt("#", DigitsDir+"p.wav", APP_NAME); start(); return 0; } void CallGenFactory::run() { while (true) { actions_mut.lock(); vector todo; time_t now; time(&now); multimap::iterator it = actions.begin(); while (it != actions.end()) { if (it->first > now) break; todo.push_back(it->second); actions.erase(it); it = actions.begin(); } actions_mut.unlock(); for (vector::iterator it=todo.begin(); it != todo.end(); it++) { createCall(*it); if (scheduled>0) scheduled--; } checkTarget(); sleep(1); } } void CallGenFactory::checkTarget() { if (!target_args) return; DBG("%d active calls, %d current target, %d already scheduled\n", active_calls.size(), target_args->get(0).asInt(), scheduled); int missing_calls = target_args->get(0).asInt() - active_calls.size() - scheduled; if (missing_calls > 0) { AmArg* to_schedule_args = new AmArg(*target_args); (*to_schedule_args)[0] = AmArg(missing_calls); AmArg ret; scheduleCalls(*to_schedule_args, ret); scheduled += missing_calls; } } void CallGenFactory::on_stop(){ ERROR("not stoppable!\n"); } AmSession* CallGenFactory::onInvite(const AmSipRequest& req) { ERROR("DUH - need session params!.\n"); return NULL; } // outgoing calls AmSession* CallGenFactory::onInvite(const AmSipRequest& req, AmArg& args) { size_t cnt = 0; int ncalls = args.get(cnt++).asInt(); int wait_time_base = args.get(cnt++).asInt(); int wait_time_rand = args.get(cnt++).asInt(); string ruri_user = args.get(cnt++).asCStr(); string ruri_host = args.get(cnt++).asCStr(); int ruri_rand_digits = args.get(cnt++).asInt(); int play_rand_digits = args.get(cnt++).asInt(); int call_time_base = args.get(cnt++).asInt(); int call_time_rand = args.get(cnt++).asInt(); return new CallGenDialog(prompts, play_rand_digits, call_time_base, call_time_rand); } void CallGenFactory::invoke(const string& method, const AmArg& args, AmArg& ret) { if (method == "createCalls"){ args.assertArrayFmt("iiissiiii"); instance()->createCalls(args, ret); } else if (method == "scheduleCalls"){ args.assertArrayFmt("iiissiiii"); instance()->scheduleCalls(args, ret); } else if (method == "setTarget"){ args.assertArrayFmt("iiissiiii"); instance()->setTarget(args, ret); } else if (method == "callGenStats"){ instance()->callGenStats(args, ret); } else if(method == "help"){ ret.push( "callgen - simple call generator\n" " method: createCalls - create calls (online - takes its time to return)\n" " method: scheduleCalls - schedule calls\n" " method: setTarget - set call count target\n" "\n" " parameters for these functions are always: \n" " int ncalls - number of calls to [make, schedule, set target]\n" " int wait_time_base - wait time btw calls, base value\n" " int wait_time_rand - wait time btw calls, random add (total = base + rand)\n" " string ruri_user - user part of ruri\n" " string ruri_host - host part of ruri\n" " int ruri_rand_digits - no of random digits to add to ruri user\n" " int play_rand_digits - no of random digits to play at the beginning\n" " int call_time_base - call timer, base value \n" " int call_time_rand - call timer, random add (total = base + rand)\n" "\n" " method: callGenStats - return some statistics\n" ); } else if(method == "_list"){ ret.push("createCalls"); ret.push("scheduleCalls"); ret.push("setTarget"); ret.push("callGenStats"); ret.push("help"); } else throw AmDynInvoke::NotImplemented(method); } void CallGenFactory::createCall(const AmArg& args) { size_t cnt = 0; int ncalls = args.get(cnt++).asInt(); int wait_time_base = args.get(cnt++).asInt(); int wait_time_rand = args.get(cnt++).asInt(); string ruri_user = args.get(cnt++).asCStr(); string ruri_host = args.get(cnt++).asCStr(); int ruri_rand_digits = args.get(cnt++).asInt(); int play_rand_digits = args.get(cnt++).asInt(); int call_time_base = args.get(cnt++).asInt(); int call_time_rand = args.get(cnt++).asInt(); string from = "sip:callgen@"+AmConfig::LocalSIPIP; string call_ruri = "sip:"+ruri_user; for (int i=0;i", from, call_ruri, string(""), // callid string(""), // headers c_args); } void CallGenFactory::createCalls(const AmArg& args, AmArg& ret) { size_t cnt = 0; int ncalls = args.get(cnt++).asInt(); int wait_time_base = args.get(cnt++).asInt(); int wait_time_rand = args.get(cnt++).asInt(); for (int i=0;i0) wait_nsec+=(rand()%wait_time_rand); DBG("sleeping %d seconds\n", wait_nsec); if (wait_nsec >0 && (i+1 < ncalls)) sleep(wait_nsec); } ret.push(0); ret.push("OK"); } void CallGenFactory::scheduleCalls(const AmArg& args, AmArg& ret) { size_t cnt = 0; int ncalls = args.get(cnt++).asInt(); int wait_time_base = args.get(cnt++).asInt(); int wait_time_rand = args.get(cnt++).asInt(); DBG("scheduling %d calls...\n", ncalls); time_t now; time(&now); actions_mut.lock(); for (int i=0;i0) wait_nsec+=(rand()%wait_time_rand); now+=wait_nsec; } actions_mut.unlock(); } void CallGenFactory::setTarget(const AmArg& args, AmArg& ret) { AmArg* old_args = target_args; target_args = new AmArg(args); if (old_args) delete old_args; DBG("target now set to %d calls\n", target_args->get(0).asInt()); } void CallGenFactory::callGenStats(const AmArg& args, AmArg& ret) { int target = 0; if (target_args) target = target_args->get(0).asInt(); string res = "CallGen statistics: \n " + int2str(active_calls.size()) + " active calls\n " + int2str(target) + " current target\n " + int2str(scheduled) +" scheduled\n "; calls_list_mut.lock(); res += int2str(past_calls.size()) + " total calls\n "; calls_list_mut.unlock(); ret.push(res.c_str()); } void CallGenFactory::reportCall(string callid, CallGenEvent ev, time_t connect_ts, time_t disconnect_ts) { calls_list_mut.lock(); // FIXME: not 100% correct: disconnect should be moving to past_calls if (ev == CGDestroy) { active_calls.erase(callid); CallInfo ci; ci.connect_ts = connect_ts; ci.disconnect_ts = disconnect_ts; ci.status = ev; past_calls[callid] = ci; } else { active_calls[callid].connect_ts = connect_ts; active_calls[callid].disconnect_ts = disconnect_ts; active_calls[callid].status = ev; } calls_list_mut.unlock(); } CallGenDialog::CallGenDialog(AmPromptCollection& prompts, int play_rand_digits, int call_time_base, int call_time_rand) : play_list(this), prompts(prompts), connect_ts(-1), disconnect_ts(-1), play_rand_digits(play_rand_digits), call_time_base(call_time_base), call_time_rand(call_time_rand), play_file(&CallGenFactory::play_file) { } CallGenDialog::~CallGenDialog() { prompts.cleanup((long)this); play_list.close(false); report(CGDestroy); } void CallGenDialog::onInvite(const AmSipRequest& r) { report(CGCreate); AmSession::onInvite(r); } void CallGenDialog::report(CallGenEvent what) { CallGenFactory::instance()->reportCall(getLocalTag(), what, connect_ts, disconnect_ts); } void CallGenDialog::onSessionStart(const AmSipReply& rep) { time(&connect_ts); report(CGConnect); // add some random digits for (int i=0;i 0) prompts.addToPlaylist("#", (long)this, play_list); play_file.loop.set(true); play_list.addToPlaylist(new AmPlaylistItem(&play_file, NULL)); // // todo (?): set loop... // prompts.addToPlaylist(PLAY_FILE, (long)this, play_list); setInOut(&play_list, &play_list); int call_timer = call_time_base; if (call_time_rand>0) call_timer+=rand()%call_time_rand; if (call_timer > 0) { AmDynInvokeFactory* UserTimer = AmPlugIn::instance()->getFactory4Di("user_timer"); if(!UserTimer){ ERROR("could not load user_timer from session_timer plug-in\n"); return; } AmDynInvoke* user_timer = UserTimer->getInstance(); if (!user_timer) { ERROR("could not get user_timer\n"); return; } DBG("setting call timer to %d seconds\n", call_timer); AmArg di_args,ret; di_args.push(CALL_TIMER); di_args.push(call_timer); di_args.push(getLocalTag().c_str()); user_timer->invoke("setTimer",di_args,ret); } } void CallGenDialog::process(AmEvent* event) { AmPluginEvent* plugin_event = dynamic_cast(event); if(plugin_event && plugin_event->name == "timer_timeout" && plugin_event->data.get(0).asInt() == CALL_TIMER) { time(&disconnect_ts); report(CGDisconnect); play_list.close(); setInOut(NULL,NULL); setStopped(); dlg.bye(); } else AmSession::process(event); } void CallGenDialog::onBye(const AmSipRequest& req) { time(&disconnect_ts); report(CGDisconnect); play_list.close(); setInOut(NULL,NULL); setStopped(); }