9a10,21 > * These features added by David C. Troy : > * - Per-queue holdtime calculation > * - Estimated holdtime announcement > * - Position announcement > * - Abandoned/completed call counters > * - Failout timer passed as optional app parameter > * - Optional monitoring of calls, started when call is answered > * > * Patch Version 1.04 2003-11-13 01 > * > * Added servicelevel statistic by Michiel Betel > * 27a40 > #include 68c81 < " Queue(queuename[|options[|URL][|announceoverride]]):\n" --- > " Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n" 81c94,96 < "it.\n"; --- > "it.\n" > " The timeout will cause the queue to fail out after a specified number of\n" > "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"; 130c145 < char announce[80]; /* Announcement to play */ --- > char announce[80]; /* Announcement to play for member when call is answered*/ 132a148,149 > int last_pos_said; /* Last position we told the user */ > time_t last_pos; /* Last time we told the user their position */ 133a151 > int queuetimeout; /* How many seconds before timing out of queue */ 152,153c170,171 < char announce[80]; /* Announcement to play */ < char context[80]; /* Announcement to play */ --- > char announce[80]; /* Announcement to play when call is answered */ > char context[80]; /* Context for this queue */ 155c173,186 < int announcetimeout; /* How often to announce their position */ --- > int announcefrequency; /* How often to announce their position */ > int announceholdtime; /* When to announce holdtime: 0 = never, -1 = every announcement, 1 = only once */ > int holdtime; /* Current avg holdtime for this queue, based on recursive boxcar filter */ > int callscompleted; /* Number of queue calls completed */ > int callsabandoned; /* Number of queue calls abandoned */ > int servicelevel; /* seconds setting for servicelevel*/ > int callscompletedinsl; /* Number of queue calls answererd with servicelevel*/ > char monfmt[8]; /* Format to use when recording calls */ > char sound_next[80]; /* Sound file: "Your call is now first in line" (def. queue-youarenext) */ > char sound_thereare[80]; /* Sound file: "There are currently" (def. queue-thereare) */ > char sound_calls[80]; /* Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/ > char sound_holdtime[80]; /* Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */ > char sound_minutes[80]; /* Sound file: "minutes." (def. queue-minutes) */ > char sound_thanks[80]; /* Sound file: "Thank you for your patience." (def. queue-thankyou) */ 287a319,416 > static int play_file(struct ast_channel *chan, char *filename) > { > int res; > > ast_stopstream(chan); > res = ast_streamfile(chan, filename, chan->language); > > if (!res) > res = ast_waitstream(chan, ""); > else > res = 0; > > if (res) { > ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name); > res = 0; > } > ast_stopstream(chan); > > return res; > } > > > static int say_position(struct queue_ent *qe) > { > int res = 0, avgholdmins; > time_t now; > > /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/ > time(&now); > if ( (now - qe->last_pos) < 15 ) > return -1; > > /* If either our position has changed, or we are over the freq timer, say position */ > if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) ) > return -1; > > ast_moh_stop(qe->chan); > /* Say we're next, if we are */ > if (qe->pos == 1) { > res += play_file(qe->chan, qe->parent->sound_next); > goto posout; > } else { > res += play_file(qe->chan, qe->parent->sound_thereare); > res += ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language); > res += play_file(qe->chan, qe->parent->sound_calls); > } > > /* Round hold time to nearest minute */ > avgholdmins = ( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60; > ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes\n", qe->parent->name, avgholdmins); > > /* If the hold time is >1 min, if it's enabled, and if it's not > supposed to be only once and we have already said it, say it */ > if (avgholdmins > 1 && (qe->parent->announceholdtime) && (!(qe->parent->announceholdtime==1 && qe->last_pos)) ) { > res += play_file(qe->chan, qe->parent->sound_holdtime); > res += ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language); > res += play_file(qe->chan, qe->parent->sound_minutes); > } > > posout: > /* Set our last_pos indicators */ > qe->last_pos = now; > qe->last_pos_said = qe->pos; > > ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", qe->chan->name, qe->parent->name, qe->pos); > res += play_file(qe->chan, qe->parent->sound_thanks); > ast_moh_start(qe->chan, qe->moh); > > return (res>0); > } > > > static void record_abandoned(struct queue_ent *qe) > { > ast_mutex_lock(&qe->parent->lock); > qe->parent->callsabandoned++; > ast_mutex_unlock(&qe->parent->lock); > } > > static void recalc_holdtime(struct queue_ent *qe) > { > int oldvalue, newvalue; > > /* Calculate holdtime using a recursive boxcar filter */ > /* Thanks to SRT for this contribution */ > /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */ > > newvalue = time(NULL) - qe->start; > > ast_mutex_lock(&qe->parent->lock); > if (newvalue <= qe->parent->servicelevel) > qe->parent->callscompletedinsl++; > oldvalue = qe->parent->holdtime; > qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2; > ast_mutex_unlock(&qe->parent->lock); > } > > 316a446 > /* Renumber the people after us in the queue based on a new count */ 610a741,743 > time_t now; > > /* This is the holding pen for callers 2 through maxlen */ 613a747 > 615c749,755 < if (qe->parent->head == qe) --- > if (qe == ch) > break; > > /* If we have timed out, break out */ > if ( qe->queuetimeout ) { > time(&now); > if ( (now - qe->start) >= qe->queuetimeout ) 616a757,762 > } > > /* Make a position announcement, if enabled */ > if (qe->parent->announcefrequency) > say_position(qe); > 639a786 > q->callscompleted++; 785a933 > record_abandoned(qe); 808a957 > recalc_holdtime(qe); 838a988,991 > /* Begin Monitoring */ > if (qe->parent->monfmt && *qe->parent->monfmt) { > ast_monitor_start( peer, qe->parent->monfmt, "", 1 ); > } 1098a1252 > char *queuetimeoutstr = NULL; 1104c1258 < ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n"); --- > ast_log(LOG_WARNING, "Queue requires an argument (queuename|[options]|[optionalurl]|[announceoverride]|[timeout])\n"); 1109a1264,1266 > /* Setup our queue entry */ > memset(&qe, 0, sizeof(qe)); > 1125a1283,1289 > queuetimeoutstr = strchr(announceoverride, '|'); > if (queuetimeoutstr) { > *queuetimeoutstr = '\0'; > queuetimeoutstr++; > qe.queuetimeout = atoi(queuetimeoutstr); > } else { > qe.queuetimeout = 0; 1130,1133c1294,1298 < printf("queue: %s, options: %s, url: %s, announce: %s\n", < queuename, options, url, announceoverride); < /* Setup our queue entry */ < memset(&qe, 0, sizeof(qe)); --- > } > > printf("queue: %s, options: %s, url: %s, announce: %s, timeout: %d\n", > queuename, options, url, announceoverride, qe.queuetimeout); > 1135a1301,1302 > qe.last_pos_said = 0; > qe.last_pos = 0; 1139a1307,1308 > /* This is the wait loop for callers 2 through maxlen */ > 1142a1312,1313 > /* Record this abandoned call */ > record_abandoned(&qe); 1155a1327,1342 > /* This is the wait loop for the head caller*/ > /* To exit, they may get their call answered; */ > /* they may dial a digit from the queue context; */ > /* or, they may may timeout. */ > > /* Leave if we have exceeded our timeout */ > if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) { > res = 1; > break; > } > > /* Make a position announcement, if enabled */ > if (qe.parent->announcefrequency) > say_position(&qe); > > /* Try calling all queue members for 'timeout' seconds */ 1158a1346,1353 > > /* Leave if we have exceeded our timeout */ > if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) { > res = 1; > break; > } > > /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */ 1160a1356,1357 > /* Record this abandoned call */ > record_abandoned(&qe); 1174a1372 > ast_stopstream(chan); 1235a1434,1439 > q->announcefrequency = 0; > q->announceholdtime = 0; > q->holdtime = 0; > q->callscompleted = 0; > q->callsabandoned = 0; > q->callscompletedinsl= 0; 1239a1444,1450 > strcpy(q->monfmt, ""); > strcpy(q->sound_next, "queue-youarenext"); > strcpy(q->sound_thereare, "queue-thereare"); > strcpy(q->sound_calls, "queue-callswaiting"); > strcpy(q->sound_holdtime, "queue-holdtime"); > strcpy(q->sound_minutes, "queue-minutes"); > strcpy(q->sound_thanks, "queue-thankyou"); 1283a1495,1512 > } else if (!strcasecmp(var->name, "monitor-format")) { > strncpy(q->monfmt, var->value, sizeof(q->monfmt) - 1); > } else if (!strcasecmp(var->name, "queue-youarenext")) { > strncpy(q->sound_next, var->value, sizeof(q->sound_next) - 1); > } else if (!strcasecmp(var->name, "queue-thereare")) { > strncpy(q->sound_thereare, var->value, sizeof(q->sound_thereare) - 1); > } else if (!strcasecmp(var->name, "queue-callswaiting")) { > strncpy(q->sound_calls, var->value, sizeof(q->sound_calls) - 1); > } else if (!strcasecmp(var->name, "queue-holdtime")) { > strncpy(q->sound_holdtime, var->value, sizeof(q->sound_holdtime) - 1); > } else if (!strcasecmp(var->name, "queue-minutes")) { > strncpy(q->sound_minutes, var->value, sizeof(q->sound_minutes) - 1); > } else if (!strcasecmp(var->name, "queue-thankyou")) { > strncpy(q->sound_thanks, var->value, sizeof(q->sound_thanks) - 1); > } else if (!strcasecmp(var->name, "announce-frequency")) { > q->announcefrequency = atoi(var->value); > } else if (!strcasecmp(var->name, "announce-holdtime")) { > q->announceholdtime = (!strcasecmp(var->value,"once")) ? 1 : ast_true(var->value); 1287a1517,1518 > } else if (!strcasecmp(var->name, "servicelevel")) { > q->servicelevel= atoi(var->value); 1344a1576 > float sl; 1362c1594,1598 < ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy\n", q->name, q->count, max, int2strat(q->strategy)); --- > sl = 0; > if(q->callscompleted > 0) > sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted); > ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%d secs holdtime), C:%d, A:%d, SL:%2.1f%% within %ds\n", > q->name, q->count, max, int2strat(q->strategy), q->holdtime, q->callscompleted, q->callsabandoned,sl,q->servicelevel);