Delphi之东进数字语音卡(SS1)可复用源码
作者:成晓旭
Blog:http://blog.csdn.net/cxxsoft
(声明:欢迎转载,请保证文章的完整性)
由于工作原因,本人将不在从事商业应有软件的开发工作,现在开始逐级“解密”自己以前写的部分自有产权代码,但愿对后来的朋友有点参考、借鉴的价值。
本代码是本人开发的计划开源的CIT通用平台的东进1号信令数字语言卡封装,设计思路与模拟语音卡的封装非常类似,在此不再赘述。有兴趣的朋友,请参考本人的另外一篇文章《Delphi之东进模拟语音卡(D 160A )可复用源码》:http://blog.csdn.net/cxxsoft/archive/2006/08/23/1108211.aspx
核心思想是一致的:卡、通道分别设计和实现;通道内,核心的还是“状态机模式”轮巡通道状态来管理硬件卡设备;板卡封装内实现不实现任何业务接口,但允许动态注入实现业务接口的对象,来近一步分发、处理板卡层采集的基础数据。
卡类源代码:
//
------------------------------------------------------------------------------
//
// 产品名称: 成晓旭的个人软件Delphi源码库
// 产品版本: CXXSoft delphi code source lib 2.0
// 模块名称: Delphi之东进数字语音卡类
// 模块描述:
// 单元文件: unDJCardSS1.pas
// 开发作者: 成晓旭
// 作者blog: http://blog.csdn.net/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 - 1 do
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 - 1 do
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 - 1 do
Trunks[Loop]: = TDJCommChannelsSS1.Create(Self,Loop,Type_Export,CommIsFSK,onTrunkEvent,onRecEvent);
for Loop: = OutTrunkNum to TotalTrunkNum - 1 do
Trunks[Loop]: = TDJCommChannelsSS1.Create(Self,Loop,Type_Import,CommIsFSK,onTrunkEvent,onRecEvent);
for Loop: = 0 to TotalTrunkNum - 1 do
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 - 1 do
begin
try
TDJCommChannelsSS1(Trunks[Loop]).DJChannelProcessor();
except
end;
end;
end;
end.
//
// 产品名称: 成晓旭的个人软件Delphi源码库
// 产品版本: CXXSoft delphi code source lib 2.0
// 模块名称: Delphi之东进数字语音卡类
// 模块描述:
// 单元文件: unDJCardSS1.pas
// 开发作者: 成晓旭
// 作者blog: http://blog.csdn.net/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 - 1 do
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 - 1 do
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 - 1 do
Trunks[Loop]: = TDJCommChannelsSS1.Create(Self,Loop,Type_Export,CommIsFSK,onTrunkEvent,onRecEvent);
for Loop: = OutTrunkNum to TotalTrunkNum - 1 do
Trunks[Loop]: = TDJCommChannelsSS1.Create(Self,Loop,Type_Import,CommIsFSK,onTrunkEvent,onRecEvent);
for Loop: = 0 to TotalTrunkNum - 1 do
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 - 1 do
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.csdn.net/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 - 1 do
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 - 1 do
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 - 1 do
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 - 1 do
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 - 1 do
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 - 1 do
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.
//
// 产品名称: 成晓旭的个人软件Delphi源码库
// 产品版本: CXXSoft delphi code source lib 2.0
// 模块名称: Delphi之东进数字语音卡通道类
// 模块描述:
// 单元文件: unDJChannelSS1.pas
// 开发作者: 成晓旭
// 作者blog: http://blog.csdn.net/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 - 1 do
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 - 1 do
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 - 1 do
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 - 1 do
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 - 1 do
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 - 1 do
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.