Как использовать спецификацию WS-Discovery для обнаружения устройств ONVIF в сети на Java? - PullRequest
0 голосов
/ 30 мая 2019

Я пытаюсь обнаружить устройства ONVIF с помощью некоторого кода Java. В частности, я пытаюсь получить их служебный адрес устройства (который, как мне кажется, является просто их IP-адресом), поскольку Базовая спецификация ONVIF отмечает (в разделе 4.3), что «Успешное обнаружение предоставляет адрес службы устройства. Когда клиент имеет адрес службы устройства, он может получать подробную информацию об устройстве через службу устройства ... ". В конечном итоге моя цель - получить эту подробную информацию об устройствах ONVIF в сети. В общем, я также ищу руководство по использованию спецификации ONVIF.

Я все еще новичок в мире веб-сервисов (и сетей в целом), поэтому прости меня, если я скажу что-нибудь глупое. Тем не менее, я приложил немало усилий к этому сам: я прочитал множество Базовых спецификаций ONVIF , Руководства разработчика приложений ONVIF и Спецификации WS-Discovery . Если позволите, я просто подведу итоги, что я знаю, чтобы вы могли сказать мне, если я на правильном пути:

  1. «Веб-сервисы» - это название стандартной технологии, использующей стандарты веб-сервисов, не зависящие от платформы и языка, такие как XML, SOAP и WSDL в IP-сети. Основная идея заключается в том, что мы хотим иметь возможность вызывать методы / функции (службы) из любого языка программирования.
  2. Веб-сервис обычно размещается на сервере; но в случае использования ONVIF поставщиком веб-услуг является устройство ONVIF (например, IP-камера). Таким образом, для взаимодействия с устройством на любом языке мы используем операции / вызовы веб-службы, поскольку вызовы веб-службы могут быть реализованы на любом языке.
  3. XML - это синтаксис описания данных (используется потому, что он не зависит от языка; его может проанализировать любой язык). SOAP - это коммуникационный протокол, используемый для получения XML-документов, наполненных SOAP, туда и обратно (в основном, вызывайте наши методы). WSDL используется для описания сервисов (это описание интерфейса веб-сервисов на основе XML). Я загрузил WSDL для управления устройствами здесь и сгенерировал с помощью WSDL-компилятора wsimport (предоставленный JDK) классы Java из WSDL для использования в моем коде. Но я понимаю, что вызов этих методов произойдет после обнаружения устройства, верно?
  4. Устройства ONVIF обнаруживаются в соответствии со спецификацией WS-Discovery. Вы отправляете сообщение Probe, а устройства, соответствующие ограничениям датчика, отправляют обратно сообщение ProbeMatch, как описано на стр. 13 и 14 в Руководстве по программированию приложений ONVIF

Здесь я начинаю путаться. Как именно я отправляю это сообщение на Java? Руководство разработчика приложений ONVIF содержит псевдокод на стр. 15, но я не могу понять, как его реализовать. Раздел 4.3.1 этого руководства - это то, на чем я застрял. Я понимаю, что «области» и «типы» - это просто ограничения, которые вы можете встроить в зонд, но они не обязательны (согласно странице 5 WS discovery spec ). Поскольку я хочу открыть для себя все устройства, я полагаю, что для запуска не нужно никаких ограничений, верно?

Так что в этом руководстве также приведен пример сообщения SOAP на стр. 110, используемого для обнаружения. Убрав из него объявление типа (потому что я не хочу этого ограничения), я понимаю, что мое SOAP-сообщение для отправки будет (я верю?) Следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<e:Envelope xmlns:e="http://www.w3.org/2003/05/soap-envelope"
 xmlns:w="http://schemas.xmlsoap.org/ws/2004/08/addressing"
 xmlns:d="http://schemas.xmlsoap.org/ws/2005/04/discovery"
 xmlns:dn="http://www.onvif.org/ver10/network/wsdl">
 <e:Header>
 <w:MessageID>uuid:84ede3de-7dec-11d0-c360-f01234567890</w:MessageID>
 <w:To e:mustUnderstand="true">urn:schemas-xmlsoap-org:ws:2005:04:discovery</w:To>
 <w:Action
a:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2005/04/discovery/Pr
obe</w:Action>
 </e:Header>
 <e:Body>
 <d:Probe>
 </d:Probe>
 </e:Body>
</e:Envelope>   

И я также понимаю, что технология WS-Discovery использовала адрес 239.255.255.259 с UDP-портом 3702 ... но это конец того, что я получаю. Как отправить это SOAP-сообщение на этот адрес и порт в Java? Как мне прочитать ответ (я думаю , что я получу сообщение ProbeMatch в форме XML-документа, наполненного SOAP, поэтому мне нужно проанализировать этот XML, чтобы получить XAddrs, но не уверен). Нужно ли как-то отправлять UDP-трансляцию этого SOAP-сообщения на этот адрес?

TL; DR : я полагаю, что для обнаружения устройств ONVIF мне нужно отправить указанное выше SOAP-сообщение по адресу 239.255.255.259 на UDP-порт 3702. Я понятия не имею, как это сделать в Java, и был просто ищу руководство; Я даже не уверен, что нахожусь на правильном пути, чтобы найти устройство.

Ответы [ 2 ]

1 голос
/ 31 мая 2019

Используя CXF WSDiscoveryClient , вы можете проверять устройство ONVIF.
По умолчанию WSDiscoveryClient использует WS-discovery 1.1, а ONVIF использует WS-discovery 1.0, поэтому вам необходимо включить WS-discovery 1.0.
Короткая реализация может быть:

import java.util.List;
import javax.xml.ws.EndpointReference;
import org.apache.cxf.ws.discovery.WSDiscoveryClient;

public class Main 
{
    public static void main(String[] args) 
    {
        WSDiscoveryClient client = new WSDiscoveryClient();
        client.setVersion10(); // use WS-discovery 1.0
        client.setDefaultProbeTimeout(1000); // timeout 1s

        System.out.println("Probe:" + client.getAddress());
        List<EndpointReference> references = client.probe();

        System.out.println("Nb answsers:" + references.size());
        for (EndpointReference ref : references)
        {
            System.out.println(ref.toString());
        }
    }
}
0 голосов
/ 05 июня 2019

Путем большого количества проб и ошибок я нашел (другое) решение моей проблемы. В большинстве случаев, вероятно, лучше использовать mpromonet, я просто хотел избежать использования значительной зависимости, такой как Apache. Я также подумал, что это можно сделать с помощью простого UDP-сообщения.

Это решение также основано на полезном коде пользователя SO Томаса здесь . Я просто упростил его код, удалив потоки, и добавил несколько комментариев. Опять же, его решение, вероятно, лучше моего (более производительное); НО, мой может быть более понятным для новичка (как я).

Вот код:

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.*;

import javax.xml.namespace.QName;
import javax.xml.soap.*;
import java.util.*;

public class ONVIFDeviceDiscoveryFIN {

   // Following constants are related to Discovery process
   public static final int WS_DISCOVERY_TIMEOUT = 4000; // 4 seconds. Time to wait to receive a packet
   public static final int WS_DISCOVERY_PORT = 3702; 
   public static final String WS_DISCOVERY_ADDRESS_IPv4 = "239.255.255.250";

   // note that the probe below MUST be given a unique urn:uuid. Devices will NOT reply if the urn:uuid is not unique! 
   public static final String WS_DISCOVERY_PROBE_MESSAGE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + 
        "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\" xmlns:tns=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">\r\n" + 
        "   <soap:Header>\r\n" + 
        "      <wsa:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>\r\n" + 
        "      <wsa:MessageID>urn:uuid:5e1cec36-03b9-4d8b-9624-0c5283982a00</wsa:MessageID>\r\n" + 
        "      <wsa:To>urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>\r\n" + 
        "   </soap:Header>\r\n" + 
        "   <soap:Body>\r\n" + 
        "      <tns:Probe>\r\n" + 
        "         <tns:Types>tds:Device</tns:Types>\r\n" + // Constraint to find just ONVIF devices hopefully? Recall we are sending a probe on the 192.168.0.50 network; if we have no constraints, it would find everything there! WS-Discovery generally is for much more than ONVIF, like printers and stuff
        "      </tns:Probe>\r\n" + 
        "   </soap:Body>\r\n" + 
        "</soap:Envelope>";

   private static ArrayList<String> getResponsesToProbe(String uuid) throws IOException{
       // TODO: add in ability to send scope and type constraints
       // NOTE: We do need to know the address of the network interface to discover devices on...
       // Function composes and sends a Probe to discover devices on the network. uuid is the urn:uuid to put in the probe. Functions returns all the SOAP-Infused XML responses (all the ProbeMatches).

       // give the probe a unique urn:uuid (we must do this for each probe!). This is generated outside function
       final String probe = WS_DISCOVERY_PROBE_MESSAGE.replaceAll("<wsa:MessageID>urn:uuid:.*</wsa:MessageID>", "<wsa:MessageID>urn:uuid:" + uuid + "</wsa:MessageID>");

       // set up the "sender and receiver"; this is the socket that we send our probe from, and where we receive back the ProbeMatch responses.
       // NOTE:  that we do need to know the address of the network interface to discover devices on... (port could be anything)
       final int port = 55000;
       DatagramSocket senderAndReceiver = new DatagramSocket(port, InetAddress.getByName("192.168.0.50")); // so you do need to know the address of your network interface to discover devices on...
       senderAndReceiver.setSoTimeout(WS_DISCOVERY_TIMEOUT);

       // send the probe 
       DatagramPacket probeMsg = new DatagramPacket(probe.getBytes(), probe.length(), InetAddress.getByName(WS_DISCOVERY_ADDRESS_IPv4), WS_DISCOVERY_PORT);
       senderAndReceiver.send(probeMsg);

       // read in the responses
       ArrayList<String> responses = new ArrayList(); // this is the collection of all SOAP-infused XML ProbeMatch responses
       byte[] receiverBuffer = new byte[8192];
       DatagramPacket receiverPacket = new DatagramPacket(receiverBuffer, receiverBuffer.length); // this is the packet that receive the response in. Get's updated with the next response on each call to .receive()
       while (true) {
           try {
               senderAndReceiver.receive(receiverPacket);
               responses.add(new String(receiverPacket.getData()));

           } catch (SocketTimeoutException e) {
               // System.out.println("Socket read timeout; taken to mean that there is no more responses -- i.e., no more Probe Matches");
               break;
           }
       }

       // close the socket
       senderAndReceiver.close();

       return responses;

   }

   public static void main(String[] args) throws IOException, SOAPException {


       final String uuid = UUID.randomUUID().toString(); // generate the uuid to add to the Probe message

       ArrayList<String> responses = getResponsesToProbe(uuid); // responses is a collection of all the SOAP-infused XML ProbeMatches . It's all of our responses to the probe; it's basically the devices we've discovered!

    }

}

Некоторые примечания по использованию этого:

  1. Чтобы использовать это решение, вам нужно знать «сетевой интерфейс», чтобы посмотреть на него. В моем коде это 192.168.0.50. Это сеть, в которой находится моя камера. Чтобы найти это, запустите команду arp -a в командной строке cmd (не знаете, как это сделать на Mac или Linux) и найдите IP-адрес вашей камеры. Интерфейс, к которому он относится, это тот, который вы хотите использовать как «192.168.0.50». В моем ограниченном понимании, эти интерфейсы в основном сегментируют вашу сеть, поэтому вам нужно выбрать подходящий для поиска устройств. Я думаю (?) Код Томаса позволяет избежать этой проблемы путем нахождения всех этих сетевых интерфейсов. Это делается в строках 81-100 в его коде .

  2. Вы ДОЛЖНЫ дать своему зонду уникальный UUID при отправке. Это была одна из моих ошибок в этом; Я тестировал UUID с жестким кодом в Probe (в WS_DISCOVERY_PROBE_MESSAGE). Это будет работать, чтобы обнаружить устройства ОДИН РАЗ; но после этого, если вы отправите зонд с тем же UUID, кажется, что устройства не будут отвечать вообще. Вы также не получите ответ об ошибке, поэтому мне было сложно это выяснить. Устройство как будто ведет внутренний журнал UUID всех полученных зондом; и если вы отправляете зонд со старым UUID, он просто отклоняет его. Или, по крайней мере, так обстоит дело с ONVIF-совместимой камерой, с которой я тестирую (AXIS M3045-V). Я не уверен, если это поведение требуется спецификацией ONVIF, но это по крайней мере очевидно в AXIS M3045-V.

  3. Примечание: SOAP обычно использует HTTP для передачи; но здесь мы используем его поверх UDP.

Надеюсь, это поможет любому, кто пытается сделать что-то подобное. Дайте мне знать, если я могу чем-нибудь помочь; На данный момент я прочитал тонны документации, так что я могу помочь!

...