作者:成晓旭
Blog : http://blog.csdn.net/cxxsoft
( 声明:欢迎转载,请保证文章的完整性 )
设计简介:
1、 将卡、通道分别单独进行设计与封装。
2、 所有的外部操作接口都封装在卡类这一类。
3、 在我的项目中,在卡类这一级还增加了适配器或者代理,分别实现了 Adapter 或 Proxy 模式;以尽可能地解耦卡设备的实现细节与具体应用业务之间的关系。因为,我们的系统中使用了几家不同的卡设备,另一方面,这些卡设备,在不同的软件系统中,又有不同的业务应用需求。
4、 当然,卡这一级,也可以实现一个统一的接口,这样对外部可以表现出相对统一的行为,以方便业务层代码的调用,比如说:在数据采集的应用中,统一的接口可以让采集控制层不必依赖于具体的采集设备和通信方式,可以一致地实现数据收发,不管通信方式是 RS232 、 RS485 、 TCP/IP 、 PSTN ,还是别的方式或者通信设备。
5、 在通道设计中,核心的就是一个“状态机模式”,通过轮巡通道状态来管理硬件卡设备,并且,还自己设计了一个业务级的“业务状态机”,来抽象业务方面需要关心的“业务状态”,通过增加“业务状态机”这样一个中间层,以解耦业务状态与设备状态之间的依赖。 ( 这一点,在我看到的所有卡厂商提供的各类 Demo 程序里面都没有这样做,这也无形中误导了很多的开发人员,我看到的所有应用软件开发的源码都是:设备细节、尤其是通道状态,与业务逻辑代码紧紧地耦合在一起,难解难分 ) 。
6、 此设计的另一个亮点是: IoC 模式的应用 (2004 年自己在设计此类时还不知道这个概念,全凭自己的经验总结出这样的设计 ) 。对通道进入“呼入成功”、“呼出成功”等业务状态的调用代码从通道类是解耦出来:设计一个接口,在各个业务状态的处理方法中,再调用接口方法,将具体的业务处理逻辑委托给实现此接口的对象。并且这个接口的实现是通过“依赖注入”实现 IoC 的。这样设计,就达到了很好的可复用性和灵活性。
7、 当然,更好的实现可以采用 AOP( 面向方法编程 ) 的思想或者实现技术,这样可复用性更好,如此设计,在业务与卡方法的调用之间,耦合度将是最低的。
8、 目前的版本,没有在代码中体现接口的实现 ……
9、 类图 ( 以后补上 ) :
10、卡类源码:
//
// 产品名称:成晓旭的个人软件Delphi源码库
// 产品版本:CXXSoftdelphicodesourcelib2.0
// 模块名称:Delphi之东进模拟语音卡类
// 模块描述:
// 单元文件:unDJCard160A.pas
// 开发作者:成晓旭
// 备注:任何人使用此文件时,请保留此段自述文件,谢谢!
// 开发时间:2004-08-03
// 修改历史:
// 修改描述:
// ------------------------------------------------------------------------------
unitunDJCard160A;
interface
uses
Windows,
unDJTC08a32,unDJNewSig,
unBaseDefine,unDJ160ADefine,
unDJChanne160A;
type
TCXXCommCard160A = class (TObject)
private
ChannelNumber:Word;
channelObject:arrayofTCXXDJChannel160A;
OnCardChannelState:TTrunkStatusEvent;
procedureStop();
procedureReleaseCommDevice();
functionGetChannelObjectOrder( const aChannelID:Word):Word;
public
constructorCreate( const trunkEvent:TTrunkStatusEvent);
destructorDestroy(); override ;
functionLoadCommDevice( const loadAll:boolean = false ):boolean;
functionStartup():boolean;
functionGetAFreeChannel():Word;
functionGetChannelNumber():Word;
functionDialPhone( const aChannelID:Word; const DialPhoneNumber:PChar):boolean;
functionHangUp( const aChannelID:Word):boolean;
end;
implementation
{TCXXCommCard160A}
constructorTCXXCommCard160A.Create( const trunkEvent:TTrunkStatusEvent);
begin
ChannelNumber: = 0 ;
Self.OnCardChannelState: = trunkEvent;
end;
destructorTCXXCommCard160A.Destroy;
var
Loop:Word;
begin
Stop();
if (Length(channelObject) > 0 )and(channelNumber > 0 )then
begin
for Loop: = 0 toChannelNumber - 1 do
begin
if Assigned(channelObject[Loop])then
begin
channelObject[Loop].Free();
channelObject[Loop]: = nil;
end;
end;
end;
ReleaseCommDevice();
end;
functionTCXXCommCard160A.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;
procedureTCXXCommCard160A.ReleaseCommDevice();
begin
DisableCard();
FreeDrv();
end;
functionTCXXCommCard160A.GetAFreeChannel():Word;
var
Loop:Word;
begin
Result: = ErrorTrunkNumber;
for Loop: = Low(channelObject)toHigh(channelObject) do
begin
if (channelObject[Loop].GetChannelType() = ctEmpty)then continue ;
if (channelObject[Loop].GetChannelStatus() = atsFree)then
begin
Result: = channelObject[Loop].GetChannelID();
break ;
end;
end;
end;
functionTCXXCommCard160A.GetChannelNumber():Word;
begin
Result: = channelNumber;
end;
functionTCXXCommCard160A.GetChannelObjectOrder(
const aChannelID:Word):Word;
var
Loop:Word;
begin
Result: = ErrorTrunkNumber;
for Loop: = Low(channelObject)toHigh(channelObject) do
begin
if (channelObject[Loop].GetChannelID = aChannelID)then
begin
Result: = Loop;
break ;
end;
end;
end;
functionTCXXCommCard160A.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;
functionTCXXCommCard160A.LoadCommDevice( const loadAll:boolean):boolean;
const
loadEmpty = true ;
var
Loop,tempNumber:Word;
isFlag:LongInt;
functionCheckLoadTrunk():boolean;
begin
Result: = loadAllor((NOTloadAll)and(TChannelType(CheckChType(Loop)) ctEmpty));
end;
begin
isFlag: = LoadDRV();
Result: = (isFlag = 0 );
if NOTResultthenExit;
tempNumber: = CheckValidCh();
Result: = EnableCard(tempNumber, 1024 * 8 ) = 0 ;
if NOTResultthen
begin
FreeDrv();
Exit;
end;
Result: = Sig_Init() = 1 ;
if NOTResultthenExit;
SetBusyPara( 700 );
SetPackRate(PACK_64KBPS);
channelNumber: = tempNumber;
SetLength(channelObject,channelNumber);
for Loop: = 0 tochannelNumber - 1 do
begin
if CheckLoadTrunk()then
begin
channelObject[Loop]: = TCXXDJChannel160A.Create(OnCardChannelState);
channelObject[Loop].CreateCommChannel(Loop);
end;
end;
end;
functionTCXXCommCard160A.Startup():boolean;
var
Loop:integer;
begin
for Loop: = 0 tochannelNumber - 1 do
begin
channelObject[Loop].Resume();
end;
Result: = true ;
end;
procedureTCXXCommCard160A.Stop();
var
Loop:integer;
begin
for Loop: = 0 tochannelNumber - 1 do
begin
channelObject[Loop].Suspend();
channelObject[Loop].Terminate();
channelObject[Loop]: = nil;
end;
end;
end.
//
// 产品名称:成晓旭的个人软件Delphi源码库
// 产品版本:CXXSoftdelphicodesourcelib2.0
// 模块名称:Delphi之东进模拟语音卡通道类
// 模块描述:
// 单元文件:unDJChanne160A.pas
// 开发作者:成晓旭
// 备注:任何人使用此文件时,请保留此段自述文件,谢谢!
// 开发时间:2004-08-03
// 修改历史:
// 修改描述:
// ------------------------------------------------------------------------------
unitunDJChanne160A;
interface
uses
Windows,Classes,SysUtils,
unBaseDefine,unDJ160ADefine,
unDJTC08a32,unDJNewSig;
Type
TCXXDJChannel160A = class (TThread)
// TCXXDJChannel160A=class(TObject)
private
channelType:TChannelType;
oldChannelState,channelState:TTrunkState;
channelID:Word;
phoneNumber: string ;
dtmfString: string ;
isConntectd:boolean;
isDialOut:boolean;
aTrunkState:TTrunkStatus;
procedureInformTrunkStatus( const aMsgFlag:TLVOperateFlag);
procedureClearTrunkStatus();
functionCheckSigHangup():boolean;
functionCheckCallIn():boolean;
functionSwitchOnCallIn():boolean;
procedureProcessCallInSuccess();
procedureProcessDialSuccess();
procedureProcessCheckDialSend();
protected
procedureExecute(); override ;
public
strMessage: string ;
OnChannelState:TTrunkStatusEvent;
constructorCreate( const trunkEvent:TTrunkStatusEvent);
destructorDestroy(); override ;
procedureCreateCommChannel( const aChennelID:Word);
procedureChannelProcessor();
functionGetChannelID():Word;
functionGetChannelStatus():TTrunkState;
functionGetChannelType():TChannelType;
functionDialPhone( const DialPhoneNumber:PChar):boolean;overload;
functionDialPhone( const DialPhoneNumber:PChar; const PreDialNumber:PChar):boolean;overload;
procedureChannelHangUp();
functionGetDialOut():boolean;
end;
implementation
{TCXXDJChannel160A}
procedureTCXXDJChannel160A.ChannelHangUp();
begin
isDialOut: = false ;
StopSigCheck(channelID);
HangUp(channelID);
Sig_ResetCheck(channelID);
StartSigCheck(channelID);
InitDTMFBuf(channelID);
ClearTrunkStatus();
InformTrunkStatus(lvofUpdate);
end;
procedureTCXXDJChannel160A.ChannelProcessor();
var
dState:Word;
begin
PUSH_PLAY();
FeedSigFunc();
CheckCallIn();
case channelStateof
atsFree:
begin
//
end;
atsCallIning:
begin
SwitchOnCallIn();
end;
atsCallInSuccess:
begin
if CheckSigHangup()thenExit;
ProcessCallInSuccess();
end;
atsCheckSendDial:
begin
ProcessCheckDialSend();
end;
atsDialing:
begin
dState: = Sig_CheckDial(channelID);
case dStateof
// S_NORESULT:
S_CONNECT:
begin
channelState: = atsDialSuccess;
isConntectd: = true ;
end;
S_BUSY,
S_NOBODY,
S_NODIALSIG,
S_NOSIGNAL:
begin
channelState: = atsHangOff;
end;
end;
strMessage: = ' <spa
发表评论
- 浏览: 1292150 次
- 性别:
- 来自: 杭州
最新评论
-
netkongjian
: 不错的软件知识,感谢分享!
软件加密方式 -
norce
: 效果不错~
JS实现图片幻灯片效果 -
zxbear
: 链接已失效
《jQuery基础教程:第2版》PDF -
架构师
: 在技术领域方面Java还是世界上最好的,而且有很多第三方控件的 ...
专访:Ruby能否成为第二个Java -
freddie
: 如何拖动表格边框调整行高和列宽?
可编辑的表格(JavaScript)
评论