Delphi之三汇模拟语音卡(SHT-8B/PCI/FAX)可复用源码
作者:成晓旭
设计简介:
1、 设计思路请参考本站Blog的另一篇文章:“Delphi之东进模拟语音卡(D160A)可复用源码”;链接http://blog.csdn.net/CXXSoft/archive/2006/08/23/1108211.aspx。
2、 其实,仔细对比,你会发现这两种卡的代码类方法签名几乎99%是一样的,也就是说,这两都之间,还需要更进一步的抽象,以解决“重复代码”或者“相似代码”的问题。
3、 更高层次的抽象,请参考我的设计文档。
4、 类图(以后补上):
5、 卡类源码:
//------------------------------------------------------------------------------//// 产品名称: 成晓旭的个人软件Delphi源码库// 产品版本: CXXSoft delphi code source lib 2.0// 模块名称: Delphi之三汇模拟语音卡类// 模块描述: // 单元文件: unSHCard8B.pas // 开发作者: 成晓旭// 备注: 任何人使用此文件时,请保留此段自述文件,谢谢!// 开发时间: 2003-12-03// 修改历史: // 修改描述://------------------------------------------------------------------------------unit unSHCard8B;interfaceuses Windows, //unDJTC08a32,unDJNewSig, unBaseDefine, //unDJ160ADefine,unDJChanne160A, unShpa3api,unSHChanne18B,unSH8BDefine;type TCXXCommCard8B = class(TObject) private ChannelNumber:Word; channelObject:array of TCXXSHChannel8B; OnCardChannelState:TTrunkStatusEvent; procedure ReleaseCommDevice(); function GetChannelObjectOrder(const aChannelID:Word):Word; function InitChannel():boolean; public constructor Create(const trunkEvent:TTrunkStatusEvent); destructor Destroy(); override; function LoadCommDevice(const loadAll:boolean=false):boolean; function Startup():boolean; function GetAFreeChannel():Word; function GetChannelNumber():Word; function DialPhone(const aChannelID:Word;const DialPhoneNumber:PChar):boolean; function HangUp(const aChannelID:Word):boolean; end;implementationconst SH_ConfigFile = 'C:ShCtiShConfig.ini'; SH_IndexFile = 'C:ShCtiShIndex.ini'; ...{ TCXXCommCard8B }constructor TCXXCommCard8B.Create(const trunkEvent:TTrunkStatusEvent);begin ChannelNumber := 0; Self.OnCardChannelState := trunkEvent;end;destructor TCXXCommCard8B.Destroy;var Loop:Word;begin if (Length(channelObject) > 0) and (channelNumber > 0) then begin for Loop := 0 to ChannelNumber - 1 do begin if Assigned(channelObject[Loop]) then begin channelObject[Loop].Free(); channelObject[Loop] := nil; end; end; end; ReleaseCommDevice();end;function TCXXCommCard8B.DialPhone(const aChannelID: Word; const DialPhoneNumber: PChar): boolean;var K:Word;begin Result := false; K := GetChannelObjectOrder(aChannelID); if (K <> ErrorTrunkNumber) and (Assigned(channelObject[K])) then begin Result := channelObject[K].DialPhone(DialPhoneNumber); end;end;procedure TCXXCommCard8B.ReleaseCommDevice();begin SsmCloseCti() ;end;function TCXXCommCard8B.GetAFreeChannel(): Word;var Loop:Word;begin Result := ErrorTrunkNumber; for Loop := Low(channelObject) to High(channelObject) do begin if NOT channelObject[Loop].CheckDialOutEnabled() then continue; if (channelObject[Loop].GetChannelStatus() = atsFree) then begin Result := channelObject[Loop].GetChannelID();break;
end; end;end;function TCXXCommCard8B.GetChannelNumber(): Word;begin Result := channelNumber;end;function TCXXCommCard8B.GetChannelObjectOrder( const aChannelID: Word): Word;var Loop:Word;begin Result := ErrorTrunkNumber; for Loop := Low(channelObject) to High(channelObject) do begin if (channelObject[Loop].GetChannelID = aChannelID) then begin Result := Loop;break;
end; end;end;function TCXXCommCard8B.HangUp(const aChannelID: Word): boolean;var K:Word;begin Result := false; K := GetChannelObjectOrder(aChannelID); if (K <> ErrorTrunkNumber) and (Assigned(channelObject[K])) then begin channelObject[K].ChannelHangUp(); Result := true; end;end;function TCXXCommCard8B.LoadCommDevice(const loadAll:boolean): boolean;const loadEmpty = true;begin Result := false; if SsmStartCti(SH_ConfigFile,SH_IndexFile) <> 0 then begin TCXXSHChannel8B.InformInvodeError(); Exit; end; if(SsmGetMaxUsableBoard() <> SsmGetMaxCfgBoard()) then begin //部分板卡初始化失败,取出失败原因 TCXXSHChannel8B.InformInvodeError(); Exit; end; Result := InitChannel();end;function TCXXCommCard8B.Startup(): boolean;var Loop:integer;begin for Loop := 0 to channelNumber - 1 do begin channelObject[Loop].Startup(); end; Result := true;end;function TCXXCommCard8B.InitChannel():boolean;var number,Loop:integer;begin Result := false; number := SsmGetMaxCh(); if (number < 0) then Exit; ChannelNumber := number; SetLength(channelObject,channelNumber); for Loop := 0 to channelNumber - 1 do begin channelObject[Loop] := TCXXSHChannel8B.Create(OnCardChannelState); channelObject[Loop].CreateCommChannel(Loop); end; Result := true;end;end.6、 通道类源码:
//------------------------------------------------------------------------------//// 产品名称: 成晓旭的个人软件Delphi源码库// 产品版本: CXXSoft delphi code source lib 2.0// 模块名称: Delphi之三汇模拟语音卡通道类// 模块描述: // 单元文件: unSHChanne18B.pas// 开发作者: 成晓旭// 备注: 任何人使用此文件时,请保留此段自述文件,谢谢!// 开发时间: 2003-12-01// 修改历史: // 修改描述://------------------------------------------------------------------------------unit unSHChanne18B;...{$DEFINE ThreadChannel}interfaceuses Windows,Classes,SysUtils, unBaseDefine,unShpa3api,unSH8BDefine, //unDJ160ADefine, unDJTC08a32,unDJNewSig;Type ...{$IFDEF ThreadChannel} TCXXSHChannel8B = class(TThread) ...{$ELSE} TCXXSHChannel8B = class(TObject) ...{$ENDIF} private channelType:TChannelType; oldChannelState,channelState:TTrunkState; channelID:Word; phoneNumber:string; dtmfString:string; aCallerId:PAnsiChar; isConntectd:boolean; isDialOut:boolean; aTrunkState:TTrunkStatus; procedure ChannelProcessor(); procedure InformTrunkStatus(const aMsgFlag: TLVOperateFlag); procedure ClearTrunkStatus(); procedure ClearChannelBuffer(); function CheckSigHangup():boolean; function CheckCallIn():boolean; function SwitchOnCallIn():boolean; procedure ProcessCallInSuccess(); procedure ProcessDialSuccess(); procedure ProcessCheckDialSend(); procedure ProcessDialOut(); procedure Stop(); //三汇卡专用方法 function GetTrunkStateBySsm(const ssmState: integer): TTrunkState; protected ...{$IFDEF ThreadChannel} procedure Execute(); override; ...{$ENDIF} public strMessage:string; OnChannelState:TTrunkStatusEvent; constructor Create(const trunkEvent:TTrunkStatusEvent); destructor Destroy();override; procedure CreateCommChannel(const aChennelID: Word); procedure Startup(); function GetChannelID():Word; function GetChannelStatus():TTrunkState; function GetChannelType():TChannelType; function DialPhone(const DialPhoneNumber:PChar):boolean;overload; function DialPhone(const DialPhoneNumber:PChar;const PreDialNumber:PChar):boolean;overload; procedure ChannelHangUp(); function GetDialOut():boolean; //三汇卡特性方法 function CheckDialOutEnabled():boolean; class procedure InformInvodeError(); end;implementation...{ TCXXSHChannel8B }procedure TCXXSHChannel8B.ChannelHangUp();begin isDialOut := false; SsmHangUp(channelID); ClearChannelBuffer(); ClearTrunkStatus(); InformTrunkStatus(lvofUpdate);end;procedure TCXXSHChannel8B.ChannelProcessor();var ssmState:integer;begin// ssmState := SsmGetChState(channelID);// channelState := GetTrunkStateBySsm(ssmState); CheckCallIn(); case channelState of atsFree: begin // end; atsCallIning: begin SwitchOnCallIn(); end; atsCallInSuccess: begin if CheckSigHangup() then Exit; ProcessCallInSuccess(); end; atsCheckSendDial: begin ProcessCheckDialSend(); end; atsDialing: begin ProcessDialOut(); end; atsDialSuccess: begin if CheckSigHangup() then Exit; ProcessDialSuccess(); strMessage := '拨号成功'; end; atsHangOff: begin ChannelHangUp(); end; end; if (oldChannelState <> channelState) then begin oldChannelState := channelState; InformTrunkStatus(lvofUpdate); end;end;function TCXXSHChannel8B.CheckCallIn(): boolean;begin Result := false; if(SsmGetChState(channelID)=2) then //检测到振铃信号 begin SsmGetCallerId(channelID, aCallerId); //取出CALLERID信息 channelState := atsCallIning; Result := true; end;...{ Result := RingDetect(channelID); if Result then begin OffHook(channelID); if isDialOut then channelState := atsDialSuccess else channelState := atsCallIning; end;}end;function TCXXSHChannel8B.CheckSigHangup(): boolean;begin Result := false; if (SsmGetChState(channelID) = S_CALL_PENDING) then begin strMessage := '对方已挂机'; InformTrunkStatus(lvofUpdate); SsmStopPlay(channelID); channelState := atsHangOff; Result := true; end;end;procedure TCXXSHChannel8B.ClearTrunkStatus();begin channelState := atsFree; oldChannelState := channelState; phoneNumber := ''; dtmfString := ''; strMessage := ''; isConntectd := false;end;constructor TCXXSHChannel8B.Create(const trunkEvent:TTrunkStatusEvent);begin GetMem(aCallerId,50); Self.OnChannelState := trunkEvent;...{$IFDEF ThreadChannel} Self.FreeOnTerminate := true; inherited Create(true);...{$ENDIF}end;destructor TCXXSHChannel8B.Destroy;begin ChannelHangUp(); Stop();...{$IFNDEF ThreadChannel} inherited Destroy();...{$ENDIF} InformTrunkStatus(lvofDelete); FreeMem(aCallerId); end;function TCXXSHChannel8B.DialPhone(const DialPhoneNumber:PChar; const PreDialNumber:PChar): boolean;begin Result := false; if (channelState <> atsFree) then Exit; phoneNumber := DialPhoneNumber; Result := (SsmPickup(channelID) = 0); if NOT Result then Exit; Result := (SsmAutoDial(channelID,DialPhoneNumber)=0); if Result then begin isDialOut := true; channelState := atsCheckSendDial; end;end;function TCXXSHChannel8B.DialPhone( const DialPhoneNumber: PChar): boolean;begin Result := DialPhone(DialPhoneNumber,'');end;...{$IFDEF ThreadChannel}procedure TCXXSHChannel8B.Execute;begin while NOT Terminated do begin Synchronize(ChannelProcessor); Sleep(10); end;end;...{$ENDIF}function TCXXSHChannel8B.GetChannelID(): Word;begin Result := channelID;end;function TCXXSHChannel8B.GetChannelStatus(): TTrunkState;begin Result := channelState;end;procedure TCXXSHChannel8B.InformTrunkStatus(const aMsgFlag: TLVOperateFlag);begin if NOT Assigned(OnChannelState) then Exit; aTrunkState.lvFlag := aMsgFlag; aTrunkState.TrunkID := IntToStr(channelID); aTrunkState.TrunkType := Ord(channelType); aTrunkState.TrunkTypeStr := ChannelTypeString[channelType]; aTrunkState.TrunkStep := Ord(channelState); aTrunkState.TrunkStepStr := TrunkStateString[channelState]; aTrunkState.TrunkPhone := phoneNumber; aTrunkState.TrunkData := dtmfString; OnChannelState(aTrunkState);end;procedure TCXXSHChannel8B.ProcessCallInSuccess();beginend;function TCXXSHChannel8B.SwitchOnCallIn(): boolean;begin SsmPickup(channelID); //摘机 ClearChannelBuffer(); channelState := atsCallInSuccess; Result := true;end;procedure TCXXSHChannel8B.ProcessDialSuccess();beginend;procedure TCXXSHChannel8B.CreateCommChannel(const aChennelID: Word);var ct:integer;begin channelID := aChennelID; ct := SsmGetChType(channelID); if (ct < 0) then Exit; channelType := TChannelType(ct); ClearTrunkStatus(); InformTrunkStatus(lvofAdd);end;function TCXXSHChannel8B.GetChannelType(): TChannelType;begin Result := channelType;end;function TCXXSHChannel8B.GetDialOut(): boolean;begin Result := isDialOut;end;procedure TCXXSHChannel8B.ProcessCheckDialSend();begin //三汇卡直接转换状态 channelState := atsDialing;end;procedure TCXXSHChannel8B.Startup();begin ...{$IFDEF ThreadChannel} Resume(); ...{$ENDIF}end;procedure TCXXSHChannel8B.Stop();begin ...{$IFDEF ThreadChannel} Suspend(); Terminate(); ...{$ENDIF}end;function TCXXSHChannel8B.CheckDialOutEnabled(): boolean;begin //内线通道、坐席通道、磁石通道和录音通道不支持本SsmPicuup操作 Result := NOT((channelType = ct1) or (channelType = ct2) or (channelType = ct3) or (channelType = ct10));end;class procedure TCXXSHChannel8B.InformInvodeError;var msgBuffer:PAnsiChar;begin GetMem(msgBuffer,255); try SsmGetLastErrMsg(msgBuffer); //记录异常日志,或者通知用户 //Showmessage(msgBuffer); finally FreeMem(msgBuffer); end;end;function TCXXSHChannel8B.GetTrunkStateBySsm( const ssmState: integer): TTrunkState;begin Result := atsFree; case ssmState of 0: Result := atsFree; 4,5,6,9: Result := atsCallIning; 7: Result := atsHangOff; //8:“断线”状态。只有录音通道才会进入本状态。暂时不处理 //8: Result := atsHangOff; //3: Result := atsCallInSuccess; end;end;procedure TCXXSHChannel8B.ProcessDialOut();var dState:integer;begin dState := SsmChkAutoDial(channelID); case dState of DIAL_VOICE, DIAL_VOICEF1, DIAL_VOICEF2: begin channelState := atsDialSuccess; isConntectd := true; end; DIAL_STANDBY, DIAL_NO_DIALTONE, DIAL_ECHO_NOVOICE, DIAL_NOVOICE, DIAL_NOANSWER, DIAL_FAILURE, DIAL_INVALID_PHONUM: begin channelState := atsHangOff; end; end; strMessage := '拨号中...';end;procedure TCXXSHChannel8B.ClearChannelBuffer();begin SsmClearRxDTMFBuf(channelID); //清空驱动程序内部的DTMF按键号码接收缓冲区 SsmClearRxBuf(channelID); //清除指定通道ch上MODEM模块的接收缓冲区中的所有数据 //“抢拨” 开关是指当在放音过程中收到对方的DTMF按键字符时,由驱动程序程序立即停止正在进行的放音操作。 //SsmSetDtmfStopPlay(channelID,true); //打开抢拨开关end;end.