· KLDP.org · KLDP.net · KLDP Wiki · KLDP BBS ·
Asterisk Source/App Queue

queue_exec


/*!\brief The starting point for all queue calls
 *
 * The process involved here is to 
 * 1. Parse the options specified in the call to Queue()
 * 2. Join the queue
 * 3. Wait in a loop until it is our turn to try calling a queue member
 * 4. Attempt to call a queue member
 * 5. If 4. did not result in a bridged call, then check for between
 *    call options such as periodic announcements etc.
 * 6. Try 4 again uless some condition (such as an expiration time) causes us to 
 *    exit the queue.
 */

* static int queue_exec(struct ast_channel *chan, void *data)
  • Parse options and set qe.expire, ringing
  • Get variable and set prio, max_penalty
  • set queue_ent structure qe
  • if (!join_queue(args.queuename, &qe, &reason)) {
    • int makeannouncement = 0;
    • check_turns:
    • res = wait_our_turn(&qe, ringing, &reason);
    • if (res)
      • goto stop;
    • for (;;) {
      • IFabandoned(QUEUE_TIMEOUT)
      • if (makeannouncement) {
        • if (qe.parent->announcefrequency && !ringing)
          • if ((res = say_position(&qe)))
            • goto stop;
      • }
      • makeannouncement = 1;
      • IFabandoned(QUEUE_TIMEOUT)
      • if (qe.parent->periodicannouncefrequency && !ringing)
        • if ((res = say_periodic_announcement(&qe)))
          • goto stop;
      • IFabandoned(QUEUE_TIMEOUT)
      • res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
      • if (res)
        • goto stop;
      • stat = get_member_status(qe.parent, qe.max_penalty);
      • if (noption && tries >= qe.parent->membercount) {
        • abandoned(QUEUE_TIMEOUT)
      • }
      • if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
        • abandoned(QUEUE_LEAVEEMPTY);
      • }
      • if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
        • abandoned(QUEUE_LEAVEUNAVAIL);
      • }
      • IFabandoned(QUEUE_TIMEOUT)
      • update_realtime_members(qe.parent);
      • res = wait_a_bit(&qe);
      • if (res)
        • goto stop;
      • if (!is_our_turn(&qe)) {
        • goto check_turns;
      • }
    • }
    • stop:
    • if (res) {
      • if (res < 0) {
        • if (!qe.handled) {
          • record_abandoned(&qe);
        • }
        • res = -1;
      • } else if (qe.valid_digits) {
      • }
    • }
    • if (res >= 0) {
      • res = 0;
      • ast_stopstream(chan);
    • }
    • leave_queue(&qe);
    • if (reason != QUEUE_UNKNOWN)
      • set_queue_result(chan, reason);
  • } else {
  • }
  • return res;

* QUEUESTATUS
  • TIMEOUT, FULL, JOINEMPTY, LEAVEEMPTY, JOINUNAVAIL, LEAVEUNAVAIL

* abandoned(QUEUESTATUS)
  • record_abandoned(&qe);
  • reason = QUEUESTATUS;
  • res = 0;
  • break;

* IFabandoned(QUEUE_TIMEOUT)
  • if (qe.expire && (time(NULL) >= qe.expire)) {
    • abandoned(QUEUE_TIMEOUT)
  • }

queue_exec 1


* queue_exec
  • queue ¿¡ join À» ¸øÇÑ °æ¿ì
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(queuename);
                AST_APP_ARG(options);
                AST_APP_ARG(url);
                AST_APP_ARG(announceoverride);
                AST_APP_ARG(queuetimeoutstr);
                AST_APP_ARG(agi);
        );
  • if (ast_strlen_zero(data)) {
    • return -1;
  • }
  • parse = ast_strdupa(data);
  • AST_STANDARD_APP_ARGS(args, parse);
  • qe.start = time(NULL);
  • if (!ast_strlen_zero(args.queuetimeoutstr))
    • qe.expire = qe.start + atoi(args.queuetimeoutstr);
  • else
    • qe.expire = 0;
  • user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
  • if (user_priority) {
    • if (sscanf(user_priority, "%d", &prio) == 1) {
    • } else {
      • prio = 0;
    • }
  • } else {
    • prio = 0;
  • }
  • if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
    • if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
    • } else {
      • max_penalty = 0;
    • }
  • } else {
    • max_penalty = 0;
  • }
  • if (args.options && (strchr(args.options, 'r')))
    • ringing = 1;
        qe.chan = chan;
        qe.prio = prio;
        qe.max_penalty = max_penalty;
        qe.last_pos_said = 0;
        qe.last_pos = 0;
        qe.last_periodic_announce_time = time(NULL);
        qe.last_periodic_announce_sound = 0;
        qe.valid_digits = 0;
  • if (!join_queue(args.queuename, &qe, &reason)) {
  • } else {
    • set_queue_result(chan, reason);
    • res = 0;
  • }
  • return res;

* join_queue return -1
  • int res = -1;
  • if (!(q = load_realtime_queue(queuename)))
    • return res;
  • stat = get_member_status(q, qe->max_penalty);
  • if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
    • *reason = QUEUE_JOINEMPTY;
  • else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
    • *reason = QUEUE_JOINUNAVAIL;
  • else if (q->maxlen && (q->count >= q->maxlen))
    • *reason = QUEUE_FULL;
  • else {
  • }
  • return res;

  • load_realtime_queue(queuename) error;
  • stat = get_member_status(q, qe->max_penalty);
  • (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
    • joinempty=true, yes, t, y, on, 1 , strict ¸é, QUEUE_NO_MEMBERS ÀÎ °æ¿ì join ¸øÇÔ
  • ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
    • joinempty °¡ strict ¸é QUEUE_NO_REACHABLE_MEMBERS À϶§µµ join ¸øÇÔ. ( || ´Â ÇÊ¿ä ¾ø´Â µíÇÔ)
  • (q->maxlen && (q->count >= q->maxlen))
    • queue °¡ ´Ù Âù °æ¿ì

* join_queue return 0
  • queue_ent list ¿¡ Ãß°¡

queue_exec 2


* queue_exec
  • queue ¿¡ join ÇÏ°í, wait_our_turn ¿¡¼­ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÑ °æ¿ì
  • ....
  • if (!join_queue(args.queuename, &qe, &reason)) {
    • int makeannouncement = 0;
    • res = wait_our_turn(&qe, ringing, &reason);
    • if (res)
      • goto stop;
    • for (;;) {
    • }
    • stop:
    • if (res) {
      • if (res < 0) {
        • if (!qe.handled) {
          • record_abandoned(&qe);
          • wait_our_turn ¼öÇàÁß ast_waitfordigit ¿¡¼­ -1 À» return ÇÑ °æ¿ì
        • }
        • res = -1;
      • } else if (qe.valid_digits) {
        • wait_our_turn ¼öÇàÁß say_position, say_periodic_announcement, ast_waitfordigit ¿¡¼­ the ascii value of the digit À» ¹ÞÀº °æ¿ì
      • }
    • }
    • if (res >= 0) {
      • wait_our_turn ¼öÇàÁß say_position, say_periodic_announcement, ast_waitfordigit ¿¡¼­ the ascii value of the digit À» ¹ÞÀº °æ¿ì
      • res = 0;
      • ast_stopstream(chan);
    • }
    • leave_queue(&qe);
    • if (reason != QUEUE_UNKNOWN)
      • set_queue_result(chan, reason);
  • } else {
  • }
  • return res;

* wait_our_turn ÀÌ 0 À» return ÇÏ´Â °æ¿ì
  • our_turn ÀÎ °æ¿ì
  • QUEUE_TIMEOUT
  • QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL
    • leavequeue(qe) ¸¦ ÇÔ.

* wait_our_turn ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÏ´Â °æ¿ì
  • if (qe->parent->announcefrequency && !ringing && (res = say_position(qe)))
    • the ascii value of the digit
  • if (qe->parent->periodicannouncefrequency && !ringing && (res = say_periodic_announcement(qe)))
    • the ascii value of the digit
  • if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
    • if (res > 0 && !valid_exit(qe, res))
    • else
      • the ascii value of the digit or -1
    • }

queue_exec 3


* queue_exec
  • queue ¿¡ join ÇÏ°í, wait_our_turn ¿¡¼­ 0 À» return
  • ....
  • if (!join_queue(args.queuename, &qe, &reason)) {
    • int makeannouncement = 0;
    • check_turns:
    • res = wait_our_turn(&qe, ringing, &reason);
    • if (res)
      • goto stop;
    • for (;;) {
    • wait_our_turn ¿¡¼­ our_turn ÀÎ °æ¿ì
    • wait_our_turn Áß QUEUE_TIMEOUT ÀÎ °æ¿ì
    • wait_our_turn Áß QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL ÀÎ °æ¿ì: leavequeue(qe) ¸¦ ÇÏ¿´À½.
      • IFabandoned(QUEUE_TIMEOUT)
      • if (makeannouncement) {
        • if (qe.parent->announcefrequency && !ringing)
          • if ((res = say_position(&qe)))
            • goto stop;
      • }
      • makeannouncement = 1;
      • IFabandoned(QUEUE_TIMEOUT)
      • if (qe.parent->periodicannouncefrequency && !ringing)
        • if ((res = say_periodic_announcement(&qe)))
          • goto stop;
      • IFabandoned(QUEUE_TIMEOUT)
      • res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
      • if (res)
        • goto stop;
      • stat = get_member_status(qe.parent, qe.max_penalty);
      • if (noption && tries >= qe.parent->membercount) {
        • abandoned(QUEUE_TIMEOUT)
      • }
      • if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
        • abandoned(QUEUE_LEAVEEMPTY);
      • }
      • if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
        • abandoned(QUEUE_LEAVEUNAVAIL);
      • }
      • IFabandoned(QUEUE_TIMEOUT)
      • update_realtime_members(qe.parent);
      • res = wait_a_bit(&qe);
      • if (res)
        • goto stop;
      • if (!is_our_turn(&qe)) {
        • our_turn ÀÌ ¾ÆÁ÷ ¾È µÇ¾úÀ¸¸é
        • goto check_turns;
      • }
      • our turn À̸é for loop
    • }
    • stop:
    • if (res) {
      • if (res < 0) {
        • wait_a_bit ¿¡¼­ ¿À·ù°¡ ¹ß»ýÇÑ °æ¿ì
        • if (!qe.handled) {
          • record_abandoned(&qe);
        • }
        • res = -1;
      • } else if (qe.valid_digits) {
        • for loop Áß say_position, say_periodic_announcement ¿¡¼­ the ascii value of the digit À» ¹ÞÀº °æ¿ì
        • for loop Áß wait_a_bit ¿¡¼­ the ascii value of the digit À» ¹ÞÀº °æ¿ì
      • }
    • }
    • if (res >= 0) {
      • res °¡ 0 ÀÎ °æ¿ì
        • IFabandoned(QUEUE_TIMEOUT), abandoned(QUEUE_TIMEOUT), abandoned(QUEUE_LEAVEEMPTY), abandoned(QUEUE_LEAVEUNAVAIL)
      • for loop Áß say_position, say_periodic_announcement ¿¡¼­ the ascii value of the digit À» ¹ÞÀº °æ¿ì
      • for loop Áß wait_a_bit ¿¡¼­ the ascii value of the digit À» ¹ÞÀº °æ¿ì
      • res = 0;
      • ast_stopstream(chan);
    • }
    • leave_queue(&qe);
    • if (reason != QUEUE_UNKNOWN)
      • set_queue_result(chan, reason);
  • } else {
  • }
  • return res;

* static int wait_a_bit(struct queue_ent *qe)
  • int retrywait = qe->parent->retry * 1000;
  • int res = ast_waitfordigit(qe->chan, retrywait);
  • if (res > 0 && !valid_exit(qe, res))
    • valid ÇÏÁö ¾ÊÀº key °¡ press µÈ °æ¿ì
    • res = 0;
  • return res;
  • retry ½Ã°£ÀÌ ÀԷ¾øÀÌ Áö³­ °æ¿ì: 0
  • valid key °¡ ÀÔ·ÂµÈ °æ¿ì: the ascii value of the digit
  • ¿À·ù°¡ ¹ß»ýÇÑ °æ¿ì: -1

queue_exec 4


* queue_exec
  • try_calling ¼öÇà: return 0 °ú return nonezero ÀÎ °æ¿ì
  • queue ¿¡ join ÇÏ°í, wait_for_answer ¿¡¼­ 0 À» return
  • ....
  • if (!join_queue(args.queuename, &qe, &reason)) {
    • int makeannouncement = 0;
    • check_turns:
    • res = wait_our_turn(&qe, ringing, &reason);
    • if (res)
      • goto stop;
    • for (;;) {
      • IFabandoned(QUEUE_TIMEOUT)
      • if (makeannouncement) {
        • if (qe.parent->announcefrequency && !ringing)
          • if ((res = say_position(&qe)))
            • goto stop;
      • }
      • makeannouncement = 1;
      • IFabandoned(QUEUE_TIMEOUT)
      • if (qe.parent->periodicannouncefrequency && !ringing)
        • if ((res = say_periodic_announcement(&qe)))
          • goto stop;
      • IFabandoned(QUEUE_TIMEOUT)
      • res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
      • if (res)
        • goto stop;
      • stat = get_member_status(qe.parent, qe.max_penalty);
      • if (noption && tries >= qe.parent->membercount) {
        • abandoned(QUEUE_TIMEOUT)
      • }
      • if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
        • abandoned(QUEUE_LEAVEEMPTY);
      • }
      • if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
        • abandoned(QUEUE_LEAVEUNAVAIL);
      • }
      • IFabandoned(QUEUE_TIMEOUT)
      • update_realtime_members(qe.parent);
      • res = wait_a_bit(&qe);
      • if (res)
        • goto stop;
      • if (!is_our_turn(&qe)) {
        • goto check_turns;
      • }
    • }
    • stop:
    • if (res) {
      • if (res < 0) {
        • if (!qe.handled) {
          • record_abandoned(&qe);
        • }
        • res = -1;
      • } else if (qe.valid_digits) {
      • }
    • }
    • if (res >= 0) {
      • res = 0;
      • ast_stopstream(chan);
    • }
    • leave_queue(&qe);
    • if (reason != QUEUE_UNKNOWN)
      • set_queue_result(chan, reason);
  • } else {
  • }
  • return res;

queue CDR


* mysql À» »ç¿ëÇϱâ À§ÇÏ¿©
  • cdr ÀÇ ¹æ½ÄÀ» µû¸£´Â °æ¿ì
    • È®À强µµ ÁÁ°í, µðÀÚÀÎ Ãø¸é¿¡¼­µµ ÁÁ´Ù.
    • queue ¸ðµâ¿¡ ´ëÇÑ dependency ¸¦ Á¡°ËÇÒ ¹æ¹ýÀÌ Àִ°¡?
    • ½ÇÁ¦·Î ¼ÕÀ» º¸¾Æ¾ß ÇÒ °ÍÀÌ ¸¹À¸¹Ç·Î, ¿À·ù°¡ ¹ß»ýÇÒ È®·üÀÌ ³ô´Ù.
  • logger.c ¸¦ ¼öÁ¤ÇÏ´Â ¹æ¹ý
    • °£´ÜÇÑ ¹æ¹ýÀ̱â´Â Çϳª, È®À强ÀÌ ¾øÀ½
    • µðÀÚÀÎ Ãø¸é¿¡¼­ ¹®Á¦°¡ ÀÖ´Ù.
    • ast_queue_log »ç¿ë

* cdr ÀÇ ¹æ¹ýÀ» µû¸¦ °æ¿ì
  • main µð·ºÅ丮 ¹ØÀÇ cdr.c ÀÇ ast_cdr_register ¸¦ ¾îµð¿¡ ³ÖÀ» °ÍÀΰ¡?
    • apps/app_queue.c
    • main/logger.c
    • º°µµÀÇ ÆÄÀÏÀ» ¸¸µç´Ù

* cdr.c ¿Í cdr.h ¿¡ ÇÊ¿äÇÑ µ¥ÀÌŸ¿Í ÇÔ¼ö ¼³Á¤
  • À̸§Àº qcdr ·Î ÇÔ.
  • queue º°·Î db table À» ¼³Á¤ÇÏ´Â °ÍÀÌ °¡´ÉÇÑ°¡?
  • queue.conf ¿¡ qcdr »ç¿ë
  • qe.parent->qcdr ·Î È®ÀÎ
  • queue ¿¡ enter ÇÑ ÀÌÈÄ¿¡ »ç¿ë°¡´É

* qcdr structure
status
qe.start
join_queue
  queue_exec Áß join_queue ¹Ù·Î ¹Ø¿¡¼­ ¼³Á¤ 
wait_our_turn
  wait_our_turn ¹Ù·Î ¾Õ¿¡¼­ setting
is_our_turn
  res=1 ÀÎ °æ¿ì¿¡ setting
try_calling
  try_calling ÀÇ time(&now) »ç¿ë
start_call
really_call (¿©·¯°³ÀÓ)
wait_for_answer
  wait_for_answer ÀÇ starttime »ç¿ë 
callcompleted
leave_queue
connected
  callstart talk ½ÃÀÛ
talk end
  TRANSFER
  COMPLETECALLER
  COMPLETEAGENT

* AgentCalled, AgentConnect, AgentComplete

agent ¿Í ÅëÈ­°¡ µÇ°í Á¤»óÁ¾·áÀÎ °æ¿ì

* case 1:
  • int ringing=0;
  • option ¿¡ r ÀÌ ÀÖÀ¸¸é
    • ringing = 1;
  • join_queue °¡ 0 À» return
    • int makeannouncement = 0;
    • wait_our_turn ÀÌ 0 À» return
    • for (;;) {
      • expire °¡ ¼³Á¤µÇ¾î ÀÖÁö ¾Ê°Å³ª, ¾ÆÁ÷ expire time ÀÌ µÇÁö ¾ÊÀº °æ¿ì
      • makeannouncement °¡ ¼³Á¤µÇ¾î ÀÖÀ¸¸é,
        • announcefrequency °¡ ¼³Á¤µÇ¾î ÀÖ°í, ringing ÀÌ 0 À̸é,
          • say_position(&qe) ÀÌ 0 À» return
      • makeannouncement = 1;
      • expire °¡ ¼³Á¤µÇ¾î ÀÖÁö ¾Ê°Å³ª, ¾ÆÁ÷ expire time ÀÌ µÇÁö ¾ÊÀº °æ¿ì
      • periodicannouncefrequency °¡ ¼³Á¤µÇ¾î ÀÖ°í, ringing ÀÌ 0 À̸é,
        • say_periodic_announcement(&qe) ÀÌ 0 À» return
      • try_calling ÀÌ 0 º¸´Ù Å« °ªÀ» return

* case 2:
  • int ringing=0;
  • option ¿¡ r ÀÌ ÀÖÀ¸¸é
    • ringing = 1;
  • join_queue °¡ 0 À» return
    • int makeannouncement = 0;
    • check_turns:
    • wait_our_turn ÀÌ 0 À» return
    • for (;;) {
      • expire °¡ ¼³Á¤µÇ¾î ÀÖÁö ¾Ê°Å³ª, ¾ÆÁ÷ expire time ÀÌ µÇÁö ¾ÊÀº °æ¿ì
      • makeannouncement °¡ ¼³Á¤µÇ¾î ÀÖÀ¸¸é,
        • announcefrequency °¡ ¼³Á¤µÇ¾î ÀÖ°í, ringing ÀÌ 0 À̸é,
          • say_position(&qe) ÀÌ 0 À» return
      • makeannouncement = 1;
      • expire °¡ ¼³Á¤µÇ¾î ÀÖÁö ¾Ê°Å³ª, ¾ÆÁ÷ expire time ÀÌ µÇÁö ¾ÊÀº °æ¿ì
      • periodicannouncefrequency °¡ ¼³Á¤µÇ¾î ÀÖ°í, ringing ÀÌ 0 À̸é,
        • say_periodic_announcement(&qe) ÀÌ 0 À» return
      • try_calling ÀÌ 0 À» return
      • noption ÀÌ ¼³Á¤µÇ¾î ÀÖÁö ¾Ê°Å³ª, tries °¡ membercount º¸´Ù ÀÛÀº °æ¿ì
      • leavewhenempty ¼³Á¤µÇ¾î ÀÖÁö ¾Ê°Å³ª, stat °¡ QUEUE_NO_MEMBERS °¡ ¾Æ´Ñ °æ¿ì
      • leavewhenempty °¡ QUEUE_EMPTY_STRICT °¡ ¾Æ´Ï°Å³ª, stat ÀÌ QUEUE_NO_REACHABLE_MEMBERS ÀÌ ¾Æ´Ñ °æ¿ì
      • expire °¡ ¼³Á¤µÇ¾î ÀÖÁö ¾Ê°Å³ª, ¾ÆÁ÷ expire time ÀÌ µÇÁö ¾ÊÀº °æ¿ì
      • wait_a_bit(&qe) ÀÌ 0 À» return
      • is_our_turn(&qe) ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return
        • for loop
      • is_our_turn(&qe) ÀÌ 0 À» return
        • check_turns ºÎÅÍ ´Ù½Ã ½ÃÀÛ

* join_queue °¡ 0 À» return ÇÏ´Â °æ¿ì
  • load_realtime_queue(queuename) ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return
  • joinempty °¡ 0 ÀÌ ¾Æ´Ï°Å³ª, stat ÀÌ QUEUE_NO_MEMBERS °¡ ¾Æ´Ï°í,
    • joinempty °¡ QUEUE_EMPTY_STRICT °¡ ¾Æ´Ï°Å³ª, stat °¡ QUEUE_NO_REACHABLE_MEMBERS µµ ¾Æ´Ï°í, QUEUE_NO_MEMBERS µµ ¾Æ´Ï°í
      • maxlen °¡ 0 À̰ųª, count °¡ maxlen º¸´Ù ÀÛÀ¸¸é

* try_calling ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return
  • expire °¡ ¼³Á¤µÇ¾î ÀÖÁö ¾Ê°Å³ª, ¾ÆÁ÷ expire time ÀÌ µÇÁö ¾ÊÀº °æ¿ì
  • outgoing »ý¼º
  • ring_one(qe, outgoing, &numbusies);
  • wait_for_answer °¡ peer ¸¦ return
  • ast_channel_make_compatible ÀÌ 0 À̳ª 0 º¸´Ù Å« °ªÀ» return
  • bridge = ast_bridge_call(qe->chan,peer, &bridge_config) ¼öÇà

Åë°è°ü·Ã


* asterisk ¿Í ÅëÈ­°¡ ¾ÈµÇ´Â °æ¿ì

* asterisk ¿Í ÅëÈ­°¡ µÈ °æ¿ì
  • queue ¿¡ ¸ø µé¾î°£ °æ¿ì
  • queue ¿¡ µé¾î°£ °æ¿ì
    • wait_our_turn ÀÌ 0ÀÌ ¾Æ´Ñ °ªÀ» retun ÇÑ °æ¿ì
    • wait_our_turn ÀÌ 0À» return ÇÑ °æ¿ì
      • for loop
        • try_calling Àü
          • break ÇÏ´Â °æ¿ì
            • time out
          • goto stop ÀÎ °æ¿ì
            • say_position ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÑ °æ¿ì
            • say_periodic_announcement °¡ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÑ °æ¿ì
        • try_calling
        • try_calling ÈÄ
          • goto stop ÀÎ °æ¿ì
            • try_calling ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÑ °æ¿ì
          • break ÇÏ´Â °æ¿ì
            • noption && tries >= qe.parent->membercount
            • °¡´ÉÇÑ member °¡ ¾ø´Â °æ¿ì
            • time out
        • wait_a_bit
          • goto stop ÀÎ °æ¿ì
            • wait_a_bit ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÑ °æ¿ì
          • goto check_turns ÀÎ °æ¿ì
            • is_our_turn ÀÌ 0 À» return ÇÑ °æ¿ì

* queue ¿¡ ¸ø µé¾î°£ °æ¿ì
  • ÀÌÀ¯
    • realtime queue ¸¦ load ÇÏÁö ¸øÇÑ °æ¿ì¿¡
      • °¡´ÉÇÑ member °¡ ¾ø´Â °æ¿ì
      • queue °¡ full ÀÎ °æ¿ì
  • °á°ú
    • log ¸¦ ³²±ä´Ù
    • dialplan ÀÇ variable ¼³Á¤
    • Queue Á¤»óÁ¾·á

* wait_our_turn ÀÌ 0À» return ÇÑ °æ¿ì
  • ÀÌÀ¯
    • autofill ÀÌ°í ù¹ø°À̰ųª, ÇöÀç ¼ø¼­±îÁö ÇÒ´çÇÒ member ¼ö°¡ ÃæºÐÇÑ °æ¿ì
    • time out
    • ÇÒ´çÇÒ member °¡ ¾ø´Â °æ¿ì (leave_queue )
  • °á°ú
    • ´ÙÀ½ ÇÁ·Î±×·¥ ¼öÇà

* wait_our_turn ÀÌ 0ÀÌ ¾Æ´Ñ °ªÀ» retun ÇÑ °æ¿ì
  • ÀÌÀ¯
    • say_position ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÑ °æ¿ì
    • say_periodic_announcement ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÑ °æ¿ì
    • ast_waitfordigit °¡ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÏ°í, return °ªÀÌ À½¼ö°Å³ª valid_exit ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÑ °æ¿ì
  • °á°ú
    • res °¡ À½¼ö¸é log ABANDON
    • res °¡ ¾ç¼ö°í valid_digits ¸é log EXITWITHKEY
    • res °¡ ¾ç¼ö¸é res ¸¦ 0 À¸·Î ¼³Á¤
    • leave_queue(&qe)
    • QUEUE_UNKNOWN ÀÌ ¾Æ´Ï¸é dialplan ÀÇ variable ¼³Á¤

* try_calling À» ¼öÇàÇÏ´Â °æ¿ì
  • join_queue return 0
  • wait_our_turn return 0
  • for loop
    • not time out
    • from second loop: say_position(&qe) return 0
    • say_periodic_announcement(&qe) return 0
    • try_calling return 0
    • not noption && tries >= qe.parent->membercount
    • member available
    • wait_a_bit return 0
    • is_out_turn return non zero

* try_calling
  • outgoing Àº callattempt ÀÇ list ÀÓ
* outgoing À» ¸¸µå´Â °úÁ¤±îÁö
  • try_calling ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÏ´Â °æ¿ì
    • ¾øÀ½.
  • try_calling ÀÌ 0 À» return ÇÏ´Â °æ¿ì
    • time out
    • callattempt ¸¦ À§ÇÑ memory alloc À» ½ÇÆÐÇÑ °æ¿ì
    • channel ¿¡ °°Àº type ÀÇ datastore °¡ ¾ø°í
      • datastore ¸¦ À§ÇÑ memory alloc À» ½ÇÆÐÇÑ °æ¿ì
      • dialed_interfaces ¸¦ À§ÇÑ memory alloc À» ½ÇÆÐÇÑ °æ¿ì
    • member ÀÇ interface °¡ Local ÀÌ ¾Æ´Ï°í
      • di ¸¦ À§ÇÑ memory alloc À» ½ÇÆÐÇÑ °æ¿ì
* outgoing À» ¸¸µçÈÄ
  • wait_for_answer ÀÇ return °ªÀÌ 0 ÀÎ °æ¿ì
    • try_calling ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÏ´Â °æ¿ì
      • to °¡ 0 ÀÌ ¾Æ´Ï¸é return -1
      • to °¡ 0 À̸é return digit
    • try_calling ÀÌ 0 À» return ÇÏ´Â °æ¿ì
      • ¾øÀ½
  • wait_for_answer ÀÇ return °ªÀÌ 0 ÀÌ ¾Æ´Ñ °æ¿ì
    • announce, reportholdtime, memberdelay Áß Çϳª¶óµµ 0 ÀÌ ¾Æ´Ñ °æ¿ì
    • ast_channel_make_compatible ÀÌ À½¼ö¸¦ return ÇÑ °æ¿ì
    • monfmt °¡ 0 ÀÌ ¾Æ´Ñ °æ¿ì
    • leave_queue(qe);
    • bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
    • attended_transfer_occurred(qe->chan) ÀÌ 0 À» return ÇÑ °æ¿ì
    • res = bridge ? bridge : 1;

* announce, reportholdtime, memberdelay Áß Çϳª¶óµµ 0 ÀÌ ¾Æ´Ñ °æ¿ì
  • try_calling ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÏ´Â °æ¿ì
    • peer->_softhangup ÀÌ 0 ÀÌ°í res2 °¡ 0 ÀÌ ¾Æ´Ï¸é return -1
  • try_calling ÀÌ 0 À» return ÇÏ´Â °æ¿ì
    • peer->_softhangup ÀÌ 0 ÀÌ ¾Æ´Ñ °æ¿ì

* ast_channel_make_compatible ÀÌ À½¼ö¸¦ return ÇÑ °æ¿ì
  • ry_calling ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÏ´Â °æ¿ì
    • return -1
  • try_calling ÀÌ 0 À» return ÇÏ´Â °æ¿ì
    • ¾øÀ½.

* monfmt °¡ 0 ÀÌ ¾Æ´Ñ °æ¿ì
  • try_calling ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÏ´Â °æ¿ì
    • ¾øÀ½
  • try_calling ÀÌ 0 À» return ÇÏ´Â °æ¿ì
    • ¾øÀ½.

* attended_transfer_occurred(qe->chan) ÀÌ 0 À» return ÇÑ °æ¿ì
  • try_calling ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÏ´Â °æ¿ì
    • ¾øÀ½
  • try_calling ÀÌ 0 À» return ÇÏ´Â °æ¿ì
    • ¾øÀ½.

time °ü·Ã

* static int say_position(struct queue_ent *qe)
  • time(&now);
* static int say_periodic_announcement(struct queue_ent *qe)
  • time(&now);
* static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
  • starttime = (long) time(NULL);
  • endtime = (long)time(NULL);
* static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
  • time(&member->lastcall);
* static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi)
  • time_t now = time(NULL);
  • time(&now);
  • time(&callstart);
* static int queue_exec(struct ast_channel *chan, void *data)
  • qe.start = time(NULL);
  • qe.last_periodic_announce_time = time(NULL);
* static int __queues_show(struct mansession *s, int manager, int fd, int argc, char **argv)
  • time(&now);
* static int manager_queues_status(struct mansession *s, const struct message *m)
  • time(&now);

* ABANDON(position|origposition|waittime)
  • The caller abandoned
  • try_calling
    • wait_for_answer ÀÌ 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÑ °æ¿ì
      • if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
        • peer->_softhangup ÀÌ 0 ÀÌ°í res2 °¡ 0 ÀÌ ¾Æ´Ï¸é
          • ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
      • }

  • queue_exec
    • if (!join_queue(args.queuename, &qe, &reason)) {
      • for (;;) {
      • stop:
        • if (res) {
          • if (res < 0) {
            • if (!qe.handled) {
              • ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
            • }
          • } else if (qe.valid_digits) {
          • }
        • }
      • }
    • }

* AGENTLOGOFF(channel|logintime)

* AGENTCALLBACKLOGOFF(exten@context|logintime|reason)

* COMPLETEAGENT(holdtime|calltime|origposition)
  • The caller was connected to an agent, and the call was terminated normally by the agent.

  • try_calling
    • wait_for_answer ÀÇ return °ªÀÌ 0 ÀÌ ¾Æ´Ñ °æ¿ì
      • announce, reportholdtime, memberdelay Áß Çϳª¶óµµ 0 ÀÌ ¾Æ´Ñ °æ¿ì
      • ast_channel_make_compatible ÀÌ À½¼ö¸¦ return ÇÑ °æ¿ì
      • monfmt °¡ 0 ÀÌ ¾Æ´Ñ °æ¿ì
      • leave_queue(qe);
      • bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
      • attended_transfer_occurred(qe->chan) ÀÌ 0 À» return ÇÑ °æ¿ì
        • if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
        • } else if (qe->chan->_softhangup) {
        • } else {
          • ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d", (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
        • }

* COMPLETECALLER(holdtime|calltime|origposition)
  • The caller was connected to an agent, and the call was terminated normally by the caller.

  • try_calling
    • wait_for_answer ÀÇ return °ªÀÌ 0 ÀÌ ¾Æ´Ñ °æ¿ì
      • announce, reportholdtime, memberdelay Áß Çϳª¶óµµ 0 ÀÌ ¾Æ´Ñ °æ¿ì
      • ast_channel_make_compatible ÀÌ À½¼ö¸¦ return ÇÑ °æ¿ì
      • monfmt °¡ 0 ÀÌ ¾Æ´Ñ °æ¿ì
      • leave_queue(qe);
      • bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
      • attended_transfer_occurred(qe->chan) ÀÌ 0 À» return ÇÑ °æ¿ì
        • if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
        • } else if (qe->chan->_softhangup) {
          • ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d", (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
        • } else {
        • }

* CONNECT(holdtime|bridgedchanneluniqueid)
  • The caller was connected to an agent.

  • try_calling
    • wait_for_answer ÀÇ return °ªÀÌ 0 ÀÌ ¾Æ´Ñ °æ¿ì
      • announce, reportholdtime, memberdelay Áß Çϳª¶óµµ 0 ÀÌ ¾Æ´Ñ °æ¿ì
      • ast_channel_make_compatible ÀÌ À½¼ö¸¦ return ÇÑ °æ¿ì
      • monfmt °¡ 0 ÀÌ ¾Æ´Ñ °æ¿ì
      • leave_queue(qe);
      • ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);

* EXITEMPTY(position|origposition|waittime)
  • The caller was exited from the queue forcefully because the queue had no reachable members and it's configured to do that to callers when there are no reachable members.

  • wait_our_turn
    • for (;;) {
      • stat = get_member_status(qe->parent, qe->max_penalty);
      • member °¡ ¾øÀ» ¶§
        • ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
    • }

  • queue_exec
    • try_calling ÀÌ 0 À» return ÇÑ °æ¿ì
    • stat = get_member_status(qe.parent, qe.max_penalty);
    • member °¡ ¾øÀ» ¶§
      • ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));

* RINGNOANSWER(ringtime)
  • After trying for ringtime ms to connect to the available queue member, the attempt ended without the member picking up the call.

  • rna
    • ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);

  • wait_for_answer
  • while (*to && !peer) {
    • for (retry = 0; retry < 2; retry++) {
    • }
    • if (pos == 1 /* not found */) {
    • }
    • winner = ast_waitfor_n(watchers, pos, to);
    • for (o = start; o; o = o->call_next) {
      • if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
      • } else if (o->chan && (o->chan == winner)) {
        • if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
        • } else if (!ast_strlen_zero(o->chan->call_forward)) {
        • }
        • f = ast_read(winner);
        • if (f) {
          • if (f->frametype == AST_FRAME_CONTROL) {
            • switch (f->subclass) {
            • case AST_CONTROL_BUSY:
              • endtime = (long)time(NULL);
              • endtime -= starttime;
              • rna(endtime * 1000, qe, on, membername, 0);
            • case AST_CONTROL_CONGESTION:
              • endtime = (long)time(NULL);
              • endtime -= starttime;
              • rna(endtime * 1000, qe, on, membername, 0);
          • }
        • } else {
          • endtime = (long) time(NULL) - starttime;
          • rna(endtime * 1000, qe, on, membername, 1);
        • }
      • }
    • }
    • if (winner == in) {
    • }
    • if (!*to) {
      • for (o = start; o; o = o->call_next)
        • rna(orig, qe, o->interface, o->member->membername, 1);
    • }
  • }
* TRANSFER(extension|context|holdtime|calltime)
  • Caller was transferred to a different extension.

  • try_calling
    • wait_for_answer ÀÇ return °ªÀÌ 0 ÀÌ ¾Æ´Ñ °æ¿ì
      • announce, reportholdtime, memberdelay Áß Çϳª¶óµµ 0 ÀÌ ¾Æ´Ñ °æ¿ì
      • ast_channel_make_compatible ÀÌ À½¼ö¸¦ return ÇÑ °æ¿ì
      • monfmt °¡ 0 ÀÌ ¾Æ´Ñ °æ¿ì
      • leave_queue(qe);
      • bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
      • attended_transfer_occurred(qe->chan) ÀÌ 0 À» return ÇÑ °æ¿ì
        • if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
          • ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld", qe->chan->exten, qe->chan->context, (long) (callstart - qe->start), (long) (time(NULL) - callstart));
        • } else if (qe->chan->_softhangup) {
        • } else {
        • }

* OURTURN(position|origposition|waittime)

* ast_queue_log(args.queuename, chan->uniqueid, "NONE", "OURTURN", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
  • is_our_turn ¿¡¼­ res=1 ÀÎ °æ¿ì¿¡ À§ÀÇ °ÍÀ» Ãß°¡ÇÏ¸é µÊ.

Version

* asterisk-0.2.0

* asterisk-0.3.0
  • manager_queues_show

ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEXXXXX", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart)); 

ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%ld|%ld|%d|%s", (long)(callstart - qe->start), (long)(time(NULL) - callstart), qe->opos, qe->chan->exten);

* time variable
  • callstart
  • qe->start

* QUEUE_NO_REACHABLE_MEMBERS

* AST_DEVICE_UNAVAILABLE

* realtime queue members


/*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
 * 
 * Here is the process of this function
 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
 *    iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
 *    member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
 *    during each iteration, we call calc_metric to determine which members should be rung when.
 * 3. Call ring_one to place a call to the appropriate member(s)
 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
 * 6. Start the monitor or mixmonitor if the option is set
 * 7. Remove the caller from the queue to allow other callers to advance
 * 8. Bridge the call.
 * 9. Do any post processing after the call has disconnected.
 *
 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
 * \param[in] options the options passed as the third parameter to the Queue() application
 * \param[in] url the url passed as the fourth parameter to the Queue() application
 * \param[in,out] tries the number of times we have tried calling queue members
 * \param[out] noption set if the call to Queue() has the 'n' option set.
 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
 */


get_member_status


* referenced
  • join_queue
        if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
                *reason = QUEUE_JOINEMPTY;
        else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
                *reason = QUEUE_JOINUNAVAIL;
  • wait_our_turn
                if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
                        *reason = QUEUE_LEAVEEMPTY;
                        ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
                        leave_queue(qe);
                        break;
                }

                /* leave the queue if no reachable agents, if enabled */
                if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
                        *reason = QUEUE_LEAVEUNAVAIL;
                        ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
                        leave_queue(qe);
                        break;
                }
  • queue_exec
    • try_calling ÀÌ 0 À» return ÇÑ °æ¿ì
                        if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
                                record_abandoned(&qe);
                                reason = QUEUE_LEAVEEMPTY;
                                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
                                res = 0;
                                break;
                        }

                        /* leave the queue if no reachable agents, if enabled */
                        if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
                                record_abandoned(&qe);
                                reason = QUEUE_LEAVEUNAVAIL;
                                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
                                res = 0;
                                break;
                        }
* static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty)
  • member µéÀ» Çϳª¾¿ Á¡°ËÇÑ´Ù.
    • max_penalty °¡ ÁÖ¾îÁø °æ¿ì, À̺¸´Ù member->penalty °¡ Å« °ÍÀº Åë°ú
    • paused member µµ Åë°ú
    • switch
      • status °¡ AST_DEVICE_INVALID ¸é break
      • status °¡ AST_DEVICE_UNAVAILABLE
        • result = QUEUE_NO_REACHABLE_MEMBERS;
        • break
      • ÀÌ¿ÜÀÇ °æ¿ì´Â return QUEUE_NORMAL
  • penalty º¸´Ù ÀÛ°í, paused °¡ ¾Æ´Ï°í, status °¡ AST_DEVICE_INVALID °¡ ¾Æ´Ï°í, status °¡ AST_DEVICE_UNAVAILABLE ÀÌ ¾Æ´Ñ member °¡ Á¸ÀçÇϸé return QUEUE_NORMAL
  • À§¿Í °°Àº member °¡ Çϳªµµ ¾ø´Â °æ¿ì, status °¡ AST_DEVICE_UNAVAILABLE ÀÎ member °¡ ÀÖÀ¸¸é return QUEUE_NO_REACHABLE_MEMBERS
  • À§¿Í °°Àº member °¡ ¾øÀ¸¸é, return QUEUE_NO_MEMBERS

* static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused)
  • struct member *cur;
  • if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
    • cur->status = ast_device_state(interface);
  • }
  • return cur;

* int ast_device_state(const char *device) main/devicestate.c
  • buf = ast_strdupa(device);
  • tech = strsep(&buf, "/");
  • number = buf;
  • if (!number) {
    • provider = strsep(&tech, ":");
      • if (!provider)
        • return AST_DEVICE_INVALID;
      • number = tech;
      • tech = NULL;
  • }
  • if (provider) {
    • return getproviderstate(provider, number);
  • }
  • chan_tech = ast_get_channel_tech(tech);
  • if (!chan_tech)
    • return AST_DEVICE_INVALID;
  • if (!chan_tech->devicestate)
    • return ast_parse_device_state(device);
  • else {
    • res = chan_tech->devicestate(number);
    • if (res == AST_DEVICE_UNKNOWN) {
      • res = ast_parse_device_state(device);
      • if (res == AST_DEVICE_UNKNOWN)
        • res = AST_DEVICE_NOT_INUSE;
      • return res;
    • } else
      • return res;
  • }

static const char *devstatestring[] = {
	/* 0 AST_DEVICE_UNKNOWN */	"Unknown",	/* Valid, but unknown state */
	/* 1 AST_DEVICE_NOT_INUSE */	"Not in use",	/* Not used */
	/* 2 AST_DEVICE IN USE */	"In use",	/* In use */
	/* 3 AST_DEVICE_BUSY */		"Busy",		/* Busy */
	/* 4 AST_DEVICE_INVALID */	"Invalid",	/* Invalid - not known to Asterisk */
	/* 5 AST_DEVICE_UNAVAILABLE */	"Unavailable",	/* Unavailable (not registred) */
	/* 6 AST_DEVICE_RINGING */	"Ringing"	/* Ring, ring, ring */
};
case AST_DEVICE_UNAVAILABLE:
    result = QUEUE_NO_REACHABLE_MEMBERS;
chan_agent.c:	res = AST_DEVICE_UNAVAILABLE;
chan_iax2.c:	res = AST_DEVICE_UNAVAILABLE;
chan_sip.c:	        res = AST_DEVICE_UNAVAILABLE;

The queue system (\ref app_queue.c) treats a member as "active"
  if devicestate is != AST_DEVICE_UNAVAILBALE && != AST_DEVICE_INVALID

When placing a call to the queue member, queue system sets a member to busy if
        != AST_DEVICE_NOT_INUSE and != AST_DEVICE_UNKNOWN

* static void set_queue_result(struct ast_channel *chan, enum queue_result res)
  • pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_resultsi.text);
    • sets the QUEUESTATUS channel variable
The Queue application sets the QUEUESTATUS channel variable upon
completion. The status of the call can be : TIMEOUT, FULL, JOINEMPTY,
LEAVEEMPTY, JOINUNAVAIL or LEAVEUNAVAIL.

Here an example....

...
exten => 3,5,Queue(scopserv-test|tH|||30)
exten => 3,6,GotoIf($["${QUEUESTATUS}" = "JOINEMPTY"]?1000)
exten => 3,7,GotoIf($["${QUEUESTATUS}" = "JOINUNAVAIL"]?1000)
exten => 3,8,GotoIf($["${QUEUESTATUS}" = "FULL"]?1000)
exten => 3,9,NoOp(Normal Queue exist)
exten => 3,10,Hangup

exten => 3,1000,Voicemail(b1000@scopserv) 

* leave
  • wait_our_turn
    • is_our_turn ÀÌ 0 À» return ÇÏ°í,
    • timeout ÀÌ ¾ÈµÇ°í,
    • leavewhenempty °¡ ¼³Á¤µÇ¾î ÀÖ°í, member status °¡ QUEUE_NO_MEMBERS ÀÎ °æ¿ì
    • leavewhenempty == QUEUE_EMPTY_STRICT ÀÌ°í member status °¡ QUEUE_NO_REACHABLE_MEMBERS ÀÎ °æ¿ì
    • leave_queue(qe);
    • return 0
  • try_calling
    • answered peer ÀÌ ÀÖ´Â °æ¿ì
  • queue_exec
    • queue ¿¡ join Çϸé
    • ¹«Á¶°Ç leave_queue ¸¦ ¼öÇàÇÔ.

wait our turn

* return 0 or the ascii value of the digit or -1
  • ast_waitfordigit ¿¡¼­ -1 À» return ÇÒ ¼ö ÀÖÀ½.

* wait_our_turn
  • queue_exec
    • if (!join_queue(args.queuename, &qe, &reason)) {
      • res = wait_our_turn(&qe, ringing, &reason);
      • if (res)
        • goto stop;
      • for (;;) {
      • }
      • stop:
      • if (res) {
        • if (res < 0) {
          • if (!qe.handled) {
          • ast_waitfordigit ¿¡¼­ -1 À» return ÇÑ °æ¿ì
          • }
          • res = -1;
        • } else if (qe.valid_digits) {
        • say_position, say_periodic_announcement, ast_waitfordigit ¿¡¼­ the ascii value of the digit À» ¹ÞÀº °æ¿ì
        • }
      • }
      • if (res >= 0) {
      • wait our turn ¿¡¼­ ¿Â °æ¿ì´Â res > 0 ¸¸ÀÓ.
      • }
    • } else {
    • }

* static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
  • int res = 0;
  • for (;;) {
    • if (is_our_turn(qe))
      • break;
    • IFreason(QUEUE_TIMEOUT)
    • stat = get_member_status(qe->parent, qe->max_penalty);
    • if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
      • leave_queue(qe);
      • reason(QUEUE_LEAVEEMPTY)
    • }
    • if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
      • leave_queue(qe);
      • reason(QUEUE_LEAVEUNAVAIL);
    • }
    • if (qe->parent->announcefrequency && !ringing && (res = say_position(qe)))
      • break;
        • announcefrequency °¡ 0 À̸é for loop
        • ringing °¡ 0 ÀÌ ¾Æ´Ï¸é for loop
        • say_position ÀÌ 0 ¸¦ return Çϸé for loop
    • IFreason(QUEUE_TIMEOUT)
    • if (qe->parent->periodicannouncefrequency && !ringing && (res = say_periodic_announcement(qe)))
      • break;
        • periodicannouncefrequency °¡ 0 À̸é for loop
        • ringing °¡ 0 ÀÌ ¾Æ´Ï¸é for loop
        • say_periodic_announcement ÀÌ 0 ¸¦ return Çϸé for loop
    • IFreason(QUEUE_TIMEOUT)
    • if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
      • if (res > 0 && !valid_exit(qe, res))
        • res = 0;
          • for loop
      • else
        • break;
          • qe->valid_digits À» 1·Î ¼³Á¤
    • }
    • IFreason(QUEUE_TIMEOUT)
  • }
  • return res;

* reason(QUEUESTATUS)
  • *reason = QUEUESTATUS;
  • break;

* IFreason(QUEUE_TIMEOUT)
  • if (qe->expire && (time(NULL) >= qe->expire)) {
    • reason(QUEUE_TIMEOUT);
  • }

* 0 À» return ÇÏ´Â °æ¿ì
  • our_turn ÀÎ °æ¿ì
  • time out
  • QUEUE_NO_MEMBERS
  • QUEUE_NO_REACHABLE_MEMBERS

* for loop
  • time out ÀÎ °æ¿ì´Â for loop ÀÇ Ã¹¹ø°¿¡¼­ ´Ù½Ã time out À» check ÇÏ¿© ºüÁ® ³ª¿È.
  • say_position(&qe)
  • say_periodic_announcement(&qe)
  • res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
  • if (res)
    • goto stop;


* leavewhenempty ÀÇ °æ¿ì ÀÌ°÷¿¡¼­ leave_queue ¸¦ ÇÏ´Â ÀÌÀ¯°¡ ¹«¾ùÀΰ¡?

* 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÏ´Â °æ¿ì
  • if (qe->parent->announcefrequency && !ringing && (res = say_position(qe)))
    • break;
  • if (qe->parent->periodicannouncefrequency && !ringing && (res = say_periodic_announcement(qe)))
    • break;
  • if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
    • if (res > 0 && !valid_exit(qe, res))
    • else
      • break;
  • }

* static int say_position(struct queue_ent *qe)
  • play_file
    • ast_streamfile
      • res = ast_waitstream(chan, AST_DIGIT_ANY);
  • playout:
  • if ((res > 0 && !valid_exit(qe, res)) || res < 0)
    • res = 0;
  • return res;
  • return 0 or the ascii value of the digit

* static int say_periodic_announcement(struct queue_ent *qe)
  • play_file
    • ast_streamfile
      • res = ast_waitstream(chan, AST_DIGIT_ANY);
  • if ((res > 0 && !valid_exit(qe, res)) || res < 0)
    • res = 0;
  • return res;
  • return 0 or the ascii value of the digit

* int ast_waitfordigit(struct ast_channel *c, int ms) main/channel.c
  • return ast_waitfordigit_full(c, ms, -1, -1);
  • Wait for a digit, no more than ms milliseconds total
    • return 1, -1, f->subclass, 0
    • hangup ÀÇ °æ¿ì -1

* play_file
  • return 0 or the ascii value of the digit

file.h:#define AST_DIGIT_ANY "0123456789#*ABCD"
file.h:#define AST_DIGIT_ANYNUM "0123456789"
* int ast_waitstream(struct ast_channel *c, const char *breakon)
  • res holds the dtmf code, invalid or valid.
  • breakon is the string with codes ast_waitstream should break on, could be AST_DIGIT_ANY or could be "" in case you do not want to break on anything.
  • So res has the ascii value of the digit you pressed and strchr checks if that value is in the breakon string, if it is, it stops the playback (as the user has started to input dtmf values).

valid_exit


* static int valid_exit(struct queue_ent *qe, char digit)
  • ÇöÀç qe->digits ¿¡ assign µÈ string ÀÇ Å©±â°¡ sizeof(qe->digits) - 2 º¸´Ù ÀÛÀ¸¸é digit ¸¦ string ³¡¿¡ append ÇÑ´Ù.
  • ³ÑÀ¸¸é qe->digits ¸¦ reset
  • ÇØ´çµÇ´Â context °¡ Àִ°¡ Á¡°Ë
  • ÇØ´çµÇ´Â context ¿¡ ÇØ´çµÇ´Â extension ÀÌ Àִ°¡ Á¡°Ë.
  • ÇØ´çµÇ´Â extension ÀÌ ÀÖÀ¸¸é ast_goto_if_exists
    • qe->valid_digits = 1;
    • return 1;

* ast_goto_if_exists
  • ast_explicit_goto
  • chan->exten ¿¡ exten À» ³Ö°í ¸®ÅÏÇÔ.

* called
  • say_position
  • say_periodic_announcement
  • wait_for_answer
  • wait_our_turn
  • wait_a_bit

queue status

TIMEOUT &#8211; the max time specified in the queue command elapsed, only checked
between retries so may not be 100% accurate.

FULL &#8211; the number of callers in the queues would exceed the maxlen= value
defined in queues.conf if another caller was added

JOINEMPTY &#8211; a call was sent to the queue but the queue had no members, does
not apply when using agentcallbacklogin since there could be unavailable
members defined but not available.

LEAVEEMPTY &#8211; the last agent was removed form the queue before alls calls we
handled, remain callers exit with this status, also acts differently when
there are only queue members that are unavaialbe

JOINUNAVAIL/LEAVEUNAVAIL &#8211; same as JOINEMPTY/LEAVEEMPTY, except that there
were still queue members, but all were status unavailable (logged out)

So if a queue is made up of only callback agents (agentcallbacklogin) then
the queuestatus will never be joinempty or leaveempty

If the maxlen=0 then there will never be a queuestatus of full

If there is no timeout in the queue command thee wll never be a questatus of
timeout

If there are no callback or static agents joinunavail/leaveunavail will
never apply.


 I don't believe that the logic you're describing here with "if a
queue is made up of only callback agents then the queuestatus will
never be joinempty or leavempty" is right. If a queue is made up of
only callback agents and none of those callback agents are presently
logged in, the status could certainly be joinempty or leavempty.

 The rest of it looks pretty good. Thanks for taking the time to
better document this!


1.4.24.1

Queue commands


queue add member - Add a channel to a specified queue
queue remove member - Removes a channel from a specified queue
queue show - Show status of a specified queue
rtcp debug ip - Enable RTCP debugging on IP
rtcp debug - Enable RTCP debugging
rtcp debug off - Disable RTCP debugging
rtcp stats - Enable RTCP stats
rtcp stats off - Disable RTCP stats
rtp debug ip - Enable RTP debugging on IP
rtp debug - Enable RTP debugging
rtp debug off - Disable RTP debugging
say load - Set/show the say mode
show parkedcalls - Lists parked calls
show queue - Show information for target queue
show queues - Show the queues
9) Queue and ACD management
01. Queue - thanks to this application you can attach a call in a queue.
02. AddQueueMember - with this application you can add an agent(member) in your queue. 
03. AgentCallbackLogin - the application allows you to log in an agent, into a queue, with callback.
04. AgentLogin - the application allows you to log in an agent into a queue.
05. RemoveQueueMember - this application allows you to remove, dynamically, an agents or members, from a queue.
06. PauseQueueMember - this application allows you to stop, temporary, the answering of calls in the queue, from a specific member.
07. UnpauseQueueMember - the usage of this application will cancel the effect of the PauseQueueMember application.
08. AgentMonitorOutgoing - this application allows you to figure out the Caller ID of the agent, who is making an outgoing call.
09. ParkedCall - this application answers parked calls 
10. Park - this application allows you to park yourself.
11. ParkAndAnnounce
* ast_register_application
  • queue_exec Queue(queuename[|optionsURLannounceoverridetimeout)
  • aqm_exec AddQueueMember(queuenameinterfacepenalty)
  • rqm_exec RemoveQueueMember(queuenamemember)
  • pqm_exec PauseQueueMember(queuenamemember)
  • upqm_exec UnpauseQueueMember(queuenamemember)
  • ql_exec
* ast_manager_register
  • Queues", 0, manager_queues_show, "Queues
  • QueueStatus", 0, manager_queues_status, "Queue Status
  • QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue
  • QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue
  • QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable
* ast_custom_function_register
  • queueagentcount_function
  • queuemembercount_function
  • queuememberlist_function
  • queuewaitingcount_function
* ast_devstate_add
  • statechange_queue

try_calling

/*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
 * 
 * Here is the process of this function
 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
 *    iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
 *    member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
 *    during each iteration, we call calc_metric to determine which members should be rung when.
 * 3. Call ring_one to place a call to the appropriate member(s)
 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
 * 6. Start the monitor or mixmonitor if the option is set
 * 7. Remove the caller from the queue to allow other callers to advance
 * 8. Bridge the call.
 * 9. Do any post processing after the call has disconnected.
 *
 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
 * \param[in] options the options passed as the third parameter to the Queue() application
 * \param[in] url the url passed as the fourth parameter to the Queue() application
 * \param[in,out] tries the number of times we have tried calling queue members
 * \param[out] noption set if the call to Queue() has the 'n' option set.
 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
 */

* try_calling
  • queue_exec
    • if (!join_queue(args.queuename, &qe, &reason)) {
      • check_turns:
      • res = wait_our_turn(&qe, ringing, &reason);
      • if (res)
        • goto stop;
      • for (;;) {
        • break and goto stop
        • ...
        • res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
        • if (res)
          • goto stop;
        • ....
        • res = wait_a_bit(&qe);
        • if (res)
          • goto stop;
        • if (!is_our_turn(&qe)) {
          • goto check_turns;
        • }
        • stop:
        • if (res) {
        • }
        • if (res >= 0) {
        • }
      • }
    • } else {
    • }
    • return res;

* try_calling return non-zero
  • while ((cur = ao2_iterator_next(&memi))) {
  • }
  • lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
  • peer = lpeer ? lpeer->chan : NULL;
  • if (!peer) {
    • if (to) {
      • res = -1;
    • } else {
      • res = digit;
    • }
  • } else {
    • if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
      • int res2;
      • res2 = ast_autoservice_start(qe->chan);
      • if (!res2) {
        • if (qe->parent->memberdelay) {
          • res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
        • }
      • }
      • res2 |= ast_autoservice_stop(qe->chan);
      • if (peer->_softhangup) {
      • } else if (res2) {
        • return -1;
      • }
    • }
    • res = ast_channel_make_compatible(qe->chan, peer);
    • if (res < 0) {
      • return -1;
    • }
    • bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
    • res = bridge ? bridge : 1;
  • }
  • out:
  • hangupcalls(outgoing, NULL);
  • return res;

* try_calling return zero
  • int res = 0;
  • ...
  • time(&now);
  • if (qe->expire && now >= qe->expire) {
    • res = 0;
    • goto out;
  • }
  • for (; options && *options; options++)
  • }
  • memi = ao2_iterator_init(qe->parent->members, 0);
  • while ((cur = ao2_iterator_next(&memi))) {
    • struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
    • if (!tmp) {
      • goto out;
    • }
    • if (!datastore) {
      • if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
        • free(tmp);
        • goto out;
      • }
      • if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
        • free(tmp);
        • goto out;
      • }
    • } else
    • AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
      • if (!strcasecmp(cur->interface, di->interface)) {
        • break;
      • }
    • }
    • if (di) {
      • free(tmp);
      • continue;
    • }
    • if (strncasecmp(cur->interface, "Local/", 6)) {
      • if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
        • free(tmp);
        • goto out;
      • }
    • }
    • if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
    • } else {
    • }
  • }
  • ring_one(qe, outgoing, &numbusies);
  • lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
  • peer = lpeer ? lpeer->chan : NULL;
  • if (!peer) {
  • } else {
    • if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
      • int res2;
      • res2 = ast_autoservice_start(qe->chan);
      • if (!res2) {
        • if (qe->parent->memberdelay) {
          • res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
        • }
      • }
      • res2 |= ast_autoservice_stop(qe->chan);
      • if (peer->_softhangup) {
        • ast_hangup(peer);
        • goto out;
      • } else if (res2) {
        • return -1;
      • }
    • }
    • res = ast_channel_make_compatible(qe->chan, peer);
    • if (res < 0) {
      • return -1;
    • }
    • bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
    • res = bridge ? bridge : 1;
  • }
  • out:
  • hangupcalls(outgoing, NULL);
  • return res;

try_calling 1


* channel.h
struct ast_datastore_info {
        const char *type;               /*!< Type of data store */
        void *(*duplicate)(void *data); /*!< Duplicate item data (used for inheritance) */
        void (*destroy)(void *data);    /*!< Destroy function */
        /*!
         * \brief Fix up channel references
         *
         * \arg data The datastore data
         * \arg old_chan The old channel owning the datastore
         * \arg new_chan The new channel owning the datastore
         *
         * This is exactly like the fixup callback of the channel technology interface.
         * It allows a datastore to fix any pointers it saved to the owning channel
         * in case that the owning channel has changed.  Generally, this would happen
         * when the datastore is set to be inherited, and a masquerade occurs.
         *
         * \return nothing.
         */
        void (*chan_fixup)(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
};

struct ast_datastore {
        char *uid;              /*!< Unique data store identifier */
        void *data;             /*!< Contained data */
        const struct ast_datastore_info *info;  /*!< Data store type information */
        unsigned int inheritance;       /*!Number of levels this item will continue to be inherited */
        AST_LIST_ENTRY(ast_datastore) entry; /*!< Used for easy linking */
};

struct member {
        char interface[80];                 /*!< Technology/Location */
        char membername[80];                /*!< Member name to use in queue logs */
        int penalty;                        /*!< Are we a last resort? */
        int calls;                          /*!< Number of calls serviced by this member */
        int dynamic;                        /*!< Are we dynamically added? */
        int realtime;                       /*!< Is this member realtime? */
        int status;                         /*!< Status of queue member */
        int paused;                         /*!< Are we paused (not accepting calls)? */
        time_t lastcall;                    /*!< When last successful call was hungup */
        unsigned int dead:1;                /*!< Used to detect members deleted in realtime */
        unsigned int delme:1;               /*!< Flag to delete entry on reload */
};
* main/global_datastores.c
const struct ast_datastore_info dialed_interface_info = {
        .type = "dialed-interface",
        .destroy = dialed_interface_destroy,
        .duplicate = dialed_interface_duplicate,
};

struct ast_dialed_interface {
        AST_LIST_ENTRY(ast_dialed_interface) list;
        char interface[1];
};



* datastore ¸¦ ¸¸µé¾î channel ¿¡ assign ÇÑ´Ù.
  • datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL)
  • datastore->data = dialed_interfaces;
  • ast_channel_datastore_add(qe->chan, datastore);

* member ÀÇ interface °¡ ÀÌ¹Ì datastore ¿¡ ÀÖ´ÂÁö Á¡°ËÇÑ´Ù.
  • datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
  • dialed_interfaces = datastore->data;
  • AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
    • if (!strcasecmp(cur->interface, di->interface)) {
      • already been dialed
    • }
  • }

* member ÀÇ interface °¡ ÀÌ¹Ì datastore ¿¡ ¾ø´Â °æ¿ì
  • strcpy(di->interface, cur->interface);
  • AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);

* try_calling
  • outgoing À» ¸¸µé¶§´Â member status ¸¦ Á¡°ËÇÏÁö ¾Ê´Â´Ù.
  • struct ast_datastore *datastore, *transfer_ds;
  • datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
    • channel ¿¡ ¿¬°áµÈ ast_datastore ¿¡¼­ ast_datastore_info ¸¦ dialed_interface_info ¿Í ºñ±³ÇÏ¿© type ÀÌ °°Àº °ÍÀÌ ÀÖÀ¸¸é, ast_datastore ¸¦ return ÇÑ´Ù.
  • if (qe->expire && now >= qe->expire) {
  • }
  • for (; options && *options; options++)
  • }
  • memi = ao2_iterator_init(qe->parent->members, 0);
  • while ((cur = ao2_iterator_next(&memi))) {
    • struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
    • if (!tmp) {
      • goto out;
    • }
    • if (!datastore) {
      • °°Àº type ÀÇ datastore °¡ ¾øÀ¸¸é
      • if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
        • datastore ¸¦ À§ÇÑ memory ¸¦ alloc À» ¸øÇϸé
        • goto out;
      • }
      • datastore->inheritance = DATASTORE_INHERIT_FOREVER;
      • if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
        • dialed_interfaces ¸¦ À§ÇÑ memory ¸¦ alloc À» ¸øÇϸé
        • goto out;
      • }
      • datastore->data = dialed_interfaces;
      • ast_channel_datastore_add(qe->chan, datastore);
        • qe->chan ¿¡ datastore ¸¦ Ãß°¡ÇÑ´Ù.
    • } else
      • dialed_interfaces = datastore->data;
        • °°Àº type ÀÇ datastore °¡ ÀÖÀ¸¸é, datastoreÀÇ data ¸¦ dialed_interfaces ¿¡ assign ÇÑ´Ù.
    • AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
      • if (!strcasecmp(cur->interface, di->interface)) {
        • break;
        • member ÀÇ interface ¿Í dialed_interfaces ÀÇ interface °¡ °°Àº °ÍÀ̸é di ´Â null ÀÌ ¾Æ´Ô.
      • }
    • }
    • if (di) {
      • continue;
      • dialed_interfaces ¿¡ member ÀÇ interface ¿Í °°Àº °ÍÀÌ ÀÌ¹Ì ÀÖÀ¸¸é ´ÙÀ½ member ¿¡ ´ëÇØ °°Àº ÀÏÀ» ¼öÇàÇÑ´Ù.
      • Áï outgoing ¿¡ ³ÖÁö¸¦ ¾Ê´Â´Ù.
    • }
    • if (strncasecmp(cur->interface, "Local/", 6)) {
      • member ÀÇ interface °¡ Local ÀÌ ¾Æ´Ï¸é.
      • if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
        • member ÀÇ interface ¸¸Å­ÀÇ memory ¸¦ ´õ alloc ÇÏÁö ¸øÇϸé
        • goto out;
        • member ÀÇ interface ¸¸Å­ÀÇ memory ¸¦ ´õ alloc ÇÑ´Ù.
        • dialed_interfaces structure ¿¡ interface °¡ ÇϳªÀÇ character ·Î Á¤ÀÇ µÇ¾î ÀÖÀ¸¹Ç·Î.
        • strcpy(di->interface, cur->interface);
        • di->interface ¿¡ member ÀÇ interface ¸¦ º¹»çÇÏ°í
      • }
      • AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
      • di ¸¦ dialed_interfaces ÀÇ ³¡¿¡ Ãß°¡ÇÑ´Ù.
    • }
    • tmp->stillgoing = -1;
    • if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
      • tmp->q_next = outgoing;
      • outgoing = tmp;
      • if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
        • break;
        • member °¡ ¼öÈ­±â¸¦ µç °æ¿ì.
          • ring Àü¿¡ ¼öÈ­±â¸¦ µé ¼ö Àִ°¡?
          • ring À̳ª ´ëÈ­ÁßÀÌ ¾Æ´Ò °æ¿ì Ç×»ó AST_STATE_UP ÀÎ ±â±â°¡ ÀÖ³ª?
    • } else {
      • free(tmp);
    • }
  • }
  • if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
    • to = (qe->expire - now) * 1000;
  • else
    • to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
  • ++qe->pending;
  • ring_one(qe, outgoing, &numbusies);
  • lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
  • peer = lpeer ? lpeer->chan : NULL;
  • if (!peer) {
  • } else {
    • if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
    • }
    • res = ast_channel_make_compatible(qe->chan, peer);
    • if (res < 0) {
    • }
    • if (qe->parent->monfmt && *qe->parent->monfmt) {
    • }
    • leave_queue(qe);
    • bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
    • if (!attended_transfer_occurred(qe->chan)) {
    • }
    • ast_hangup(peer);
    • res = bridge ? bridge : 1;
  • }
  • out:
  • hangupcalls(outgoing, NULL);
  • return res;

try_calling 2


  • datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
  • if (qe->expire && now >= qe->expire) {
  • }
  • for (; options && *options; options++)
  • }
  • memi = ao2_iterator_init(qe->parent->members, 0);
  • while ((cur = ao2_iterator_next(&memi))) {
  • }
  • if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
    • to = (qe->expire - now) * 1000;
  • else
    • to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
  • ++qe->pending;
  • ring_one(qe, outgoing, &numbusies);
  • lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
  • peer = lpeer ? lpeer->chan : NULL;
  • if (!peer) {
    • qe->pending = 0
    • if (to) {
      • wait_for_n ¿¡¼­ member µé º¸´Ù caller ÀÇ ÀÀ´äÀÌ ¸ÕÀúÀÌ°í, ÀÀ´äÀÌ hangup ÀÎ °æ¿ì
      • res = -1
    • } else {
      • ring_one ¿¡¼­ ast_call ¿¡ ÀÀ´äÇÑ member °¡ ¾ø´Â °æ¿ì
      • wait_for_n ¿¡¼­ member µé º¸´Ù caller ÀÇ ÀÀ´äÀÌ ¸ÕÀúÀÌ°í, ÀÀ´äÀÌ * ÀÎ °æ¿ì
      • wait_for_n ¿¡¼­ member µé º¸´Ù caller ÀÇ ÀÀ´äÀÌ ¸ÕÀúÀÌ°í, ÀÀ´äÀÌ valid digit ÀÎ °æ¿ì
      • res = digit
      • ÀÀ´äÀÌ valid digit ÀÎ °æ¿ì¸¸ digit °¡ 0 ÀÌ ¾Æ´Ô
    • }
  • } else {
    • ring_one ¿¡¼­ ast_call ¿¡ ÀÀ´äÇÏ°í, channel ÀÇ state °¡ AST_STATE_UP À̸é
    • member °¡ ÀÀ´äÀ» ÇÏ°í, f->subclass °¡ AST_CONTROL_ANSWER ¿©¾ß ÇÔ.
    • time(&now);
    • recalc_holdtime(qe, (now - qe->start));
    • callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
    • member = lpeer->member;
    • hangupcalls(outgoing, peer);
    • outgoing = NULL;
    • if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
      • res2 = ast_autoservice_start(qe->chan);
      • if (!res2) {
      • }
      • res2 |= ast_autoservice_stop(qe->chan);
      • if (peer->_softhangup) {
        • ast_hangup(peer);
        • goto out;
      • } else if (res2) {
        • ast_hangup(peer);
        • return -1;
      • }
    • }
    • ast_moh_stop(qe->chan);
    • res = ast_channel_make_compatible(qe->chan, peer);
    • if (res < 0) {
      • ast_hangup(peer);
      • return -1;
    • }
    • if (qe->parent->monfmt && *qe->parent->monfmt) {
    • }
    • leave_queue(qe);
    • if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
      • ast_channel_sendurl(peer, url);
    • }
    • if (!ast_strlen_zero(agi)) {
    • }
    • qe->handled++;
    • time(&callstart);
    • transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
    • bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
    • if (!attended_transfer_occurred(qe->chan)) {
      • if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
      • } else if (qe->chan->_softhangup) {
      • } else {
      • }
      • update_queue(qe->parent, member, callcompletedinsl);
    • }
    • ast_hangup(peer);
    • res = bridge ? bridge : 1;
  • }
  • out:
  • hangupcalls(outgoing, NULL)

try_calling 3


* try_calling
  • datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
  • if (qe->expire && now >= qe->expire) {
  • }
  • for (; options && *options; options++)
  • }
  • memi = ao2_iterator_init(qe->parent->members, 0);
  • while ((cur = ao2_iterator_next(&memi))) {
  • }
  • if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
    • to = (qe->expire - now) * 1000;
  • else
    • to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
  • ++qe->pending;
  • ring_one(qe, outgoing, &numbusies);
  • lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
  • peer = lpeer ? lpeer->chan : NULL;
  • if (!peer) {
  • } else {
    • if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
    • }
    • res = ast_channel_make_compatible(qe->chan, peer);
    • if (res < 0) {
      • record_abandoned(qe);
      • ast_hangup(peer);
      • return -1;
    • }
    • if (qe->parent->monfmt && *qe->parent->monfmt) {
      • return ÇÏÁö ¾ÊÀ½.
    • }
    • leave_queue(qe);
    • if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
      • ast_channel_sendurl(peer, url);
      • 'URL' allows you to specify a URL that will be sent to the called party if the channel supports it.
    • }
    • if (!ast_strlen_zero(agi)) {
      • app = pbx_findapp("agi");
      • if (app) {
        • agiexec = ast_strdupa(agi);
        • ret = pbx_exec(qe->chan, app, agiexec);
      • }
      • AGI parameter will setup an AGI script to be executed on the calling party's channel once they are connected to a queue member.
    • }
    • qe->handled++;
    • transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
    • bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
    • if (!attended_transfer_occurred(qe->chan)) {
      • if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
        • TRANSFER
      • } else if (qe->chan->_softhangup) {
        • COMPLETECALLER
      • } else {
        • COMPLETEAGENT
      • }
      • if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {
        • ast_channel_datastore_remove(qe->chan, tds);
      • }
      • update_queue(qe->parent, member, callcompletedinsl);
    • }
    • if (transfer_ds) {
    • }
    • ast_hangup(peer);
    • res = bridge ? bridge : 1;
  • }
  • out:
  • hangupcalls(outgoing, NULL);
  • return res;

* transfer
  • char oldextenAST_MAX_EXTENSION="";
  • char oldcontextAST_MAX_CONTEXT="";
  • ...
  • ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
  • ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
  • time(&callstart);
  • transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
  • bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
  • if (!attended_transfer_occurred(qe->chan)) {
    • if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
      • TRANSFER
      • bridge µÈ ÈÄ¿¡ channel ÀÇ context ³ª exten ÀÌ º¯°æµÇ´Â °æ¿ì
      • Áï ÅëÈ­Áß transfer ¸¦ ÇÑ °æ¿ì
    • } else if (qe->chan->_softhangup) {
      • caller °¡ hangup ÇÑ °æ¿ì
    • } else {
      • agent °¡ hangup ÇÑ °æ¿ì
    • }
    • if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {
      • ast_channel_datastore_remove(qe->chan, tds);
    • }
  • }
  • queue_transfer_info ¿¡ ÇØ´çµÇ´Â datastore °¡ ¾ø¾îÁö´Â °æ¿ì°¡ Àִ°¡?
  • if (transfer_ds) {
    • ast_channel_datastore_free(transfer_ds);
  • }

* static struct ast_datastore *setup_transfer_datastore(struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
  • struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
  • if (!qtds) {
    • return NULL;
  • }
  • if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
    • return NULL;
  • }
  • ds->data = qtds;
  • ast_channel_datastore_add(qe->chan, ds);
  • return ds;

* static int attended_transfer_occurred(struct ast_channel *chan)
  • return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;

/*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
 *
 * When a caller is atxferred, then the queue_transfer_info datastore
 * is removed from the channel. If it's still there after the bridge is
 * broken, then the caller was not atxferred.
 *
 * \note Only call this with chan locked
 */
static int attended_transfer_occurred(struct ast_channel *chan)
{
        return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
}


ring_one


* Place a call to a queue member.
  • 1 if a member was called successfully
    • ring_entry °¡ 1 À» return ÇÑ °æ¿ì
  • 0 find_best °¡ null À» return ÇÑ °æ¿ì
    • ¸ðµç member ÀÇ stillgoing °¡ 0 ÀÎ °æ¿ì
    • stillgoing ÀÌ -1 ÀÎ member °¡ ÀÖÀ¸¸é, ±× member ÀÇ chan Àº null ÀÌ ¾Æ´Ï¾î¾ß ÇÔ.

* referenced
  • try_calling
  • wait_for_answer

Once metrics have been calculated for each member, this function is used to place a call to the appropriate member (or members). The low-level channel-handling and error detection is handled in ring_entry

* static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
  • int ret = 0;
  • while (ret == 0) {
    • struct callattempt *best = find_best(outgoing);
    • if (!best) {
      • break;
      • stillgoing ÀÌ -1 ÀÌ°í chan ÀÌ ¼³Á¤µÇÁö ¾ÊÀº callattempt °¡ ¾ø´Â °æ¿ì
    • }
    • if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
      • for (cur = outgoing; cur; cur = cur->q_next) {
        • if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
          • ret |= ring_entry(qe, cur, busies);
          • best ÀÇ metric °ú metric ÀÌ °°Àº callattempt µé¿¡ ´ëÇØ ringentry
        • }
      • }
    • } else {
      • ret = ring_entry(qe, best, busies);
      • best callattempt ¿¡ ´ëÇØ ring_entry
    • }
  • }
  • return ret;

* find_best return NULL
  • outgoing ÀÇ stillgoing ÀÌ ¸ðµÎ 0 ÀÎ °æ¿ì
  • cur->chan ÀÌ 0 ÀÎ °æ¿ì?
    • ast_request ¸¦ ¾ÆÁ÷ ÇÏÁö ¾ÊÀº member
  • Á» ´õ ÀÚ¼¼È÷ º¼ °Í
  • for wait_for_answer ÀÇ retry ºÎºÐ°ü·Ã

* static struct callattempt *find_best(struct callattempt *outgoing)
  • for (cur = outgoing; cur; cur = cur->q_next) {
    • if (cur->stillgoing && !cur->chan && (!best || cur->metric < best->metric)) {
      • best = cur;
    • }
  • }
  • return best;
  • stillgoing ÀÌ -1 ÀÌ°í chan ÀÌ ¾ÆÁ÷ ¾ø´Â ù¹ø° callattempt ¸¦ best ·Î ¼³Á¤
  • stillgoing ÀÌ -1 ÀÌ°í chan ÀÌ ¾ÆÁ÷ ¾ø´Â callattempt ÀÇ metric ¿Í ¾Õ¿¡ ¼³Á¤µÈ best ÀÇ metric ¸¦ ºñ±³ÇÏ¿© »õ·Î¿î °ÍÀÌ ÀûÀ¸¸é best ¸¦ ÇöÀç callattempt ·Î Àç¼³Á¤
  • Áï stillgoing ÀÌ -1 ÀÌ°í chan ÀÌ ¾ÆÁ÷ ¾ø´Â callattempt Áß metric °¡ °¡Àå ÀûÀº °ÍÀ» ¼±Á¤ÇÔ.

ring_entry


/*! \brief Part 2 of ring_one
 *
 * Does error checking before attempting to request a channel and call a member. This
 * function is only called from ring_one
 */

* stillgoing in callattempt
  • try_calling set stillgoing to -1
  • ring_entry, wait_for_answer, do_hang set stillgoing to 0
  • used in wait_for_answer, find_best, ring_one

* stillgoing in wait_for_answer
  • wait_for_answer set stillgoing to 1

* static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
  • if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
    • lastcall ÀÌÈÄ wrapuptime ¸¸Å­ ½Ã°£ÀÌ Áö³ªÁö ¾ÊÀº °æ¿ì
    • tmp->stillgoing = 0;
    • (*busies)++;
    • return 0;
  • }
  • if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
    • tmp->stillgoing = 0;
    • return 0;
    • inuse versus busies ?
  • }
  • if (tmp->member->paused) {
    • member °¡ paused »óÅÂÀÎ °æ¿ì
    • tmp->stillgoing = 0;
    • return 0;
  • }
  • if (use_weight && compare_weight(qe->parent,tmp->member)) {
    • ÀÌ member °¡ ¼ÓÇÑ ´Ù¸¥ queue °¡ ´õ ³ôÀº weight ¸¦ °¡Áö°í ÀÖ´Â °æ¿ì
    • Áï ´Ù¸¥ queue ¿¡¼­ »ç¿ëÀÇ ¿ì¼±±ÇÀÌ ÀÖÀ½.
    • tmp->stillgoing = 0;
    • (*busies)++;
    • return 0;
  • }
  • tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
  • if (!tmp->chan) {
    • asr_request ¿¡¼­ null ÀÌ return µÈ °æ¿ì
    • tmp->stillgoing = 0;
    • update_status(tmp->member->interface, ast_device_state(tmp->member->interface));
    • qe->parent->rrpos++;
    • (*busies)++;
    • return 0;
  • }
  • if ((res = ast_call(tmp->chan, location, 0))) {
    • INVITE message ¸¦ º¸³¿.
    • ast_call ¿¡¼­ non-zero °ªÀ» return
    • do_hang(tmp);
    • (*busies)++;
    • update_status(tmp->member->interface, ast_device_state(tmp->member->interface));
    • return 0;
  • } else if (qe->parent->eventwhencalled) {
    • manager_event
  • }
  • update_status(tmp->member->interface, ast_device_state(tmp->member->interface));
  • return 1;

ringinuse - (default value - no) - If you want the queue to avoid sending calls to members whose devices are known to be 'in use' (via the channel driver supporting that device state) uncomment this option. (Note: only the SIP channel driver currently is able to report 'in use').
weight - when one channel is included in more than one queue, the queue with the higher weight will be the first one which will handle an incoming call on this channel.
static int compare_weight(struct call_queue *rq, struct member *member)
traverse all defined queues which have calls waiting and contain this member
return 0 if no other queue has precedence (higher weight) or 1 if found

wait_for_answer

/*! \brief Check if members are available
 *
 * This function checks to see if members are available to be called. If any member
 * is available, the function immediately returns QUEUE_NORMAL. If no members are available,
 * the appropriate reason why is returned
 */
if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
                to = (qe->expire - now) * 1000;
else
                to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;

lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);

* wait_for_answer * static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
  • struct callattempt *peer = NULL;
  • while (*to && !peer) {
    • for (retry = 0; retry < 2; retry++) {
      • make watchers and call_next
      • for (o = outgoing; o; o = o->q_next) {
      • }
      • if (pos > 1 || !stillgoing || (qe->parent->strategy != QUEUE_STRATEGY_RINGALL))
        • break;
      • ring_one(qe, outgoing, &numbusies);
    • }
    • if (pos == 1) {
      • no watchers
      • *to = 0;
      • return NULL;
    • }
    • winner = ast_waitfor_n(watchers, pos, to);
    • for (o = start; o; o = o->call_next) {
      • if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
        • if (!peer) {
          • peer = o;
        • }
      • } else if (o->chan && (o->chan == winner)) {
        • forward ó¸®
        • f = ast_read(winner);
        • if (f) {
          • if (f->frametype == AST_FRAME_CONTROL) {
            • f->subclass °¡ AST_CONTROL_ANSWER ÀÌ°í peer ÀÌ NULL À̸é
              • peer = o;
          • }
        • } else {
        • }
      • }
    • }
    • if (winner == in) {
      • f = ast_read(in);
      • if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
        • *to = -1;
        • return NULL;
      • }
      • if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
        • *to = 0;
        • return NULL;
      • }
      • if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
        • *to = 0;
        • *digit = f->subclass;
        • return NULL;
      • }
    • }
    • if (!*to) {
    • }
  • }
  • return peer;

wait_for_answer 1


* wait_for_answer
  • watchers ¿Í callattempt ÀÇ call_next »ý¼º
  • struct callattempt *peer = NULL;
  • starttime = (long) time(NULL);
  • while (*to && !peer) {
    • int numlines, retry, pos = 1;
    • for (retry = 0; retry < 2; retry++) {
      • for (o = outgoing; o; o = o->q_next) {
        • if (o->stillgoing) {
          • stillgoing = 1;
          • if (o->chan) {
            • QUEUE_STRATEGY_RINGALL ÀÌ ¾Æ´Ï¸é, ring_entry ¸¦ best ¿¡ ´ëÇؼ­¸¸ ÇϹǷÎ, chan ÀÌ »ý¼ºµÈ °ÍÀº ÇÑ°³ÀÓ.
            • watcherspos++ = o->chan;
            • if (!start)
              • start = o;
            • else
              • prev->call_next = o;
            • prev = o;
          • }
        • }
      • }
      • if (pos > 1 || !stillgoing || (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) )
        • break;
        • pos >1 : callattempt ÀÇ stillgoing °¡ -1 ÀÌ°í chan ÀÌ »ý¼ºµÇ¾î ÀÖ´Â °æ¿ì
        • !stillgoing : callattempt ÀÇ stillgoing °¡ -1 ÀÎ callattempt °¡ Çϳªµµ ¾ø´Â °æ¿ì
        • QUEUE_STRATEGY_RINGALL ÀÌ ¾Æ´Ñ °æ¿ì
      • ring_one(qe, outgoing, &numbusies);
      • QUEUE_STRATEGY_RINGALL ÀÌ°í callattempt ÀÇ stillgoing °¡ -1 ÀÎ callattempt °¡ Çϳªµµ ¾ø´Â °æ¿ì, ring_one À» ¼öÇàÇÏ¿© metric °ªÀÌ ÇöÀç º¸´Ù Å« callattempt µéÀ» ã¾Æ Çѹø ´õ ¼öÇàÇÑ´Ù.
    • }
    • if (pos == 1 /* not found */) {
      • *to = 0;
      • return NULL;
      • ring_entry ¿¡¼­ ast_request °¡ ¼º°øÇÑ °ÍÀÌ ¾ø´Â °æ¿ì
    • }
    • winner = ast_waitfor_n(watchers, pos, to);
    • for (o = start; o; o = o->call_next) {
      • if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
      • } else if (o->chan && (o->chan == winner)) {
      • }
    • }
    • if (winner == in) {
    • }
    • if (!*to) {
    • }
  • }
  • return peer;

wait_for_answer 2


* wait_for_answer
  • while (*to && !peer) {
    • for (retry = 0; retry < 2; retry++) {
      • for (o = outgoing; o; o = o->q_next) {
      • }
    • }
    • if (pos == 1 /* not found */) {
    • }
    • winner = ast_waitfor_n(watchers, pos, to);
    • for (o = start; o; o = o->call_next) {
      • if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
      • } else if (o->chan && (o->chan == winner)) {
        • if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
        • } else if (!ast_strlen_zero(o->chan->call_forward)) {
        • }
        • f = ast_read(winner);
        • if (f) {
        • } else {
        • }
      • }
    • }
    • if (winner == in) {
    • }
    • if (!*to) {
    • }
  • }
  • return peer;


wait_for_answer 2-1


* wait_for_answer
  • while (*to && !peer) {
    • for (retry = 0; retry < 2; retry++) {
      • for (o = outgoing; o; o = o->q_next) {
      • }
    • }
    • if (pos == 1 /* not found */) {
    • }
    • watchers ¿Í callattempt ÀÇ call_next »ý¼º
    • winner = ast_waitfor_n(watchers, pos, to);
    • Wait for x amount of time on a file descriptor to have input
    • return a file descriptor which have input
    • for (o = start; o; o = o->call_next) {
      • if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
        • try_calling Áß calc_metric ¸¦ ¼öÇàÇÏ°í break ÇÑ °æ¿ì
        • ÀÌ ¿ÜÀÇ °æ¿ìµµ Àִ°¡?
        • if (!peer) {
          • peer = o;
        • }
      • } else if (o->chan && (o->chan == winner)) {
        • if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
        • chan_dahdi.c, chan_mgcp.c, chan_skinny.c
        • chan_sip.c
        • AST_STRING_FIELD(call_forward); /*!< Where to forward to if asked to dial on this interface */
        • call_forward °¡ ÀÖ´Â channel ÀÌ winner °¡ µÉ ¼ö Àִ°¡?
        • call_forward °¡ ÀÖÁö¸¸, forward °¡ Çã¿ëµÇÁö ¾Ê°Ô ¼³Á¤µÇ¾î ÀÖ´Â °æ¿ì
          • numnochan++;
          • do_hang(o);
          • winner = NULL;
          • continue;
        • } else if (!ast_strlen_zero(o->chan->call_forward)) {
          • tech ¼³Á¤
          • o->chan = ast_request(tech, in->nativeformats, stuff, &status);
          • if (!o->chan) {
            • o->stillgoing = 0;
            • numnochan++;
          • } else {
            • channel ¼³Á¤
            • if (ast_call(o->chan, tmpchan, 0)) {
              • do_hang(o);
              • numnochan++;
            • }
          • }
          • ast_hangup(winner);
          • continue;
          • o->chan À» forward channel ·Î ¹Ù²Ù°í, while loop ·Î ±âȸ¸¦ ´Ù½ÃÁÜ.
        • }
        • f = ast_read(winner);
        • if (f) {
        • } else {
        • }
      • }
    • }
    • if (winner == in) {
    • }
    • if (!*to) {
    • }
  • }
  • return peer;

wait_for_answer 2-2


* wait_for_answer
  • while (*to && !peer) {
    • for (retry = 0; retry < 2; retry++) {
      • for (o = outgoing; o; o = o->q_next) {
      • }
    • }
    • if (pos == 1 /* not found */) {
    • }
    • winner = ast_waitfor_n(watchers, pos, to);
    • for (o = start; o; o = o->call_next) {
      • if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
      • } else if (o->chan && (o->chan == winner)) {
        • if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
        • } else if (!ast_strlen_zero(o->chan->call_forward)) {
        • }
        • f = ast_read(winner);
        • if (f) {
          • if (f->frametype == AST_FRAME_CONTROL) {
            • switch (f->subclass) {
            • case AST_CONTROL_ANSWER:
              • if (!peer) {
                • peer = o;
              • }
              • break;
            • case AST_CONTROL_BUSY:
            • case AST_CONTROL_CONGESTION:
              • endtime-rna0-do_hang-ringone
              • numbusies++;
              • break;
            • case AST_CONTROL_RINGING
            • case AST_CONTROL_OFFHOOK
            • default:
            • while loop ¸¦ ÅëÇØ ´Ù½Ã Á¡°ËÇÔ.
          • }
          • ast_frfree(f);
        • } else {
          • endtime-rna1-do_hang-ringone
          • }
        • }
      • }
    • }
    • if (winner == in) {
    • }
    • if (!*to) {
    • }
  • }
  • return peer;

* endtime-rna-do_hang-ringone
  • endtime = (long) time(NULL) - starttime;
  • rna(endtime * 1000, qe, on, membername, 0 or 1);
  • do_hang(o);
  • if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
    • if (qe->parent->timeoutrestart)
      • *to = orig;
    • ring_one(qe, outgoing, &numbusies);
  • }

* member °¡ ÀÀ´äÀ» ÇÑ °æ¿ì, ast_read(winner) ÀÇ °ªÀÌ AST_CONTROL_BUSY, AST_CONTROL_CONGESTION ÀÎ °æ¿ì while loop ¸¦ ÅëÇØ ±âȸ¸¦ ´Ù½Ã ÁÜ.

* member °¡ ÀÀ´äÀ» ÇÑ °æ¿ì, ast_read(winner) ÀÇ °ªÀÌ 0 À̸é while loop ¸¦ ÅëÇØ ±âȸ¸¦ ´Ù½Ã ÁÜ.

wait_for_answer 3


* wait_for_answer
  • while (*to && !peer) {
    • for (retry = 0; retry < 2; retry++) {
      • for (o = outgoing; o; o = o->q_next) {
      • }
    • }
    • if (pos == 1) {
    • }
    • winner = ast_waitfor_n(watchers, pos, to);
    • for (o = start; o; o = o->call_next) {
      • if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
      • } else if (o->chan && (o->chan == winner)) {
      • }
    • }
    • if (winner == in) {
      • caller ¿¡¼­ ÀÔ·ÂÀÌ ÀÖ´Â °æ¿ì
      • f = ast_read(in);
      • if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
      • caller °¡ hangup ÇÑ °æ¿ì
        • *to = -1;
        • if (f)
          • ast_frfree(f);
        • return NULL;
      • }
      • if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
      • caller °¡ * À» ´­·¯ ¿¬°áÀ» ²÷Àº °æ¿ì
        • *to = 0;
        • ast_frfree(f);
        • return NULL;
      • }
      • if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
        • caller °¡ valid ÇÑ exten À» ´©¸¥°æ¿ì
        • *to = 0;
        • *digit = f->subclass;
        • ast_frfree(f);
        • return NULL;
      • }
      • ast_frfree(f);
    • }
    • if (!*to) {
      • time ÀÌ ³²¾Æ ÀÖÀ¸¸é RINGNOANSWER log ÇÏ°í while loop
      • for (o = start; o; o = o->call_next)
        • rna(orig, qe, o->interface, o->member->membername, 1);
    • }
  • }
  • return peer;

* static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause)
  • ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
  • if (qe->parent->autopause && pause) {
    • if (!set_member_paused(qe->parent->name, interface, 1)) {
    • } else {
    • }
  • }
  • return;

strategy

* strategy
  • ringall: All available agent should ring until one responds. (Default)
  • roundrobin: ring the each available agent one by one.
  • leastrecent: Call the agent who is longest idle in available agents
  • fewestcalls: ring the agent with fewest completed calls from this queue.
  • Random: Select the agent randomly from this queue.
  • rrmemory: Round-robin with memory. Starts the series in which, after the last call to the series.

* calc_metric, ring_one, find_best, store_next

Second settings for service level (default 0)
; Used for service level statistics (calls answered within service level time
; frame)
;servicelevel = 60

* static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
  • time(&member->lastcall);
  • member->calls++;
  • q->callscompleted++;
  • if (callcompletedinsl)
    • q->callscompletedinsl++;

* call completed in service level

QUEUE_PRIO, QUEUE_MAX_PENALTY

* priority
  • exten => 111,1,Playback(welcome)
  • exten => 111,2,Set(QUEUE_PRIO=10)
  • exten => 111,3,Queue(support)

* queue_exec
  • user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
  • sscanf(user_priority, "%d", &prio)
  • prio = 0;
  • qe.prio = prio

* join_queue
  • if ((!inserted) && (qe->prio > cur->prio)) {
    • insert_entry(q, prev, qe, &pos);
    • inserted = 1;
  • }

* penalty: a member is not considered available if his penalty is less than QUEUE_MAX_PENALT
  • ¹Ý´ëÀÎ °ÍÀ¸·Î ÇÁ·Î±×·¥Àº µÇ¾î ÀÖ´Â °Í °°À½.

* QUEUE_MAX_PENALTY: Maximum member penalty allowed to answer caller

* penalty
  • member => device/extension,penalty

* queue_exec
  • max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY")
  • (sscanf(max_penalty_str, "%d", &max_penalty)
  • max_penalty = 0;
  • qe.max_penalty = max_penalty;
  • stat = get_member_status(qe.parent, qe.max_penalty);
* join_queue
  • stat = get_member_status(q, qe->max_penalty);

* wait_our_turn
  • stat = get_member_status(qe->parent, qe->max_penalty);

* get_member_status
  • if (max_penalty && (member->penalty > max_penalty)) {
    • ao2_ref(member, -1);
    • continue;
  • }

* calc_metric
  • if (qe->max_penalty && (mem->penalty > qe->max_penalty))
    • return -1;

stillgoing

struct callattempt {
        struct callattempt *q_next;
        struct callattempt *call_next;
        struct ast_channel *chan;
        char interface[256];
        int stillgoing;
        int metric;
        int oldstatus;
        time_t lastcall;
        struct member *member;
};

* try_calling
  • tmp->stillgoing = -1;
  • if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
    • tmp->q_next = outgoing;
    • outgoing = tmp;
  • ring_one(qe, outgoing, &numbusies);
  • lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);

* return °ª
  • mem->penalty > qe->max_penalty ¸é -1
  • ¾Æ´Ï¸é ¹«Á¶°Ç 0

* static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
  • max_penalty º¸´Ù penalty °¡ Å« member ¸é return -1
    • outgoing ¿¡ Ãß°¡µÇÁö ¾ÊÀ½.
  • QUEUE_STRATEGY_RINGALL
    • member penalty ¿¡ 1000000 À» °öÇÑ °ªÀ» tmp metric ·Î assign
  • QUEUE_STRATEGY_ROUNDROBIN
  • QUEUE_STRATEGY_RRMEMORY
    • pos °ª°ú member penalty ¿¡ 1000000 À» °öÇÑ °ªÀ» ´õÇÏ¿© tmp metric ¿¡ assign
    • pos °ªÀº wrapped ¿Í rrpos ¿¡ µû¶ó °è»êµÊ.
  • QUEUE_STRATEGY_RANDOM
    • ast_random À» 1000 À¸·Î ³ª´« °ª°ú member penalty ¿¡ 1000000 À» °öÇÑ °ªÀ» ´õÇÏ¿© tmp metric ¿¡ assign
  • QUEUE_STRATEGY_FEWESTCALLS
    • mem->calls ¿Í member penalty ¿¡ 1000000 À» °öÇÑ °ªÀ» ´õÇÏ¿© tmp metric ¿¡ assign
  • QUEUE_STRATEGY_LEASTRECENT
    • call µÈ ÀûÀÌ ¾øÀ¸¸é 0 À», call µÈ ÀûÀÌ ÀÖÀ¸¸é 1000000 - (time(NULL) - mem->lastcall) °ªÀ» member penalty ¿¡ 1000000 À» °öÇÑ °ª¿¡ ´õÇÏ¿© tmp metric ¿¡ assign
    • lastcall °ªÀº ast_bridge_call ÈÄ¿¡ attended_transfer_occurred(qe->chan) ÀÌ 0 À» return ÇÑ °æ¿ì, update_queue ¸¦ ÇÏ´Â time À¸·Î ¼³Á¤µÊ.
    • Áï ÅëÈ­°¡ ³¡³­ µÚÀÇ ½Ã°£À¸·Î »ý°¢µÊ.

* static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
  • int ret = 0;
  • while (ret == 0) {
    • struct callattempt *best = find_best(outgoing);
    • if (!best) break;
    • if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
      • metric ÀÌ °¡Àå ÀÛÀº °ªÀÇ member µé¿¡ ´ëÇØ ring_entry ¼öÇà
      • for (cur = outgoing; cur; cur = cur->q_next) {
        • if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
          • ret |= ring_entry(qe, cur, busies);
        • }
      • }
    • } else {
      • ret = ring_entry(qe, best, busies);
    • }
  • }
  • return ret;

* static struct callattempt *find_best(struct callattempt *outgoing)
  • metric °ªÀÌ °¡Àå ÀÛÀº member ¸¦ return ÇÔ.
  • for (cur = outgoing; cur; cur = cur->q_next) {
    • if (cur->stillgoing && !cur->chan && (!best || cur->metric < best->metric)) {
      • best = cur;
    • }
  • }
  • return best;

* static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)

* static void do_hang(struct callattempt *o)
  • o->stillgoing = 0;
  • ast_hangup(o->chan);
  • o->chan = NULL;

ast_hangup() hangs up the connection and also frees all the memory associated with the channel. You should never do that for inbound channels because Asterisk needs to access the channel data after it leaves your application. 

position


* join_queue
                inserted = 0;
                prev = NULL;
                cur = q->head;
                while (cur) {
                        /* We have higher priority than the current user, enter
                         * before him, after all the other users with priority
                         * higher or equal to our priority. */
                        if ((!inserted) && (qe->prio > cur->prio)) {
                                insert_entry(q, prev, qe, &pos);
                                inserted = 1;
                        }
                        cur->pos = ++pos;
                        prev = cur;
                        cur = cur->next;
                }

* leave_queue
        prev = NULL;
        for (cur = q->head; cur; cur = cur->next) {
                if (cur == qe) {
                        q->count--;

                        /* Take us out of the queue */
                        manager_event(EVENT_FLAG_CALL, "Leave",
                                "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
                                qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
                        if (option_debug)
                                ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
                        /* Take us out of the queue */
                        if (prev)
                                prev->next = cur->next;
                        else
                                q->head = cur->next;
                } else {
                        /* Renumber the people after us in the queue based on a new count */
                        cur->pos = ++pos;
                        prev = cur;
                }
        }

* pos ´Â ÃʱⰪ 0 À¸·Î ½ÃÀÛÇÏ¿© Â÷·Ê´ë·Î queue entry ¿¡ Àç¼³Á¤ÇÔ.

* leave_queue °¡ Áߺ¹ ½ÃÇàµÇ´õ¶óµµ, qe °¡ ¾øÀ¸¹Ç·Î ·çÇÁ¸¸ µ¹°í return ÇÔ.

channel register and request 1.4.24

/*! \brief Register a new telephony channel in Asterisk */
* int ast_channel_register(const struct ast_channel_tech *tech)
  • ¸ðµç channel driver ´Â ÀÌ ÇÔ¼ö¸¦ ÅëÇØ °¢°¢ÀÇ type, format ±×¸®°í requester ÇÔ¼ö¸¦ channel list ¿¡ µî·ÏÇÑ´Ù.
  • ÀÌ¹Ì µî·ÏµÇ¾î ÀÖÀ¸¸é return -1;
  • chan = ast_calloc(1, sizeof(*chan)))
  • ÇÊ¿äÇÑ memory alloc À» ¸øÇϸé return -1;
  • chan->tech = tech;
  • AST_LIST_INSERT_HEAD(&backends, chan, list);
    • channel list ¿¡ insert
  • return 0;

/*! \brief 
        Structure to describe a channel "technology", ie a channel driver 
        See for examples:
        \arg chan_iax2.c - The Inter-Asterisk exchange protocol
        \arg chan_sip.c - The SIP channel driver
        \arg chan_zap.c - PSTN connectivity (TDM, PRI, T1/E1, FXO, FXS)

        If you develop your own channel driver, this is where you
        tell the PBX at registration of your driver what properties
        this driver supports and where different callbacks are 
        implemented.
*/
struct ast_channel_tech {
        const char * const type;
        const char * const description;
        int capabilities;               /*!< Bitmap of formats this channel can handle */
        int properties;                 /*!< Technology Properties */
        /*! \brief Requester - to set up call data structures (pvt's) */
        struct ast_channel *(* const requester)(const char *type, int format, void *data, int *cause);

        int (* const devicestate)(void *data);  /*!< Devicestate call back */

        /*! \brief Start sending a literal DTMF digit */
        int (* const send_digit_begin)(struct ast_channel *chan, char digit);
        /*! \brief Stop sending a literal DTMF digit */
        int (* const send_digit_end)(struct ast_channel *chan, char digit, unsigned int duration);

        /*! \brief Call a given phone number (address, etc), but don't
           take longer than timeout seconds to do so.  */
        int (* const call)(struct ast_channel *chan, char *addr, int timeout);
        /*! \brief Hangup (and possibly destroy) the channel */
        int (* const hangup)(struct ast_channel *chan);
        /*! \brief Answer the channel */
        int (* const answer)(struct ast_channel *chan);

        /*! \brief Read a frame, in standard format (see frame.h) */
        struct ast_frame * (* const read)(struct ast_channel *chan);
        /*! \brief Write a frame, in standard format (see frame.h) */
        int (* const write)(struct ast_channel *chan, struct ast_frame *frame);

        /*! \brief Display or transmit text */
        int (* const send_text)(struct ast_channel *chan, const char *text);
        /*! \brief Display or send an image */
        int (* const send_image)(struct ast_channel *chan, struct ast_frame *frame);

        /*! \brief Send HTML data */
        int (* const send_html)(struct ast_channel *chan, int subclass, const char *data, int len);

        /*! \brief Handle an exception, reading a frame */
        struct ast_frame * (* const exception)(struct ast_channel *chan);

        /*! \brief Bridge two channels of the same type together */
        enum ast_bridge_result (* const bridge)(struct ast_channel *c0, struct ast_channel *c1, int flags,
                                                struct ast_frame **fo, struct ast_channel **rc, int timeoutms);

        /*! \brief Indicate a particular condition (e.g. AST_CONTROL_BUSY or AST_CONTROL_RINGING or AST_CONTROL_CONGESTION */
        int (* const indicate)(struct ast_channel *c, int condition, const void *data, size_t datalen);

        /*! \brief Fix up a channel:  If a channel is consumed, this is called.  Basically update any ->owner links */
        int (* const fixup)(struct ast_channel *oldchan, struct ast_channel *newchan);

        /*! \brief Set a given option */
        int (* const setoption)(struct ast_channel *chan, int option, void *data, int datalen);
        /*! \brief Query a given option */
        int (* const queryoption)(struct ast_channel *chan, int option, void *data, int *datalen);

        /*! \brief Blind transfer other side (see app_transfer.c and ast_transfer() */
        int (* const transfer)(struct ast_channel *chan, const char *newdest);

        /*! \brief Write a frame, in standard format */
        int (* const write_video)(struct ast_channel *chan, struct ast_frame *frame);

        /*! \brief Find bridged channel */
        struct ast_channel *(* const bridged_channel)(struct ast_channel *chan, struct ast_channel *bridge);

        /*! \brief Provide additional read items for CHANNEL() dialplan function */
        int (* func_channel_read)(struct ast_channel *chan, char *function, char *data, char *buf, size_t len);
        /*! \brief Provide additional write items for CHANNEL() dialplan function */
        int (* func_channel_write)(struct ast_channel *chan, char *function, char *data, const char *value);

        /*! \brief Retrieve base channel (agent and local) */
        struct ast_channel* (* get_base_channel)(struct ast_channel *chan);

        /*! \brief Set base channel (agent and local) */
        int (* set_base_channel)(struct ast_channel *chan, struct ast_channel *base);
};

* struct ast_channel *ast_request(const char *type, int format, void *data, int *cause)
  • *cause = AST_CAUSE_NOTDEFINED;
  • channel list ¿¡ lock À» °É¼ö ¾øÀ¸¸é return NULL;
  • AST_LIST_TRAVERSE(&backends, chan, list) {
    • channel list ¿¡¼­ type ÀÌ ¸Â´Â channel À» ã´Â´Ù.
    • res = ast_translator_best_choice(&fmt, &capabilities);
      • ¸Â´Â codec ÀÌ Àִ°¡ ã´Â´Ù.
    • ¸ø ãÀ¸¸é, *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
    • return NULL;
    • chan->tech->requester °¡ ¾øÀ¸¸é return NULL;
    • if (!(c = chan->tech->requester(type, capabilities | videoformat, data, cause)))
      • return NULL;
    • return c;
  • }
  • ¸Â´Â channel type À» ¸ø ãÀ¸¸é, *cause = AST_CAUSE_NOSUCHDRIVER;
  • return NULL;

queue.conf

* member
Member
It is possible to intervene directly in the queues.conf agents in the form of static

member => Technology Resource [, Malus]

&#8211; Also e.g. member => Zap / 2 - them (may be used several times, see queues.conf). But this can result in problems with joinempty and leavewhenempty, since these agents always be available, even if it is in fact not at their apparatus. It also has the disadvantage that always finds an agent is assigned to an apparatus and not from another apparatus from register.

We therefore prefer to use dynamic form and arrange queue support in the form:

member => Agent / AgentenNr

two agents 1001 and 1002 to:

member => Agent/1001
member => Agent/1002


 member => Agent/@1  ; a group
 member => Agent/501  ; a single agent
 member => Agent/:1,1  ; Any agent in group 1, wait for first available, but consider with penalty

The penalty parameter: You can have agents that are less likely to take calls (e.g. imagine a sales queue, you'd have the sales people with no penalty, you might have the receptionists with a penalty of 1 and us propeller heads in technical support with a penalty of 2). The technical support people would only be offered a call from the sales queue if all the sales people and the receptionists were busy. 

If you include groups in your queue definition the calls get routed in the order of the group regardless of the specified strategy. So I just have a member= line for each agent. 

Asterisk 1.4
The (very old and undocumented) ability to use BYEXTENSION for dialing
instead of ${EXTEN} has been removed.

load_module 1.4.21

* static int load_module(void)
  • if (!reload_queues())
    • return AST_MODULE_LOAD_DECLINE;

* static int reload_queues(void)
  • cfg = ast_config_load("queues.conf")
  • cat = NULL;
  • while ((cat = ast_category_browse(cfg, cat)) ) {
    • ast_config ¿¡ ÀÖ´Â category list ¿¡¼­ ast_category structure ¸¦ Çϳª¾¿ °¡Á®¿Í cat ¿¡ assign ÇÑ´Ù.
    • if (!strcasecmp(cat, "general")) {
      • category °¡ general À̸é ÇØ´çµÇ´Â º¯¼öµéÀÇ °ªÀ» ÇØ´çÇÏ´Â static º¯¼öµé¿¡ assign ÇÑ´Ù.
      • queue_persistent_members, autofill_default, montype_default
    • } else {
      • general ÀÌ ¾Æ´Ñ category name Àº queue name ÀÌ´Ù.
      • queue list ¸¦ traverse Çϸç ÀÌ¹Ì Á¸ÀçÇÏ´ÂÁö Á¡°ËÇÑ´Ù.
      • ÇØ´çÇÏ´Â queue °¡ ¾øÀ¸¸é, ast_caterogy structure ¸¦ À§ÇÑ memory ¸¦ ÇÒ´çÇÏ°í new °ªÀ» 1 ·Î ¼³Á¤ÇÑ´Ù.
      • ÇØ´çÇÏ´Â queue °¡ ÀÖÀ¸¸é new ¸¦ 0 À¸·Î ¼³Á¤ÇÑ´Ù.
      • if (q) {
        • init_queue(q);
        • clear_queue(q);
        • mem_iter = ao2_iterator_init(q->members, 0);
        • for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
          • if (!strcasecmp(var->name, "member")) {
            • newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
            • ao2_link(q->members, newm);
            • add_to_interfaces(interface);
            • q->membercount++;
          • } else {
            • queue_set_param(q, var->name, var->value, var->lineno, 1);
          • }
        • }
        • AST_LIST_INSERT_HEAD(&queues, q, list);
      • } else ¸é memory alloc error ¸¦ ÃëÇØ¾ß Çϴµ¥....
    • }
  • }
  • AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
  • }
  • return 1;

* queue.conf ÆÄÀÏÀ» º¯°æ½ÃŲÈÄ reload ¸¦ ÇÏ¿© Àû¿ë½ÃŲ´Ù.(?)

* static int reload(void)
  • reload_queues();
  • return 0;

AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
                .load = load_module,
                .unload = unload_module,
                .reload = reload,
               );
Lets say you have 2 SIP users defined in your sip.conf and you want to add a 3rd. You add them to the file then execute the command 'sip reload'. This re-reads your sip.conf and allows the 3rd to register.
With RealTime, all you do is add 1 new record to the table that sipusers has been bound to. No reloading necessary. 
Asterisk Real Time allows storage of users / peers and other configuration data in database such as MySQL. It is possible to use any database that has ODBC support. This way, there is no need to perform manual configuration reload for the modified entries to take effect. With the introduction of Asterisk Real Time it is possible to add/remove users or make call flow changes by entering data into appropriate database table(s). Once configured, Asterisk Real Time engine will perform database lookup(s) on a per call basis allowing for run time configuration changes.
http://books.google.co.kr/books?id=vtQxJ3oSm64C&pg=PA268&lpg=PA268&dq=asterisk+realtime&source=bl&ots=LVZcC6Io37&sig=Cx6ArjJC9sG1ORkViIEcNdQBbpY&hl=ko&ei=PihVSsmjMpWQ6AOTjtXqDw&sa=X&oi=book_result&ct=result&resnum=6


ast function

* ast_request

* ast_call

* wait_for_answer




sponsored by andamiro
sponsored by cdnetworks
sponsored by HP

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2009-11-04 16:13:52
Processing time 0.0783 sec