3

我正在使用 Starpy 和 Twisted 为 Asterisk IVR 实现开发一个 FastAgi 应用程序。到目前为止,该应用程序在只有一个电话呼叫时运行良好。

在建立第二个呼叫后,来自 Asterisk 的所有连续响应都将发送到第二个呼叫:属于第一个呼叫的流音频在第二个呼叫中听到,Hangup() 断开第二个呼叫(第​​一个保持连接直到手动断开与软电话的连接)

我正在使用带有 Elastix 发行版的 X-lite 软件电话。我的 FastAgi 服务器在 Windows 笔记本电脑上。在 Extensions_custom.conf 我有这些条目来路由呼叫:

扩展 => 2000,1,AGI(agi://10.0.0.7:4573) 扩展 => 2000,n,Hangup()

这是在服务器上设置协议的方式:

logging.basicConfig()
fastagi.log.setLevel( logging.DEBUG )
f = fastagi.FastAGIFactory(MyIVRApplication())
reactor.listenTCP(4573, f, 50, '10.0.0.167') 
reactor.run()

我使用 starpy 提供的示例之一构建了我的应用程序,即 DialPlan() 应用程序,它只播放应用程序被访问的次数。即使那个应用程序在运行而不是我的应用程序时也有同样的问题,只能正确处理一个调用。

我使用的是无线网络,但使用电缆时也会发生同样的情况。我尝试了带有星号的虚拟机(在 virtualbox 中)和物理机。一样。在 Android 手机上安装了不同的软件电话。一样。使用物理IP电话。一样。我唯一还没有尝试过的是将我的 FastAgi 服务器移动到 linux 机器上,而不是使用我的 windows 笔记本电脑。

任何帮助将不胜感激。

提前致谢。

赫克托

编辑:

我正在从 Asterisk 添加 AGI 调试日志。如您所见,在第二个呼叫建立后,所有的 Tx 和 Rx 命令都从第二个通道发送/接收。Asterisk 不再与保持连接的第一个通道对话。

== Using SIP RTP TOS bits 184
== Using SIP RTP CoS mark 5
-- Executing [2000@from-internal:1] AGI("SIP/4001-00000006", "agi://10.0.0.167") in new stack
AGI Tx >> agi_network: yes
<SIP/4001-00000006>AGI Tx >> agi_request: agi://10.0.0.167
<SIP/4001-00000006>AGI Tx >> agi_channel: SIP/4001-00000006
<SIP/4001-00000006>AGI Tx >> agi_language: en
<SIP/4001-00000006>AGI Tx >> agi_type: SIP
<SIP/4001-00000006>AGI Tx >> agi_uniqueid: 1360854557.6
<SIP/4001-00000006>AGI Tx >> agi_version: 1.8.11.0
<SIP/4001-00000006>AGI Tx >> agi_callerid: 4001
<SIP/4001-00000006>AGI Tx >> agi_calleridname: device
<SIP/4001-00000006>AGI Tx >> agi_callingpres: 0
<SIP/4001-00000006>AGI Tx >> agi_callingani2: 0
<SIP/4001-00000006>AGI Tx >> agi_callington: 0
<SIP/4001-00000006>AGI Tx >> agi_callingtns: 0
<SIP/4001-00000006>AGI Tx >> agi_dnid: 2000
<SIP/4001-00000006>AGI Tx >> agi_rdnis: unknown
<SIP/4001-00000006>AGI Tx >> agi_context: from-internal
<SIP/4001-00000006>AGI Tx >> agi_extension: 2000
<SIP/4001-00000006>AGI Tx >> agi_priority: 1
<SIP/4001-00000006>AGI Tx >> agi_enhanced: 0.0
<SIP/4001-00000006>AGI Tx >> agi_accountcode:
<SIP/4001-00000006>AGI Tx >> agi_threadid: -1219851376
<SIP/4001-00000006>AGI Tx >>
<SIP/4001-00000006>AGI Rx << ANSWER
<SIP/4001-00000006>AGI Tx >> 200 result=0
<SIP/4001-00000006>AGI Rx << STREAM FILE "custom/bienvenida" '' 0
    -- Playing 'custom/bienvenida' (escape_digits='') (sample_offset 0)
<SIP/4001-00000006>AGI Tx >> 200 result=0 endpos=24402
<SIP/4001-00000006>AGI Rx << GET DATA "custom/cuando_este_listo" 5000.0 1
    -- <SIP/4001-00000006> Playing 'custom/cuando_este_listo.slin' (language 'en')
<SIP/4001-00000006>AGI Tx >> 200 result=
<SIP/4001-00000006>AGI Rx << STREAM FILE "custom/14" '' 0
    -- Playing 'custom/14' (escape_digits='') (sample_offset 0)
<SIP/4001-00000006>AGI Tx >> 200 result=0 endpos=7028
<SIP/4001-00000006>AGI Rx << STREAM FILE "custom/menos" '' 0
    -- Playing 'custom/menos' (escape_digits='') (sample_offset 0)
<SIP/4001-00000006>AGI Tx >> 200 result=0 endpos=6762
<SIP/4001-00000006>AGI Rx << STREAM FILE "custom/9" '' 0
    -- Playing 'custom/9' (escape_digits='') (sample_offset 0)
<SIP/4001-00000006>AGI Tx >> 200 result=0 endpos=5666
<SIP/4001-00000006>AGI Rx << GET DATA "" 5000.0 2
  == Using SIP RTP TOS bits 184
  == Using SIP RTP CoS mark 5
    -- Executing [2000@from-internal:1] AGI("SIP/4002-00000007", "agi://10.0.0.167") in new stack
AGI Tx >> agi_network: yes
<SIP/4002-00000007>AGI Tx >> agi_request: agi://10.0.0.167
<SIP/4002-00000007>AGI Tx >> agi_channel: SIP/4002-00000007
<SIP/4002-00000007>AGI Tx >> agi_language: en
<SIP/4002-00000007>AGI Tx >> agi_type: SIP
<SIP/4002-00000007>AGI Tx >> agi_uniqueid: 1360854568.7
<SIP/4002-00000007>AGI Tx >> agi_version: 1.8.11.0
<SIP/4002-00000007>AGI Tx >> agi_callerid: 4002
<SIP/4002-00000007>AGI Tx >> agi_calleridname: device
<SIP/4002-00000007>AGI Tx >> agi_callingpres: 0
<SIP/4002-00000007>AGI Tx >> agi_callingani2: 0
<SIP/4002-00000007>AGI Tx >> agi_callington: 0
<SIP/4002-00000007>AGI Tx >> agi_callingtns: 0
<SIP/4002-00000007>AGI Tx >> agi_dnid: 2000
<SIP/4002-00000007>AGI Tx >> agi_rdnis: unknown
<SIP/4002-00000007>AGI Tx >> agi_context: from-internal
<SIP/4002-00000007>AGI Tx >> agi_extension: 2000
<SIP/4002-00000007>AGI Tx >> agi_priority: 1
<SIP/4002-00000007>AGI Tx >> agi_enhanced: 0.0
<SIP/4002-00000007>AGI Tx >> agi_accountcode:
<SIP/4002-00000007>AGI Tx >> agi_threadid: -1220097136
<SIP/4002-00000007>AGI Tx >>
<SIP/4002-00000007>AGI Rx << ANSWER
<SIP/4002-00000007>AGI Tx >> 200 result=0
<SIP/4002-00000007>AGI Rx << STREAM FILE "custom/bienvenida" '' 0
    -- Playing 'custom/bienvenida' (escape_digits='') (sample_offset 0)
<SIP/4001-00000006>AGI Tx >> 200 result= (timeout)
<SIP/4002-00000007>AGI Rx << STREAM FILE "custom/respuesta_incorrecta" '' 0
    -- Playing 'custom/respuesta_incorrecta' (escape_digits='') (sample_offset 0)
<SIP/4002-00000007>AGI Tx >> 200 result=0 endpos=14260
<SIP/4002-00000007>AGI Rx << GET DATA "custom/cuando_este_listo" 5000.0 1
    -- <SIP/4002-00000007> Playing 'custom/cuando_este_listo.slin' (language 'en')
<SIP/4002-00000007>AGI Tx >> 200 result=
    -- <SIP/4002-00000007>AGI Script agi://10.0.0.167 completed, returning 0
<SIP/4002-00000007>AGI Tx >> HANGUP
    -- Executing [2000@from-internal:2] Hangup("SIP/4002-00000007", "") in new stack
  == Spawn extension (from-internal, 2000, 2) exited non-zero on 'SIP/4002-00000007'
    -- Executing [h@from-internal:1] Macro("SIP/4002-00000007", "hangupcall") in new stack
    -- Executing [s@macro-hangupcall:1] GotoIf("SIP/4002-00000007", "1?endmixmoncheck") in new stack
    -- Goto (macro-hangupcall,s,9)
    -- Executing [s@macro-hangupcall:9] NoOp("SIP/4002-00000007", "End of MIXMON check") in new stack
    -- Executing [s@macro-hangupcall:10] GotoIf("SIP/4002-00000007", "1?nomeetmemon") in new stack
    -- Goto (macro-hangupcall,s,15)
    -- Executing [s@macro-hangupcall:15] NoOp("SIP/4002-00000007", "MEETME_RECORDINGFILE=") in new stack
    -- Executing [s@macro-hangupcall:16] GotoIf("SIP/4002-00000007", "1?noautomon") in new stack
    -- Goto (macro-hangupcall,s,18)
    -- Executing [s@macro-hangupcall:18] NoOp("SIP/4002-00000007", "TOUCH_MONITOR_OUTPUT=") in new stack
    -- Executing [s@macro-hangupcall:19] GotoIf("SIP/4002-00000007", "1?noautomon2") in new stack
    -- Goto (macro-hangupcall,s,25)
    -- Executing [s@macro-hangupcall:25] NoOp("SIP/4002-00000007", "MONITOR_FILENAME=") in new stack
    -- Executing [s@macro-hangupcall:26] GotoIf("SIP/4002-00000007", "1?skiprg") in new stack
    -- Goto (macro-hangupcall,s,29)
    -- Executing [s@macro-hangupcall:29] GotoIf("SIP/4002-00000007", "1?skipblkvm") in new stack
    -- Goto (macro-hangupcall,s,32)
    -- Executing [s@macro-hangupcall:32] GotoIf("SIP/4002-00000007", "1?theend") in new stack
    -- Goto (macro-hangupcall,s,34)
    -- Executing [s@macro-hangupcall:34] Hangup("SIP/4002-00000007", "") in new stack
  == Spawn extension (macro-hangupcall, s, 34) exited non-zero on 'SIP/4002-00000007' in macro 'hangupcall'
  == Spawn extension (from-internal, h, 1) exited non-zero on 'SIP/4002-00000007'
localhost*CLI>

编辑:

这是我用作指南的代码。它是 Starpy 库中作为示例提供的应用程序。当我把这个应用程序我得到相同的结果。我放这个是因为我的有点太大了。

#! /usr/bin/env python
"""Read digits from the user in various ways..."""
from twisted.internet import reactor, defer
from starpy import fastagi, error
import logging, time

log = logging.getLogger( 'hellofastagi' )

class DialPlan( object ):
    """Stupid little application to report how many times it's been accessed"""
    def __init__( self ):
        self.count = 0
    def __call__( self, agi ):
        """Store the AGI instance for later usage, kick off our operations"""
        self.agi = agi 
        return self.start()
    def start( self ):
        """Begin the dial-plan-like operations"""
        return self.agi.answer().addCallbacks( self.onAnswered, self.answerFailure )
    def answerFailure( self, reason ):
        """Deal with a failure to answer"""
        log.warn( 
            """Unable to answer channel %r: %s""", 
            self.agi.variables['agi_channel'], reason.getTraceback(),
        )
        self.agi.finish()
    def onAnswered( self, resultLine ):
        """We've managed to answer the channel, yay!"""
        self.count += 1
        return self.agi.wait( 2.0 ).addCallback( self.onWaited )
    def onWaited( self, result ):
        """We've finished waiting, tell the user the number"""
        return self.agi.sayNumber( self.count, '*' ).addErrback(
            self.onNumberFailed,
        ).addCallbacks(
            self.onFinished, self.onFinished,
        )
    def onFinished( self, resultLine ):
        """We said the number correctly, hang up on the user"""
        return self.agi.finish()
    def onNumberFailed( self, reason ):
        """We were unable to read the number to the user"""
        log.warn( 
            """Unable to read number to user on channel %r: %s""",
            self.agi.variables['agi_channel'], reason.getTraceback(),
        )

    def onHangupFailure( self, reason ):
        """Failed trying to hang up"""
        log.warn( 
            """Unable to hang up channel %r: %s""", 
            self.agi.variables['agi_channel'], reason.getTraceback(),
        )

if __name__ == "__main__":
    logging.basicConfig()
    fastagi.log.setLevel( logging.DEBUG )
    f = fastagi.FastAGIFactory(DialPlan())
    reactor.listenTCP(4573, f, 50, '10.0.0.167') # only binding on local interface
    reactor.run()
4

1 回答 1

1

感谢您提供完整信息:

现在,您所拥有的设置的明显缺陷如下: Dialplan() 在最初传递给 FastAGIFactory 时被启动。从那里开始 - 你总是一遍又一遍地访问同一个类的实例。现在 - 每当有新调用进入时,都会调用Dialplan().__call__()方法并且Dialplan().agi参数会被最后一次调用覆盖。查看您使用的示例,我建议尝试以下方法,然后从那里获取:

#! /usr/bin/env python
"""Read digits from the user in various ways..."""
from twisted.internet import reactor, defer
from starpy import fastagi, error
import logging, time

log = logging.getLogger( 'hellofastagi' )




class DialPlan( object ):
    """Stupid little application to report how many times it's been accessed"""
    def __init__( self,application, agi ):
        self.application = application
        self.agi = agi
    def start( self ):
        """Begin the dial-plan-like operations"""
        return self.agi.answer().addCallbacks( self.onAnswered, self.answerFailure )
    def answerFailure( self, reason ):
        """Deal with a failure to answer"""
        log.warn( 
            """Unable to answer channel %r: %s""", 
            self.agi.variables['agi_channel'], reason.getTraceback(),
        )
        self.agi.finish()
    def onAnswered( self, resultLine ):
        """We've managed to answer the channel, yay!"""
        return self.agi.wait( 2.0 ).addCallback( self.onWaited )
    def onWaited( self, result ):
        """We've finished waiting, tell the user the number"""
        return self.agi.sayNumber( self.application.count, '*' ).addErrback(
            self.onNumberFailed,
        ).addCallbacks(
            self.onFinished, self.onFinished,
        )
    def onFinished( self, resultLine ):
        """We said the number correctly, hang up on the user"""
        return self.agi.finish()
    def onNumberFailed( self, reason ):
        """We were unable to read the number to the user"""
        log.warn( 
            """Unable to read number to user on channel %r: %s""",
            self.agi.variables['agi_channel'], reason.getTraceback(),
        )

    def onHangupFailure( self, reason ):
        """Failed trying to hang up"""
        log.warn( 
            """Unable to hang up channel %r: %s""", 
            self.agi.variables['agi_channel'], reason.getTraceback(),
        )


class CallCounterApplication(object):
    def __init__( self ):
        self.count = 0

    def __call__(self,agi):
        self.count = self.count+1
        dp = Dialplan(self,agi)
        return dp.start()


if __name__ == "__main__":
    logging.basicConfig()
    fastagi.log.setLevel( logging.DEBUG )
    f = fastagi.FastAGIFactory(CallCounterApplication())
    reactor.listenTCP(4573, f, 50, '10.0.0.167') # only binding on local interface
    reactor.run()

我在这里所做的是创建一个应用程序容器,它将在每次成功调用时创建一个新的 Dialplan() 实例。

编辑:请注意 - 因为我没有可用的星号框,所以没有经过测试,并且在 1:1 复制粘贴时可能无法运行。但是原理还是一样的

于 2013-02-21T07:39:14.323 回答