mirror of https://github.com/sipwise/sems.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
465 lines
13 KiB
465 lines
13 KiB
/*
|
|
* $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 <stdlib.h>
|
|
#include <time.h>
|
|
|
|
#define APP_NAME "callgen"
|
|
#define PLAY_FILE "play_file"
|
|
|
|
#define CALL_TIMER 1
|
|
|
|
#include <vector>
|
|
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<AmArg> todo;
|
|
time_t now;
|
|
time(&now);
|
|
multimap<time_t, AmArg>::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<AmArg>::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<ruri_rand_digits;i++)
|
|
call_ruri+=int2str(rand()%10);
|
|
|
|
call_ruri+="@"+ruri_host;
|
|
|
|
AmArg* c_args = new AmArg(args);
|
|
|
|
DBG("placing new call to %s\n", call_ruri.c_str());
|
|
AmSession* s = AmUAC::dialout("callgen", // user
|
|
APP_NAME,
|
|
call_ruri,
|
|
"<" + from + ">", 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;i<ncalls;i++) {
|
|
createCall(args);
|
|
|
|
int wait_nsec = wait_time_base;
|
|
if (wait_time_rand>0)
|
|
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;i<ncalls;i++) {
|
|
actions.insert(std::make_pair(now, args));
|
|
|
|
int wait_nsec = wait_time_base;
|
|
if (wait_time_rand>0)
|
|
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<play_rand_digits;i++)
|
|
prompts.addToPlaylist(int2str(rand()%10), (long)this, play_list);
|
|
if (play_rand_digits > 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<AmPluginEvent*>(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();
|
|
}
|