Elastix 2.5的系统是Centos 5.9,Asterisk 11。在测试dialpan拨号方案使用odbc mysql的时候报错驱动/usr/lib64/libmyodbc3_r.so找不到,经验证是系统的mysql-connector-odbc没有安装。
在通过yum安装的时候又发现源已经停止服务了,无法下载,找到一个国外源fq下载完,在另外一台机子上则是选择本地安装yum包,下载地址:
https://centos.pkgs.org/5/centos-x86_64/mysql-connector-odbc-3.51.26r1127-2.el5.x86_64.rpm.html (x64)
https://centos.pkgs.org/5/centos-i386/mysql-connector-odbc-3.51.26r1127-2.el5.i386.rpm.html (x86)
下载完后可以放到当前目录 shell执行:
yum -y localinstall mysql-connector-odbc-3.51.26r1127-2.el5.x86_64.rpm
安装完成后还需配置/etc/odbc.ini:
[MYSQL-asteriskcdrdb] Description = MySQL connection to 'asterisk' database Driver = MySQL Database = asteriskcdrdb Server = localhost Port = 3306 Socket = /var/lib/mysql/mysql.sock User = root Password = MyPass Option = 3
配置完成后可以在shell中测试:
[root@Elastix ~]# isql -v MYSQL-asteriskcdrdb +---------------------------------------+ | Connected! | | | | sql-statement | | help [tablename] | | quit | | | +---------------------------------------+ SQL> show tables; +-----------------------------------------------------------------+ | Tables_in_asteriskcdrdb | +-----------------------------------------------------------------+ | cdr | +-----------------------------------------------------------------+ SQLRowCount returns 1 1 rows fetched
这就算完成了一半,还需在/etc/asterisk/res_odbc.conf中配置dsn:
[asteriskcdrdb] enabled=>yes dsn=>MySQL-asteriskcdrdb pooling=>no limit=>1 pre-connect=>yes share_connections => yes isolation => repeatable_read forcecommit => yes
在/etc/asterisk/func_odbc.conf中配置自定义函数(此处dsn指res_odbc.conf中的[name]):
[SQL]
dsn=asteriskcdrdb
readsql=${ARG1}
synopsis=ODBC_SQL(sql)
具体配置说明详见:https://wiki.asterisk.org/wiki/display/AST/Getting+Asterisk+Connected+to+MySQL+via+ODBC
http://www.asteriskdocs.org/en/3rd_Edition/asterisk-book-html-chunk/getting_funky.html
配置完后注意在Asterisk CLI中执行reload或者shell执行:
asterisk -x reload
最后就可以在dialpan中测试使用了,如下面的例子(/etc/asterisk/extensions.conf)拨打876在asterisk日志中输出结果:
[from-internal]
exten => 876,1,Answer
exten => 876,n,Noop(${ODBC_SQL("select now()")})
exten => 876,n,Noop(${ODBC_SQL("select top 1 uniquid from cdr order by calldate desc")})
exten => 876,n,Background(star)
exten => 876,n,Wait(60)
include => from-internal-noxfer
include => from-internal-xfer
include => bad-number ; auto-generated
首先你得有个获取队列情况的接口。如果使用ami的话太麻烦,接收的数据控制不好断节,所以我用了php+shell的形式获取数据。由于是局域网内使用,所以没加验证,有需求的自己加一下。
vi /var/www/html/cli.php
按i进入插入模式,粘贴上如下代码:
<?php
if(isset($_GET["cmd"])){ //先判断是否是预期参数
$fp=popen("asterisk -x \"".$_GET["cmd"]."\"","r"); //asterisk -x单句命令执行
while(!feof($fp))
echo fgets($fp,4096);
pclose($fp);
}
?>
按Esc输入":wq"保存退出。好了这个接口就可以使用来执行Asterisk CLI命令了。
然后通过你自己的坐席数据库接口,结合ahk写一个客户端。
;数据库配置信息
host = 135.230.71.1
api = http://%host%/sqlapi.php
#MaxMem 1024
;检查连通性
if Not InStr(ping_info := ping(host),"正常")
{
MsgBox, 4112, 网络错误, % ping_info
ExitApp
}
;创建GUI
gui, add, edit, x0 y0 w800 h400 vshow,
gui, show, , 本地呼叫系统坐席队列监控
gui, +AlwaysOnTop +Resize
;循环监控坐席状态 3秒间隔
Loop
{
out =
Queue := QueueStatus() ;获取队列状态数组
tp := get_result_obj("select 队列,业务 from [我是口令呼叫系统数据库].[dbo].[呼叫系统业务分派] where 状态=1 and ((cast(getdate() as time) between 开始时间1 and 结束时间1) or (cast(getdate() as time) between 开始时间2 and 结束时间2));") ;这是一个获取当前正在执行的任务的语句
task := []
for k,v in tp
task[v[1]] := v[2]
tp := get_result_obj("select 分机,工号,姓名 from [我是口令呼叫系统数据库].[dbo].[呼叫系统分机信息] where [工号] is not null") ;获取已经登记的坐席信息
user := []
for k,v in tp
user[v[1]] := {"id":v[2],"name":v[3]}
;格式化输出
for group,gv in Queue
{
out .= task[group] "(" group ") 策略:" gv["strategy"] " " gv["incall"] "个等待来电 平均摘机时间:" gv["holdtime"] "(秒)平均通话时长:" gv["talktime"] "(秒)`n"
for exten,mv in gv["member"]
{
sp =
loop % 20-strlen(user[exten]["name"])
sp .= " "
;很笨的一种汉化替换方法
status := RegExReplace(mv["status"],"In use","通话中")
status := RegExReplace(status,"Not in use","空闲")
status := RegExReplace(status,"Ringing","振铃")
status := RegExReplace(status,"Unavailable","不可用")
status := RegExReplace(status,"On hold","保持")
out .= " 坐席:" user[exten]["name"] "(" user[exten]["id"] " " exten ")" sp " 类型:" mv["kind"] " 状态:" status " 本次签入后呼叫量:" mv["callcount"] " 最后一次摘机:" mv["lastcalltime"] "秒前`n"
}
for k,cv in gv["caller"]
out .= " 来电:" k "." cv["phone"] " 等待时间:" cv["wait"] "秒`n"
out .= "`n"
}
GuiControl, , show, % out
Sleep, 3000
}
Return
;界面调整尺寸
GuiSize:
GuiControl, move, show, % "w" A_GuiWidth "h" A_GuiHeight
Return
;获取状态函数
QueueStatus(){
src := URLDownloadToVar("http://135.230.71.2/cli.php?cmd=queue show") ;你的Asterisk的cli接口
queues := []
Loop, Parse, src, `n, `r
{
line := A_LoopField
if RegExMatch(line,"(\d+) has (\d+) calls \(max unlimited\) in '(\w+)' strategy \((\d+)s holdtime, (\d+)s talktime\), W:\d+, C:\d+, A:\d+, SL:[0-9\.]+% within \d+s",qtm) ;队列标题匹配
{
cqueue := qtm1
queues[cqueue] := {"incall":qtm2,"strategy":qtm3,"holdtime":qtm4,"talktime":qtm5,member:{},caller:{}}
}
else if RegExMatch(line,"\s+sip/(\d+) with penalty 1 \(ringinuse disabled\) \((\w+)\) \((.+)\) has taken (\d+) calls \(last was (\d+) secs ago\)",mcm) ;坐席匹配1
queues[cqueue]["member"][mcm1] := {"kind":mcm2,"status":mcm3,"callcount":mcm4,"lastcalltime":mcm5}
else if RegExMatch(line,"\s+sip/(\d+) with penalty 1 \(ringinuse disabled\) \((\w+)\) \((.+)\) has taken no calls yet",mcm) ;坐席匹配2
queues[cqueue]["member"][mcm1] := {"kind":mcm2,"status":mcm3,"callcount":0,"lastcalltime":"NULL"}
else if RegExMatch(line,"\s+(\d+). DAHDI/i1/(\d+)-\w+ \(wait: 0:(\d+), prio: 0\)",ccm) ;来电匹配
queues[cqueue]["caller"][ccm1] := {"phone":ccm2,"wait":ccm3}
}
Return queues
}
;返回csv带标题格式查询结果
get_result_with_colname(sql){
global api
result := URLDownloadToVar(api "?str=" urlencode(sql "我是口令") "&o=1","UTF-8")
return RegExReplace(result,"`n$","")
}
;返回csv查询结果
get_result(sql){
global api
result := URLDownloadToVar(api "?str=" urlencode(sql "我是口令"),"UTF-8")
return RegExReplace(result,"`n$","")
}
;返回查询数组
get_result_obj(sql){
global api
result := URLDownloadToVar(api "?str=" urlencode(sql "我是口令"),"UTF-8")
;return RegExReplace(result,"`n$","")
line := strsplit(result,"`n","`r")
for k,v in line
{
if v
line[k] := strsplit(v,",")
Else
line.remove(k)
}
return line
}
;返回1行数据
get_1_result(sql){
global api
result := URLDownloadToVar(api "?str=" urlencode(sql "我是口令"),"UTF-8")
return RegExReplace(result,"^([^\n]*)\n.*$","$1")
}
;返回执行影响结果
get_rowcount(sql){
global api
result := URLDownloadToVar(api "?str=" urlencode(sql "我是口令") "&o=2","UTF-8")
return RegExReplace(result,"^([^\n]*)\n.*$","$1")
}
URLDownloadToVar(url, Encoding = "",Method="GET",postData=""){ ;网址,编码,请求方式,post数据
hObject:=ComObjCreate("WinHttp.WinHttpRequest.5.1")
if Method = GET
{
Try
{
hObject.Open("GET",url)
hObject.Send()
}
catch e
return -1
}
else if Method = POST
{
Try
{
hObject.Open("POST",url,False)
hObject.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
hObject.Send(postData)
}
catch e
return -1
}
if (Encoding && hObject.ResponseBody)
{
oADO := ComObjCreate("adodb.stream")
oADO.Type := 1
oADO.Mode := 3
oADO.Open()
oADO.Write(hObject.ResponseBody)
oADO.Position := 0
oADO.Type := 2
oADO.Charset := Encoding
return oADO.ReadText(), oADO.Close()
}
return hObject.ResponseText
}
Ansi2UTF8(sString)
{
Ansi2Unicode(sString, wString, 0)
Unicode2Ansi(wString, zString, 65001)
Return zString
}
UTF82Ansi(zString)
{
Ansi2Unicode(zString, wString, 65001)
Unicode2Ansi(wString, sString, 0)
Return sString
}
Ansi2Unicode(ByRef sString, ByRef wString, CP = 0)
{
nSize := DllCall("MultiByteToWideChar"
, "Uint", CP
, "Uint", 0
, "Uint", &sString
, "int", -1
, "Uint", 0
, "int", 0)
VarSetCapacity(wString, nSize * 2)
DllCall("MultiByteToWideChar"
, "Uint", CP
, "Uint", 0
, "Uint", &sString
, "int", -1
, "Uint", &wString
, "int", nSize)
}
Unicode2Ansi(ByRef wString, ByRef sString, CP = 0)
{
nSize := DllCall("WideCharToMultiByte"
, "Uint", CP
, "Uint", 0
, "Uint", &wString
, "int", -1
, "Uint", 0
, "int", 0
, "Uint", 0
, "Uint", 0)
VarSetCapacity(sString, nSize)
DllCall("WideCharToMultiByte"
, "Uint", CP
, "Uint", 0
, "Uint", &wString
, "int", -1
, "str", sString
, "int", nSize
, "Uint", 0
, "Uint", 0)
}
urlencode(string){
string := Ansi2UTF8(string)
StringLen, len, string
Loop % len
{
SetFormat, IntegerFast, hex
StringMid, out, string, %A_Index%, 1
hex := Asc(out)
hex2 := hex
StringReplace, hex, hex, 0x, , All
SetFormat, IntegerFast, d
hex2 := hex2
If (hex2==33 || (hex2>=39 && hex2 <=42) || hex2==45 || hex2 ==46 || (hex2>=48 && hex2<=57) || (hex2>=65 && hex2<=90) || hex2==95 || (hex2>=97 && hex2<=122) || hex2==126)
content .= out
Else
content .= "`%" hex
}
Return content
}
ping(ip){
FileEncoding,
RunWait, %ComSpec% /c ping -n 1 %ip% >%A_Temp%\ahk_ping.tmp, , Hide
FileRead, content, %A_Temp%\ahk_ping.tmp
StringReplace, content, content, `r, , All
StringSplit, var, content, `n
If content Contains 请求超时,Request timed out
Return "请求超时"
If content Contains 找不到主机,could not find host
Return "找不到主机 "
If content Contains 无法访问目标主机,Destination host unreachable
Return "无法访问目标主机 "
Else
{
time := RegExReplace(var3, "(来自|Reply from) \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}[\s的回复|]*: (字节|bytes)=\d{1,3}\ (时间|time)[=<](\d{1,3})ms TTL=\d{1,3}","$4")
Return "正常 time:" time "ms"
}
FileEncoding, UTF-8
}
;调试用 输出数组
show_obj(obj,menu_name:=""){
static id
if menu_name =
{
main = 1
id++
menu_name := id
}
Menu, % menu_name, add,
Menu, % menu_name, DeleteAll
for k,v in obj
{
if (IsObject(v))
{
id++
submenu_name := id
Menu, % submenu_name, add,
Menu, % submenu_name, DeleteAll
Menu, % menu_name, add, % k ? "【" k "】[obj]" : "", :%submenu_name%
show_obj(v,submenu_name)
}
Else
{
Menu, % menu_name, add, % k ? "【" k "】" v: "", MenuHandler
}
}
if main = 1
menu,% menu_name, show
MenuHandler:
return
}
GuiClose:
ExitApp
好了,看下效果:
1、 因为在安装asterisk的时候是没有对应的codec_g729.so和codec_g723.so模块的。
想要查看自己的asterisk有什么模块,可以进入asterisk控制台:
默认没有可用模块的情况:
添加可用G729和G723模块以后的情况:

下面讲述如何添加可用模块:
全过程请参照以下网页配置:
http://wiki.kolmisoft.com/index.php/G723/G729_Codec_installation#Testing
下载对应模块地址:
请根据自己服务器的配置和环境选择对应的模块下载。
1、需要修改对应的confi文件:/etc/asterisk/codecs.conf下添加
[g723]
; 6.3Kbps stream, default
sendrate=63
; 5.3Kbps
;sendrate=53
2、 在sip.conf或者iax.conf中全局下添加:a
disallow=all
allow=g729
allow=g723
操作上需要进行如下操作:
1、 下载对应硬件和软件编译环境的G729和G723语音模块。
2、 上传到服务器对应的文件夹/usr/lib/asterisk/modules/
3、 改名对应的模块,对应的名字为codec_g729.so和codec_g723.so
4、 给两个对应的模块赋予权限:chmod 777 codec_g729.so 和chmod 777 codec_g723.so
5、 在asterisk的控制台上使用命令加载刚才添加的两个模块:module load codec_g729.so和
module load codec_g723.so.
然后手动加载模块:
加载之后,查看看是否支持此模块:
使用命令查看是否有此模块:
至此,添加成功可以使用g729和g723编码进行通讯了。
49 queries in 5.419 seconds |