[Home]

Summary:ASTERISK-20509: app_queue parameters setinterfacevar, setqueueentryvar, setqueuevar, membermacro are only used prior to bridging channel, but should happen any time app_queue attempts a connection to the member (regardless of whether it's answered)
Reporter:Jim Van Meggelen (jimvanm)Labels:
Date Opened:2012-10-04 08:16:12Date Closed:
Priority:MinorRegression?No
Status:Open/NewComponents:Applications/app_queue
Versions:1.8.16.0 10.8.0 11.0.0-beta2 13.18.4 Frequency of
Occurrence
Constant
Related
Issues:
Environment:Tested on typical Linux install (Ubuntu, RHEL, etc)Attachments:
Description:When app_queue decides to present a call to a member (i.e. call has been in queue for a period of time waiting for a member to become available), an opportunity exists to perform further evaluation of the state of the queue immediately prior to completing the connection to the selected member. These channel variables (and macro) are potentially useful for performing routing decisions prior to connection. Unfortunately, they are only set prior to bridging the call to the member, and thus cannot be used under all circumstances.

As an example, it might in some cases be desirable to route a call elsewhere (or even return congestion(), passing the call back into the queue with no loss of place in line), rather than connect to the selected agent. Perhaps the nature of the call has suddenly become such that this is no longer the best agent to handle this call, and the call should be returned to the queue to try another agent. Perhaps the nature of the call (or the state of the queue) is such that this call needs to be routed somewhere else, and the agent freed up to handle other calls currently waiting in the queue. Perhaps the agent forgot to answer the call (they are not at their desk), and rather than just blindly returning the call to the queue, it would be better to send the call elsewhere.

This does not work. Instead, unless the call is actually answered by the member channel, none of the variables get set, and the macro is not triggered, and thus useful information is not available to any dialplan code happening in the macro (or a local channel defined as a member) until it's too late to make any routing decisions.

What should happen instead, is that rather than setting these variables just prior to bridging the call, they should be set as soon as the attempt to connect to the member channel is initiated (i.e. at the moment the queue decides to ring the queue member). This would ensure that in all cases, the macro will be triggered, and cause these variables to be available regardless of the state returned by the member channel. As it stands now, unless the member channel is actually answered, these variables do not get set, which greatly limits their use, since they cannot be used to perform routing decisions.

We are in the process of documenting this functionality for the 4th Edition of Asterisk, The Definitive Guide, and thus have to make a decision as to whether we document this limitation in app_queue, or see if it can be fixed. We decided that creating a bug for this would be appropriate.

Either I, or Leif Madsen, can be contacted in regards to this (he is aware of this bug).

Thanks and regards,

Jim Van Meggelen
Comments:By: Matt Jordan (mjordan) 2012-10-04 09:09:58.091-0500

Knowing app_queue, does this happen if you're the first person waiting in a queue and agents are not available (either ringing and fail to answer, or just not available) and you bounce out of the queue?  Or does it only happen to people waiting in a queue that are in slots 2+?

By: Matt Jordan (mjordan) 2012-10-04 09:46:24.032-0500

Pasting mine and Leif's conversation from asterisk-dev here:

{quote}
(09:35:26 AM) leifmadsen: mjordan: so I guess, think of it like a channel variable that was set with Set(__FOOBAR=baz), that would be available in the Local channel that the Queue() was calling, not just in the GoSub that the Dial (within the Local channel execution) was calling.
(09:35:35 AM) leifmadsen: mjordan: it should be relatively simple to lab up though yes
(09:35:43 AM) mjordan: leifmadsen: is it only with Local channels?
(09:36:02 AM) leifmadsen: mjordan: no, the Local channel really doesn't have anything to do with it -- it's when the values are available that the problem is
(09:36:18 AM) leifmadsen: it's just easiest to see with a Local channel since that's the only time you can execute dialplan :)
(09:36:28 AM) mjordan: leifmadsen: good.  I was hoping that it was just a temporal issue, and not a 'local channels can't see this' issue.
(09:36:33 AM) leifmadsen: nope
(09:36:40 AM) leifmadsen: it's just where the values are accessible
(09:36:42 AM) leifmadsen: so for example
(09:36:48 AM) leifmadsen: this is the same thing
(09:37:25 AM) leifmadsen: Queue --> Member --> Local --> Dial(...,U(some_subroutine)) --> [some_subroutine] --> DumpChan() --> Bridge()
(09:37:46 AM) leifmadsen: is the same as just using SIP channel (instead of Local) and the queuemacro functionality
(09:37:50 AM) mjordan: right
(09:38:18 AM) leifmadsen: basically, it would be nice if the channel variables were available when the SIP channel was called, not just prior to bridging
(09:38:35 AM) leifmadsen: so when the values are set, it almost seems like they just need to be moved up the stack
(09:39:00 AM) leifmadsen: and I think the change would be really quite safe since the values would still be available in the same location they are now, but also earlier
(09:39:44 AM) leifmadsen: I think that gives you enough info to understand though
(09:39:52 AM) mjordan: leifmadsen: indeed.
(09:40:09 AM) mjordan: I understand the issue, but I'm not sure of the answer :-)
{quote}

By: Jim Van Meggelen (jimvanm) 2012-10-04 13:08:05.843-0500

I didn't test what happens if the call falls out of app_queue, since the way it's documented is that these features are part of the process of bridging a call to a member (and thus there's no reason to expect it to work under any other circumstances). The bug has nothing to do with calls waiting in the queue, of that I'm fairly certain. It only happens when the queue 1) determines that a member is available, 2) presents the call to the member, and 3) the call is bridged to that member channel (answered). I think the key here is that the channel bridge has to happen for these events to be triggered. So, for example, it works if the member is a physical channel (SIP, for example), or a local channel with an Answer() in it, and it would probably work if any other condition in the local channel caused the call to be answered (Meetme(), Playback(), etc). The problem is that once those things happen, the queue is finished with the call. Something like Congestion() within the local channel in order to dump the call back into the queue with the same rank in line it had before (something we have used in production to pull data from func_odbc just before connecting to the agent), cannot be used, because using the Congestion() app means the call never got bridged, and thus those variables never got set. Routing the call elsewhere is limited, since we don't get to read the value of those variables in the decision to re-route the call.

It'd be pretty cool to have those variables available if call flow continues past the Queue() portion of the dialplan, but I wasn't really expecting that to work, so I didn't do any lab work on it. I was focused specifically on the process of sending the call to the member channel. I would think those variables would be useful all over the place, so it'd be great if queue can set them just about any time there is a reason to refresh them, but that's not what this bug is about. Also, I'm not sure how complex that would be, so I've limited the focus here on the actual point where app_queue tries to send the call to the member.

Our testing thus far has shown that these variables (and macro) only happen if a successful bridge to the member channel happens (i.e. the agent answers the call, or an answer of some kind happens in the local channel). Under all other conditions we tried, those variables do not get set (nor is the macro called).

Leif and I figure that at a high-level (respecting that we don't know what this means from a coding perspective), those variables are currently set at the moment the channels are bridged (incoming caller to answering agent), and what should instead happen is that they are set from the moment app_queue presents the call to the member (attempts to ring the member), regardless of what happens from there. That way, we can use a local channel as a queue member, and perform all sort of dialplan magic before the caller is bridged to wherever they end up. Most importantly, this allows us to return Congestion() to app_queue after evaluating all the variables, which can be very useful.

Jim

By: Matt Jordan (mjordan) 2012-10-17 16:48:00.198-0500

So I played around with this a bit, and dug into where the macro should be called in {{app_queue}}.  As it turns out, the variables should be set on the calling/member channel prior to any macro or gosub being called - the "just before it gets bridged" does indeed to happen before the macro/gosub invocation.  So we're good there.

The problem here is that we're never actually bridged, since we aren't answered.  The diaplan execution in the {{[reject]}} context happens on the {{;2}} channel, as its the target of the Local channel dial.  That dialplan execution doesn't count as being bridged however, since there is no bidirectional flow of audio between the calling channel and whatever the Local channel is connected to.

Note that once I put an Answer() in the Local channel target (and some extra NOTICEs indicating when the variables are actually set and on what channels) I got the variables as you hoped.  The dialplan execution is shown below:

{noformat}
*CLI>   == Using SIP RTP CoS mark 5
   -- Executing [103@from-digium-phones:1] NoOp("SIP/digium01-00000000", "") in new stack
   -- Executing [103@from-digium-phones:2] Goto("SIP/digium01-00000000", "Services,sales,1") in new stack
   -- Goto (Services,sales,1)
   -- Executing [sales@Services:1] Verbose("SIP/digium01-00000000", "1,Queue Test") in new stack
Queue Test
   -- Executing [sales@Services:2] Queue("SIP/digium01-00000000", "sales") in new stack
   -- Music class default requested but no musiconhold loaded.
   -- Executing [congest@reject:1] DumpChan("Local/congest@reject-00000000;2", "") in new stack

Dumping Info For Channel: Local/congest@reject-00000000;2:
================================================================================
Info:
Name=               Local/congest@reject-00000000;2
Type=               Local
UniqueID=           Asterisk-01-1350510119.2
LinkedID=           Asterisk-01-1350510119.0
CallerIDNum=        101
CallerIDName=       Digium 01
ConnectedLineIDNum= 103
ConnectedLineIDName=(N/A)
DNIDDigits=         (N/A)
RDNIS=              (N/A)
Parkinglot=        
Language=           en
State=              Ring (4)
Rings=              0
NativeFormat=       (ulaw)
WriteFormat=        ulaw
ReadFormat=         ulaw
RawWriteFormat=     ulaw
RawReadFormat=      ulaw
WriteTranscode=     No
ReadTranscode=      No
1stFileDescriptor=  -1
Framesin=           0
Framesout=          0
TimetoHangup=       0
ElapsedTime=        0h0m0s
DirectBridge=       <none>
IndirectBridge=     <none>
Context=            reject
Extension=          congest
Priority=           1
CallGroup=          
PickupGroup=        
Application=        DumpChan
Data=               (Empty)
Blocking_in=        (Not Blocking)

Variables:
================================================================================
   -- Executing [congest@reject:2] Answer("Local/congest@reject-00000000;2", "") in new stack
   -- Local/congest@reject-00000000;1 answered SIP/digium01-00000000
[Oct 17 16:41:59] NOTICE[14786][C-00000000]: app_queue.c:5430 try_calling: Setting variables on SIP/digium01-00000000, Local/congest@reject-00000000;1
[Oct 17 16:41:59] NOTICE[14786][C-00000000]: app_queue.c:5440 try_calling: Setting variables on SIP/digium01-00000000, Local/congest@reject-00000000;1
   -- Executing [s@macro-TestMemberMacro:1] Verbose("Local/congest@reject-00000000;1", "1,Testing entry vars") in new stack
Testing entry vars
   -- Executing [s@macro-TestMemberMacro:2] DumpChan("Local/congest@reject-00000000;1", "") in new stack

Dumping Info For Channel: Local/congest@reject-00000000;1:
================================================================================
Info:
Name=               Local/congest@reject-00000000;1
Type=               Local
UniqueID=           Asterisk-01-1350510119.1
LinkedID=           Asterisk-01-1350510119.0
CallerIDNum=        103
CallerIDName=       (N/A)
ConnectedLineIDNum= 101
ConnectedLineIDName=Digium 01
DNIDDigits=         (N/A)
RDNIS=              (N/A)
Parkinglot=        
Language=           en
State=              Up (6)
Rings=              0
NativeFormat=       (ulaw)
WriteFormat=        ulaw
ReadFormat=         ulaw
RawWriteFormat=     ulaw
RawReadFormat=      ulaw
WriteTranscode=     No
ReadTranscode=      No
1stFileDescriptor=  -1
Framesin=           1
Framesout=          0
TimetoHangup=       0
ElapsedTime=        0h0m0s
DirectBridge=       <none>
IndirectBridge=     <none>
Context=            macro-TestMemberMacro
Extension=          s
Priority=           2
CallGroup=          
PickupGroup=        
Application=        DumpChan
Data=               (Empty)
Blocking_in=        (Not Blocking)

Variables:
MACRO_DEPTH=1
MACRO_PRIORITY=1
MACRO_CONTEXT=reject
MACRO_EXTEN=sales
QUEUESRVLEVELPERF=0.0
QUEUESRVLEVEL=0
QUEUEABANDONED=0
QUEUECOMPLETED=0
QUEUETALKTIME=0
QUEUEHOLDTIME=0
QUEUECALLS=1
QUEUESTRATEGY=rrmemory
QUEUEMAX=0
QUEUENAME=sales
QEORIGINALPOS=1
QEHOLDTIME=0
MEMBERREALTIME=0
MEMBERDYNAMIC=0
MEMBERPENALTY=0
MEMBERLASTCALL=0
MEMBERCALLS=0
MEMBERNAME=Local/congest@reject/n
MEMBERINTERFACE=Local/congest@reject/n
================================================================================
   -- Executing [congest@reject:3] Congestion("Local/congest@reject-00000000;2", "") in new stack
 == Spawn extension (reject, congest, 3) exited non-zero on 'Local/congest@reject-00000000;2'
 == Spawn extension (Services, sales, 2) exited non-zero on 'SIP/digium01-00000000'
{noformat}

Note that the channel variables are set on the calling channel and the {{;1}} side of the Local channel, which is sort of what we expected based on the problem report.  Of course, the {{Answer}} doesn't really help us here, because the idea is that we may not *want* to Answer the call (and thus muck up those evil things known as CDRs), so I don't think this is really a solution.

So all of this explains what is happening, but doesn't answer the question of whether or not this is a bug.

The use case you described is certainly valid (and is similar to problems that [Pre-Dial|https://wiki.asterisk.org/wiki/display/AST/Pre-Dial+Handlers] solved in Asterisk 11), but Pre-Dial wasn't implemented in Queues, so that's not really a workaround available here.

The implementation is actually doing what it describes - that is, right before the call is bridged, implying two way audio (and thus something answering the calling channel), you get the Macro invocation.  So that also matches.

But it would be very useful if the channel variables were applied further before the Macro invocation, that is, before the Member itself was dialed but after channel creation (aka, Pre-Dial).

I'm leaning towards "feature request", or at the very least, application of a feature that was developed for other Asterisk apps.  I'm open to other suggestions however.

By: Jim Van Meggelen (jimvanm) 2012-10-18 06:39:13.935-0500

I understand where you're coming from, and I think what we'll have to do then is document that these features will be useful in situations relating to logging, but not so for call routing.

Thanks for having a look at it. We'll document as appropriate.

I would recommend leaving this as a feature request, as having this work as described in this ticket would greatly enhance the usefulness of those features.

Thanks,

Jim