392 lines
13 KiB
Python
392 lines
13 KiB
Python
"""BSD 2-Clause License
|
|
|
|
Copyright (c) 2019, Allied Vision Technologies GmbH
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
"""
|
|
|
|
import enum
|
|
from typing import Tuple, List, Callable, Dict
|
|
from .c_binding import call_vimba_c, byref, sizeof, decode_cstr
|
|
from .c_binding import VmbInterface, VmbInterfaceInfo, VmbHandle, VmbUint32
|
|
from .feature import discover_features, FeatureTypes, FeaturesTuple, FeatureTypeTypes
|
|
from .shared import filter_features_by_name, filter_features_by_type, filter_affected_features, \
|
|
filter_selected_features, filter_features_by_category, \
|
|
attach_feature_accessors, remove_feature_accessors, read_memory, \
|
|
write_memory, read_registers, write_registers
|
|
from .util import TraceEnable, RuntimeTypeCheckEnable, EnterContextOnCall, LeaveContextOnCall, \
|
|
RaiseIfOutsideContext
|
|
from .error import VimbaFeatureError
|
|
|
|
|
|
__all__ = [
|
|
'InterfaceType',
|
|
'Interface',
|
|
'InterfaceEvent',
|
|
'InterfaceChangeHandler',
|
|
'InterfacesTuple',
|
|
'InterfacesList',
|
|
'discover_interfaces',
|
|
'discover_interface'
|
|
]
|
|
|
|
|
|
# Forward declarations
|
|
InterfaceChangeHandler = Callable[['Interface', 'InterfaceEvent'], None]
|
|
InterfacesTuple = Tuple['Interface', ...]
|
|
InterfacesList = List['Interface']
|
|
|
|
|
|
class InterfaceType(enum.IntEnum):
|
|
"""Enum specifying all interface types.
|
|
|
|
Enum values:
|
|
Unknown - Interface is not known to this VimbaPython version.
|
|
Firewire - 1394
|
|
Ethernet - Gigabit Ethernet
|
|
Usb - USB 3.0
|
|
CL - Camera Link
|
|
CSI2 - CSI-2
|
|
"""
|
|
Unknown = VmbInterface.Unknown
|
|
Firewire = VmbInterface.Firewire
|
|
Ethernet = VmbInterface.Ethernet
|
|
Usb = VmbInterface.Usb
|
|
CL = VmbInterface.CL
|
|
CSI2 = VmbInterface.CSI2
|
|
|
|
|
|
class InterfaceEvent(enum.IntEnum):
|
|
"""Enum specifying an Interface Event
|
|
|
|
Enum values:
|
|
Missing - A known interface disappeared from the bus
|
|
Detected - A new interface was discovered
|
|
Reachable - A known interface can be accessed
|
|
Unreachable - A known interface cannot be accessed anymore
|
|
"""
|
|
Missing = 0
|
|
Detected = 1
|
|
Reachable = 2
|
|
Unreachable = 3
|
|
|
|
|
|
class Interface:
|
|
"""This class allows access to an interface such as USB detected by Vimba.
|
|
Interface is meant to be used in conjunction with the "with" - statement. On entering a context,
|
|
all Interface features are detected and can be accessed within the context. Static Interface
|
|
properties like Name can be accessed outside the context.
|
|
"""
|
|
|
|
@TraceEnable()
|
|
@LeaveContextOnCall()
|
|
def __init__(self, info: VmbInterfaceInfo):
|
|
"""Do not call directly. Access Interfaces via vimba.Vimba instead."""
|
|
self.__handle: VmbHandle = VmbHandle(0)
|
|
self.__info: VmbInterfaceInfo = info
|
|
self.__feats: FeaturesTuple = ()
|
|
self.__context_cnt: int = 0
|
|
|
|
@TraceEnable()
|
|
def __enter__(self):
|
|
if not self.__context_cnt:
|
|
self._open()
|
|
|
|
self.__context_cnt += 1
|
|
return self
|
|
|
|
@TraceEnable()
|
|
def __exit__(self, exc_type, exc_value, exc_traceback):
|
|
self.__context_cnt -= 1
|
|
|
|
if not self.__context_cnt:
|
|
self._close()
|
|
|
|
def __str__(self):
|
|
return 'Interface(id={})'.format(self.get_id())
|
|
|
|
def __repr__(self):
|
|
rep = 'Interface'
|
|
rep += '(__handle=' + repr(self.__handle)
|
|
rep += ',__info=' + repr(self.__info)
|
|
rep += ')'
|
|
return rep
|
|
|
|
def get_id(self) -> str:
|
|
"""Get Interface Id such as VimbaUSBInterface_0x0."""
|
|
return decode_cstr(self.__info.interfaceIdString)
|
|
|
|
def get_type(self) -> InterfaceType:
|
|
"""Get Interface Type such as InterfaceType.Usb."""
|
|
return InterfaceType(self.__info.interfaceType)
|
|
|
|
def get_name(self) -> str:
|
|
"""Get Interface Name such as Vimba USB Interface."""
|
|
return decode_cstr(self.__info.interfaceName)
|
|
|
|
def get_serial(self) -> str:
|
|
"""Get Interface Serial or '' if not set."""
|
|
return decode_cstr(self.__info.serialString)
|
|
|
|
@TraceEnable()
|
|
@RaiseIfOutsideContext()
|
|
@RuntimeTypeCheckEnable()
|
|
def read_memory(self, addr: int, max_bytes: int) -> bytes: # coverage: skip
|
|
"""Read a byte sequence from a given memory address.
|
|
|
|
Arguments:
|
|
addr: Starting address to read from.
|
|
max_bytes: Maximum number of bytes to read from addr.
|
|
|
|
Returns:
|
|
Read memory contents as bytes.
|
|
|
|
Raises:
|
|
TypeError if parameters do not match their type hint.
|
|
RuntimeError if called outside "with" - statement.
|
|
ValueError if addr is negative.
|
|
ValueError if max_bytes is negative.
|
|
ValueError if the memory access was invalid.
|
|
"""
|
|
# Note: Coverage is skipped. Function is untestable in a generic way.
|
|
return read_memory(self.__handle, addr, max_bytes)
|
|
|
|
@TraceEnable()
|
|
@RaiseIfOutsideContext()
|
|
@RuntimeTypeCheckEnable()
|
|
def write_memory(self, addr: int, data: bytes): # coverage: skip
|
|
"""Write a byte sequence to a given memory address.
|
|
|
|
Arguments:
|
|
addr: Address to write the content of 'data' to.
|
|
data: Byte sequence to write at address 'addr'.
|
|
|
|
Raises:
|
|
TypeError if parameters do not match their type hint.
|
|
RuntimeError if called outside "with" - statement.
|
|
ValueError if addr is negative.
|
|
"""
|
|
# Note: Coverage is skipped. Function is untestable in a generic way.
|
|
return write_memory(self.__handle, addr, data)
|
|
|
|
@TraceEnable()
|
|
@RaiseIfOutsideContext()
|
|
@RuntimeTypeCheckEnable()
|
|
def read_registers(self, addrs: Tuple[int, ...]) -> Dict[int, int]: # coverage: skip
|
|
"""Read contents of multiple registers.
|
|
|
|
Arguments:
|
|
addrs: Sequence of addresses that should be read iteratively.
|
|
|
|
Returns:
|
|
Dictionary containing a mapping from given address to the read register values.
|
|
|
|
Raises:
|
|
TypeError if parameters do not match their type hint.
|
|
RuntimeError if called outside "with" - statement.
|
|
ValueError if any address in addrs is negative.
|
|
ValueError if the register access was invalid.
|
|
"""
|
|
# Note: Coverage is skipped. Function is untestable in a generic way.
|
|
return read_registers(self.__handle, addrs)
|
|
|
|
@TraceEnable()
|
|
@RaiseIfOutsideContext()
|
|
@RuntimeTypeCheckEnable()
|
|
def write_registers(self, addrs_values: Dict[int, int]): # coverage: skip
|
|
"""Write data to multiple registers.
|
|
|
|
Arguments:
|
|
addrs_values: Mapping between register addresses and the data to write.
|
|
|
|
Raises:
|
|
TypeError if parameters do not match their type hint.
|
|
ValueError if any address in addrs_values is negative.
|
|
ValueError if the register access was invalid.
|
|
"""
|
|
# Note: Coverage is skipped. Function is untestable in a generic way.
|
|
return write_registers(self.__handle, addrs_values)
|
|
|
|
@RaiseIfOutsideContext()
|
|
def get_all_features(self) -> FeaturesTuple:
|
|
"""Get access to all discovered features of this Interface.
|
|
|
|
Returns:
|
|
A set of all currently detected features.
|
|
|
|
Raises:
|
|
RuntimeError if called outside "with" - statement.
|
|
"""
|
|
return self.__feats
|
|
|
|
@TraceEnable()
|
|
@RaiseIfOutsideContext()
|
|
@RuntimeTypeCheckEnable()
|
|
def get_features_affected_by(self, feat: FeatureTypes) -> FeaturesTuple:
|
|
"""Get all features affected by a specific interface feature.
|
|
|
|
Arguments:
|
|
feat - Feature to find features that are affected by 'feat'.
|
|
|
|
Returns:
|
|
A set of features affected by changes on 'feat'.
|
|
|
|
Raises:
|
|
TypeError if parameters do not match their type hint.
|
|
RuntimeError if called outside "with" - statement.
|
|
VimbaFeatureError if 'feat' is not a feature of this interface.
|
|
"""
|
|
return filter_affected_features(self.__feats, feat)
|
|
|
|
@TraceEnable()
|
|
@RaiseIfOutsideContext()
|
|
@RuntimeTypeCheckEnable()
|
|
def get_features_selected_by(self, feat: FeatureTypes) -> FeaturesTuple:
|
|
"""Get all features selected by a specific interface feature.
|
|
|
|
Arguments:
|
|
feat - Feature to find features that are selected by 'feat'.
|
|
|
|
Returns:
|
|
A set of features selected by changes on 'feat'.
|
|
|
|
Raises:
|
|
TypeError if 'feat' is not of any feature type.
|
|
RuntimeError if called outside "with" - statement.
|
|
VimbaFeatureError if 'feat' is not a feature of this interface.
|
|
"""
|
|
return filter_selected_features(self.__feats, feat)
|
|
|
|
@RaiseIfOutsideContext()
|
|
@RuntimeTypeCheckEnable()
|
|
def get_features_by_type(self, feat_type: FeatureTypeTypes) -> FeaturesTuple:
|
|
"""Get all interface features of a specific feature type.
|
|
|
|
Valid FeatureTypes are: IntFeature, FloatFeature, StringFeature, BoolFeature,
|
|
EnumFeature, CommandFeature, RawFeature
|
|
|
|
Arguments:
|
|
feat_type - FeatureType used find features of that type.
|
|
|
|
Returns:
|
|
A set of features of type 'feat_type'.
|
|
|
|
Raises:
|
|
TypeError if parameters do not match their type hint.
|
|
RuntimeError if called outside "with" - statement.
|
|
"""
|
|
return filter_features_by_type(self.__feats, feat_type)
|
|
|
|
@RaiseIfOutsideContext()
|
|
@RuntimeTypeCheckEnable()
|
|
def get_features_by_category(self, category: str) -> FeaturesTuple:
|
|
"""Get all interface features of a specific category.
|
|
|
|
Arguments:
|
|
category - category for filtering.
|
|
|
|
Returns:
|
|
A set of features of category 'category'.
|
|
|
|
Raises:
|
|
TypeError if parameters do not match their type hint.
|
|
RuntimeError if called outside "with" - statement.
|
|
"""
|
|
return filter_features_by_category(self.__feats, category)
|
|
|
|
@RaiseIfOutsideContext()
|
|
@RuntimeTypeCheckEnable()
|
|
def get_feature_by_name(self, feat_name: str) -> FeatureTypes:
|
|
"""Get an interface feature by its name.
|
|
|
|
Arguments:
|
|
feat_name - Name to find a feature.
|
|
|
|
Returns:
|
|
Feature with the associated name.
|
|
|
|
Raises:
|
|
TypeError if parameters do not match their type hint.
|
|
RuntimeError if called outside "with" - statement.
|
|
VimbaFeatureError if no feature is associated with 'feat_name'.
|
|
"""
|
|
feat = filter_features_by_name(self.__feats, feat_name)
|
|
|
|
if not feat:
|
|
raise VimbaFeatureError('Feature \'{}\' not found.'.format(feat_name))
|
|
|
|
return feat
|
|
|
|
@TraceEnable()
|
|
@EnterContextOnCall()
|
|
def _open(self):
|
|
call_vimba_c('VmbInterfaceOpen', self.__info.interfaceIdString, byref(self.__handle))
|
|
|
|
self.__feats = discover_features(self.__handle)
|
|
attach_feature_accessors(self, self.__feats)
|
|
|
|
@TraceEnable()
|
|
@LeaveContextOnCall()
|
|
def _close(self):
|
|
for feat in self.__feats:
|
|
feat.unregister_all_change_handlers()
|
|
|
|
remove_feature_accessors(self, self.__feats)
|
|
self.__feats = ()
|
|
|
|
call_vimba_c('VmbInterfaceClose', self.__handle)
|
|
|
|
self.__handle = VmbHandle(0)
|
|
|
|
|
|
@TraceEnable()
|
|
def discover_interfaces() -> InterfacesList:
|
|
"""Do not call directly. Access Interfaces via vimba.System instead."""
|
|
|
|
result = []
|
|
inters_count = VmbUint32(0)
|
|
|
|
call_vimba_c('VmbInterfacesList', None, 0, byref(inters_count), sizeof(VmbInterfaceInfo))
|
|
|
|
if inters_count:
|
|
inters_found = VmbUint32(0)
|
|
inters_infos = (VmbInterfaceInfo * inters_count.value)()
|
|
|
|
call_vimba_c('VmbInterfacesList', inters_infos, inters_count, byref(inters_found),
|
|
sizeof(VmbInterfaceInfo))
|
|
|
|
for info in inters_infos[:inters_found.value]:
|
|
result.append(Interface(info))
|
|
|
|
return result
|
|
|
|
|
|
@TraceEnable()
|
|
def discover_interface(id_: str) -> Interface:
|
|
"""Do not call directly. Access Interfaces via vimba.System instead."""
|
|
|
|
# Since there is no function to query a single interface, discover all interfaces and
|
|
# extract the Interface with the matching ID.
|
|
inters = discover_interfaces()
|
|
return [i for i in inters if id_ == i.get_id()].pop()
|