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)
queue_exec 1 ¶* queue_exec
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); );
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;
queue_exec 2 ¶* queue_exec
queue_exec 3 ¶* queue_exec
queue_exec 4 ¶* queue_exec
queue CDR ¶* mysql À» »ç¿ëÇϱâ À§ÇÏ¿©
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 agent ¿Í ÅëÈ°¡ µÇ°í Á¤»óÁ¾·áÀÎ °æ¿ì ¶* case 1:
Åë°è°ü·Ã ¶* asterisk ¿Í ÅëÈ°¡ ¾ÈµÇ´Â °æ¿ì
* asterisk ¿Í ÅëÈ°¡ µÈ °æ¿ì
time °ü·Ã ¶* static int say_position(struct queue_ent *qe)
* AGENTCALLBACKLOGOFF(exten@context|logintime|reason)
* COMPLETEAGENT(holdtime|calltime|origposition)
* ast_queue_log(args.queuename, chan->uniqueid, "NONE", "OURTURN", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
Version ¶* asterisk-0.2.0
* asterisk-0.3.0
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
* 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
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;
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; }
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)
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)
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 ¶* return 0 or the ascii value of the digit or -1
* 0 ÀÌ ¾Æ´Ñ °ªÀ» return ÇÏ´Â °æ¿ì
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)
valid_exit ¶* static int valid_exit(struct queue_ent *qe, char digit)
queue status ¶TIMEOUT – the max time specified in the queue command elapsed, only checked between retries so may not be 100% accurate. FULL – the number of callers in the queues would exceed the maxlen= value defined in queues.conf if another caller was added JOINEMPTY – 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 – 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 – 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
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
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 ÇÑ´Ù.
try_calling 2 ¶
try_calling 3 ¶* try_calling
/*! \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.
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)
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
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)
wait_for_answer 1 ¶* wait_for_answer
wait_for_answer 2 ¶* wait_for_answer
wait_for_answer 2-1 ¶* wait_for_answer
wait_for_answer 2-2 ¶* wait_for_answer
* member °¡ ÀÀ´äÀ» ÇÑ °æ¿ì, ast_read(winner) ÀÇ °ªÀÌ 0 À̸é while loop ¸¦ ÅëÇØ ±âȸ¸¦ ´Ù½Ã ÁÜ.
wait_for_answer 3 ¶* wait_for_answer
strategy ¶* strategy
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)
QUEUE_PRIO, QUEUE_MAX_PENALTY ¶* priority
* penalty
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
* static void do_hang(struct callattempt *o)
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)
/*! \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)
queue.conf ¶* member
Member It is possible to intervene directly in the queues.conf agents in the form of static member => Technology Resource [, Malus] – 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)
* static int reload(void)
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 |