Отправлять команды VISCA на IP-камеру с помощью python-onvif-zeep или valkka и службы sendreceiveserialcommand от DeviceIO

Эта проблема

Недавно я приобрел IP-камеру Active Silicon и пытаюсь управлять ею с помощью python-onvif-zeep или valkka . В камере реализован стандарт ONVIF Profile S. Я хотел бы отправить камере увеличение/увеличение, фокусировку и другие основные команды VISCA с помощью сервиса SendReceiveSerialData, как описано в DeviceIO wsdl, однако я не могу заставить его работать. Мои познания в ONVIF, SOAP и zeep довольно ограничены, поэтому, пожалуйста, простите меня, если это слишком очевидно!

Минимальный воспроизводимый пример и необходимые изменения

MRE

Вот код Python, который у меня есть (я заменил исходный код python-onvif-zeep, который у меня был, на valkka, потому что он делает все немного аккуратнее):

#! /usr/bin/env python3

from valkka.onvif import OnVif, DeviceManagement, Media, DeviceIO, PTZ, getWSDLPath
from zeep import Client
import zeep.helpers
from zeep.wsse.username import UsernameToken
import time

try:
    device_service = DeviceManagement(
        ip = "10.0.0.250",
        port=8000,
        user = "",
        password = ""
    )
except Exception as e:
    print(e)

cap = device_service.ws_client.GetCapabilities()
print("CAPABILITIES: \n{}".format(cap))
srv = device_service.ws_client.GetServices(True)
print("SERVICES: \n{}".format(srv))

try:
    deviceIO_service = DeviceIO(
        ip = "10.0.0.250",
        port=8000,
        user = "",
        password = ""
    )
except Exception as e:
    print(e)

# element = deviceIO_service.zeep_client.get_element('ns10:SendReceiveSerialCommand')
# print(element)

ports = deviceIO_service.ws_client.GetSerialPorts()
# print (ports)
serial_token = ports[0].token
# print(serial_token)

zoomin = bytes.fromhex('81 01 04 07 02 FF')
#zoomout = bytes.fromhex('81 01 04 07 03 FF')
#zoomin = b'\x81\x01\x04\x07\x02\xff'

data = {
    'SerialData': zoomin
}

ack = deviceIO_service.ws_client.SendReceiveSerialCommand(serial_token, data)
print(ack)
time.sleep(3)

Первая проблема, с которой я столкнулся, заключалась в том, что служба SendReceiveSerialCommand не работала, и возникла следующая ошибка:

raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
Необходимые изменения

Причиной этого является файл библиотеки deviceIO.wsdl. Он не содержит элемента «Токен», в отличие от DeviceIO wsdl, поэтому мне пришлось вручную добавить его под элементом SendReceiveSerialCommand:

    <xs:element name = "SendReceiveSerialCommand">
        <xs:annotation>
            <xs:documentation>Transmitting arbitrary data to the connected serial device and then receiving its response data.</xs:documentation>
        </xs:annotation>
        <xs:complexType>
            <xs:sequence>
                <xs:element name = "Token" type = "tt:ReferenceToken" minOccurs = "0">
                    <xs:annotation>
                                <xs:documentation>The physical serial port reference to be used when this request is invoked.</xs:documentation>
                        </xs:annotation>
                    </xs:element>
                <xs:element name = "SerialData" type = "tmd:SerialData" minOccurs = "0">
                    <xs:annotation>
                        <xs:documentation>The serial port data.</xs:documentation>
                    </xs:annotation>
                </xs:element>
                <xs:element name = "TimeOut" type = "xs:duration" minOccurs = "0">
                    <xs:annotation>
                        <xs:documentation>Indicates that the command should be responded back within the specified period of time.</xs:documentation>
                    </xs:annotation>
                </xs:element>
                <xs:element name = "DataLength" type = "xs:integer" minOccurs = "0">
                    <xs:annotation>
                        <xs:documentation>This element may be put in the case that data length returned from the connected serial device is already determined as some fixed bytes length. It indicates the length of received data which can be regarded as available.</xs:documentation>
                    </xs:annotation>
                </xs:element>
                <xs:element name = "Delimiter" type = "xs:string" minOccurs = "0">
                    <xs:annotation>
                        <xs:documentation>This element may be put in the case that the delimiter codes returned from the connected serial device is already known. It indicates the termination data sequence of the responded data. In case the string has more than one character a device shall interpret the whole string as a single delimiter. Furthermore a device shall return the delimiter character(s) to the client.</xs:documentation>
                    </xs:annotation>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name = "SendReceiveSerialCommandResponse">
        <xs:annotation>
            <xs:documentation>Receiving the response data.</xs:documentation>
        </xs:annotation>
        <xs:complexType>
            <xs:sequence>
                <xs:element name = "SerialData" type = "tmd:SerialData" minOccurs = "0"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

После исправления этой части ошибки уходят, но камера не приближается/отдаляется (в зависимости от того, какая команда VISCA передается). Кроме того, подтверждение возвращается как «Нет».

Ничего не изменится, если я заполню словарь всеми полями из службы SendReceiveSerialCommand следующим образом:

serial_data = {
    'SerialData': zoomin,
    'TimeOut': "PT0M0.1S",
    'DataLength': "100",
    'Delimiter': "",
}

Установка

Я запускаю этот скрипт Python на Ubuntu 20.04 с Python 3.8. Настройка представляет собой сеть между камерой и ноутбуком с назначенными статическими IP-адресами.

Обратите внимание, что камера работает, так как я могу успешно отправлять команды на камеру через веб-интерфейс и при запуске примера C# (можно найти в разделе «Загрузки» -> «Программное обеспечение») на компьютере с Windows.

Заранее благодарю вас за ваше время и усилия, чтобы помочь мне!

Мутабельность и переработка объектов в Python
Мутабельность и переработка объектов в Python
Объекты являются основной конструкцией любого языка ООП, и каждый язык определяет свой собственный синтаксис для их создания, обновления и...
Другой маршрут в Flask Python
Другой маршрут в Flask Python
Flask - это фреймворк, который поддерживает веб-приложения. В этой статье я покажу, как мы можем использовать @app .route в flask, чтобы иметь другую...
14 Задание: Типы данных и структуры данных Python для DevOps
14 Задание: Типы данных и структуры данных Python для DevOps
Проверить тип данных используемой переменной, мы можем просто написать: your_variable=100
Python PyPDF2 - запись метаданных PDF
Python PyPDF2 - запись метаданных PDF
Python скрипт, который будет записывать метаданные в PDF файл, для этого мы будем использовать PDF ридер из библиотеки PyPDF2 . PyPDF2 - это...
Переменные, типы данных и операторы в Python
Переменные, типы данных и операторы в Python
В Python переменные используются как место для хранения значений. Пример переменной формы:
Почему Python - идеальный выбор для проекта AI и ML
Почему Python - идеальный выбор для проекта AI и ML
Блог, которым поделился Harikrishna Kundariya в нашем сообществе Developer Nation Community.
2
0
168
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Мне удалось заставить ваш пример работать, используя следующий код. Данные не были переданы в функцию в правильном формате, поэтому сообщение SOAP было неполным.

Подробную информацию о передаче структур данных SOAP см. в документации zeep.

deviceio_type_factory = deviceIO_service.zeep_client.type_factory("http://www.onvif.org/ver10/deviceIO/wsdl")
serial_data = deviceio_type_factory.SerialData(Binary=zoomin)

ack = deviceIO_service.ws_client.SendReceiveSerialCommand(Token= ports[0].token, SerialData=serial_data, TimeOut='PT0M6S', DataLength='100', Delimiter='')

visca_ack_comp = ack['Binary'].hex()
print(visca_ack_comp )

Вот что получаю с камеры: 9041ff9051ff (ACK+COMP).

Обратите внимание: вы можете использовать файл WDSL из ONVIF вместо того, чтобы создавать свой собственный, так как он правильный.

class MyDeviceIO(OnVif):
    wsdl_file = "https://www.onvif.org/ver10/deviceio.wsdl"
    namespace = "http://www.onvif.org/ver10/deviceIO/wsdl"
    sub_xaddr = "DeviceIO"
    port      = "DeviceIOBinding"

Я не могу тебя отблагодарить. Я был уверен, что делаю что-то не так при отправке данных, но это было описано в python-onvif-zeep в разделе «настройка (управление) вашей камерой». Так или иначе! Спасибо еще раз :)

ekptwtos 30.11.2022 13:32

Другие вопросы по теме