Oh yes.... User data...

So one self assinged I'd and one user supplied.

I suggest a string as the data type with an arbitrary limit of 2k

On Sep 19, 2008, at 6:35 AM, "Richard Open Source" <oss.richard@gmail.com> wrote:

I agree.
 
I have started working on a library like asterisk-java (now onhold but hopefully can continue working on it in a couple of weeks) using groovy (will name it fs-groovy :))
 
The way I check if the command was successful is to look for the Event with CHANNEL_EXECUTE_COMPLETE and Application and ApplicationData.
 
Here is how I implemented it. Still needs more work though.
--
2
3 import org.apache.mina.common.IoSession
4 import java.util.concurrent.BlockingQueue
5 import java.util.concurrent.Executors
6 import java.util.concurrent.ExecutorService
7 import java.util.concurrent.Future
8 import java.util.concurrent.Callable
9 import java.util.concurrent.ExecutionException
10 import java.util.concurrent.TimeoutException
11 import java.util.concurrent.TimeUnit
12
13
14
15 class Session {
16         private IoSession session
17         private FSEventHandler handler
18         private BlockingQueue<String> msgQ
19         private final ExecutorService executor = Executors.newSingleThreadExecutor()
20         private final static long DEFAULT_TIMEOUT = 5000
21        
22         def data
23        
24         def Session(IoSession s, BlockingQueue q) {
25                 session = s
26                 msgQ = q
27         }
28
29         private def executeAndWait(Closure task, long timeout=0) {
30         Future <CommandResult> f = executor.submit(task as Callable)       
31         def result
32         def boolean success = false
33                 try {
34                         if (timeout != 0) {
35                                 result = f.get(timeout, TimeUnit.MILLISECONDS)
36                         } else {
37                                 result = f.get()
38                         }                               
39                         if (result.code == CommandResult.OK) data = result.data
40                 } catch (ExecutionException e) {
41                         // Should log here
42                 } catch (TimeoutException e) {
43                         f.cancel(true)
44                 }
45                
46                 return result
47         }       
48        
49         def answer() {   
50                 def task = {
51                         def done = false
52                         def r = new CommandResult()
53                         sendMessage("answer")
54                         while (! done) {
55                                 def m = msgQ.take()
56                                 if ((m?.event?.Name == "CHANNEL_EXECUTE_COMPLETE") && (m?.Application == "answer")) {
57                                         done = true
58                                         r.code = CommandResult.OK
59                                         r.data = m
60                                 }                                       
61                         }                               
62                         return r               
63                 }
64         executeAndWait(task, DEFAULT_TIMEOUT)
65         }
66        
67         def unset(var) {
68                 def task = {
69                                 def done = false
70                                 def r = new CommandResult()
71                                 sendMessage("unset", var)
72                                 while (! done) {
73                                         def m = msgQ.take()
74                                         if ((m?.event?.Name == "CHANNEL_EXECUTE_COMPLETE")
75                                                         && (m?.Application == "unset")
76                                                         && (m?.ApplicationData == var)) {
77                                                 done = true
78                                                 r.code = CommandResult.OK
79                                                 r.data = m
80                                         }                                       
81                                 }                               
82                                 return r               
83                         }
84                 executeAndWait(task, DEFAULT_TIMEOUT)           
85         }
86        
87         def queueDtmf(dtmfs) {
88                 def task = {
89                                 def done = false
90                                 def r = new CommandResult()
91                                 sendMessage("queue_dtmf", dtmfs)
92                                 while (! done) {
93                                         def m = msgQ.take()
94                                         if ((m?.event?.Name == "CHANNEL_EXECUTE_COMPLETE")
95                                                         && (m?.Application == "queue_dtmf")
96                                                         && (m?.ApplicationData == dtmfs)) {
97                                                 done = true
98                                                 r.code = CommandResult.OK
99                                                 r.data = m
100                                         }                                       
101                                 }                               
102                                 return r               
103                         }
104                 executeAndWait(task, DEFAULT_TIMEOUT)           
105         }
106        
107 /*
108         def hangup() {
109                 def task = {
110                                 def done = false
111                                 def r = new CommandResult()
112                                 sendMessage("hangup")
113                                 while (! done) {
114                                         def m = msgQ.take()
115                                         if ((m?.event?.Name == "CHANNEL_EXECUTE_COMPLETE")
116                                                         && (m?.Application == "queue_dtmf")
117                                                         && (m?.ApplicationData == dtmfs)) {
118                                                 done = true
119                                                 r.code = CommandResult.OK
120                                                 r.data = m
121                                         }                                       
122                                 }                               
123                                 return r               
124                         }
125                 executeAndWait(task, DEFAULT_TIMEOUT)           
126         }       
127 */     
128
129         def setVariable(String var, String value) {
130                 def task = {
131                                 def done = false
132                                 def r = new CommandResult()
133                                 sendMessage("set", "${var}=${value}")
134                                 while (! done) {
135                                         def m = msgQ.take()
136                                         if ((m?.event?.Name == "CHANNEL_EXECUTE_COMPLETE")
137                                                         && (m?.Application == "set")
138                                                         && (m?.ApplicationData == "${var}=${value}")) {
139                                                 done = true
140                                                 r.code = CommandResult.OK
141                                                 r.data = m
142                                         }                                       
143                                 }                               
144                                 return r               
145                         }
146                 executeAndWait(task, DEFAULT_TIMEOUT)   
147        
148         }
149        
150         def export(String var) {
151                 set("export_vars", var)
152         }
153        
154         def bridge(String number) {
155                 def task = {
156                                 def done = false
157                                 def r = new CommandResult()
158                                 sendMessage("bridge", number)
159                                 while (! done) {
160                                         def m = msgQ.take()
161 //                                      println m
162                                         if (((m?.event?.Name == "CHANNEL_EXECUTE_COMPLETE")
163                                                         && (m?.Application == "bridge")
164                                                         && (m?.ApplicationData == number)) ||
165                                                         ((m?.event?.Name == "CHANNEL_UNBRIDGE")
166                                                         && (m?.variable.bridge_channel == number)) || (m?.SESSIONCLOSED == "true")) {
167                                                 done = true
168                                                 r.code = CommandResult.OK
169                                                 r.data = m
170                                         }                                       
171                                 }                               
172                                 return r               
173                         }
174                 executeAndWait(task)
175         }
176        
177         def promptForDigits(int min, int max, String soundFile, String variableName, long timeout, String terminator) {
178                 def task = {
179                                 def done = false
180                                 def r = new CommandResult()
181                                 def appData = "${min} ${max} ${soundFile} ${variableName} ${timeout} ${terminator}"
182                                 sendMessage("read", appData, true)
183                                 while (! done) {
184                                         def m = msgQ.take()
185                                         if ((m?.event?.Name == "CHANNEL_EXECUTE_COMPLETE")
186                                                         && (m?.Application == "read")
187                                                         && (m?.ApplicationData == appData)) {
188                                                 done = true
189                                                 r.code = CommandResult.OK
190                                                 r.data = m
191                                         }                                       
192                                 }                               
193                                 return r               
194                         }
195                 executeAndWait(task, timeout)
196         }
197        
198         def originate(String url) {
199                 sendMessage("originate", url, true)
200         }
201        
202         def sleep(int sec) {
203                 sendMessage("sleep", new Integer(sec*1000).toString())
204         }
205        
206         def say(String phrase) {
207                 sendMessage("phrase", "spell,$phrase")
208         }
209        
210         def script() {
211                 return "jeprox"
212         }
213        
214         private def sendMessage(String app, String arg=null, boolean event_lock=false) {               
215                 String msg = "sendmsg\ncall-command: execute\nexecute-app-name: ${app}"
216                 if (arg) {
217                         msg += "\nexecute-app-arg: ${arg}"
218                 }
219                 if (event_lock) {
220                         msg += "\nevent-lock: ${event_lock}"
221                 }
222                 System.out.println(msg)
223                 session.write("$msg\n\n")
224         }
225 }

On Thu, Sep 18, 2008 at 5:49 PM, Luke Graybill <killarny@gmail.com> wrote:
I've been doing a lot of work recently with FreeSWITCH's mod_event_socket, and I wanted to comment a bit about the syntax used for commands through the socket while using asynchronous mode. I haven't tried the synchronous mode yet, as I always want to be free to be able to execute commands without waiting for other commands to finish. For instance, I need to be able to collect DTMF events while I'm playing a sound file, so that the user can do things like select menu items without listening to the entire menu first.

Asterisk's AMI protocol allows you to specify an ActionID along with every command that you send. Asterisk then includes this ActionID with every event that is related to that command, making it cake to coordinate an asynchronous client.

However, even in async mode, FreeSWITCH's mod_event_socket doesn't communicate any identifying information for command responses, or for events triggered by a previous command, unless one uses the bgapi command set. This command set is not applicable to every situation, though. It only applies to commands which manipulate the call; if one needs to manipulate the channel, then messages must be used through the sendmsg command set, which doesn't provide any specific identifying information.

Now, to complicate things, with all commands (even bgapi) the protocol works something like this: you send a command, and wait for a response from mod_event_socket. This response is assumed to be immediate before anything else the client might receive from mod_event_socket, and in the case of bgapi, this response will contain a job-id to use for comparing job-related events later.

For example, for the following command:

sendmsg
call-command: execute
execute-app-name: answer\n\n

The response is this:

Content-Type: command/reply
Reply-Text: +OK

That response is generic to nearly every single command sent, and is only really saying "The last transmission was a valid command, and didn't immediately fail". The command may actually fail later, and command specific feedback is generally contained in later events (which have no unique identifying information).

My issue here is that this seems to be forcing an asynchronous client to rely upon a synchronous ordering of response directly following command, thus violating the very concepts of an asynchronous protocol in which there should be no assumed order. Not only that, but this method increases the complexity of a client, which must be aware of limitations that wouldn't ordinarily be required by a true asynchronous protocol. An asynchronous client should be unconcerned with listening for a synchronous response to every command.

My suggested solution is to apply the job-id concept from bgapi to messages as well, and to go a step further; borrow the Asterisk idea of transmitting an identifier along with each command. Every response and event related to that command should then contain the very same identifier in the header.

_______________________________________________
Freeswitch-users mailing list
Freeswitch-users@lists.freeswitch.org
http://lists.freeswitch.org/mailman/listinfo/freeswitch-users
UNSUBSCRIBE:http://lists.freeswitch.org/mailman/options/freeswitch-users
http://www.freeswitch.org


uld then contain the very same identifier in the header.
_______________________________________________
Freeswitch-users mailing list
Freeswitch-users@lists.freeswitch.org
http://lists.freeswitch.org/mailman/listinfo/freeswitch-users
UNSUBSCRIBE:http://lists.freeswitch.org/mailman/options/freeswitch-users
http://www.freeswitch.org


v>
_______________________________________________
Freeswitch-users mailing list
Freeswitch-users@lists.freeswitch.org
http://lists.freeswitch.org/mailman/listinfo/freeswitch-users
UNSUBSCRIBE:http://lists.freeswitch.org/mailman/options/freeswitch-users
http://www.freeswitch.org