接下来是最关键的对象登场了:语音控制核心线程!
namespace CallCenterApp
{
public class CallThread : MainThread
{
public SocketListener _socketListener;
public CardHelper.refreshlv _RefreshLv;
int chn;
Trunk[] _channel;
private static readonly string recordpaht = ConfigurationManager.AppSettings["recordpath"];
public CallThread(Trunk[] ichannel,int ichn)
{
chn = ichn;
_channel = ichannel;
}
public override void run()
{
do
{
base.run();
MainEvent();
Thread.Sleep(50);
} while (isStart);
}
private void MainEvent()
{
Tc08a32.PUSH_PLAY();
Tc08a32.FeedSigFunc();
for (int i = 0; i < chn; i++)
{
_RefreshLv(_channel[i]);
if (_channel[i].chnstatus == CHN_STATUS.LINE_INIT)
{
ResetChannel(i);
}
else
{
if (_channel[i].chntype == CHN_TYPE.CHTYPE_TRUNK)
{
if (Tc08a32.Sig_CheckBusy(i) == 1) //监测到忙音
{
if (_channel[i].connchn != -1) Tc08a32.StartPlaySignal(_channel[i].connchn, 2); ////给与外线相连的内线忙音
ResetChannel(i);
}
}
if (_channel[i].chntype == CHN_TYPE.CHTYPE_USER)
{
if (!Tc08a32.OffHookDetect(i) && _channel[i].chnstatus != CHN_STATUS.LINE_IDLE && _channel[i].busyflag == false)
ResetChannel(i);
}
}
DoWork(i);
}
}
private void ResetChannel(int ichn)
{
Tc08a32.StopPlayFile(ichn);
Tc08a32.StartPlaySignal(ichn, (int)SingleVType.SIG_STOP);
_channel[ichn].callee = string.Empty;
_channel[ichn].caller = string.Empty;
_channel[ichn].calltime = string.Empty;
_channel[ichn].chn = ichn;
_channel[ichn].connchn = -1;
_channel[ichn].filename = null;
_channel[ichn].dtmflength = 0;
_channel[ichn].dtmf = string.Empty;
_channel[ichn].play_count = 0;
_channel[ichn].workflag = false;
_channel[ichn].busyflag = false;
_channel[ichn].eventtype = -1;
_channel[ichn].dispstatus = CardHelper.GetDescByStatus(CHN_STATUS.LINE_IDLE);
_channel[ichn].chnstatus = CHN_STATUS.LINE_IDLE;
if (_channel[ichn].chntype == CHN_TYPE.CHTYPE_TRUNK)
{
_myLog(LogFile.Trace, "重置外线通道属性,通道号" + ichn.ToString());
Tc08a32.HangUp(ichn);
Tc08a32.InitDtmfBuf(ichn);
Tc08a32.ResetCallerIDBuffer(ichn);
}
if (_channel[ichn].chntype == CHN_TYPE.CHTYPE_USER)
{
_myLog(LogFile.Trace, "重置内线通道属性,通道号" + ichn.ToString());
Tc08a32.FeedPower(ichn);
}
}
//核心线程
private void DoWork(int i)
{
switch (_channel[i].chnstatus)
{
case CHN_STATUS.LINE_IDLE:
{
if (Tc08a32.RingDetect(i)) //检测外线振铃,或者内线是否摘机
{
_channel[i].busyflag = true;
if (_channel[i].chntype == CHN_TYPE.CHTYPE_TRUNK)
{
_channel[i].dispstatus = "外线用户振铃";
byte[] IDStr = new byte[128];
/*
int callerId = Tc08a32.GetCallerIDStr(i, IDStr);
if (callerId == 3 || callerId == 4)
{
Tc08a32.OffHook(i); //外线摘机
Tc08a32.StartSigCheck(i);
string caller = Encoding.UTF8.GetString(IDStr);
_channel[i].caller = caller;
_myLog(LogFile.Trace, "接收的主叫号码:" + caller);
_channel[i].chnstatus = CHN_STATUS.LINE_WELCOME;
}*/
Tc08a32.OffHook(i); //外线摘机
Tc08a32.StartSigCheck(i);
_channel[i].chnstatus = CHN_STATUS.LINE_WELCOME;
}
/*
if (_channel[i].chntype == CHN_TYPE.CHTYPE_USER)
{
Tc08a32.StartHangUpDetect(i);
Tc08a32.StartPlaySignal(i, (int)SingleVType.SIG_DIALTONE);
_channel[i].dispstatus = "内线用户振铃";
_channel[i].chnstatus = CHN_STATUS.LINE_GET_DTMF; //内线收DTMF
}*/
}
}
break;
case CHN_STATUS.LINE_WELCOME:
{
Tc08a32.InitDtmfBuf(i); //清空接收DTMF的缓冲区
_channel[i].dtmflength = 0;
_channel[i].dispstatus = "对外线用户播放提示音...";
StringBuilder filename = new StringBuilder("voc//1.pcm");
_channel[i].filename = filename;
Tc08a32.StartPlayFile(i, filename, 1);
_myLog(LogFile.Trace, "播放提示音:" + filename);
_channel[i].play_count = 1;
Tc08a32.StartSigCheck(i);
Tc08a32.StartTimer(i, pubparam.TIMER_NO); //通道启动计时器
_channel[i].chnstatus = CHN_STATUS.LINE_WELCOME1;
filename = null;
}
break;
case CHN_STATUS.LINE_WELCOME1:
{
int check2 = Tc08a32.ReadCheckResult(i, 1);
if (check2 == 0x21)
{
_myLog(LogFile.Trace, "播放欢迎词时,检测到外线挂机");
_channel[i].chnstatus = CHN_STATUS.LINE_WAIT_HANGUP;
break;
}
int b = -1;
b = Tc08a32.GetDtmfCode(i);
if (b < 0 || b > 16)
{
if (Tc08a32.CheckPlayEnd(i) == 1) //放音结束
{
if (_channel[i].play_count < 2)
{
_channel[i].play_count++;
Tc08a32.StartPlayFile(i, _channel[i].filename, 0);
_myLog(LogFile.Trace, "播放提示音:" + _channel[i].filename);
}
else
{
_channel[i].chnstatus = CHN_STATUS.LINE_WAIT_HANGUP;
}
}
break;
}
else
{
_myLog(LogFile.Trace, "播放欢迎词检测到其他按键,dtmf=:" + b.ToString()+"chn ="+i.ToString());
Tc08a32.StopPlayFile(i);
Tc08a32.StartPlaySignal(i, (int)SingleVType.SIG_STOP);
if (_channel[i].chntype == CHN_TYPE.CHTYPE_USER)
{
_channel[i].dispstatus = "内线收DTMF号码...";
}
if (_channel[i].chntype == CHN_TYPE.CHTYPE_TRUNK)
{
_channel[i].dispstatus = "接收外线DTMF按键...";
switch (b)
{
case 12: //#号键,返回主菜单:
{
_channel[i].chnstatus = CHN_STATUS.LINE_WELCOME;
break;
}
case 1: //播放水厂地址
{
_channel[i].dispstatus = "对外播放水厂地址信息...";
StringBuilder filename = new StringBuilder("voc//9.pcm");
_myLog(LogFile.Trace, "播放水厂地址:" + filename);
_channel[i].filename = filename;
Tc08a32.StartPlayFile(i, filename, 0);
_channel[i].play_count = 1;
_channel[i].eventtype = 1;
Tc08a32.StartTimer(i, pubparam.TIMER_NO); //通道启动计时器
_channel[i].chnstatus = CHN_STATUS.LINE_PLAY_ONLY;
}
break;
case 2:
{
StringBuilder filename = new StringBuilder("voc//10.pcm");
_channel[i].filename = filename;
Tc08a32.StartPlayFile(i, filename, 0);
_channel[i].play_count = 1;
_channel[i].eventtype = 2;
_channel[i].chnstatus = CHN_STATUS.LINE_SELECT;
}
break;
case 3: //播放水质监测中心电话:85723236多文件拼接播放方式
{
StringBuilder filename = new StringBuilder("voc//6.pcm"); //播放整个文件
_channel[i].filename = filename;
Tc08a32.StartPlayFile(i, filename, 0);
_channel[i].play_count = 1;
_channel[i].eventtype = 3;
_channel[i].chnstatus = CHN_STATUS.LINE_PLAY_ONLY;
}
break;
case 4: //播报停水通知文件
{
object f = CardHelper.GetFileNameByDb(1);
StringBuilder sb = new StringBuilder();
if (f == null)
{
sb.Append("voc//8.pcm"); //您好,没有停水通知
}
else
{
string filepah = recordpaht + f.ToString();
bool bexsit = CardHelper.fileExist(filepah, recordpaht);
if (bexsit)
{
sb.Append(filepah); //播放停水通知文件
}
else
{
sb.Append("voc//8.pcm"); //您好,没有停水通知
}
}
_channel[i].filename = sb;
Tc08a32.StartPlayFile(i, sb, 0);
_channel[i].play_count = 1;
_channel[i].eventtype = 9;
_channel[i].chnstatus = CHN_STATUS.LINE_PLAY_ONLY;
}
break;
case 9: //告知客户端为咨询
{
Tc08a32.StopPlayFile(i);
_channel[i].eventtype = 3;
_channel[i].chnstatus = CHN_STATUS.LINE_CHECK_IDLE;
}
break;
case 10: //0号键:转坐席://告知客户端为受理
{
Tc08a32.StopPlayFile(i);
_channel[i].eventtype = 4;
_channel[i].chnstatus = CHN_STATUS.LINE_CHECK_IDLE;
}
break;
default:
{
StringBuilder filename = new StringBuilder("voc//7.pcm"); //选择了其他按键
_channel[i].filename = filename;
Tc08a32.StartPlayFile(i, filename, 0);
_channel[i].play_count = 1;
_channel[i].chnstatus = CHN_STATUS.LINE_OTHER_KEY;
}
break;
}
}
}
break;
}
case CHN_STATUS.LINE_SELECT: //第二集菜单的选择
{
if (Tc08a32.Sig_CheckBusy(i) == 1)
{
Tc08a32.HangUp(i);
_channel[i].chnstatus = CHN_STATUS.LINE_INIT;
break;
}
int b = Tc08a32.GetDtmfCode(i);
if (b < 0 || b > 16)
{
if (Tc08a32.CheckPlayEnd(i) == 1) //放音结束
{
if (_channel[i].play_count < 2)
{
_channel[i].play_count++;
Tc08a32.StartPlayFile(i, _channel[i].filename, 0);
break;
}
else
{
_channel[i].chnstatus = CHN_STATUS.LINE_WAIT_HANGUP;
}
}
}
else
{
Tc08a32.StopPlayFile(i);
Tc08a32.StartPlaySignal(i, (int)SingleVType.SIG_STOP);
switch (b)
{
case 1: //非节假日 4
{
StringBuilder filename = new StringBuilder("voc//4.pcm");
_channel[i].filename = filename;
Tc08a32.StartPlayFile(i, filename, 0);
_channel[i].play_count = 1;
_channel[i].chnstatus = CHN_STATUS.LINE_PLAY_ONLY;
}
break;
case 2: //节假日 5
{
StringBuilder filename = new StringBuilder("voc//5.pcm");
_channel[i].filename = filename;
Tc08a32.StartPlayFile(i, filename, 0);
_channel[i].play_count = 1;
_channel[i].chnstatus = CHN_STATUS.LINE_PLAY_ONLY;
}
break;
case 12: //#号键,返回主菜单:
{
_channel[i].chnstatus = CHN_STATUS.LINE_WELCOME;
}
break;
default:
{
StringBuilder filename = new StringBuilder("voc//7.pcm"); //选择了其他按键
_channel[i].filename = filename;
Tc08a32.StartPlayFile(i, filename, 0);
_channel[i].play_count = 1;
_channel[i].chnstatus = CHN_STATUS.LINE_OTHER_KEY;
}
break;
}
}
}
break;
case CHN_STATUS.LINE_PLAY_ONLY: //播放水厂地址,播放水质电话
if (Tc08a32.CheckPlayEnd(i) == 1)
{
if (_channel[i].play_count < 2)
{
_channel[i].play_count++;
Tc08a32.StartPlayFile(i, _channel[i].filename, 0);
break;
}
else
{
_channel[i].chnstatus = CHN_STATUS.LINE_WAIT_HANGUP;
}
}
break;
case CHN_STATUS.LINE_OTHER_KEY: //其他按键.
{
if (Tc08a32.CheckPlayEnd(i) == 1)
{
if (_channel[i].play_count < 2)
{
_channel[i].play_count++;
Tc08a32.StartPlayFile(i, _channel[i].filename, 0);
_channel[i].chnstatus = CHN_STATUS.LINE_WELCOME1;
break;
}
else
{
_channel[i].chnstatus = CHN_STATUS.LINE_WAIT_HANGUP;
}
}
}
break;
case CHN_STATUS.LINE_CHECK_IDLE: //检测是否有空闲坐席
{
int userchn = GetIdleUserChn();
if (userchn < 0) //提示坐席忙请等待.
{
StringBuilder filename = new StringBuilder("voc//11.pcm"); //对不起,坐席全忙.
_channel[i].filename = filename;
Tc08a32.StartPlayFile(i, filename, 0);
_channel[i].play_count = 1;
_channel[i].chnstatus = CHN_STATUS.LINE_CHECK_IDLE;
}
else //连通坐席人员...
{
_channel[i].connchn = userchn;
_channel[userchn].connchn = i;
_channel[userchn].busyflag = true; //将该通道占用。
Tc08a32.StartPlaySignal(i,(int)SingleVType.SIG_RINGBACK);
Tc08a32.FeedRealRing(userchn); //对某一路内线通道馈断续的铃流
_channel[i].chnstatus = CHN_STATUS.LINE_LINK_USER;
_channel[userchn].chnstatus = CHN_STATUS.LINE_LINK_USER;
}
}
break;
case CHN_STATUS.LINE_LINK_USER: //连通坐席
{
if (Tc08a32.OffHookDetect(_channel[i].connchn)) //检测外线振铃,或者内线是否摘机
{
//再处理相应的状态....
Tc08a32.StartPlaySignal(i, (int)SingleVType.SIG_STOP);
Tc08a32.FeedPower(_channel[i].connchn);
_channel[i].dispstatus = "内线用户摘机";
Tc08a32.StopPlayFile(_channel[i].connchn);
int k = Tc08a32.SetLink( _channel[i].connchn,i);
Tc08a32.StartHangUpDetect(_channel[i].connchn); //开始挂机检测
Tc08a32.InitDtmfBuf(_channel[i].connchn); //如果不初始化,则无法收到按键...
_channel[i].chnstatus = CHN_STATUS.LINE_LINK_CONNECT;
_channel[_channel[i].connchn].chnstatus = CHN_STATUS.LINE_LINK_CONNECT;
Tc08a32.StartSigCheck(_channel[i].connchn);
Tc08a32.StartSigCheck(i);
//发送消息到客户端:::::://eventtype 有具体的定义 ...
string caller = "12345678";
SendMessageToClient(caller, _channel[i].connchn, _channel[i].eventtype);
}
}
break;
//case CHN_STATUS.LINE_RECORDING:
case CHN_STATUS.LINE_LINK_CONNECT:
{
_channel[i].dispstatus = "话路联通";
//内线挂机
int check = Tc08a32.HangUpDetect(i);
if (check == 1)
{
_myLog(LogFile.Trace, "话路通,内线挂机.." + i.ToString());
}
//联通过程中,任何一方挂机。(检测不到内线挂机???)
int check2 = Tc08a32.ReadCheckResult(i, 1);
if (check2 == 0x21)
{
_myLog(LogFile.Trace, "话路通,外线挂机.." + i.ToString());
}
if(check2 == 0x21 || check == 1)
{
_myLog(LogFile.Trace, "停止可能的录音,开始挂机流程.." + i.ToString());
Tc08a32.StopRecordFile(i);
_channel[i].chnstatus = CHN_STATUS.LINE_WAIT_HANGUP;
//将另一方挂断.
if (_channel[i].connchn >= 0 && _channel[_channel[i].connchn].chnstatus != CHN_STATUS.LINE_IDLE)
{
Tc08a32.HangUp(_channel[i].connchn);
_channel[_channel[i].connchn].chnstatus = CHN_STATUS.LINE_INIT;
}
}
if (_channel[i].chntype == CHN_TYPE.CHTYPE_TRUNK)
{
break;
}
//下列是内线处理。。。。。。。。
int b = Tc08a32.GetDtmfCode(i);
if (b == 11) //是否需要开始录音 *
{
_channel[_channel[i].connchn].dispstatus = "话路录音";
bool f = Tc08a32.StartRecordFile(_channel[i].connchn, Encoding.UTF8.GetBytes(CardHelper.GetFileName()), 36000 * 100);
_myLog(LogFile.Trace, "对外线通道录音,通道号:" + _channel[i].connchn.ToString());
}
if (b == 12) //#号键:坐席挂机、对外线放音(外线暂时不能挂)
{
_myLog(LogFile.Trace, "接收到#,通道号:" + _channel[i].connchn.ToString());
//检测socket消息队列...........
Tc08a32.StopRecordFile(i);
if (_channel[i].chntype == CHN_TYPE.CHTYPE_USER)
{
_myLog(LogFile.Trace, "准备内线挂机,并对外线放音,通道号:" + _channel[i].connchn.ToString());
_channel[i].chnstatus = CHN_STATUS.LINE_WAIT_HANGUP;
//准备接收Socket通讯,开始对外放音...
}
}
}
break;
case CHN_STATUS.LINE_WAIT_HANGUP:
{
Tc08a32.HangUp(i);
_channel[i].chnstatus = CHN_STATUS.LINE_INIT;
}
break;
case CHN_STATUS.LINE_PLAY_RECORD:
if (Tc08a32.CheckIndexPlayFile(i) == 1)
{
if (_channel[i].play_count < 2)
{
_channel[i].play_count++;
Tc08a32.StartIndexPlayFile(i);
break;
}
else
{
Tc08a32.StopIndexPlayFile(i);
_channel[i].chnstatus = CHN_STATUS.LINE_WAIT_HANGUP;
}
}
break;
default: break;
}
}
//来自socket客户端的消息...
public void listener_CbMessage(string ip, string message)
{
_myLog(LogFile.Trace, "回调到语音卡线程,IP:" + ip + " 信息:" + message);
//对收到的数据进行解析,然后做相应的处理.
if (message == string.Empty)
{
_myLog(LogFile.Trace, "内容为空!为什么?");
return;
}
string[] sip = ip.Split(':');
string[] data = message.Split('|');
if (data == null || data.Length < 2)
{
_myLog(LogFile.Trace, "内容不全,什么意思?");
return;
}
int ifunc = int.Parse(data[0]);
switch (ifunc)
{
case -1: //注销
{
_myLog(LogFile.Trace, "坐席离开!");
}
break;
case 1: //坐席注册
{
if (data[1].ToString() != "hello qianjia")
{
_myLog(LogFile.Trace, "注册信息不正确!");
}
else
{
//注册
CardHelper.ChangerRegStatusOfInner(sip[0], 1);
_myLog(LogFile.Trace, "坐席注册信息成功,IP:" + sip[0]);
}
}
break;
case 2: //对外线放音电话号码
{
int ichn = CardHelper.GetChnByIp(sip[0]); //内线通道
if (ichn < 0)
{
_myLog(LogFile.Trace, "没有该IP对应的通道号,IP:" + sip[0]);
}
else
{
int outichn = _channel[ichn].connchn; //外线通道
if (outichn > -1)
{
string telephone = data[1];
string[] files = CardHelper.GetFileNameByTelephone(telephone);
if (files == null)
{
_myLog(LogFile.Trace, "无法获取语音文件,可能是传入的号码有误,原文:" + telephone);
break;
}
else
{
Tc08a32.StopPlayFile(outichn);
Tc08a32.RsetIndexPlayFile(outichn);
foreach (string s in files)
{
Tc08a32.AddIndexPlayFile(outichn, new StringBuilder(s));
}
Tc08a32.StartIndexPlayFile(outichn);
_channel[outichn].play_count = 1;
_channel[ichn].chnstatus = CHN_STATUS.LINE_WAIT_HANGUP;
_channel[outichn].chnstatus = CHN_STATUS.LINE_PLAY_RECORD;
}
}
else
{
_myLog(LogFile.Trace, "没有对应的连接通道!");
}
}
}
break;
default:
_myLog(LogFile.Trace, "未知功能按键!");
break;
}
}
/// <summary>
/// 根据业务流程给对应的客户端发送消息
/// </summary>
/// <param name="message"></param>
/// <param name="ichn"></param>
private void SendMessageToClient(string message,int ichn,int eventtype)
{
if (_socketListener.Connection == null)
{
_myLog(LogFile.Error, "socket的监听不为空!");
return;
}
string senders = string.Empty;
foreach (configObject obj in pubvar.configlist)
{
if(ichn == obj.ClinetLine && obj.ClientStatus == 1) //==1 表示已经注册信息
{
senders = obj.ClinetIP;
break;
}
}
if (senders == string.Empty)
{
_myLog(LogFile.Error, "没有对应的socket客户端,无法发送消息!");
return;
}
foreach (DictionaryEntry socket in _socketListener.Connection)
{
string[] sip = socket.Key.ToString().Split(':');
if (senders == sip[0])
{
string temp = eventtype.ToString()+"|"+ message ;
byte[] bs = Encoding.UTF8.GetBytes(temp);
if (((Connection)(socket.Value))._connection != null)
{
((Connection)(socket.Value))._connection.Send(bs, bs.Length, 0);
_myLog(LogFile.Trace, "发送消息到对应的客户端:" + senders + " 消息内容:" + temp);
bs = null;
}
break;
}
}
}
private int GetIdleUserChn()
{
int userchn = -1;
for (int j = 0; j < chn; j++)
{
if (_channel[j].chntype == CHN_TYPE.CHTYPE_USER && _channel[j].chnstatus == CHN_STATUS.LINE_IDLE)
{
userchn = j;
_myLog(LogFile.Trace, "获取一个空闲通道.通道号:." + j.ToString());
break;
}
}
return userchn;
}
}
}