日本在线www-日本在线播放一区-日本在线不卡免费视频一区-日本在线不卡视频-成人影院久久久久久影院-成人影院一区二区三区

ABB
關注中國自動化產業發展的先行者!
工業智能邊緣計算2025年會
CAIAC 2025
2025工業安全大會
OICT公益講堂
當前位置:首頁 >> 案例 >> 案例首頁

案例頻道

Delphi環境下使用定制接口開發OPC數據訪問客戶程序
  • 企業:控制網     領域:電源    
  • 點擊數:6212     發布時間:2005-07-13 17:23:40
  • 分享到:
簡要的介紹了OPC以及使用定制接口開發OPC數據訪問客戶程序的背景知識。文中較為詳細的講述了如何Delphi環境下使用定制接口開發OPC數據訪問客戶程序。



1  引言


圖1  OPC應用架構



    OPC(用于過程控制的OLE)是一個工業標準。它由一些世界上占領先地位的自動化系統和硬件、軟件公司與微軟公司緊密合作而建立的。這個標準定義了應用Microsoft操作系統在基于PC 的客戶機之間交換自動化實時數據的方法。管理這個標準的國際組織是OPC基金會。因為應用程序要和不同的設備,比如PLC、變頻器、現場總線的儀表等通訊,如果不同的設備廠家都遵守一個相同的程序接口標準的話,那么程序和不同設備的溝通將變得非常容易。OPC 就是這樣一種工業標準,它是OLE for Process Control 的英文縮寫。OPC 是基于微軟的COM(Component Object Model,組件對象模型)和OLE(Object Linking and Embedding,對象鏈接與嵌入)技術之上的。和以前不同的是,現在設備廠家提供
不同的OPC Server。OPC Server負責從設備中取數據和寫數據。人們所要做的就是利用統一的COM 規范編寫OPC Client??蛻舫绦蚝蚈PC Server 打交道。OPC Server是一座在客戶和硬件設備之間的橋梁,通過它,人們可以很容易的取得現場的溫度、壓力、流量、位置等信號,以及控制現場的閥門開度、電機轉速等。注意客戶程序和服務器程序可以在同一臺計算機上,也可以在不同的計算機上,區別是使用COM 還是使用DCOM(Distributed Component Object Model,分布式組件對象模型)。如圖1所示。

2  OPC數據訪問服務器接口方式

    OPC數據訪問規范提供了兩套接口方案,即定制接口(Custom Interface)和自動化接口(Automation Interface)。其中自動化接口是對定制接口的進一步封裝。定制接口效率高,采用它能夠發揮OPC服務器的最佳性能。用C++編寫訪問OPC服務器的程序一般采用定制接口方案;對于VB、VBA、EXCEL等編程軟件或工具,不能直接訪問通用接口,而要通過自動化接口,因此使用VB或VBA等語言的客戶程序一般使用自動化接口。


圖2  OPC數據訪問方式

3  OPC數據訪問服務器的數據訪問方法

(1)  同步數據訪問處理
    OPC 服務器把按照OPC 客戶應用程序的要求得到的數據訪問結果,作為方法的參數返回給OPC 客戶程序。OPC 客戶應用程序在結果被返回之前必須處于等待狀態。
(2)  異步數據訪問處理
    OPC 服務器接到 OPC 應用程序的要求后,幾乎立即將方法返回。OPC 客戶應用程序隨后可以進行其他處理。當OPC 完成數據訪問時,觸發OPC 客戶應用程序的一部訪問事件,將數據訪問結果傳送給OPC 應用程序。OPC 應用程序在事件處理程序中接受OPC 服務器傳送來的數據。
(3)  訂閱方式數據采集
    此種方式是OPC 客戶應用程序不需要向OPC 服務器程序提出數據請求,而是自動接收OPC 服務器送來的變化通知信號的訂閱方式數據采集。服務器按照一定的更新周期更新OPC 服務器的數據緩沖器的數值,如果發現數值有變化時,就會以數據變化事件通知OPC 客戶應用程序。OPC 服務器支持不敏感帶寬 (Dead Band),當OPC標簽的數據類型是模擬量時,只有當前值與上一次值的偏差的絕對值超過一定的限度時,才更新緩沖器的數據并通知OPC 客戶應用程序。這樣就可以忽略模擬量的微小變化,從而減輕OPC 服務器和OPC 客戶的負擔。
(4)  刷新方式
    刷新是一種特殊形式的訂閱,它強制更新活動組中的所有活動項數據,而不管這些數據與以前相比是否有了變化。

4  使用Delphi進行COM程序開發的優點

    Delphi是功能強大的應用程序開發工具。它具有功能強大、運行速度快、易于學習、使用和開發效率高等特點。它是可視化應用編程環境,可重用性面向對象編程語言,快速編譯器和數據庫的完美組合。編寫OPC定制接口的客戶程序的本質就是編寫COM客戶程序,而使用Delphi進行COM開發時,人們會發現Object Pascal為COM提供了強大的語言支持。主要有以下幾點:
(1)  Variant 和 OleVariant支持
    直接使用C和C++處理變體需要調用VariantInit(),VariantCopy(),VariantClear()等函數。而Object Pascal對變體的支持,使得在使用Variant和OleVariant類型的情況下,編譯器能夠自動生成對API的變體支持程序的調用。
(2)  可變數組支持
    在Delphi中一旦一個Variant包含了一個可變數組,就可以用標準數組的下標來訪問數組元素。與C和C++中手工生成的安全數組相比,Object Pascal的封裝功能要簡潔的多,且不易出錯。
(3)  后期綁定Automation支持
    Object Pascal對于Variant和OleVariant的支持使得編寫后期綁定的自動化(Automation)客戶程序成為可能。因此使用Delphi來開發基于自動化接口的OPC客戶程序也是很方便的。
(4)  寬字符串支持
    Delphi提供的寬字符串(WideString)類型是一個和COM BSTR字符串兼容的字符串。而傳統方式下,使用標準的諸如SysStringLen()等API函數與BSTR一起工作是一件相當麻煩的事。
(5)  接口支持
    Object Pascal為合乎COM規范的接口提供了完全自包容的實現代碼,接口的實現不需要任何的COM API函數。程序員不需要考慮在傳統COM編程中的引用計數和接口查詢等底層細節。
(6)  Dispinterface接口支持
    Object Pascal對于Dispinterface的支持使得編寫支持雙接口的COM程序非常容易。

5  Delphi環境下使用定制接口開發OPC數據訪問客戶程序

    OPC規范中規定OPC服務器必須提供定制接口,而自動化接口則可以有選擇地提供。因此編寫使用定制接口的OPC客戶程序更具有一般意義,而且使用定制接口的效率要遠遠高于使用自動化接口的效率。本文限于篇幅只討論針對OPC數據訪問服務器的客戶應用程序。
5.1  OPC數據訪問服務器簡介
    OPCDataAccess服務器是最基本的OPC服務器,它包括OPCServer、OPCGroup和OPCItem三類典型的對象。OPC服務器對象維護有關服務器的信息并用作OPC組對象的容器,而OPC組對象維護組的信息,提供包容OPC項的機制,并管理OPC項。OPC組提供了客戶程序組織數據的手段。有兩種類型的組:公共(Public)組和局部(Local)組。公共組可以被多個客戶共享,而局部組只能被一個客戶使用。每個組中都可以定義一個或多個OPC項。OPC項代表了與服務器中的數據的連接??蛻舫绦驅PC項的操作都是通過包容此項的OPC組來進行的,而不是直接把OPC項作為一個對象來操作。每個OPC項都有值(Value)、品質(Quality)和時間戳(Time Stamp)三個屬性。



圖3  OPC數據訪問服務器的數據組織方式

    人們要的就是上面的item,這就是點,人們所謂的點,就是PLC的I/O點、儀表的數值等。編寫客戶端的程序的過程實際上就是對在OPC服務器中的數據項目(Item)進行操作。這就要求人們要了解OPC數據訪問服務器不同對象的接口的功能:
(1)  OPCServer對象接口
    OPCServer對象是OPC中的首要對象,它提供了如下接口:
    IUnknown接口是COM的標準接口;
   IOPCServer接口可對OPCGroup對象進行有關操作;
   IOPCServerPublicGroups接口為客戶和服務器提供了管理公共組的功能;
   IOPCBrowseServerAddressSpace接口提供了客戶瀏覽服務器數據項的功能;
   IOPCItemProperties接口讓客戶能夠瀏覽與ItemID相關的可訪問屬性;
   IOPCCommon接口提供了設置和詢問LocaleID的功能;
   IPersistFile接口允許客戶裝載和保存服務器的配置信息;
   IConnectionPointContainer接口允許用戶探查發現連接點。
(2)  OPCGroup對象接口
   OPCGroup對象是管理數據項集合的對象,它提供的接口如下:
   IUnknown接口是COM的標準接口;
   IOPCItemMgt接口為客戶提供了添加,刪除和控制組中數據項的功能;
   IOPCGroupStateMgt接口允許客戶管理組中的所有狀態信息;
   IOPCPublicGroupStateMgt接口用來將私有組轉換為公共組;
   IOPCSyncIO接口允許用戶對服務器執行同步讀寫操作;
   IOPCAsyncIO接口允許客戶對服務器執行異步讀寫操作;
   IOPCAsyncIO2接口用來替代IOPCAsyncIO接口;
   IConnectionPointContainer接口允許用戶探查發現連接點;
   IDataObject接口允許客戶和使用OPC數據流格式的組之間產生連接。
5.2  同步讀寫方式編程
   編程部分限于篇幅只列出核心代碼,關于一些類型定義,接口描述均省略了。
(1)  COM庫的初始化
   在調用任何COM或OLE API函數之前,必須要用CoIntialize()函數來對COM庫進行初始化,為了關閉COM庫在最后一次調用COM庫后,要調用CoUnitialize()函數。使用Delphi開發時,事情就簡化了,人們只需要在程序中包括ComObj單元即可,這樣應用程序在調用Application.Initialize()時會自動調用CoInitialize()函數,而ComObj單元的finalization部分會自動調用CoUnitialize()函數。只需要一句代碼:
Uses ComObj;
(2)  創建服務器對象
   Const ServerProgID = 'hua.da2.1';//OPC服務器的注冊名稱
   Var 
   ServerIf: IOPCServer;//聲明服務器對象接口
   HR: HResult;//用來保存函數返回值
   ServerIf := CreateComObject(ProgIDToClassID(ServerProgID)) as IOPCServer;
   / /本函數用來獲取服務器對象的IOPCServer接口,這是一個COM庫函數
(3)  添加組對象
   Var  
   GroupIf: IOPCItemMgt; 
   GroupHandle: OPCHANDLE;
   HR:=ServerAddGroup(ServerIf, 'MyGroup', True, 500, 0, GroupIf, GroupHandle);
   //本函數用來對IOPCServer.AddGroup方法進行包裝,在服務器對象中添加一個名為MyGroup的組對象,激活狀態為true,更新率為500毫秒。如果組對象添加成功,則組對象接口保存在GroupIf中,組對象句柄保存在GroupHandle中。其實現過程如下:
   function ServerAddGroup(ServerIf: IOPCServer; Name: string; Active: BOOL;
        UpdateRate: DWORD; ClientHandle: OPCHANDLE; var GroupIf: IOPCItemMgt;
        var ServerHandle: OPCHANDLE): HResult;
var
  PercentDeadBand: Single;
  RevisedUpdateRate: DWORD;
begin
  Result := E_FAIL;
  if ServerIf <> nil then
  begin
    PercentDeadBand := 0.0;
    Result := ServerIf.AddGroup(PWideChar(WideString(Name)), Active, UpdateRate,
                            ClientHandle, nil, @PercentDeadBand, 0,
                            ServerHandle, RevisedUpdateRate, IOPCItemMgt,
                            IUnknown(GroupIf));
  end;
  if Failed(Result) then
  begin
    GroupIf := nil;
  end;
end;
(4)  添加項目
const  Item0Name = 'item.hua.bstr';//要添加的項目名稱
Var 
ItemType: TVarType;
Item0Handle: OPCHANDLE;
HR := GroupAddItem(GroupIf, Item0Name, 0, VT_EMPTY, Item0Handle,ItemType);
//本函數用來對IOPCItemMgt.AddItems進行包裝,在組對象中添加一個類型為VT_EMPTY,名稱為item.hua.bstr的項目,如果添加成功,則項目句柄保存在Item0Handle中,實際項目類型保存在ItemType中。其實現過程如下:
function GroupAddItem(GroupIf: IOPCItemMgt; ItemID: string;
          ClientHandle: OPCHANDLE; DataType: TVarType;
          var ServerHandle: OPCHANDLE; var CanonicalType: TVarType): HResult;
var
  ItemDef: OPCITEMDEF;
  Results: POPCITEMRESULTARRAY;
  Errors: PResultList;
begin
  if GroupIf = nil then
  begin
    Result := E_FAIL;
    Exit;
  end;
  with ItemDef do
  begin
    szAccessPath := '';
    szItemID := PWideChar(WideString(ItemID));
    bActive := True;
    hClient := ClientHandle;
    dwBlobSize := 0;
    pBlob := nil;
    vtRequestedDataType := DataType;
  end;
  Result := GroupIf.AddItems(1, @ItemDef, Results, Errors);
  if Succeeded(Result) then
  begin
    Result := Errors[0];
    try
      if Succeeded(Result) then
      begin
        ServerHandle := Results[0].hServer;
        CanonicalType := Results[0].vtCanonicalDataType;
      end;
    finally
      CoTaskMemFree(Results[0].pBlob);
      CoTaskMemFree(Results);
      CoTaskMemFree(Errors);
    end;
  end;
end;
(5)  同步讀
Var
ItemValue: string;
ItemQuality: Word;
HR := ReadOPCGroupItemValue(GroupIf, Item0Handle, ItemValue, ItemQuality);
//本函數用來同步讀取組中的項目值,如果讀取成功,則項目值保存在ItemValue中,項目質量保存在ItemQuality中,其實現過程如下:
function ReadOPCGroupItemValue(GroupIf: IUnknown; ItemServerHandle: OPCHANDLE;
          var ItemValue: string; var ItemQuality: Word): HResult;
var
  SyncIOIf: IOPCSyncIO;
  Errors: PResultList;
  ItemValues: POPCITEMSTATEARRAY;
begin
  Result := E_FAIL;
  try
    SyncIOIf := GroupIf as IOPCSyncIO;
  except
    SyncIOIf := nil;
  end;
  if SyncIOIf <> nil then
  begin
    Result := SyncIOIf.Read(OPC_DS_CACHE, 1, @ItemServerHandle, ItemValues,
                            Errors);
    if Succeeded(Result) then
    begin
      Result := Errors[0];
      CoTaskMemFree(Errors);
      ItemValue := VarToStr(ItemValues[0].vDataValue);
      ItemQuality := ItemValues[0].wQuality;
      VariantClear(ItemValues[0].vDataValue);
      CoTaskMemFree(ItemValues);
    end;
  end;
end;
(6)  同步寫
ItemValue:='hello,the operation is sync-write';
HR := WriteOPCGroupItemValue(GroupIf, Item0Handle, ItemValue);
//本函數用來將ItemValue同步寫入Item0Handle所代表的項目中,其實現過程如下:
function WriteOPCGroupItemValue(GroupIf: IUnknown; ItemServerHandle: OPCHANDLE;
          ItemValue: OleVariant): HResult;
var
  SyncIOIf: IOPCSyncIO;
  Errors: PResultList;
begin
  Result := E_FAIL;
  try
    SyncIOIf := GroupIf as IOPCSyncIO;
  except
    SyncIOIf := nil;
  end;
  if SyncIOIf <> nil then
  begin
    Result := SyncIOIf.Write(1, @ItemServerHandle, @ItemValue, Errors);
    if Succeeded(Result) then
    begin
      Result := Errors[0];
      CoTaskMemFree(Errors);
    end;
  end;
end;
(7)  斷開服務器連接
   HR := ServerIf.RemoveGroup(GroupHandle, False);
   //本函數用來將在服務器中創建的組GroupHandle刪除,服務器對象在程序結束時會自動銷毀。
5.3  異步讀寫方式編程
    使用異步方式進行讀寫編程,需要客戶提供IOPCDataCallback接口,還需要使用到COM的連接點容器,連接點和接收器等相關知識。以下關于異步讀寫方式編程中和同步方式相同的部分均未給出其實現部分。下面給出異步讀寫方式編程的主要步驟:
   (1)  COM庫的初始化   //實現過程同上
   (2)  創建服務器對象 //實現過程同上
   (3)  添加組對象 //實現過程同上
   (4)  添加項目  //實現過程同上
   (5)  實現IOPCDataCallback接口
   為了使用連接點,客戶必須創建同時支持IUnknown和IOPCDataCallback接口的對象。下面的TOPCDataCallback從TInterfacedObject繼承所以支持IUnknown接口,它實現了IOPCDataCallback接口。
type
   // 本類用來接收 IConnectionPointContainer 的數據變化回調
  TOPCDataCallback = class(TInterfacedObject, IOPCDataCallback)
  public
    function OnDataChange(dwTransid: DWORD; hGroup: OPCHANDLE;
      hrMasterquality: HResult; hrMastererror: HResult; dwCount: DWORD;
      phClientItems: POPCHANDLEARRAY; pvValues: POleVariantArray;
      pwQualities: PWordArray; pftTimeStamps: PFileTimeArray;
      pErrors: PResultList): HResult; stdcall;
    function OnReadComplete(dwTransid: DWORD; hGroup: OPCHANDLE;
      hrMasterquality: HResult; hrMastererror: HResult; dwCount: DWORD;
      phClientItems: POPCHANDLEARRAY; pvValues: POleVariantArray;
      pwQualities: PWordArray; pftTimeStamps: PFileTimeArray;
      pErrors: PResultList): HResult; stdcall;
    function OnWriteComplete(dwTransid: DWORD; hGroup: OPCHANDLE;
      hrMastererr: HResult; dwCount: DWORD; pClienthandles: POPCHANDLEARRAY;
      pErrors: PResultList): HResult; stdcall;
    function OnCancelComplete(dwTransid: DWORD; hGroup: OPCHANDLE):
      HResult; stdcall;
  end;
(6)  連接IOPCDataCallback接口
   客戶程序除了實現接收器對象外,還必須建立接收器與連接點對象之間的連接關系
var 
  AsyncConnection: Longint;
  OPCDataCallback: IOPCDataCallback;
  OPCDataCallback := TOPCDataCallback.Create;//創建接收器對象
  HR := GroupAdvise2(GroupIf, OPCDataCallback, AsyncConnection);
//本函數實現了接收器對象與連接點對象之間建立關系的過程,如果連接成功則用AsyncConnection來標識這個連接,其具體實現如下:
function GroupAdvise2(GroupIf: IUnknown; OPCDataCallback: IOPCDataCallback;
          var AsyncConnection: Longint): HResult;
var
  ConnectionPointContainer: IConnectionPointContainer;
  ConnectionPoint: IConnectionPoint;
begin
  Result := E_FAIL;
  try
    ConnectionPointContainer := GroupIf as IConnectionPointContainer;
  except
    ConnectionPointContainer := nil;
  end;
  if ConnectionPointContainer <> nil then
  begin
    Result := ConnectionPointContainer.FindConnectionPoint(IID_IOPCDataCallback,
      ConnectionPoint);
    if Succeeded(Result) and (ConnectionPoint <> nil) then
    begin
      Result := ConnectionPoint.Advise(OPCDataCallback as IUnknown,
        AsyncConnection);
    end;
  end;
end;
(7)  異步讀寫
   客戶進行異步讀寫操作只需要簡單的調用IOPCAsyncIO2接口的Read,Write等方法,這個過程不需要客戶程序等待,函數的返回值中并不包括操作的結果,而操作的具體結果會由服務器通過調用客戶的IOPCDataCallback接口中的OnDataChange,OnReadComplete,OnWriteComplete,OnCancelComplete等方法來返回給客戶。下面給出IOPCAsyncIO2接口的Delphi描述:
IOPCAsyncIO2 = interface(IUnknown)
    ['{39C13A71-011E-11D0-9675-0020AFD8ADB3}']
    function Read(
            dwCount:                    DWORD;
            phServer:                   POPCHANDLEARRAY;
            dwTransactionID:            DWORD;
      out   pdwCancelID:                DWORD;
      out   ppErrors:                   PResultList): HResult; stdcall;
    function Write(
            dwCount:                    DWORD;
            phServer:                   POPCHANDLEARRAY;
            pItemValues:                POleVariantArray;
            dwTransactionID:            DWORD;
      out   pdwCancelID:                DWORD;
      out   ppErrors:                   PResultList): HResult; stdcall;
    function Refresh2(
            dwSource:                   OPCDATASOURCE;
            dwTransactionID:            DWORD;
      out   pdwCancelID:                DWORD): HResult; stdcall;
    function Cancel2(
            dwCancelID:                 DWORD): HResult; stdcall;
    function SetEnable(
            bEnable:                    BOOL): HResult; stdcall;
    function GetEnable(
      out   pbEnable:                   BOOL): HResult; stdcall;
  end;
   當服務器中的數據發生變化時,服務器會調用客戶的IOPCDataCallback接口中OnDataChange函數,當服務器異步讀完成后會調用客戶的IOPCDataCallback接口中OnReadComplete函數,當服務器異步寫完成后會調用客戶的IOPCDataCallback接口中OnWriteComplete函數,當服務器撤銷操作完成后會調用客戶的IOPCDataCallback接口中OnCancelComplete函數。限于篇幅,這里只給出客戶的IOPCDataCallback接口中OnDataChange函數實現,其他函數的實現過程類似。
function TOPCDataCallback.OnDataChange(dwTransid: DWORD; hGroup: OPCHANDLE;
  hrMasterquality: HResult; hrMastererror: HResult; dwCount: DWORD;
  phClientItems: POPCHANDLEARRAY; pvValues: POleVariantArray;
  pwQualities: PWordArray; pftTimeStamps: PFileTimeArray;
  pErrors: PResultList): HResult;
var
  ClientItems: POPCHANDLEARRAY;
  Values: POleVariantArray;
  Qualities: PWORDARRAY;
  I: Integer;
  NewValue: string;
begin
  Result := S_OK;
  ClientItems := POPCHANDLEARRAY(phClientItems);
  Values := POleVariantArray(pvValues);
  Qualities := PWORDARRAY(pwQualities);
  for I := 0 to dwCount - 1 do
  begin
    if Qualities[I] = OPC_QUALITY_GOOD then
    begin
      NewValue := VarToStr(Values[I]);
      Form1.LblValue.Caption :=NewValue;
    end
    else begin
      ShowMessage('Callback received for item , but quality not good');
    end;
  end;
end;
(8)  斷開IOPCDataCallback接口
GroupUnadvise2(GroupIf, AsyncConnection);
//斷開客戶與組對象GroupIf之間用AsyncConnection標識的IOPCDataCallback接口連接,其實現過程如下:
function GroupUnadvise2(GroupIf: IUnknown; var AsyncConnection: Longint): HResult;
var
  ConnectionPointContainer: IConnectionPointContainer;
  ConnectionPoint: IConnectionPoint;
begin
  Result := E_FAIL;
  try
    ConnectionPointContainer := GroupIf as IConnectionPointContainer;
  except
    ConnectionPointContainer := nil;
  end;
  if ConnectionPointContainer <> nil then
  begin
    Result := ConnectionPointContainer.FindConnectionPoint(IID_IOPCDataCallback,
      ConnectionPoint);
    if Succeeded(Result) and (ConnectionPoint <> nil) then
    begin
      Result := ConnectionPoint.Unadvise(AsyncConnection);
    end;
  end;
end;
(9)  斷開服務器連接 //實現過程同上

6  結語

    使用OPC的定制接口進行客戶程序的編程,需要一些COM知識的理解以及對于以下開發工具的熟練使用,因此相對于自動化接口的編程來說時比較困難的,但是使用定制接口的效率和靈活性卻是使用其他方式所無法比擬的。本文只是粗淺的介紹了在Delphi環境下使用定制接口進行OPC客戶程序的開發,希望能達到拋磚引玉的效果。

熱點新聞

推薦產品

x
  • 在線反饋
1.我有以下需求:



2.詳細的需求:
姓名:
單位:
電話:
郵件:
主站蜘蛛池模板: 欧美日韩在线成人免费-欧美日韩在线成人看片a-欧美日韩在线不卡-欧美日韩在线播放-自拍偷拍三级-自拍偷拍欧美亚洲 | 日本最新中文字幕-日本最新在线-日本最新伦中文字幕-日本综合在线-国产 日韩 欧美 高清-国产 欧美日韩 在线播放 | 国产精品福利在线观看入口-国产精品福利在线观看秒播-国产精品福利在线观看免费不卡-国产精品福利一区二区亚瑟-四虎免费入口-四虎免费看片 | 国产一区二区高清,久久亚洲私人国产精品va,日韩精品观看,国产高清美女一级毛片久久,国产区亚洲区,日本久久香蕉一本一道 | 九九国产在线视频-九九国产在线观看-九九国产在线-九九国产视频-亚洲夜色夜色综合网站-亚洲羞羞视频 | 四色草视频-四散的尘埃在线观看-四库国产精品成人-四虎最新网址入口-国产精品一区二区三区四区五区-国产精品一区二区三区四区 | 香蕉久久综合-香蕉久久夜色精品国产尤物-香蕉久久夜色精品国产-香蕉久久久久-久久网站视频-久久网免费 | 国产欧美精品一区二区三区四区-国产欧美精品一区二区三区-国产欧美精品一区二区-国产欧美精品系列在线播放-天天爽天天-天天视频一区二区三区 | 一个色综合高清在线观看-一个色在线视频-一个色在线-一个人在线免费观看www-久久99网-久久99视频免费 | 一区二区视频在线观看高清视频在线-一区二区三区无码高清视频-一区二区三区无码被窝影院-一区二区三区四区国产-久久re视频精品538在线-久久re热在线视频精99 | 国产福利自产拍在线观看-国产福利资源网在线观看-国产福利资源-国产福利专区精品视频-双性少爷受糙汉攻h-双性肉文高h | 精品在线视频播放-精品在线免费播放-精品在线观看一区-精品在线观看国产-亚洲 男人 天堂-亚州视频一区二区 | 五月天婷婷视频,九九爱这里只有精品,热和尚三区四区,善良的嫂子3在线播放,久久久久久久久久久久久久,热er99久久6国产精品免费 | 与子敌伦刺激对白亂輪亂性-与嫂子同居的日子在线观看-与邻居换娶妻子2在线观看-瑜伽牲交AV-久久久96-久久久91精品国产一区二区 | 久久这里只有精品国产99-久久这里只有精品2-久久这里只有精品1-久久这里只精品热在线99-在线少女漫画-在线涩涩免费观看国产精品 国产精选一区二区-国产精选一区-国产精选污视频在线观看-国产精选91热在线观看-特级黄色视频毛片-特级黄色免费片 | 波多野结衣的av一区二区三区-波多野结衣的电影-波多野结衣的中文-波多野结衣第二页视频-波多野结衣电影网-波多野结衣电影一区二区 | 日韩精品电影在线观看-日韩精品电影在线-日韩精品电影一区-日韩精品电影-国产日韩欧美综合-国产日韩欧美在线一区二区三区 | 欧美日韩一日韩一线不卡-欧美日韩一区在线观看-欧美日韩一区视频-欧美日韩一区二区综合在线视频-在线免费观看中文字幕-在线免费观看日本视频 | 国产精品jlzz视频-国产精品jizz在线观看直播-国产精品jizz在线观看网站-国产精品jizz在线观看软件-日日爽夜夜操-日日爽天天干 | 国产日韩精品欧美一区-国产日韩高清一区二区三区-国产日韩不卡免费精品视频-国产日产欧美精品一区二区三区-午夜国产精品免费观看-午夜国产精品理论片久久影院 | 久久久久久一级毛片免费野外-久久久久久一级毛片免费无遮挡-久久久久久亚洲精品影院-久久久久久亚洲精品不卡-午夜久久精品-午夜精品在线视频 | 九九香蕉-九九线精品视频-九九五月天-九九天天影视-天天干b-天天干2018 | 亚洲成人7777-亚洲成人777-亚洲成人18-亚洲成片在线观看12345ba-国农村精品国产自线拍-国内做爰免费视频 | 岛国精品在线观看-岛国精品在线-岛国大片在线免费观看-岛国大片在线观看完整版-日本老师xxxx88免费视频-日本久久综合网 | 免费黄色在线观看视频-免费黄色在线观看-免费黄色在线电影-免费黄色在线-成人精品一区二区三区电影-成人精品一区二区三区 | 一级毛片在线看-一级毛片在线播放免费-一级毛片一级毛片免费毛片-一级毛片一级毛片-九九51精品国产免费看-九号影院 | 精品国产91乱码一区二区三区,成人国产一区二区三区精品,亚洲一区免费在线观看,日韩在线一区二区三区免费视频,波多野吉衣在线观看,日韩一级精品久久久久 | 亚洲精品在线观看视频-亚洲精品在线观看-亚洲精品在线第一页-亚洲精品在线播放视频-护士伦理-护士撩起裙子让你桶的视频 | 欧美黄网在线-欧美黄色影院-欧美黄色影视-欧美黄色一级网站-99免费看-99免费精品视频 | 国产一区二区三区四区五区加勒比-国产一区二区三区四区五区六区-国产一区二区三区四区五在线观看-国产一区二区三区无码A片-国产一区二区三区-国产一区二区三区亚洲欧美 | 亚洲免费在线观看-做羞羞的事情的免费视频-最终痴汉电车在线观看-最新综艺-最新自拍偷拍-最新在线精品国自拍视频 | 日本久久久久久久,97久久精品一区二区三区,狠狠色噜噜狠狠狠狠97,日日干综合,五月天婷婷在线观看高清,九色福利视频 | 美女三级毛片-美女牲交视频一级毛片无遮挡-美女视频大全视频a免费九-美女视频黄a视频全免费网站色窝-美女视频黄的全是免费-美女视频秀色福利视频 | 亚洲 日韩 色 图网站-亚洲 图片 另类 综合 小说-亚洲 无码 欧美 经典-亚洲 校园 春色 另类 图片-亚洲 校园 欧美 国产 另类-亚洲 在线 成 人色色 | 在线免费观看国产精品,日韩经典在线,香蕉久久网,爽妇网s,国内在线观看精品免费视频,欧美另类图片亚洲偷 | 国产三级高清午夜羞羞视频-国产三级高清在线观看-国产三级观看久久-国产三级国产av品爱网-国产三级国产精品-国产三级国产精品国产国在线观看 | 天天干天天操天天碰-天天干天天操天天摸-天天干天天操天天干-天天干天天操天天插-欧美一级久久久久久久久大-欧美一区二区VA毛片视频 | 国产精品高潮呻吟AV久久-国产精品高潮呻吟AV久久床戏-国产精品高潮呻吟AV久久动漫-国产精品高潮呻吟AV久久黄-国产精品高潮呻吟AV久久无码-国产精品高潮呻吟爱久久AV无码 | 国产成人av网站网址-国产成人av无码精品-国产成人av无码精品天堂-国产成人av无码片在线观看-国产成人av无码一区二区三区不卡-国产成人av无码永久免费 | a级在线免费-a级在线看-a级在线观看免费-a级在线观看-日韩avdvd-日韩aa在线观看 | 一区二区在线视频观看-一区二区在线免费视频-一区二区在线看-一区二区在线电影-久久精品久久精品国产大片-久久精品久久精品 |