Вызовы Delphi Onvif SOAP

Я бы хотел начать использовать Delphi для доступа и управления IP-камерами по протоколу Onvif SOAP.

Однако я не понимаю, как на самом деле выполнять вызовы из Delphi. Я импортировал следующий WDSL:

http://www.onvif.org/ver20/ptz/wsdl/ptz.wsdl

и модуль был создан в Delphi. Но как мне звонить в камеру? Должен ли я как-то использовать это сгенерированное вместе с THTTPRIO? Как указать IP-адрес имеющейся у меня камеры (сейчас это камера Axis Q1755).

Если бы кто-то мог помочь мне начать работу, указав правильное направление, я был бы очень благодарен.


person TomRay74    schedule 28.09.2014    source источник
comment
Чтобы указать адрес P: см. stackoverflow.com/questions/3596868/   -  person mjn    schedule 29.09.2014


Ответы (2)


Если вы использовали импортер WSDL Delphi, он сгенерирует необходимые классы для представления любых request из SOAP веб-сервиса и соответствующего ему response.

Также будет сгенерирован wrapper, который вы будете использовать для фактических вызовов. Обычно для этого используются следующие методы:

function mySoapMethod(myRequest: TmyRequestType): TMyResponse;

Чтобы позвонить, вы в основном делаете следующее:

  1. Получите wrapper ссылку на экземпляр. В модуле должен быть метод, называемый чем-то похожим на GetWrapper.
  2. При необходимости вы создаете экземпляр для request type и устанавливаете все его свойства. Имейте в виду, что может быть и не существует класса запроса как такового, если методу SOAP просто нужны несколько базовых типов в качестве параметров. Также учтите, что любой достаточно сложный request type может означать, что вам нужно будет создать экземпляры объектов и назначить их в качестве свойств запросу.
  3. Используйте соответствующий wrapper method для отправки запроса (или установите параметры, если это простой вызов).
  4. Получите объект response и действуйте с ним по мере необходимости.

В псевдокоде это будет следующее:

myWrapper := GetMyWrapper();
myRequest := TMyRequest.Create;
//set myRequest properties
myResponse := myWrapper.mySoapMethod(myRequest);
//do whatever you need with the response

О оболочке

wrapper будет реализацией интерфейса IInvokable с некоторыми добавленными методами. На самом деле у него должен быть один метод для каждого SOAP method, который вы можете вызвать.

Обычно его объявление интерфейса будет примерно таким:

TmyWrapper = interface(IInvokable)
['...'] //GUID here
  function oneMethod(...): oneMethodResponse;
  function anotherMethod(...): anotherMethodResponse;
end;

function getMyWrapper(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): TmyWrapper ;

И реализация функции будет выглядеть так:

function GetMyWrapper(UseWSDL: Boolean; Addr: string; HTTPRIO: THTTPRIO): TmyWrapper;
const
  defWSDL = 'http://<soap service IP and port>/<soap service name>?wsdl';
  defURL  = 'http://<soap service IP and port>/<soap service name>';
  defSvc  = '<default service name>';
  defPrt  = '<default service port>';
var
  RIO: THTTPRIO;
begin
  Result := nil;
  if (Addr = '') then
  begin
    if UseWSDL then
      Addr := defWSDL
    else
      Addr := defURL;
  end;
  if HTTPRIO = nil then
    RIO := THTTPRIO.Create(nil)
  else
    RIO := HTTPRIO;
  try
    Result := (RIO as TmyWrapper );
    if UseWSDL then
    begin
      RIO.WSDLLocation := Addr;
      RIO.Service := defSvc;
      RIO.Port := defPrt;
    end else
      RIO.URL := Addr;
  finally
    if (Result = nil) and (HTTPRIO = nil) then
      RIO.Free;
  end;
end;

Некоторые примечания на стороне:

  • Вы можете внедрить экземпляр HTTPRIO в wrapper, когда получите его экземпляр, используя метод getXYZWrapper. Это может помочь вам, например, установить другой SOAP URL или проверить сгенерированный XML.
  • Иногда Delphi генерирует XML для отправки несколько иначе, чем SOAP ожидает. Если вы думаете, что это должно работать, но не работает, проверьте сгенерированный XML и сравните его с тем, что должно быть. Вы можете использовать событие onBeforeExecute объекта HTTPRIO, чтобы изменить XML перед его отправкой.
  • Если он вам нужен, вы также можете использовать метод onAfterExecute объекта HTTPRIO для проверки ответа XML.
person Guillem Vicens    schedule 28.09.2014
comment
Спасибо за развернутый ответ. К сожалению, у меня нет функции Wrapper в сгенерированном модуле, которая выглядит как та, которую вы предложили. У меня есть несколько классов TRemotable, а также несколько перечислений и интерфейсов. Но как мне подключить их к объекту HTTPRIO для вызова моей камеры? - person TomRay74; 29.09.2014
comment
@ TomRay74, извините, вчера было поздно, и я написал этот ответ из головы. Я обновлю его, чтобы подробнее рассказать о оболочке. - person Guillem Vicens; 29.09.2014

вы можете использовать мои импортированные WSDL от Delphi XE7

Медиа: Media.WSDL

Изображение: Imaging.wsdl

управление устройством: devicemgmt.wsdl


обычно импортированные WSDL имеют такую ​​функцию, как

function GetMySoap(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): MySoap;

но его нет в импортированном wsdl onvif.

добавьте эту функцию в экземпляр вашего проекта, Media2 - это имя класса IInvokable

function GetWSSoap(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): Media2;
const
  defWSDL = 'http://192.168.1.1/onvif/Media?WSDL';
  defURL  = 'http://192.168.1.1/onvif/Media';
  defSvc  = 'Media';
  defPrt  = '80';
var
  RIO: THTTPRIO;
begin
  Result := nil;
  if (Addr = '') then
  begin
    if UseWSDL then
      Addr := defWSDL
    else
      Addr := defURL;
  end;
  if HTTPRIO = nil then
    RIO := THTTPRIO.Create(nil)
  else
    RIO := HTTPRIO;
  try
    Result := (RIO as Media2);
    if UseWSDL then
    begin
      RIO.WSDLLocation := Addr;
      RIO.Service := defSvc;
      RIO.Port := defPrt;
    end else
      RIO.URL := Addr;
  finally
    if (Result = nil) and (HTTPRIO = nil) then
      RIO.Free;
  end;
end;

другое решение, получите общую информацию, отправив http requst Пример запроса GetProfiles - это имя методов

<?xml version="1.0"?> <soap:Envelope 
xmlns:soap="http://www.w3.org/2003/05/soap-envelope" 
xmlns:wsdl="http://www.onvif.org/ver10/media/wsdl">
<soap:Header>
<Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1"> 
<UsernameToken> 
<Username>%s</Username> 
<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">%s</Password> 
<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">%s</Nonce> 
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">%s</Created> 
</UsernameToken> 
</Security> 
</soap:Header>
<soap:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
<GetProfiles xmlns="http://www.onvif.org/ver10/media/wsdl" /> 
</soap:Body> 
</soap:Envelope>

Код кнопки:

ip: http://192.168.1.1

url: onvif / media

  GetONVIFPasswordDigest(ed_user.text, ED_pass.text, PasswordDigest, Nonce, Created);
  User_Pass := Format(M_Request, [UserName, PasswordDigest, Nonce, Created]);
  ONVIFRequest(ED_IP.Text + ED_URL.Text,User_Pass,Result);

Процедура создания паролей:

procedure GetONVIFPasswordDigest(const UserName, Password: String; Var PasswordDigest, Nonce, Created: String);
Var
  i: Integer;
  raw_nonce, bnonce, digest , S_In: TBytes;
  raw_digest: TBytes;
  DT : TDateTime;
begin
  SetLength(raw_nonce, 20);
  for i := 0 to High(raw_nonce) do
    raw_nonce[i] := Random(256);
  bnonce := TNetEncoding.Base64.Encode(raw_nonce);
  Nonce := BytesToString(bnonce);
  DT := Now();
  Created := GetONVIFDateTime(DT);
  S_In := raw_nonce + StringToBytes(Created) + StringToBytes(Password);
  raw_digest := SHA1(S_In);
  digest := TNetEncoding.Base64.Encode(raw_digest);
  PasswordDigest := BytesToString(digest);
end;

процедура получения данных:

procedure ONVIFRequest(const Addr: String; const InStream, OutStream: TStringStream);overload;
Var
  idhtp1: TIdHTTP;
  Uri: TIdURI;
begin
  idhtp1 := TIdHTTP.Create;
  Uri := TIdURI.Create(Addr);
  try
    With idhtp1 do
    begin
      AllowCookies := True;
      HandleRedirects := True;
      Request.Accept := 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
      Request.UserAgent := 'Mozilla/3.0 (compatible; Indy Library)';
      Request.Host := '';
      Request.Connection := '';
      Request.Accept := '';
      Request.UserAgent := '';

      Request.CustomHeaders.Clear;
      Request.ContentType := 'text/xml;charset=utf-8';
      Request.CustomHeaders.Add('Host: ' + Uri.Host);
      //-------------------
      request.username := ed_user.text;
      request.password := ed_pass.text;
      HTTPOptions := [hoInProcessAuth,hoForceEncodeParams,hoNoProtocolErrorException, hoWantProtocolErrorContent];
      //-------------------

      ProtocolVersion := pv1_1;
      Post(Addr, InStream, OutStream);
    end;
  finally
    Uri.Free;
    idhtp1.Free;
  end;
end;
procedure ONVIFRequest(const Addr, Request: String; Var Answer: String);overload;
Var
  InStream, OutStream: TStringStream;
begin
  InStream := TStringStream.Create(Request);
  OutStream := TStringStream.Create;
  try
    ONVIFRequest(Addr, InStream, OutStream);
    Answer := OutStream.DataString;
  finally
    InStream.Free;
    OutStream.Free;
  end;
end;

Вы также можете найти лучший образец здесь

https://github.com/Laex/Delphi-ONVIF
person Ali Reza Nazari    schedule 24.07.2019