//------------------------------------------------------------------------------ // // 产品名称: 成晓旭的个人软件Delphi源码库 // 产品版本: CXXSoft delphi code source lib 2.0 // 模块名称: Delphi之东进数字语音卡类 // 模块描述: // 单元文件: unDJCardSS1.pas // 开发作者: 成晓旭 // 作者blog: http://blog.youkuaiyun.com/CXXSoft // 备注: 任何人使用此文件时,请保留此段自述文件,谢谢! // 开发时间: 2005-01-09 // 修改历史: // 修改描述: //------------------------------------------------------------------------------ unit unDJCardSS1; interface uses Windows,SysUtils,Classes, unBaseDefine, Tce1_32, Tce1_FSK; type TDJCommCardSS1 =class(TThread) private onTrunkEvent:TTrunkSatausEvent; onRecEvent:TTrunkReceiveEvent; isLoadFSK:boolean; CommIsFSK:boolean; Trunks:array of TObject; function InitCardForFSK():boolean; procedure FreeCardForFSK(); procedure ThreadKernelProcess(); protected procedure Execute(); override; public TotalTrunkNum,InTrunkNum,OutTrunkNum:Word; TrunkConnected:array of boolean; constructor Create(const aStateEvent: TTrunkSatausEvent;const aRecEvent:TTrunkReceiveEvent); destructor Destroy(); override; //初始化中继卡 function InitCard(const isFSKComm: boolean): Boolean; //释放中继卡 function FreeCommCard():boolean; //开始运行中继卡管理模块 procedure StartRunCommModule(); //获取一个空闲通道(指定通道类型) function GetAFreeTrunkByType(const aTrunkType:TTrunkType):integer; //获取一个空闲呼出通道 function GetAFreeTrunkByOut():integer; //挂断指定通道 procedure HangOfByTrunkID(const aChannelID:integer); //通过指定通道拨号 procedure DialPhoneByTrunkID(const aChannelID:integer;const phoneNumber,callerNumber:PChar;const aDevID:integer=-1); //通过指定通道发送数据 function SendStringByTrunkID(const aChannelID:integer;const sendBuffer:PChar):boolean; end; implementation ...{ TDJCommCardSS1 } uses unDJChannelSS1; constructor TDJCommCardSS1.Create(const aStateEvent: TTrunkSatausEvent; const aRecEvent:TTrunkReceiveEvent); begin inherited Create(true); Self.FreeOnTerminate :=true; onTrunkEvent := aStateEvent; onRecEvent := aRecEvent; end; destructor TDJCommCardSS1.Destroy(); begin Self.Suspend(); Self.Terminate(); FreeCommCard(); end; procedure TDJCommCardSS1.DialPhoneByTrunkID(const aChannelID: integer; const phoneNumber, callerNumber: PChar;const aDevID:integer); begin if (aChannelID > ErrTrunkId) and (DJTrk_CheckTrunkFree(aChannelID)) then TDJCommChannelsSS1(Trunks[aChannelID]).StartDialPhone(phoneNumber,callerNumber,aDevID,); end; procedure TDJCommCardSS1.Execute; begin while NOT Terminated do begin Synchronize(ThreadKernelProcess); Sleep(1); end; end; procedure TDJCommCardSS1.FreeCardForFSK(); begin if CommIsFSK and isLoadFSK then begin DJFsk_Release(); end; end; function TDJCommCardSS1.FreeCommCard(): boolean; var Loop:Word; begin Sleep(1000); if TotalTrunkNum >0 then begin for Loop:=0 to TotalTrunkNum -1do begin if Assigned(TDJCommChannelsSS1(Trunks[Loop])) then begin TDJCommChannelsSS1(Trunks[Loop]).Free(); TDJCommChannelsSS1(Trunks[Loop]) := nil; end; end; end; DJSys_DisableCard(); FreeCardForFSK(); Result :=true; end; function TDJCommCardSS1.GetAFreeTrunkByOut(): integer; begin Result := GetAFreeTrunkByType(Type_Export); end; function TDJCommCardSS1.GetAFreeTrunkByType( const aTrunkType: TTrunkType): integer; var Loop:Word; begin Result := ErrTrunkID; for Loop :=0 to TotalTrunkNum -1do begin if ((TDJCommChannelsSS1(Trunks[Loop]).GetTrunkType() = aTrunkType) and (DJTrk_CheckTrunkFree(Loop))) then begin Result := Loop; break; end; end; end; procedure TDJCommCardSS1.HangOfByTrunkID(const aChannelID: integer); begin TDJCommChannelsSS1(Trunks[aChannelID]).InOutHangOff(); end; function TDJCommCardSS1.InitCard(const isFSKComm: boolean): Boolean; const PromptFile ='Prompt.ini'; var Loop: Integer; isOK: boolean; TimeOfNow: SystemTime; begin Result := False; CommIsFSK := isFSKComm; isOK :=(DJSys_EnableCard('',PromptFile)=_ERR_OK); if Not isOK then exit; isOK := InitCardForFSK(); if NOT isOK then Exit; isLoadFSK := isOK; GetLocalTime(TimeOfNow); DJSys_SetSysTime(TimeOfNow.wHour,TimeOfNow.wMinute,TimeOfNow.wSecond); TotalTrunkNum:=DJTrk_GetTotalTrunkNum(); InTrunkNum:=TotalTrunkNum shr 1; OutTrunkNum:=TotalTrunkNum - InTrunkNum; SetLength(Trunks,TotalTrunkNum); SetLength(TrunkConnected,TotalTrunkNum); for Loop:=0 to OutTrunkNum-1do Trunks[Loop]:=TDJCommChannelsSS1.Create(Self,Loop,Type_Export,CommIsFSK,onTrunkEvent,onRecEvent); for Loop:=OutTrunkNum to TotalTrunkNum-1do Trunks[Loop]:=TDJCommChannelsSS1.Create(Self,Loop,Type_Import,CommIsFSK,onTrunkEvent,onRecEvent); for Loop:=0 to TotalTrunkNum -1do begin TDJCommChannelsSS1(Trunks[Loop]).ClearTrunkBuffer(csReceiving); end; DJSys_AutoApplyDtmf(ENABLEDTMF); //自动分配DTMF资源 DJSys_EnableAutoKB(); //自动回送KB信号 isOK:=isOK and DJSys_EnableDtmfSend(); Result := isOK; end; function TDJCommCardSS1.InitCardForFSK(): boolean; var k:integer; begin Result :=true; if CommIsFSK then begin k := DJFsk_InitForFsk(SZK_Mode); Result := (k =1); end; end; function TDJCommCardSS1.SendStringByTrunkID(const aChannelID: integer; const sendBuffer: PChar):boolean; begin Result := TDJCommChannelsSS1(Trunks[aChannelID]).SendString(sendBuffer); end; procedure TDJCommCardSS1.StartRunCommModule(); begin Resume(); end; procedure TDJCommCardSS1.ThreadKernelProcess(); var Loop:Word; begin DJSys_PushPlay(); for Loop :=0 to TotalTrunkNum -1do begin try TDJCommChannelsSS1(Trunks[Loop]).DJChannelProcessor(); except end; end; end; end.
通道类源代码:
//------------------------------------------------------------------------------ // // 产品名称: 成晓旭的个人软件Delphi源码库 // 产品版本: CXXSoft delphi code source lib 2.0 // 模块名称: Delphi之东进数字语音卡通道类 // 模块描述: // 单元文件: unDJChannelSS1.pas // 开发作者: 成晓旭 // 作者blog: http://blog.youkuaiyun.com/CXXSoft // 备注: 任何人使用此文件时,请保留此段自述文件,谢谢! // 开发时间: 2005-01-09 // 修改历史: // 修改描述: //------------------------------------------------------------------------------ unit unDJChannelSS1; interface uses Windows, SysUtils, unBaseDefine, Tce1_32, Tce1_FSK, unDJCardSS1; type TCXXStatus = (csSending,csReceiving,csPlaying); TDJCommChannelsSS1 =class(TObject) private CommIsFSK:boolean; controller:TDJCommCardSS1; TrunkID:integer; TrunkStep:TTrunkStep; MaxBuffer: array [0..DTMF_BUFFER_SIZE-1] of Char; msgChannel:TTrunkStatusInfo; msgFrame:TRecCommFrame; commFrameNumber,recPos:Word; subStatus:TCXXStatus; commPhone:string; commFrameStr:string; //应该进一步优化为注入的接口,非简单的回调句柄 onTrunkState:TTrunkSatausEvent; onRecEvent:TTrunkReceiveEvent; InOutType:TTrunkType; function SendDataFromTrunk():boolean; function CheckSendDataEnd():boolean; procedure SaveMaxBufferToFrameStr(); procedure ProcessConnected(); //注意:此方法与具体业务的通信协议存在严重依赖关系(IoC实现依赖反转) function CheckReceiveOverFSK(const dataBuffer:array of char;const dataNumber:Word):boolean; //注意:此方法与具体业务的通信协议存在严重依赖关系(IoC实现依赖反转) function CheckReceiveOverDTMF(const dataBuffer:array of char;const dataNumber:Word):boolean; function GetCommData(const dataBuffer:array of char;const dataNumber:Word):string; function ReceiveDataFromTrunk():boolean; procedure InformChannelStatus(const aStep:TTrunkStep;const lvof:TLVOperateFlag); procedure InformBusinessStatus(const aCommData:string;const cif:TCommInformFlag); procedure InformDialStatus(const aStep:TTrunkStep); procedure InWaitingIntoToConnect(); function GetCommFrameFromSendString(const commFrame:string):string; procedure RegisterTrunkEvent(const trunkStateEvent:TTrunkSatausEvent); procedure RegisterReceiveEvent(const trunkRecEvent:TTrunkReceiveEvent); public constructor Create(const trunkController: TDJCommCardSS1; const TrkID: Integer; const TrunkType: TTrunkType;const isFSKComm: boolean; const aStateEvent: TTrunkSatausEvent;const aRecEvent:TTrunkReceiveEvent); destructor Destroy; override; //获取通道状态 function GetTrunkType():TTrunkType; procedure DJChannelProcessor(); //通道挂机 procedure InOutHangOff(); //开始拨号 procedure StartDialPhone(const phoneNumber,callerNumber:PChar;const aDevID:integer=-1); //发送通信数据 function SendString(const pchSend:PChar):boolean; //清空通道数据缓冲 procedure ClearTrunkBuffer(const aSB:TCXXStatus); //获取通道号 function GetTrunkID():integer; end; implementation ...{ TDJCommChannelsSS1 } const Frame_FillChar = #0; Leader_Flag = $55; HeadNumber =30; hasLeader =true; function TDJCommChannelsSS1.CheckSendDataEnd(): boolean; begin Result :=false; if CommIsFSK then begin if (DJFsk_CheckSendFSKEnd(TrunkID,SZK_Mode) =1) then begin DJFsk_StopSend(TrunkID,SZK_Mode); Result :=true; end; end else begin if DJTrk_CheckDtmfSendEnd(TrunkID) then begin DJVoc_StopPlayFile(TrunkID); Result :=true; end; end; if Result then ClearTrunkBuffer(csReceiving); end; procedure TDJCommChannelsSS1.ClearTrunkBuffer(const aSB:TCXXStatus); begin subStatus := aSB; if CommIsFSK then DJFsk_ResetFskBuffer(TrunkID,SZK_Mode) else DJTrk_InitDtmfBufNew(TrunkID); commFrameNumber :=0; recPos :=0; end; constructor TDJCommChannelsSS1.Create(const trunkController: TDJCommCardSS1; const TrkID: Integer; const TrunkType: TTrunkType; const isFSKComm: boolean; const aStateEvent: TTrunkSatausEvent;const aRecEvent:TTrunkReceiveEvent); var t:TTrunkType; begin inherited Create; RegisterTrunkEvent(aStateEvent); RegisterReceiveEvent(aRecEvent); controller :=trunkController; TrunkID:=TrkID; commPhone :=''; TrunkStep:=TTrunkStep(-1); t := TrunkType; if DJTrk_SetTrunkType(TrkID,t) then InOutType:=TrunkType; CommIsFSK := isFSKComm; controller.TrunkConnected[TrunkID] :=false; ClearTrunkBuffer(csReceiving); InformChannelStatus(Step_Free,lvofAdd); end; destructor TDJCommChannelsSS1.Destroy(); begin inherited; DJTrk_BackwardHangUp(TrunkID); end; procedure TDJCommChannelsSS1.DJChannelProcessor(); var aStep: TTrunkStep; begin //DJSys_PushPlay(); aStep:= DJTrk_GetTrunkStatus(TrunkID); //状态变化 if TrunkStep <> aStep then begin TrunkStep:= aStep; InformChannelStatus(TrunkStep,lvofUpdate); end; //前向挂机 if (TrunkStep<>Step_Free) and DJTrk_CheckForwardHangUp(TrunkID) then begin InOutHangOff(); end; //入中继拨入,等待接续(建立连接) if (TrunkStep = Step_Wait) and (DJTrk_CheckTrunkIn(TrunkID)) then begin InWaitingIntoToConnect(); end; //通道连接已经建立 if (TrunkStep = Step_Connect) then begin ProcessConnected(); end; //出通道拨号失败 if TrunkStep=Step_DialFail then begin InformDialStatus(TrunkStep); end; if TrunkStep=Step_Delay then Exit; //出入通道空闲 if TrunkStep = Step_Free then begin //等待接收呼入 end; end; function TDJCommChannelsSS1.GetTrunkID(): integer; begin Result := Self.TrunkID; end; procedure TDJCommChannelsSS1.InformChannelStatus(const aStep: TTrunkStep;const lvof:TLVOperateFlag); begin msgChannel.lvFlag := lvof; msgChannel.TrunkID := IntToStr(Self.TrunkID); msgChannel.DeviceID :=''; msgChannel.TrunkTypeStr:=TrunkTypeInStr[InOutType]; msgChannel.TrunkStep := Ord(aStep); msgChannel.TrunkStepStr:=TrunkStepInStr[aStep]; if aStep = Step_Free then begin msgChannel.Phone:=''; msgChannel.Data:=''; end else begin msgChannel.Phone:=commPhone; msgChannel.Data:=commFrameStr; end; if Assigned(onTrunkState) then onTrunkState(msgChannel); end; procedure TDJCommChannelsSS1.InformDialStatus(const aStep: TTrunkStep); var dStatus:TDialStatus; begin dStatus := DJTrk_GetDialStatus(TrunkID); case dStatus of DS_Busy,DS_OverTime,DS_NoUser,DS_LineError: begin InOutHangOff(); end; end; end; procedure TDJCommChannelsSS1.InformBusinessStatus(const aCommData: string;const cif:TCommInformFlag); begin //依赖注入的业务处理接口调用,实现业务处理的 end; procedure TDJCommChannelsSS1.InOutHangOff(); begin DJTrk_BackwardHangUp(TrunkID); controller.TrunkConnected[TrunkID] :=false; InformBusinessStatus('',cifDisconnected); end; procedure TDJCommChannelsSS1.InWaitingIntoToConnect; begin DJVoc_PlayFile(TrunkID,'.Voicedtmf13'); DJVoc_StopPlayFile(TrunkID); end; procedure TDJCommChannelsSS1.ProcessConnected(); var ss:TCXXStatus; begin if NOT controller.TrunkConnected[TrunkID] then begin controller.TrunkConnected[TrunkID] :=true; ss := csReceiving; InformBusinessStatus('',cifConnected); ClearTrunkBuffer(ss); end; case subStatus of csSending: begin if CheckSendDataEnd() then begin InformChannelStatus(Step_Connect,lvofUpdate); InformBusinessStatus(commFrameStr,cifSend); end; end; csReceiving: begin if ReceiveDataFromTrunk() then begin msgFrame.CommFrame := commFrameStr; msgFrame.CommType := Comm_FSK; InformChannelStatus(Step_Connect,lvofUpdate); InformBusinessStatus(commFrameStr,cifReceive); end; end; csPlaying: begin end; end; end; function TDJCommChannelsSS1.ReceiveDataFromTrunk(): boolean; var num,Loop:integer; tempBuffer:array[0..DTMF_BUFFER_SIZE-1] of Char; begin Result :=false; try if Self.CommIsFSK then begin //FSK方式版本 FillChar(tempBuffer,DTMF_BUFFER_SIZE,Frame_FillChar); num := DJFsk_GetFSK(TrunkID,tempBuffer,SZK_MODE); if (num >0) then begin if CheckReceiveOverFSK(tempBuffer,num) then begin Self.commFrameStr := GetCommData(tempBuffer,num); Self.ClearTrunkBuffer(csReceiving); Result :=true; end; end; end else begin //DTMF方式版本 num := DJTrk_GetReceiveDtmfNumNew(TrunkID); if num >0 then begin for Loop :=0 to num -1do begin MaxBuffer[recPos+Loop] := DJTrk_GetDtmfCodeNew(TrunkID); recPos := (recPos +1) mod DTMF_BUFFER_SIZE; end; Inc(commFrameNumber,num); if CheckReceiveOverDTMF(tempBuffer,num) then begin ClearTrunkBuffer(csReceiving); Result :=true; end; end; end; except Result :=false; end; end; procedure TDJCommChannelsSS1.RegisterReceiveEvent( const trunkRecEvent: TTrunkReceiveEvent); begin onRecEvent := trunkRecEvent; end; procedure TDJCommChannelsSS1.RegisterTrunkEvent( const trunkStateEvent: TTrunkSatausEvent); begin onTrunkState := trunkStateEvent; end; procedure TDJCommChannelsSS1.SaveMaxBufferToFrameStr(); var Loop:Word; begin commFrameStr :=''; for Loop :=0 to commFrameNumber -1do begin commFrameStr := commFrameStr + MaxBuffer[Loop]; end; end; function TDJCommChannelsSS1.SendDataFromTrunk():boolean; begin Result :=false; if controller.TrunkConnected[TrunkID] then begin if CommIsFSK then begin Result := DJFsk_SendFSK(TrunkID,@MaxBuffer[0],commFrameNumber,SZK_Mode) =1; end else begin Result := DJTrk_SendDtmfStr(TrunkID,@MaxBuffer[0]) =1; end; end; if Result then subStatus := csSending; end; function TDJCommChannelsSS1.SendString(const pchSend: PChar):boolean; var Loop:integer; strTemp:string; begin Result :=false; if Self.CommIsFSK and hasLeader then begin //加FSK前导字符的版本 strTemp := GetCommFrameFromSendString(pchSend); commFrameNumber := Length(strTemp); if commFrameNumber >0 then begin for Loop :=0 to commFrameNumber-1do MaxBuffer[Loop] := strTemp[Loop+1]; MaxBuffer[commFrameNumber] := #0; SaveMaxBufferToFrameStr(); Result := SendDataFromTrunk(); end; end else begin //不加前导字符的版本 commFrameNumber := Length(pchSend); if commFrameNumber >0 then begin for Loop :=0 to commFrameNumber-1do MaxBuffer[Loop] := pchSend[Loop]; MaxBuffer[commFrameNumber] := #0; SaveMaxBufferToFrameStr(); Result := SendDataFromTrunk(); end; end; end; procedure TDJCommChannelsSS1.StartDialPhone(const phoneNumber, callerNumber: PChar;const aDevID:integer); begin if DJTrk_CheckTrunkFree(TrunkID) and(Trim(phoneNumber) <>'') then begin commPhone := Trim(phoneNumber); DJTrk_StartDial(TrunkID,PChar(commPhone),''); end; end; function TDJCommChannelsSS1.CheckReceiveOverFSK(const dataBuffer: array of char; const dataNumber: Word): boolean; begin //业务实现方法:判定通信帧串发送结束 Result :=true; end; function TDJCommChannelsSS1.GetCommData(const dataBuffer: array of char; const dataNumber: Word): string; var Loop:Word; begin Result :=''; if dataNumber <=0 then Exit; for Loop :=0 to dataNumber -1do begin if (dataBuffer[Loop] <> Frame_FillChar) then Result := Result + dataBuffer[Loop]; end; end; function TDJCommChannelsSS1.GetCommFrameFromSendString( const commFrame: string): string; var Loop:integer; begin Result := commFrame; for Loop :=0 to HeadNumber -1do Result := CHR(Leader_Flag) + Result; end; function TDJCommChannelsSS1.CheckReceiveOverDTMF( const dataBuffer: array of char; const dataNumber: Word): boolean; begin //业务实现方法:判定通信帧串发送结束 Result :=true; end; function TDJCommChannelsSS1.GetTrunkType(): TTrunkType; begin Result := Self.InOutType; end; end.