Недавно я приобрел IP-камеру Active Silicon и пытаюсь управлять ею с помощью python-onvif-zeep или valkka . В камере реализован стандарт ONVIF Profile S. Я хотел бы отправить камере увеличение/увеличение, фокусировку и другие основные команды VISCA с помощью сервиса SendReceiveSerialData, как описано в DeviceIO wsdl, однако я не могу заставить его работать. Мои познания в ONVIF, SOAP и zeep довольно ограничены, поэтому, пожалуйста, простите меня, если это слишком очевидно!
Вот код 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.
Заранее благодарю вас за ваше время и усилия, чтобы помочь мне!
Мне удалось заставить ваш пример работать, используя следующий код. Данные не были переданы в функцию в правильном формате, поэтому сообщение 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 в разделе «настройка (управление) вашей камерой». Так или иначе! Спасибо еще раз :)