Radiation-resistantCamera/Vimba_6_0/VimbaPython/Source/vimba/interface.py

392 lines
13 KiB
Python
Raw Normal View History

2025-04-30 01:26:04 +00:00
"""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()