diff --git a/docs/manual.docbook b/docs/manual.docbook index 151d6f8..f4b98bd 100644 --- a/docs/manual.docbook +++ b/docs/manual.docbook @@ -1412,20 +1412,20 @@ not present, current time as returned by localtime() is used. somewhere which must be writable to root. We also need some test audio file for sending it. Let's take the beep which is distributed with &cs;. - mkdir incoming-examples -chown 777 incoming-examples -cd incoming-examples + mkdir capisuite-examples +chmod 777 capisuite-examples # make it world-writeable +cd capisuite-examples cp /usr/local/share/capisuite/beep.la . Perhaps you must change the path in the last line to reflect your installation. Now copy and paste the example shown here to a file called example.py - in this directory. + in this directory. Don't forget to change the third line. example.py import capisuite -my_path="/path/to/the/just/created/incoming-examples/" +my_path="/path/to/the/just/created/capisuite-examples/" def callIncoming(call,service,call_from,call_to): capisuite.connect_voice(call,10) @@ -1535,7 +1535,7 @@ ImportError: No module named capisuite example.py, improved import capisuite -my_path="/path/to/the/just/created/incoming-examples/" +my_path="/path/to/the/just/created/capisuite-examples/" def callIncoming(call,service,call_from,call_to): try: @@ -1547,8 +1547,9 @@ def callIncoming(call,service,call_from,call_to): capisuite.disconnect(call) else: capisuite.reject(call,1) + capisuite.disconnect(call) except capisuite.CallGoneError: - pass + capisuite.disconnect(call) @@ -1563,8 +1564,13 @@ def callIncoming(call,service,call_from,call_to): Have a look at the called number (please replace 123 with the number &cs; should accept)... + + Play the announcement we recorded in the last section. If you don't + like it, simply record a new one and move the recorded.la + again to announce.la. + - ... and ignore the call if it isn't what we want to accept. + Ignore the call if it isn't what we want to accept. The second parameter tells the exact reason for the reject - you can ignore a call (any other ISDN device or phone will still be ringing for that number) by using 1, actively disconnect by @@ -1572,18 +1578,18 @@ def callIncoming(call,service,call_from,call_to): available in the ISDN specification (see for available codes). - - Play the announcement we recorded in the last section. If you don't - like it, simply record a new one and move the recorded.la - again to announce.la. + + You always have to call disconnect at the end of your script, + as this will wait for the end of the call, while reject only initiates + the call reject. Otherwise you'll get a warning in the error log stating that you script + didn't disconnect completely. This is the block handling CallGoneError - the exception &cs; raises when the call is disconnected by the other party. - This time, we just define it and do nothing (pass - is the keyword for do nothing in Python). In normal - scripts, you'll perhaps log the end of the call or free any ressources - you allocated earlier here. + You should also call disconnect here, because it + can take some time from the disconnection signal to the completion + of the process. @@ -1610,20 +1616,21 @@ def callIncoming(call,service,call_from,call_to): The function will find a new unique filename in the given directory. The created filename will be "prefix-XXX.suffix" where XXX - is the next free number started at 0. The file is created with a size of 0 bytes and the name is - returned as filename. + is the next free number started at 0. The next free number is remembered in a file + prefix-nextnr and the created name is returned as + filename. Now we can simply add this call to our script: using unique filenames import capisuite,cs_helpers -my_path="/path/to/the/just/created/incoming-examples/" +my_path="/path/to/the/just/created/capisuite-examples/" def callIncoming(call,service,call_from,call_to): try: - filename=cs_helpers.uniqueName(my_path,"voice","la") if (call_to=="123"): + filename=cs_helpers.uniqueName(my_path,"voice","la") capisuite.connect_voice(call,10) capisuite.audio_send(call,my_path+"announce.la") capisuite.audio_send(call,my_path+"beep.la") @@ -1632,8 +1639,7 @@ def callIncoming(call,service,call_from,call_to): else: capisuite.reject(call,1) except capisuite.CallGoneError: - pass - + capisuite.disconnect(call) If you're interested in other functions which cs_helpers.py defines, just have a look at the reference at @ref. @@ -1652,7 +1658,7 @@ def callIncoming(call,service,call_from,call_to): Adding fax functions import capisuite,cs_helpers,os -my_path="/path/to/the/just/created/incoming-examples/" +my_path="/path/to/the/just/created/capisuite-examples/" def callIncoming(call,service,call_from,call_to): try: @@ -1665,13 +1671,14 @@ def callIncoming(call,service,call_from,call_to): capisuite.audio_receive(call,filename,20,3,1) dtmf=capisuite.read_DTMF(call,0) if (dtmf=="X"): - os.unlink(filename) + if (os.access(filename,os.R_OK)): + os.unlink(filename) faxIncoming(call) capisuite.disconnect(call) else: capisuite.reject(call,1) except capisuite.CallGoneError: - pass + capisuite.disconnect(call) def faxIncoming(call): capisuite.switch_to_faxG3(call,"+49 123 45678","Test headline") @@ -1724,11 +1731,11 @@ def faxIncoming(call): "X", a fax machine is calling and we should start our fax handling routines. - As we have created a file for storing the received voice call by - calling cs_helpers.uniqueName which we don't need any - more (as we'll create a new file named fax-XXX.sff), - we must delete it by calling os.unlink. There's a small - chance that + Possibly, the announcement was so short that the recording has started + already before the fax is recognized. We won't save an empty file containing + only the fax beep and so we test if it was created (os.access + checks for the existence of a file) and delete it if needed by calling + os.unlink. Fax handling was realized in a seperate function which is called here. @@ -1757,10 +1764,10 @@ def faxIncoming(call): - Now we've finished our small tutorial. Now it's up to you - you can play with + Congrats. You've finished my small tutorial. Now it's up to you - you can play with the created script and try to make it more complete. There's still much to do - sending received calls to a user via E-Mail, log connections, ... If you want to - complete this script, the will be helpful. + complete this script, will be helpful. You can also read on here to have a short look on the idle scripts, followed by a quick overview of the structure of the default scripts shipped with &cs;. @@ -1774,13 +1781,13 @@ def faxIncoming(call): As written before, the idle script will be called by &cs; in regular intervals allowing you to look for stored jobs somewhere and sending them to the destinations. - The example shown here will look for a file fax-XXXX.sff in - the example directory. This file will be faxed to the destination indicated by + The example shown here will look for a file job-XXXX.sff in + the example directory we created in the last section. This file will be faxed to the + destination indicated by XXXX if found. If you have no valid destination where you can - send test faxes to, how about using &cs; for receiving also? If you want to do this, - replace XXXX by the number our just created incoming script will - handle. In this case, replace XXXX by the number your incoming script - handles. This won't work if your ISDN card can't handle two fax transfers at the same time + send test faxes to, how about using &cs; for receiving also? In this case, replace + XXXX by the number your incoming script handles. This won't work + if your ISDN card can't handle two fax transfers at the same time (some old AVM B1 cards have this limitation, for example). We now need one or more fax files in the SFF format for our tests, so please @@ -1792,13 +1799,13 @@ def faxIncoming(call): which searches the files and extracts the destination numbers first. If this works, we can continue by adding the &cs; specific calls later. - idle_test.py + idle_example.py import os,re -my_path="/path/to/your/example/dir/" +my_path="/path/to/your/capisuite-examples/" files=os.listdir(my_path) -files=filter (lambda s: re.match("fax-.*\.sff",s),files) +files=filter (lambda s: re.match("job-.*\.sff",s),files) for job in files: destination=job[4:-3] @@ -1820,7 +1827,7 @@ for job in files: This line is a little bit more tricky. It filters out all filenames - which doesn't follow the rule starting with "fax-", then any + which doesn't follow the rule starting with "job-", then any number of chars, ending with ".sff" from the list. This is done by the filter function. The function expects the name of a function which checks the rule as first parameter and the list to filter @@ -1844,18 +1851,143 @@ for job in files: - Now, save the script as idle_test.py in our example dir and run - it by calling python idle_test.py. + Now, save the script as idle_example.py in our example dir and run + it by calling python idle_example.py. If you have provided SFF files with the right names they should be shown line by line now. But... Obviously something doesn't work right here. The destination includes the ".". Indeed, I've made a mistake when indexing the string. It should be - destination=job[4:-4] instead of [4:-3]. It was a good - idea that we didn't + destination=job[4:-4] instead of [4:-3]. So let's change + that and test again. It should work now. + + As we know now that the basic parts work, we can add the real communication functions. + + Please save this example to idle_example.py in your example directory, again. + + idle_example.py, version for &cs; + import os,re,capisuite + +my_path="/path/to/your/capisuite-examples/" +my_number="678" +my_stationID="+49 123 45678" +my_headline="example headline" + +def idle(capi): + files=os.listdir(my_path) + files=filter (lambda s: re.match("job-.*\.sff",s),files) + + for job in files: + destination=job[4:-4] + capisuite.log("sending "+job+" to destination "+destination,1) + try: + (call,result)=capisuite.call_faxG3(capi,1,my_number,destination,60,my_stationID,my_headline) + if (result!=0): + capisuite.log("job "+job+" failed at call setup with reason "+str(hex(result)),1) + os.rename(my_path+job,my_path+"failed-"+job) + return + capisuite.fax_send(call,my_path+job) + (result,resultB3)=capisuite.disconnect(call) + except capisuite.CallGoneError: + (result,resultB3)=capisuite.disconnect(call) + + if (result in (0,0x3400,0x3480,0x3490) and resultB3==0): + capisuite.log("job "+job+" was successful",1) + os.rename(my_path+job,my_path+"done-"+job) + return + else: + capisuite.log("job "+job+" failed during send with reasons "+str(hex(result))+","+str(hex(resultB3)),1) + os.rename(my_path+job,my_path+"failed-"+job) + + + + + Some parameters for sending the fax are set here. my_number is your own number + which is used for sending the fax. my_stationID is the fax station ID, which will + be transmitted to the other fax machine and shown on the sent fax page. Only digits and "+" are allowed. + You can also define a short text which will show up in the fax headline in fax_headline. + + + + As explained in , you have to define a function called + idle which will be executed in regular intervals by &cs; then. So all code + is moved to this function. + + + We can't print messages to stdout as the script will be run in the context of a daemon. So &cs; + provides functions for creating entries in the &cs; log file. log expects at + least two parameters: the message and a log level. This level corresponds to the log level setting + in the global &cs; configuration (see ). If the level of the message is + less or equal the level set in the configuration, it is printed to the logs. + So you can insert messages for debug purposes which aren't printed to the logs in normal operation + by using levels higher than 1. + + + This function initiates an outgoing call using the fax service. The parameters are: + + reference to the Capi object you got from &cs; (parameter to idle). + the (number of the) controller to use for outgoing calls. The first controller has always number "1". + own number to use for the outgoing call + destination number to call + maximum time to wait for a successful connection in seconds + the fax station ID + fax headline + + The function returns a tuple containing of a reference to the created call and an error value. + + + This block checks if the connection was successful. For a detailled description of possible error values, + please see the . 0 means "everything was ok, call is established". + + + If the call wasn't successful, rename the fax file to prevent the script from sending the same file + over and over. + + + Don't forget to exit the idle function if the call couldn't be established! + + + Another very simple &cs; command: send the given file as fax document. + + + We previously ignored the reasons why a call was disconnected. Now we have to + analyze them because we need to know if the file was transferred succesful. Therefore, + disconnect returns a tuple containing of the physical and logical error value. Every + ISDN connection contains a physical and (at least) a logical connection. One could imagine the physical + connection as "the wire" connecting us to our destination, while the logical connection refers to + the fax protocol which uses this "wire". You have to look on both values to see if everything was ok. + + + Allowed values for the physical connection are 0,0x3400,0x3480 and 0x3490. These all mean "no error occured, + call was disconnected normally". The logical may only be 0 if everything went ok. For further information + of error values, refer to , please. + + + + After you've saved the file and changed the default values to your own configuration, please alter the value of + idle_script in the &cs; configuration to point to this script. + + Restart &cs; and watch the logs. Some seconds later, the job-XXX.sff files should've been sent and renamed to either + done-job-XXX.sff or failed-job-XXX.sff. If the job failed, please consult the error log and the error values explained in + and . + + Hopefully, this tutorial helped you in understanding how to code your own scripts. Please continue with changing the + examples or the files distributed with &cs; (read before). You will find a + complete reference of the available commands in . + + If you have any trouble in getting your scripts up and running, please use the &cs; mailing lists. Structural overview of the default scripts - jjj + incoming.py + + + idle.py + + + cs_helpers.py + + + &cs; command reference @@ -1891,6 +2023,7 @@ for job in files: by &cs;. + 0 - Normal call clearing, no error 3301 - Protocol error layer 1 (broken line or B-channel removed by signalling protocol) 3302 - Protocol error layer 2 3303 - Protocol error layer 3 @@ -1914,6 +2047,7 @@ for job in files: are mapped to the CAPI numbers see the CAPI specification, parameter "Info". + 3400 - Normal termination, no reason available 3480 - Normal termination 3481 - Unallocated (unassigned) number 3482 - No route to specified transit network @@ -2050,62 +2184,10 @@ for job in files: 300A - Flags not supported (reserved bits) 300B - Facility not supported 300C - Data length not supported by current protocol - 300D - Reset procedure not supported by current protocol + 300D - Reset procedure not supported by current protocol - - -