Delphi之東進模擬語音卡(D160A)可複用原始碼

gudesheng發表於2008-01-03
  
Delphi之東進模擬語音卡(D160A)可複用原始碼

作者:成曉旭

Bloghttp://blog.csdn.net/cxxsoft

(宣告:歡迎轉載,請保證文章的完整性)

設計簡介:

1、   將卡、通道分別單獨進行設計與封裝。

2、   所有的外部操作介面都封裝在卡類這一類。

3、   在我的專案中,在卡類這一級還增加了介面卡或者代理,分別實現了AdapterProxy模式;以儘可能地解耦卡裝置的實現細節與具體應用業務之間的關係。因為,我們的系統中使用了幾家不同的卡裝置,另一方面,這些卡裝置,在不同的軟體系統中,又有不同的業務應用需求。

4、   當然,卡這一級,也可以實現一個統一的介面,這樣對外部可以表現出相對統一的行為,以方便業務層程式碼的呼叫,比如說:在資料採集的應用中,統一的介面可以讓採集控制層不必依賴於具體的採集裝置和通訊方式,可以一致地實現資料收發,不管通訊方式是RS232RS485TCP/IPPSTN,還是別的方式或者通訊裝置。

5、   在通道設計中,核心的就是一個“狀態機模式”,通過輪巡通道狀態來管理硬體卡裝置,並且,還自己設計了一個業務級的“業務狀態機”,來抽象業務方面需要關心的“業務狀態”,通過增加“業務狀態機”這樣一箇中間層,以解耦業務狀態與裝置狀態之間的依賴。(這一點,在我看到的所有卡廠商提供的各類Demo程式裡面都沒有這樣做,這也無形中誤導了很多的開發人員,我看到的所有應用軟體開發的原始碼都是:裝置細節、尤其是通道狀態,與業務邏輯程式碼緊緊地耦合在一起,難解難分)

6、   此設計的另一個亮點是:IoC模式的應用(2004年自己在設計此類時還不知道這個概念,全憑自己的經驗總結出這樣的設計)。對通道進入“呼入成功”、“撥出成功”等業務狀態的呼叫程式碼從通道類是解耦出來:設計一個介面,在各個業務狀態的處理方法中,再呼叫介面方法,將具體的業務處理邏輯委託給實現此介面的物件。並且這個介面的實現是通過“依賴注入”實現IoC的。這樣設計,就達到了很好的可複用性和靈活性。

7、   當然,更好的實現可以採用AOP(面向方法程式設計)的思想或者實現技術,這樣可複用性更好,如此設計,在業務與卡方法的呼叫之間,耦合度將是最低的。

8、   目前的版本,沒有在程式碼中體現介面的實現……

9、   類圖(以後補上)

    10、卡類原始碼:

//------------------------------------------------------------------------------
//
//      產品名稱:   成曉旭的個人軟體Delphi原始碼庫
//      產品版本:   CXXSoft delphi code source lib 2.0
//      模組名稱:   Delphi之東進模擬語音卡類
//      模組描述:   
//      單元檔案:   unDJCard160A.pas   
//      開發作者:   成曉旭
//      備註:       任何人使用此檔案時,請保留此段自述檔案,謝謝!
//      開發時間:   2004-08-03
//      修改歷史:   
//      修改描述:
//------------------------------------------------------------------------------
unit unDJCard160A;

interface
uses
  Windows,
  unDJTC08a32,unDJNewSig,
  unBaseDefine,unDJ160ADefine,
  unDJChanne160A;
type
  TCXXCommCard160A 
= class(TObject)
  
private

    ChannelNumber:Word;
    channelObject:array of TCXXDJChannel160A;
    OnCardChannelState:TTrunkStatusEvent;

    procedure Stop();
    procedure ReleaseCommDevice();
    function  GetChannelObjectOrder(
const aChannelID:Word):Word;

  
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

{ TCXXCommCard160A }
constructor TCXXCommCard160A.Create(
const trunkEvent:TTrunkStatusEvent);
begin
  ChannelNumber :
= 0;
  Self.OnCardChannelState :
= trunkEvent;
end;

destructor TCXXCommCard160A.Destroy;
var
  Loop:Word;
begin
  Stop();
  
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 TCXXCommCard160A.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 TCXXCommCard160A.ReleaseCommDevice();
begin
  DisableCard();
  FreeDrv();
end;

function TCXXCommCard160A.GetAFreeChannel(): Word;
var
  Loop:Word;
begin
  Result :
= ErrorTrunkNumber;
  
for Loop := Low(channelObject) to High(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;

function TCXXCommCard160A.GetChannelNumber(): Word;
begin
  Result :
= channelNumber;
end;

function TCXXCommCard160A.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 TCXXCommCard160A.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 TCXXCommCard160A.LoadCommDevice(
const loadAll:boolean): boolean;
const
  loadEmpty 
= true;
var
  Loop,tempNumber:Word;
  isFlag:LongInt;
  function CheckLoadTrunk():boolean;
  begin
    Result :
= loadAll or ((NOT loadAll) and(TChannelType(CheckChType(Loop)) <> ctEmpty));
  end;
begin
  isFlag :
= LoadDRV();
  Result :
= (isFlag=0);
  
if NOT Result then Exit;
  tempNumber :
= CheckValidCh();
  Result :
= EnableCard(tempNumber,1024*8)=0;
  
if NOT Result then
  begin
    FreeDrv();
    Exit;
  end;
  Result :
= Sig_Init()=1;
  
if NOT Result then Exit;
  SetBusyPara(
700);
  SetPackRate(PACK_64KBPS );
  channelNumber :
= tempNumber;
  SetLength(channelObject,channelNumber);
  
for Loop := 0 to channelNumber - 1 do
  begin
    
if CheckLoadTrunk() then
    begin
      channelObject[Loop] :
= TCXXDJChannel160A.Create(OnCardChannelState);
      channelObject[Loop].CreateCommChannel(Loop);
    end;
  end;
end;

function TCXXCommCard160A.Startup(): boolean;
var
  Loop:integer;
begin
  
for Loop := 0 to channelNumber - 1 do
  begin
    channelObject[Loop].Resume();
  end;
  Result :
= true;
end;

procedure TCXXCommCard160A.Stop();
var
  Loop:integer;
begin
  
for Loop := 0 to channelNumber - 1 do
  begin
    channelObject[Loop].Suspend();
    channelObject[Loop].Terminate();
    channelObject[Loop] :
= nil;
  end;
end;

end.
11、              通道類原始碼:
//------------------------------------------------------------------------------
//
//      產品名稱:   成曉旭的個人軟體Delphi原始碼庫
//      產品版本:   CXXSoft delphi code source lib 2.0
//      模組名稱:   Delphi之東進模擬語音卡通道類
//      模組描述:   
//      單元檔案:   unDJChanne160A.pas   
//      開發作者:   成曉旭
//      備註:       任何人使用此檔案時,請保留此段自述檔案,謝謝!
//      開發時間:   2004-08-03
//      修改歷史:   
//      修改描述:
//------------------------------------------------------------------------------
unit unDJChanne160A;

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;
    
    procedure InformTrunkStatus(
const aMsgFlag: TLVOperateFlag);

    procedure ClearTrunkStatus();
    function  CheckSigHangup():boolean;
    function  CheckCallIn():boolean;
    function  SwitchOnCallIn():boolean;

    procedure ProcessCallInSuccess();
    procedure ProcessDialSuccess();

    procedure ProcessCheckDialSend();
  
protected
    procedure Execute(); 
override;
  
public
    strMessage:
string;
    OnChannelState:TTrunkStatusEvent;

    constructor Create(
const trunkEvent:TTrunkStatusEvent);
    destructor Destroy();
override;

    procedure CreateCommChannel(
const aChennelID: Word);
    procedure ChannelProcessor();

    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;
  end;
implementation

{ TCXXDJChannel160A }

procedure TCXXDJChannel160A.ChannelHangUp();
begin
  isDialOut :
= false;
  StopSigCheck(channelID);
  HangUp(channelID);
  Sig_ResetCheck(channelID);
  StartSigCheck(channelID);
  InitDTMFBuf(channelID);
  ClearTrunkStatus();
  InformTrunkStatus(lvofUpdate);
end;

procedure TCXXDJChannel160A.ChannelProcessor();
var
  dState:Word;
begin
  PUSH_PLAY();
  FeedSigFunc();
  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
      dState :
= Sig_CheckDial(channelID);
      
case dState of
  
//      S_NORESULT:
        S_CONNECT:
        begin
          channelState :
= atsDialSuccess;
          isConntectd :
= true;
        end;
        S_BUSY,
        S_NOBODY,
        S_NODIALSIG,
        S_NOSIGNAL:
        begin
          channelState :
= atsHangOff;
        end;
      end;
      strMessage :
= '撥號中...';
    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 TCXXDJChannel160A.CheckCallIn(): boolean;
begin
  Result :
= RingDetect(channelID);
  
if Result then
  begin
    OffHook(channelID);
    
if isDialOut then
      channelState :
= atsDialSuccess
    
else
      channelState :
= atsCallIning;
  end;
end;

function TCXXDJChannel160A.CheckSigHangup(): boolean;
begin
  Result :
= false;
  
if (Sig_CheckBusy(channelID)=1) then
  begin
    strMessage :
= '對方已掛機';
    InformTrunkStatus(lvofUpdate);
    StopPlayFile(channelID);
    channelState :
= atsHangOff;
    Result :
= true;
  end;
end;

procedure TCXXDJChannel160A.ClearTrunkStatus();
begin
  channelState :
= atsFree;
  oldChannelState :
= channelState;
  phoneNumber :
= '';
  dtmfString :
= '';
  strMessage :
= '';
  isConntectd :
= false;
end;

constructor TCXXDJChannel160A.Create(
const trunkEvent:TTrunkStatusEvent);
begin
  Self.OnChannelState :
= trunkEvent;
  Self.FreeOnTerminate :
= true;
  inherited Create(
true);
end;


destructor TCXXDJChannel160A.Destroy;
begin
  Suspend();
  ChannelHangUp();
  
//inherited Destroy();
end;

function TCXXDJChannel160A.DialPhone(
const DialPhoneNumber:PChar;
  
const PreDialNumber:PChar): boolean;
begin
  isDialOut :
= true;
  phoneNumber :
= DialPhoneNumber;
  OffHook(channelID);
  InitDTMFBuf(channelID);
  Result :
= Sig_StartDial(channelID,DialPhoneNumber,PreDialNumber,0)=1;
  channelState :
= atsCheckSendDial;
end;

function TCXXDJChannel160A.DialPhone(
  
const DialPhoneNumber: PChar): boolean;
begin
  Result :
= DialPhone(DialPhoneNumber,'');
end;

procedure TCXXDJChannel160A.Execute;
begin
  
while NOT Terminated do
  begin
    Synchronize(ChannelProcessor);
    Sleep(
10);
  end;
end;

function TCXXDJChannel160A.GetChannelID(): Word;
begin
  Result :
= channelID;
end;

function TCXXDJChannel160A.GetChannelStatus(): TTrunkState;
begin
  Result :
= channelState;
end;

procedure TCXXDJChannel160A.InformTrunkStatus(
const aMsgFlag: TLVOperateFlag);
begin
  aTrunkState.lvFlag :
= aMsgFlag;
  aTrunkState.TrunkID :
= IntToStr(channelID);
  aTrunkState.TrunkType :
= channelType;
  aTrunkState.TrunkTypeStr :
= ChannelTypeString[channelType];
  aTrunkState.TrunkStep :
= channelState;
  aTrunkState.TrunkStepStr :
= TrunkStateString[channelState];
  aTrunkState.TrunkPhone :
= phoneNumber;
  aTrunkState.TrunkData :
= dtmfString;
  OnChannelState(aTrunkState);
end;

procedure TCXXDJChannel160A.ProcessCallInSuccess();
begin

end;

function TCXXDJChannel160A.SwitchOnCallIn(): boolean;
begin
  OffHook(channelID);
  InitDTMFBuf(channelID);
  StartSigCheck(channelID);
  channelState :
= atsCallInSuccess;
  Result :
= true;
end;

procedure TCXXDJChannel160A.ProcessDialSuccess();
begin

end;

procedure TCXXDJChannel160A.CreateCommChannel(
const aChennelID: Word);
begin
  channelID :
= aChennelID;
  channelType :
= TChannelType(CheckChType(channelID));
  ClearTrunkStatus();
  InformTrunkStatus(lvofAdd);
end;

function TCXXDJChannel160A.GetChannelType(): TChannelType;
begin
  Result :
= channelType;
end;

function TCXXDJChannel160A.GetDialOut(): boolean;
begin
  Result :
= isDialOut;
end;

procedure TCXXDJChannel160A.ProcessCheckDialSend();
begin
  
if(CheckSendEnd(channelID) = 1) then
  begin
    StartSigCheck(channelID);
    channelState :
= atsDialing;
  end;
end;

end.


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1108211


相關文章