Delphi之三汇模拟语音卡(SHT-8B/PCI/FAX)可复用源码
作者:成晓旭
设计简介:
1、 设计思路请参考本站Blog的另一篇文章:“Delphi之东进模拟语音卡(D160A)可复用源码”;链接http://blog.youkuaiyun.com/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;

interface
uses
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;


implementation

const
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}

interface

uses
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();
begin

end;

function TCXXSHChannel8B.SwitchOnCallIn():
boolean
;
begin
SsmPickup(channelID);
//
摘机
ClearChannelBuffer();
channelState :
=
atsCallInSuccess;
Result :
=
true
;
end;

procedure TCXXSHChannel8B.ProcessDialSuccess();
begin

end;

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.