AVT相机arm版本SDK
This commit is contained in:
25
Vimba_6_0/VimbaPython/Source/LICENSE.txt
Normal file
25
Vimba_6_0/VimbaPython/Source/LICENSE.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
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.
|
||||
97
Vimba_6_0/VimbaPython/Source/README.txt
Normal file
97
Vimba_6_0/VimbaPython/Source/README.txt
Normal file
@@ -0,0 +1,97 @@
|
||||
Vimba Python API
|
||||
===============
|
||||
|
||||
Prerequisites
|
||||
===============
|
||||
To use Vimba Python API, you need Python version 3.7 or higher.
|
||||
|
||||
|
||||
Installing Python - Windows
|
||||
---------------
|
||||
If your system requires multiple, coexisting Python versions, consider
|
||||
using pyenv-win to install and maintain multiple Python installations:
|
||||
https://github.com/pyenv-win/pyenv-win
|
||||
|
||||
We recommend installing Python with admin rights.
|
||||
|
||||
1. Install Python: https://www.python.org/downloads/windows/
|
||||
2. If pip >21.2 is used, read the instructions for all operating systems below.
|
||||
3. To verify the installation, open the command prompt and enter:
|
||||
|
||||
python --version
|
||||
python -m pip --version
|
||||
|
||||
Please ensure that the Python version is 3.7 or higher and pip uses this Python version.
|
||||
|
||||
|
||||
Installing Python - Linux
|
||||
---------------
|
||||
On Linux systems, the Python installation process depends heavily on the distribution.
|
||||
If python3.7 (or higher) is not available for your distribution or your system requires
|
||||
multiple python versions to coexist, use pyenv:
|
||||
https://realpython.com/intro-to-pyenv/
|
||||
|
||||
1. Install or update python3.7 with the packet manager of your distribution.
|
||||
2. Install or update pip with the packet manager of your distribution.
|
||||
3. To verify the installation, open a console and enter:
|
||||
|
||||
python --version
|
||||
python -m pip --version
|
||||
|
||||
|
||||
Installing the Vimba Python API
|
||||
===============
|
||||
All operating systems:
|
||||
|
||||
Open a terminal and navigate to the VimbaPython installation directory that
|
||||
you have admin privileges/write permission for,
|
||||
for example, C:\Users\Public\Documents\Allied Vision\Vimba_5.x\VimbaPython_Source
|
||||
|
||||
Users who want to change the API's sources can find them in the Vimba examples
|
||||
directory, for example:
|
||||
C:\Users\Public\Documents\Allied Vision\Vimba_5.x\VimbaPython_Source
|
||||
|
||||
Please note that Allied Vision can offer only limited support if an application
|
||||
uses a modified version of the API.
|
||||
|
||||
Troubleshooting: If you don't have write permisson for the above-mentioned directories:
|
||||
|
||||
Download VimbaPython (in the correct version needed for your Vimba installation) from
|
||||
https://github.com/alliedvision/VimbaPython and install it from that directory.
|
||||
|
||||
Or you can downgrade pip to a version <2.3 with, for example:
|
||||
|
||||
python -m pip install --upgrade pip==21.1.2
|
||||
|
||||
After the VimbaPython installation is complete, you can upgrade pip again to the latest version.
|
||||
|
||||
|
||||
Basic Installation
|
||||
---------------
|
||||
Execute the following command:
|
||||
|
||||
python -m pip install .
|
||||
|
||||
|
||||
Installation with optional NumPy and OpenCV export
|
||||
---------------
|
||||
Execute the following command:
|
||||
|
||||
python -m pip install .[numpy-export,opencv-export]
|
||||
|
||||
|
||||
Helper scripts for Linux
|
||||
---------------
|
||||
For Linux systems helper scripts named `Install.sh` and `Uninstall.sh` to install and uninstall
|
||||
VimbaPython are provided. They will automatically detect if there is a currently active virtual
|
||||
environment. To install or uninstall VimbaPython for one of the system wide Python installations,
|
||||
admin rights are required (use `sudo`). To get further details on why the scripts do not offer your
|
||||
desired Python installation or to troubleshoot problems, a debug flag is provided (for example
|
||||
`./Install.sh -d`).
|
||||
|
||||
ARM users only:
|
||||
If installation of "opencv-export" fails, pip is not able to install
|
||||
"opencv-python" for your ARM board. This is a known issue on ARM boards.
|
||||
If you are affected by this, install VimbaPython without optional dependencies
|
||||
and try to install OpenCV in a different way (for example, with your operating system's packet manager).
|
||||
The OpenCV installation can be verified by running the example "Examples/asychronous_grab_opencv.py".
|
||||
346
Vimba_6_0/VimbaPython/Source/Tests/basic_tests/c_binding_test.py
Normal file
346
Vimba_6_0/VimbaPython/Source/Tests/basic_tests/c_binding_test.py
Normal file
@@ -0,0 +1,346 @@
|
||||
"""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 unittest
|
||||
import ctypes
|
||||
|
||||
from vimba.c_binding import *
|
||||
|
||||
|
||||
class VimbaCommonTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_decode_cstr_behavior(self):
|
||||
# Expected Behavior:
|
||||
# c_char_p() == ''
|
||||
# c_char_p(b'foo') == 'foo'
|
||||
|
||||
expected = ''
|
||||
actual = decode_cstr(ctypes.c_char_p())
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
expected = 'test'
|
||||
actual = decode_cstr(ctypes.c_char_p(b'test').value)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_decode_flags_zero(self):
|
||||
# Expected Behavior: In case no bytes are set the
|
||||
# zero value of the Flag Enum must be returned
|
||||
|
||||
expected = (VmbFeatureFlags.None_,)
|
||||
actual = decode_flags(VmbFeatureFlags, 0)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_decode_flags_some(self):
|
||||
# Expected Behavior: Given Integer must be decided correctly.
|
||||
# the order of the fields does not matter for this test.
|
||||
|
||||
expected = (
|
||||
VmbFeatureFlags.Write,
|
||||
VmbFeatureFlags.Read,
|
||||
VmbFeatureFlags.ModifyWrite
|
||||
)
|
||||
|
||||
input_data = 0
|
||||
|
||||
for val in expected:
|
||||
input_data |= int(val)
|
||||
|
||||
actual = decode_flags(VmbFeatureFlags, input_data)
|
||||
|
||||
# Convert both collections into a list and sort it.
|
||||
# That way order doesn't matter. It is only important that values are
|
||||
# decoded correctly.
|
||||
self.assertEqual(list(expected).sort(), list(actual).sort())
|
||||
|
||||
|
||||
class CBindingVimbaCTypesTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_enum_vmb_error(self):
|
||||
self.assertEqual(VmbError.Success, 0)
|
||||
self.assertEqual(VmbError.InternalFault, -1)
|
||||
self.assertEqual(VmbError.ApiNotStarted, -2)
|
||||
self.assertEqual(VmbError.NotFound, -3)
|
||||
self.assertEqual(VmbError.BadHandle, -4)
|
||||
self.assertEqual(VmbError.DeviceNotOpen, -5)
|
||||
self.assertEqual(VmbError.InvalidAccess, -6)
|
||||
self.assertEqual(VmbError.BadParameter, -7)
|
||||
self.assertEqual(VmbError.StructSize, -8)
|
||||
self.assertEqual(VmbError.MoreData, -9)
|
||||
self.assertEqual(VmbError.WrongType, -10)
|
||||
self.assertEqual(VmbError.InvalidValue, -11)
|
||||
self.assertEqual(VmbError.Timeout, -12)
|
||||
self.assertEqual(VmbError.Other, -13)
|
||||
self.assertEqual(VmbError.Resources, -14)
|
||||
self.assertEqual(VmbError.InvalidCall, -15)
|
||||
self.assertEqual(VmbError.NoTL, -16)
|
||||
self.assertEqual(VmbError.NotImplemented_, -17)
|
||||
self.assertEqual(VmbError.NotSupported, -18)
|
||||
self.assertEqual(VmbError.Incomplete, -19)
|
||||
self.assertEqual(VmbError.IO, -20)
|
||||
|
||||
def test_enum_vmb_pixel_format(self):
|
||||
self.assertEqual(VmbPixelFormat.Mono8, 0x01080001)
|
||||
self.assertEqual(VmbPixelFormat.Mono10, 0x01100003)
|
||||
self.assertEqual(VmbPixelFormat.Mono10p, 0x010A0046)
|
||||
self.assertEqual(VmbPixelFormat.Mono12, 0x01100005)
|
||||
self.assertEqual(VmbPixelFormat.Mono12Packed, 0x010C0006)
|
||||
self.assertEqual(VmbPixelFormat.Mono12p, 0x010C0047)
|
||||
self.assertEqual(VmbPixelFormat.Mono14, 0x01100025)
|
||||
self.assertEqual(VmbPixelFormat.Mono16, 0x01100007)
|
||||
self.assertEqual(VmbPixelFormat.BayerGR8, 0x01080008)
|
||||
self.assertEqual(VmbPixelFormat.BayerRG8, 0x01080009)
|
||||
self.assertEqual(VmbPixelFormat.BayerGB8, 0x0108000A)
|
||||
self.assertEqual(VmbPixelFormat.BayerBG8, 0x0108000B)
|
||||
self.assertEqual(VmbPixelFormat.BayerGR10, 0x0110000C)
|
||||
self.assertEqual(VmbPixelFormat.BayerRG10, 0x0110000D)
|
||||
self.assertEqual(VmbPixelFormat.BayerGB10, 0x0110000E)
|
||||
self.assertEqual(VmbPixelFormat.BayerBG10, 0x0110000F)
|
||||
self.assertEqual(VmbPixelFormat.BayerGR12, 0x01100010)
|
||||
self.assertEqual(VmbPixelFormat.BayerRG12, 0x01100011)
|
||||
self.assertEqual(VmbPixelFormat.BayerGB12, 0x01100012)
|
||||
self.assertEqual(VmbPixelFormat.BayerBG12, 0x01100013)
|
||||
self.assertEqual(VmbPixelFormat.BayerGR12Packed, 0x010C002A)
|
||||
self.assertEqual(VmbPixelFormat.BayerRG12Packed, 0x010C002B)
|
||||
self.assertEqual(VmbPixelFormat.BayerGB12Packed, 0x010C002C)
|
||||
self.assertEqual(VmbPixelFormat.BayerBG12Packed, 0x010C002D)
|
||||
self.assertEqual(VmbPixelFormat.BayerGR10p, 0x010A0056)
|
||||
self.assertEqual(VmbPixelFormat.BayerRG10p, 0x010A0058)
|
||||
self.assertEqual(VmbPixelFormat.BayerGB10p, 0x010A0054)
|
||||
self.assertEqual(VmbPixelFormat.BayerBG10p, 0x010A0052)
|
||||
self.assertEqual(VmbPixelFormat.BayerGR12p, 0x010C0057)
|
||||
self.assertEqual(VmbPixelFormat.BayerRG12p, 0x010C0059)
|
||||
self.assertEqual(VmbPixelFormat.BayerGB12p, 0x010C0055)
|
||||
self.assertEqual(VmbPixelFormat.BayerBG12p, 0x010C0053)
|
||||
self.assertEqual(VmbPixelFormat.BayerGR16, 0x0110002E)
|
||||
self.assertEqual(VmbPixelFormat.BayerRG16, 0x0110002F)
|
||||
self.assertEqual(VmbPixelFormat.BayerGB16, 0x01100030)
|
||||
self.assertEqual(VmbPixelFormat.BayerBG16, 0x01100031)
|
||||
self.assertEqual(VmbPixelFormat.Rgb8, 0x02180014)
|
||||
self.assertEqual(VmbPixelFormat.Bgr8, 0x02180015)
|
||||
self.assertEqual(VmbPixelFormat.Rgb10, 0x02300018)
|
||||
self.assertEqual(VmbPixelFormat.Bgr10, 0x02300019)
|
||||
self.assertEqual(VmbPixelFormat.Rgb12, 0x0230001A)
|
||||
self.assertEqual(VmbPixelFormat.Bgr12, 0x0230001B)
|
||||
self.assertEqual(VmbPixelFormat.Rgb14, 0x0230005E)
|
||||
self.assertEqual(VmbPixelFormat.Bgr14, 0x0230004A)
|
||||
self.assertEqual(VmbPixelFormat.Rgb16, 0x02300033)
|
||||
self.assertEqual(VmbPixelFormat.Bgr16, 0x0230004B)
|
||||
self.assertEqual(VmbPixelFormat.Argb8, 0x02200016)
|
||||
self.assertEqual(VmbPixelFormat.Rgba8, 0x02200016)
|
||||
self.assertEqual(VmbPixelFormat.Bgra8, 0x02200017)
|
||||
self.assertEqual(VmbPixelFormat.Rgba10, 0x0240005F)
|
||||
self.assertEqual(VmbPixelFormat.Bgra10, 0x0240004C)
|
||||
self.assertEqual(VmbPixelFormat.Rgba12, 0x02400061)
|
||||
self.assertEqual(VmbPixelFormat.Bgra12, 0x0240004E)
|
||||
self.assertEqual(VmbPixelFormat.Rgba14, 0x02400063)
|
||||
self.assertEqual(VmbPixelFormat.Bgra14, 0x02400050)
|
||||
self.assertEqual(VmbPixelFormat.Rgba16, 0x02400064)
|
||||
self.assertEqual(VmbPixelFormat.Bgra16, 0x02400051)
|
||||
self.assertEqual(VmbPixelFormat.Yuv411, 0x020C001E)
|
||||
self.assertEqual(VmbPixelFormat.Yuv422, 0x0210001F)
|
||||
self.assertEqual(VmbPixelFormat.Yuv444, 0x02180020)
|
||||
self.assertEqual(VmbPixelFormat.YCbCr411_8_CbYYCrYY, 0x020C003C)
|
||||
self.assertEqual(VmbPixelFormat.YCbCr422_8_CbYCrY, 0x02100043)
|
||||
self.assertEqual(VmbPixelFormat.YCbCr8_CbYCr, 0x0218003A)
|
||||
|
||||
def test_enum_vmb_interface(self):
|
||||
self.assertEqual(VmbInterface.Unknown, 0)
|
||||
self.assertEqual(VmbInterface.Firewire, 1)
|
||||
self.assertEqual(VmbInterface.Ethernet, 2)
|
||||
self.assertEqual(VmbInterface.Usb, 3)
|
||||
self.assertEqual(VmbInterface.CL, 4)
|
||||
self.assertEqual(VmbInterface.CSI2, 5)
|
||||
|
||||
def test_enum_vmb_access_mode(self):
|
||||
self.assertEqual(VmbAccessMode.None_, 0)
|
||||
self.assertEqual(VmbAccessMode.Full, 1)
|
||||
self.assertEqual(VmbAccessMode.Read, 2)
|
||||
self.assertEqual(VmbAccessMode.Config, 4)
|
||||
self.assertEqual(VmbAccessMode.Lite, 8)
|
||||
|
||||
def test_enum_vmb_feature_data(self):
|
||||
self.assertEqual(VmbFeatureData.Unknown, 0)
|
||||
self.assertEqual(VmbFeatureData.Int, 1)
|
||||
self.assertEqual(VmbFeatureData.Float, 2)
|
||||
self.assertEqual(VmbFeatureData.Enum, 3)
|
||||
self.assertEqual(VmbFeatureData.String, 4)
|
||||
self.assertEqual(VmbFeatureData.Bool, 5)
|
||||
self.assertEqual(VmbFeatureData.Command, 6)
|
||||
self.assertEqual(VmbFeatureData.Raw, 7)
|
||||
self.assertEqual(VmbFeatureData.None_, 8)
|
||||
|
||||
def test_enum_vmb_feature_persist(self):
|
||||
self.assertEqual(VmbFeaturePersist.All, 0)
|
||||
self.assertEqual(VmbFeaturePersist.Streamable, 1)
|
||||
self.assertEqual(VmbFeaturePersist.NoLUT, 2)
|
||||
|
||||
def test_enum_vmb_feature_visibility(self):
|
||||
self.assertEqual(VmbFeatureVisibility.Unknown, 0)
|
||||
self.assertEqual(VmbFeatureVisibility.Beginner, 1)
|
||||
self.assertEqual(VmbFeatureVisibility.Expert, 2)
|
||||
self.assertEqual(VmbFeatureVisibility.Guru, 3)
|
||||
self.assertEqual(VmbFeatureVisibility.Invisible, 4)
|
||||
|
||||
def test_enum_vmb_feature_flags(self):
|
||||
self.assertEqual(VmbFeatureFlags.None_, 0)
|
||||
self.assertEqual(VmbFeatureFlags.Read, 1)
|
||||
self.assertEqual(VmbFeatureFlags.Write, 2)
|
||||
self.assertEqual(VmbFeatureFlags.Volatile, 8)
|
||||
self.assertEqual(VmbFeatureFlags.ModifyWrite, 16)
|
||||
|
||||
def test_enum_vmb_frame_status(self):
|
||||
self.assertEqual(VmbFrameStatus.Complete, 0)
|
||||
self.assertEqual(VmbFrameStatus.Incomplete, -1)
|
||||
self.assertEqual(VmbFrameStatus.TooSmall, -2)
|
||||
self.assertEqual(VmbFrameStatus.Invalid, -3)
|
||||
|
||||
def test_enum_vmd_frame_flags(self):
|
||||
self.assertEqual(VmbFrameFlags.None_, 0)
|
||||
self.assertEqual(VmbFrameFlags.Dimension, 1)
|
||||
self.assertEqual(VmbFrameFlags.Offset, 2)
|
||||
self.assertEqual(VmbFrameFlags.FrameID, 4)
|
||||
self.assertEqual(VmbFrameFlags.Timestamp, 8)
|
||||
|
||||
|
||||
class VimbaCTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_call_vimba_c_valid(self):
|
||||
# Expectation for valid call: No exceptions, no errors
|
||||
expected_ver_info = (1, 9, 0)
|
||||
ver_info = VmbVersionInfo()
|
||||
|
||||
call_vimba_c('VmbVersionQuery', byref(ver_info), sizeof(ver_info))
|
||||
|
||||
ver_info = (ver_info.major, ver_info.minor, ver_info.patch)
|
||||
|
||||
# Not an actual check for compatibility. Just make sure a sensible value was filled
|
||||
self.assertGreaterEqual(ver_info, expected_ver_info)
|
||||
|
||||
def test_call_vimba_c_invalid_func_name(self):
|
||||
# Expectation: An invalid function name must throw an AttributeError
|
||||
|
||||
ver_info = VmbVersionInfo()
|
||||
self.assertRaises(AttributeError, call_vimba_c, 'VmbVersionQuer', byref(ver_info),
|
||||
sizeof(ver_info))
|
||||
|
||||
def test_call_vimba_c_invalid_arg_number(self):
|
||||
# Expectation: Invalid number of arguments with sane types.
|
||||
# must lead to TypeErrors
|
||||
|
||||
ver_info = VmbVersionInfo()
|
||||
self.assertRaises(TypeError, call_vimba_c, 'VmbVersionQuery', byref(ver_info))
|
||||
|
||||
def test_call_vimba_c_invalid_arg_type(self):
|
||||
# Expectation: Arguments with invalid types must lead to TypeErrors
|
||||
|
||||
# Call with unexpected base types
|
||||
self.assertRaises(ctypes.ArgumentError, call_vimba_c, 'VmbVersionQuery', 0, 'hi')
|
||||
|
||||
# Call with valid ctypes used wrongly
|
||||
ver_info = VmbVersionInfo()
|
||||
self.assertRaises(ctypes.ArgumentError, call_vimba_c, 'VmbVersionQuery', byref(ver_info),
|
||||
ver_info)
|
||||
|
||||
def test_call_vimba_c_exception(self):
|
||||
# Expectation: Errors returned from the C-Layer must be mapped
|
||||
# to a special Exception Type call VimbaCError. This error must
|
||||
# contain the returned Error Code from the failed C-Call.
|
||||
|
||||
# VmbVersionQuery has two possible Errors (taken from VimbaC.h):
|
||||
# - VmbErrorStructSize: The given struct size is not valid for this version of the API
|
||||
# - VmbErrorBadParameter: If "pVersionInfo" is NULL.
|
||||
|
||||
ver_info = VmbVersionInfo()
|
||||
|
||||
try:
|
||||
call_vimba_c('VmbVersionQuery', byref(ver_info), sizeof(ver_info) - 1)
|
||||
self.fail("Previous call must raise Exception.")
|
||||
|
||||
except VimbaCError as e:
|
||||
self.assertEqual(e.get_error_code(), VmbError.StructSize)
|
||||
|
||||
try:
|
||||
call_vimba_c('VmbVersionQuery', None, sizeof(ver_info))
|
||||
self.fail("Previous call must raise Exception.")
|
||||
|
||||
except VimbaCError as e:
|
||||
self.assertEqual(e.get_error_code(), VmbError.BadParameter)
|
||||
|
||||
|
||||
class ImageTransformTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_call_vimba_image_transform_valid(self):
|
||||
# Expectation for valid call: No exceptions, no errors
|
||||
expected_ver_info = EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION
|
||||
v = VmbUint32()
|
||||
|
||||
call_vimba_image_transform('VmbGetVersion', byref(v))
|
||||
|
||||
ver_info = str(v.value >> 24 & 0xff) + '.' + str(v.value >> 16 & 0xff)
|
||||
|
||||
self.assertEqual(expected_ver_info, ver_info)
|
||||
|
||||
def test_call_vimba_c_invalid_func_name(self):
|
||||
# Expectation: An invalid function name must throw an AttributeError
|
||||
v = VmbUint32()
|
||||
self.assertRaises(AttributeError, call_vimba_image_transform, 'VmbGetVersio', byref(v))
|
||||
|
||||
def test_call_vimba_c_invalid_arg_number(self):
|
||||
# Expectation: Invalid number of arguments with sane types must lead to TypeErrors
|
||||
self.assertRaises(TypeError, call_vimba_image_transform, 'VmbGetVersion')
|
||||
|
||||
def test_call_vimba_c_invalid_arg_type(self):
|
||||
# Expectation: Arguments with invalid types must lead to TypeErrors
|
||||
self.assertRaises(ctypes.ArgumentError, call_vimba_image_transform, 'VmbGetVersion',
|
||||
VmbDouble())
|
||||
self.assertRaises(ctypes.ArgumentError, call_vimba_image_transform, 'VmbGetVersion', 0)
|
||||
self.assertRaises(ctypes.ArgumentError, call_vimba_image_transform, 'VmbGetVersion',
|
||||
'invalid')
|
||||
|
||||
def test_call_vimba_c_exception(self):
|
||||
# Expectation: Failed operations must raise a VimbaCError
|
||||
self.assertRaises(VimbaCError, call_vimba_image_transform, 'VmbGetVersion', None)
|
||||
176
Vimba_6_0/VimbaPython/Source/Tests/basic_tests/interface_test.py
Normal file
176
Vimba_6_0/VimbaPython/Source/Tests/basic_tests/interface_test.py
Normal file
@@ -0,0 +1,176 @@
|
||||
"""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 unittest
|
||||
from vimba import *
|
||||
|
||||
|
||||
class InterfaceTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.vimba = Vimba.get_instance()
|
||||
self.vimba._startup()
|
||||
|
||||
inters = self.vimba.get_all_interfaces()
|
||||
|
||||
if not inters:
|
||||
self.vimba._shutdown()
|
||||
self.skipTest('No Interface available to test against. Abort.')
|
||||
|
||||
def tearDown(self):
|
||||
self.vimba._shutdown()
|
||||
|
||||
def test_interface_decode_id(self):
|
||||
# Expectation all interface ids can be decoded in something not ''
|
||||
for i in self.vimba.get_all_interfaces():
|
||||
self.assertNotEqual(i.get_id(), '')
|
||||
|
||||
def test_interface_decode_type(self):
|
||||
# Expectation all interface types be in interface types
|
||||
excpected = (
|
||||
InterfaceType.Firewire,
|
||||
InterfaceType.Ethernet,
|
||||
InterfaceType.Usb,
|
||||
InterfaceType.CL,
|
||||
InterfaceType.CSI2,
|
||||
)
|
||||
|
||||
for i in self.vimba.get_all_interfaces():
|
||||
self.assertIn(i.get_type(), excpected)
|
||||
|
||||
def test_interface_decode_name(self):
|
||||
# Expectation all interface names can be decoded in something not ''
|
||||
for i in self.vimba.get_all_interfaces():
|
||||
self.assertNotEqual(i.get_name(), '')
|
||||
|
||||
def test_interface_decode_serial(self):
|
||||
# Expectation: Serials can be '' on some interfaces. This test success
|
||||
# if get serial does not raise
|
||||
for i in self.vimba.get_all_interfaces():
|
||||
self.assertNoRaise(i.get_serial)
|
||||
|
||||
def test_interface_get_all_features(self):
|
||||
# Expectation: Call get_all_features raises RuntimeError outside of with
|
||||
# Inside of with return a non empty set
|
||||
with self.vimba.get_all_interfaces()[0] as inter:
|
||||
self.assertNotEqual(inter.get_all_features(), ())
|
||||
|
||||
def test_interface_get_features_affected_by(self):
|
||||
# Expectation: Call get_features_affected_by raises RuntimeError outside of with.
|
||||
# Inside with it must either return and empty set if the given feature has no affected
|
||||
# Feature or a set off affected features
|
||||
with self.vimba.get_all_interfaces()[0] as inter:
|
||||
try:
|
||||
affects_feats = inter.get_feature_by_name('DeviceUpdateList')
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.skipTest('Test requires Feature \'DeviceUpdateList\'.')
|
||||
|
||||
try:
|
||||
not_affects_feats = inter.get_feature_by_name('DeviceCount')
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.skipTest('Test requires Feature \'DeviceCount\'.')
|
||||
|
||||
self.assertTrue(affects_feats.has_affected_features())
|
||||
self.assertNotEquals(inter.get_features_affected_by(affects_feats), ())
|
||||
|
||||
self.assertFalse(not_affects_feats.has_affected_features())
|
||||
self.assertEquals(inter.get_features_affected_by(not_affects_feats), ())
|
||||
|
||||
def test_interface_get_features_selected_by(self):
|
||||
# Expectation: Call get_features_selected_by raises RuntimeError outside of with.
|
||||
# Inside with it must either return and empty set if the given feature has no selected
|
||||
# Feature or a set off affected features
|
||||
with self.vimba.get_all_interfaces()[0] as inter:
|
||||
try:
|
||||
selects_feats = inter.get_feature_by_name('DeviceSelector')
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.skipTest('Test requires Feature \'DeviceSelector\'.')
|
||||
|
||||
try:
|
||||
not_selects_feats = inter.get_feature_by_name('DeviceCount')
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.skipTest('Test requires Feature \'DeviceCount\'.')
|
||||
|
||||
self.assertTrue(selects_feats.has_selected_features())
|
||||
self.assertNotEquals(inter.get_features_selected_by(selects_feats), ())
|
||||
|
||||
self.assertFalse(not_selects_feats.has_selected_features())
|
||||
self.assertEquals(inter.get_features_selected_by(not_selects_feats), ())
|
||||
|
||||
def test_interface_get_features_by_type(self):
|
||||
# Expectation: Call get_features_by_type raises RuntimeError outside of with
|
||||
# Inside of with return a non empty set for IntFeature (DeviceCount is IntFeature)
|
||||
with self.vimba.get_all_interfaces()[0] as inter:
|
||||
self.assertNotEqual(inter.get_features_by_type(IntFeature), ())
|
||||
|
||||
def test_interface_get_features_by_category(self):
|
||||
# Expectation: Call get_features_by_category raises RuntimeError outside of with
|
||||
# Inside of with return a non empty set for /DeviceEnumeration)
|
||||
with self.vimba.get_all_interfaces()[0] as inter:
|
||||
self.assertNotEqual(inter.get_features_by_category('/DeviceEnumeration'), ())
|
||||
|
||||
def test_interface_get_feature_by_name(self):
|
||||
# Expectation: Call get_feature_by_name raises RuntimeError outside of with
|
||||
# Inside of with return dont raise VimbaFeatureError for 'DeviceCount'
|
||||
# A invalid name must raise VimbaFeatureError
|
||||
with self.vimba.get_all_interfaces()[0] as inter:
|
||||
self.assertNoRaise(inter.get_feature_by_name, 'DeviceCount')
|
||||
self.assertRaises(VimbaFeatureError, inter.get_feature_by_name, 'Invalid Name')
|
||||
|
||||
def test_interface_context_manager_reentrancy(self):
|
||||
# Expectation: Implemented Context Manager must be reentrant, not causing
|
||||
# multiple interface openings (would cause C-Errors)
|
||||
with self.vimba.get_all_interfaces()[0] as inter:
|
||||
with inter:
|
||||
with inter:
|
||||
pass
|
||||
|
||||
def test_interface_api_context_sensitivity_inside_context(self):
|
||||
# Expectation: Interface has functions that shall only be callable inside the Context,
|
||||
# calling outside must cause a runtime error. This test check only if the RuntimeErrors
|
||||
# are triggered then called Outside of the with block.
|
||||
inter = self.vimba.get_all_interfaces()[0]
|
||||
|
||||
self.assertRaises(RuntimeError, inter.read_memory, 0, 0)
|
||||
self.assertRaises(RuntimeError, inter.write_memory, 0, b'foo')
|
||||
self.assertRaises(RuntimeError, inter.read_registers, ())
|
||||
self.assertRaises(RuntimeError, inter.write_registers, {0: 0})
|
||||
self.assertRaises(RuntimeError, inter.get_all_features)
|
||||
|
||||
# Enter scope to get handle on Features as valid parameters for the test:
|
||||
# Don't to this in production code because the features will be invalid if used.
|
||||
with inter:
|
||||
feat = inter.get_all_features()[0]
|
||||
|
||||
self.assertRaises(RuntimeError, inter.get_features_affected_by, feat)
|
||||
self.assertRaises(RuntimeError, inter.get_features_selected_by, feat)
|
||||
self.assertRaises(RuntimeError, inter.get_features_by_type, IntFeature)
|
||||
self.assertRaises(RuntimeError, inter.get_features_by_category, 'foo')
|
||||
self.assertRaises(RuntimeError, inter.get_feature_by_name, 'foo')
|
||||
@@ -0,0 +1,84 @@
|
||||
"""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 unittest
|
||||
|
||||
from vimba.util import *
|
||||
|
||||
|
||||
class TestObj:
|
||||
@LeaveContextOnCall()
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@EnterContextOnCall()
|
||||
def __enter__(self):
|
||||
pass
|
||||
|
||||
@LeaveContextOnCall()
|
||||
def __exit__(self, _1, _2, _3):
|
||||
pass
|
||||
|
||||
@RaiseIfOutsideContext()
|
||||
def works_inside_context(self):
|
||||
pass
|
||||
|
||||
@RaiseIfInsideContext()
|
||||
def works_outside_context(self):
|
||||
pass
|
||||
|
||||
|
||||
class ContextDecoratorTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.test_obj = TestObj()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_raise_if_inside_context(self):
|
||||
# Expectation: a decorated method must raise a RuntimeError if a
|
||||
# Decorated function is called within a with - statement and
|
||||
# run properly outside of the context.
|
||||
|
||||
self.assertNoRaise(self.test_obj.works_outside_context)
|
||||
|
||||
with self.test_obj:
|
||||
self.assertRaises(RuntimeError, self.test_obj.works_outside_context)
|
||||
|
||||
self.assertNoRaise(self.test_obj.works_outside_context)
|
||||
|
||||
def test_raise_if_outside_context(self):
|
||||
# Expectation: a decorated method must raise a RuntimeError if a
|
||||
# Decorated function is called outside a with - statement and
|
||||
# run properly inside of the context.
|
||||
|
||||
self.assertRaises(RuntimeError, self.test_obj.works_inside_context)
|
||||
|
||||
with self.test_obj:
|
||||
self.assertNoRaise(self.test_obj.works_inside_context)
|
||||
|
||||
self.assertRaises(RuntimeError, self.test_obj.works_inside_context)
|
||||
@@ -0,0 +1,256 @@
|
||||
"""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 unittest
|
||||
from typing import Union, Optional, Tuple, Callable, Dict, Type
|
||||
from vimba.util import *
|
||||
|
||||
|
||||
class RuntimeTypeCheckTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_func_mixed_args_kwargs_and_defaults(self):
|
||||
# Expectation: The typecheck must be able to deal with a valid mixture of args, kwargs
|
||||
# and default values.
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def test_func(a: int, b: str, c: float = 11.0 / 7.0):
|
||||
pass
|
||||
|
||||
self.assertNoRaise(test_func, 1, '2', 0.0)
|
||||
self.assertNoRaise(test_func, c=0.0, b='str', a=1)
|
||||
self.assertNoRaise(test_func, 1, c=0.0, b='str')
|
||||
self.assertNoRaise(test_func, 1, b='str')
|
||||
self.assertNoRaise(test_func, 1, 'str')
|
||||
|
||||
self.assertRaises(TypeError, test_func, c=0.0, b='str', a=0.0)
|
||||
self.assertRaises(TypeError, test_func, c='invalid type', b='str', a=0.0)
|
||||
|
||||
def test_func_no_hints(self):
|
||||
# Expectation: Functions without type hints
|
||||
# should not throw any type errors
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def test_func(arg1, arg2):
|
||||
return str()
|
||||
|
||||
self.assertNoRaise(test_func, 'str', 0)
|
||||
|
||||
def test_func_some_hints(self):
|
||||
# Expectation: Type checks are only enforced on Arguments with hint.
|
||||
# Argument without hints should be accepted
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def test_func(arg1, arg2: int):
|
||||
return str()
|
||||
|
||||
self.assertNoRaise(test_func, 'str', 0)
|
||||
self.assertNoRaise(test_func, 0.5, 0)
|
||||
self.assertRaises(TypeError, test_func, 'str', 0.0)
|
||||
|
||||
def test_object(self):
|
||||
# Expectation: The runtime checker must work on Objects just as on
|
||||
# functions.
|
||||
|
||||
class TestObject:
|
||||
@RuntimeTypeCheckEnable()
|
||||
def __init__(self, arg1: str, arg2: int):
|
||||
pass
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def __call__(self, arg: str) -> str:
|
||||
return arg
|
||||
|
||||
# Invalid construction
|
||||
self.assertRaises(TypeError, TestObject, 0.0, 0)
|
||||
|
||||
obj = TestObject('str', 0)
|
||||
self.assertNoRaise(obj, 'arg')
|
||||
|
||||
self.assertRaises(TypeError, obj, 0.0)
|
||||
|
||||
def test_type(self):
|
||||
# Expectation: types as parameters must be detected like any other values.
|
||||
@RuntimeTypeCheckEnable()
|
||||
def func(arg: Type[int]):
|
||||
pass
|
||||
|
||||
self.assertNoRaise(func, int)
|
||||
self.assertRaises(TypeError, func, str)
|
||||
self.assertRaises(TypeError, func, 0)
|
||||
|
||||
def test_union(self):
|
||||
# Expectation: int and string are valid parameters. Everything else must throw
|
||||
@RuntimeTypeCheckEnable()
|
||||
def func(arg: Union[int, str]) -> Union[int, str]:
|
||||
return arg
|
||||
|
||||
self.assertNoRaise(func, 0)
|
||||
self.assertNoRaise(func, 'str')
|
||||
self.assertRaises(TypeError, func, 0.0)
|
||||
|
||||
def test_optional(self):
|
||||
# Expectation: For optionals the check must accept the given type or None.
|
||||
# Anything else must lead to an TypeError
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def func(arg: Optional[int]) -> Optional[str]:
|
||||
return str(arg)
|
||||
|
||||
self.assertNoRaise(func, 0)
|
||||
self.assertNoRaise(func, None)
|
||||
self.assertRaises(TypeError, func, 'str')
|
||||
|
||||
def test_tuple(self):
|
||||
# Expectation: Fixed size tuples checking must verify that size and type order is
|
||||
# enforced.
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def func(arg: Tuple[int, str, float]) -> Tuple[float, int, str]:
|
||||
i, s, f = arg
|
||||
return (f, i, s)
|
||||
|
||||
self.assertNoRaise(func, (1, 'str', 0.1))
|
||||
|
||||
self.assertRaises(TypeError, func, (1, 'str'))
|
||||
self.assertRaises(TypeError, func, (1, 'str', 0.0, 'extra'))
|
||||
self.assertRaises(TypeError, func, ('str1', 'str', 0.0))
|
||||
|
||||
def test_tuple_var_length(self):
|
||||
# Expectation: Var length tuples checking must verify that contained type is enforced.
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def func(arg: Tuple[int, ...]) -> Tuple[str, ...]:
|
||||
return tuple([str(i) for i in arg])
|
||||
|
||||
self.assertNoRaise(func, ())
|
||||
self.assertNoRaise(func, (1,))
|
||||
self.assertNoRaise(func, (1, 2, 3, 4, 5, 6))
|
||||
self.assertRaises(TypeError, func, ('str', ))
|
||||
self.assertRaises(TypeError, func, (1, 'str'))
|
||||
|
||||
def test_tuple_empty(self):
|
||||
# Empty Tuples must satisfy the requirements to Tuple types as argument and results
|
||||
@RuntimeTypeCheckEnable()
|
||||
def func(arg: Tuple[int, ...]) -> Tuple[int, ...]:
|
||||
return ()
|
||||
|
||||
self.assertNoRaise(func, ())
|
||||
self.assertEqual(func(()), ())
|
||||
|
||||
def test_tuple_union(self):
|
||||
# Tuples of union types must be detected correctly
|
||||
@RuntimeTypeCheckEnable()
|
||||
def func(arg: Tuple[Union[int, str], ...]):
|
||||
return arg
|
||||
|
||||
self.assertNoRaise(func, (0,))
|
||||
self.assertNoRaise(func, ('1',))
|
||||
self.assertNoRaise(func, (2, 3))
|
||||
self.assertNoRaise(func, ('4', '5'))
|
||||
self.assertNoRaise(func, (6, '7'))
|
||||
self.assertNoRaise(func, ('8', 9))
|
||||
self.assertRaises(TypeError, func, (2, 0.0))
|
||||
|
||||
def test_dict(self):
|
||||
# Expectation: Dictionaries must be detected correctly.
|
||||
@RuntimeTypeCheckEnable()
|
||||
def func(arg: Dict[int, str]):
|
||||
pass
|
||||
|
||||
self.assertNoRaise(func, {0: 'ok'})
|
||||
self.assertRaises(TypeError, func, None)
|
||||
self.assertRaises(TypeError, func, 0)
|
||||
self.assertRaises(TypeError, func, 'No Dict')
|
||||
self.assertRaises(TypeError, func, {0.0: 'Err'})
|
||||
self.assertRaises(TypeError, func, {0: b'bytes'})
|
||||
|
||||
def test_callable_no_func(self):
|
||||
# Expectation: The Callable verification shall fail if given Parameter is no callable.
|
||||
@RuntimeTypeCheckEnable()
|
||||
def func(fn: Callable[[], None]):
|
||||
fn()
|
||||
|
||||
self.assertRaises(TypeError, func, 'no_callable')
|
||||
|
||||
def test_callable_func(self):
|
||||
# Expectation: A Callable without any hints must comply as long as the number of parameters
|
||||
# matches to given hints. The Return Type doesn't matter if not given.
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def func(fn: Callable[[str, float], int], arg1: str, arg2: float) -> int:
|
||||
return fn(arg1, arg2)
|
||||
|
||||
def ok(arg1, arg2):
|
||||
return 0.0
|
||||
|
||||
def err1(arg1):
|
||||
return 'str'
|
||||
|
||||
def err2(arg1, arg2, arg3):
|
||||
return 23
|
||||
|
||||
self.assertNoRaise(func, ok, 'str', 0.0)
|
||||
self.assertRaises(TypeError, func, err1, 'str', 0.0)
|
||||
self.assertRaises(TypeError, func, err2, 'str', 0.0)
|
||||
|
||||
def test_callable_obj(self):
|
||||
# Expectation: A Object that is callable must pass the runtime check
|
||||
@RuntimeTypeCheckEnable()
|
||||
def func(fn: Callable[[str], None], arg: str) -> str:
|
||||
return fn(arg)
|
||||
|
||||
class Ok:
|
||||
def __call__(self, arg: str) -> str:
|
||||
return str
|
||||
|
||||
class Err1:
|
||||
def __call__(self) -> str:
|
||||
return 'Err1'
|
||||
|
||||
class Err2:
|
||||
def __call__(self, arg1: str, arg2: str) -> str:
|
||||
return arg1 + arg2
|
||||
|
||||
self.assertNoRaise(func, Ok(), 'str')
|
||||
self.assertRaises(TypeError, func, Err1(), 'str')
|
||||
self.assertRaises(TypeError, func, Err2(), 'str')
|
||||
|
||||
def test_callable_lambda(self):
|
||||
# Expectation: RuntimeTypeCheck must behave with lambas as with functions
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def func(fn: Callable[[str, float], int], arg1: str, arg2: float) -> int:
|
||||
return fn(arg1, arg2)
|
||||
|
||||
self.assertNoRaise(func, lambda a1, a2: 0.0, 'str', 0.0)
|
||||
self.assertRaises(TypeError, func, lambda a1: 'foo', 'str', 0.0)
|
||||
self.assertRaises(TypeError, func, lambda a1, a2, a3: 23, 'str', 0.0)
|
||||
@@ -0,0 +1,157 @@
|
||||
"""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 unittest
|
||||
|
||||
from vimba.util import *
|
||||
|
||||
|
||||
class TracerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# Enable logging and setup hidden buffer
|
||||
self.log = Log.get_instance()
|
||||
self.log._test_buffer = []
|
||||
|
||||
self.log.enable(LOG_CONFIG_CRITICAL_CONSOLE_ONLY)
|
||||
|
||||
def tearDown(self):
|
||||
# Disable logging and clear hidden buffer
|
||||
self.log.disable()
|
||||
|
||||
self.log._test_buffer = None
|
||||
|
||||
def test_trace_inactive(self):
|
||||
# Expectation: A disabled log must not contain any trace entries.
|
||||
|
||||
@TraceEnable()
|
||||
def test_func(arg):
|
||||
return str(arg)
|
||||
|
||||
self.log.disable()
|
||||
|
||||
self.assertEqual(test_func(1), '1')
|
||||
self.assertFalse(self.log._test_buffer)
|
||||
|
||||
self.assertEqual(test_func('test'), 'test')
|
||||
self.assertFalse(self.log._test_buffer)
|
||||
|
||||
self.assertEqual(test_func(2.0), '2.0')
|
||||
self.assertFalse(self.log._test_buffer)
|
||||
|
||||
def test_trace_normal_exit(self):
|
||||
# Expectation: Must not throw on call normal func.
|
||||
# Each call traced call must add two Log entries:
|
||||
|
||||
@TraceEnable()
|
||||
def test_func(arg):
|
||||
return str(arg)
|
||||
|
||||
self.assertEqual(test_func(1), '1')
|
||||
self.assertEqual(len(self.log._test_buffer), 2)
|
||||
|
||||
self.assertEqual(test_func('test'), 'test')
|
||||
self.assertEqual(len(self.log._test_buffer), 4)
|
||||
|
||||
self.assertEqual(test_func(2.0), '2.0')
|
||||
self.assertEqual(len(self.log._test_buffer), 6)
|
||||
|
||||
def test_trace_raised_exit(self):
|
||||
# Expectation: Throws internally thrown exception and adds two log entries
|
||||
# Each call traced call must add two Log entries:
|
||||
|
||||
@TraceEnable()
|
||||
def test_func(arg):
|
||||
raise TypeError('my error')
|
||||
|
||||
self.assertRaises(TypeError, test_func, 1)
|
||||
self.assertEqual(len(self.log._test_buffer), 2)
|
||||
|
||||
self.assertRaises(TypeError, test_func, 'test')
|
||||
self.assertEqual(len(self.log._test_buffer), 4)
|
||||
|
||||
self.assertRaises(TypeError, test_func, 2.0)
|
||||
self.assertEqual(len(self.log._test_buffer), 6)
|
||||
|
||||
def test_trace_function(self):
|
||||
# Expectation: Normal functions must be traceable
|
||||
@TraceEnable()
|
||||
def test_func():
|
||||
pass
|
||||
|
||||
test_func()
|
||||
self.assertEqual(len(self.log._test_buffer), 2)
|
||||
|
||||
test_func()
|
||||
self.assertEqual(len(self.log._test_buffer), 4)
|
||||
|
||||
test_func()
|
||||
self.assertEqual(len(self.log._test_buffer), 6)
|
||||
|
||||
def test_trace_lambda(self):
|
||||
# Expectation: Lambdas must be traceable
|
||||
|
||||
test_lambda = TraceEnable()(lambda: 0)
|
||||
|
||||
test_lambda()
|
||||
self.assertEqual(len(self.log._test_buffer), 2)
|
||||
|
||||
test_lambda()
|
||||
self.assertEqual(len(self.log._test_buffer), 4)
|
||||
|
||||
test_lambda()
|
||||
self.assertEqual(len(self.log._test_buffer), 6)
|
||||
|
||||
def test_trace_object(self):
|
||||
# Expectation: Objects must be traceable including constructors.
|
||||
class TestObj:
|
||||
@TraceEnable()
|
||||
def __init__(self, arg):
|
||||
self.arg = arg
|
||||
|
||||
@TraceEnable()
|
||||
def __str__(self):
|
||||
return 'TestObj({})'.format(str(self.arg))
|
||||
|
||||
@TraceEnable()
|
||||
def __repr__(self):
|
||||
return 'TestObj({})'.format(repr(self.arg))
|
||||
|
||||
@TraceEnable()
|
||||
def __call__(self):
|
||||
pass
|
||||
|
||||
test_obj = TestObj('test')
|
||||
self.assertEqual(len(self.log._test_buffer), 2)
|
||||
|
||||
str(test_obj)
|
||||
self.assertEqual(len(self.log._test_buffer), 4)
|
||||
|
||||
repr(test_obj)
|
||||
self.assertEqual(len(self.log._test_buffer), 6)
|
||||
|
||||
test_obj()
|
||||
self.assertEqual(len(self.log._test_buffer), 8)
|
||||
@@ -0,0 +1,89 @@
|
||||
"""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 unittest
|
||||
|
||||
from vimba.c_binding import _select_vimba_home
|
||||
from vimba.error import VimbaSystemError
|
||||
|
||||
|
||||
class RankVimbaHomeCandidatesTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_empty_gentl_path(self):
|
||||
candidates = []
|
||||
with self.assertRaises(VimbaSystemError):
|
||||
_select_vimba_home(candidates)
|
||||
|
||||
def test_empty_string(self):
|
||||
candidates = ['']
|
||||
with self.assertRaises(VimbaSystemError):
|
||||
_select_vimba_home(candidates)
|
||||
|
||||
def test_single_bad_vimba_home_candidate(self):
|
||||
candidates = ['/some/path']
|
||||
with self.assertRaises(VimbaSystemError):
|
||||
_select_vimba_home(candidates)
|
||||
|
||||
def test_single_good_vimba_home_candidate(self):
|
||||
candidates = ['/opt/Vimba_3_1']
|
||||
expected = '/opt/Vimba_3_1'
|
||||
self.assertEquals(expected, _select_vimba_home(candidates))
|
||||
|
||||
def test_presorted_vimba_home_candidates(self):
|
||||
candidates = ['/home/username/Vimba_4_0', '/opt/some/other/gentl/provider']
|
||||
expected = '/home/username/Vimba_4_0'
|
||||
self.assertEqual(expected, _select_vimba_home(candidates))
|
||||
|
||||
def test_unsorted_vimba_home_candidates(self):
|
||||
candidates = ['/opt/some/other/gentl/provider', '/home/username/Vimba_4_0']
|
||||
expected = '/home/username/Vimba_4_0'
|
||||
self.assertEqual(expected, _select_vimba_home(candidates))
|
||||
|
||||
def test_many_vimba_home_candidates(self):
|
||||
candidates = ['/some/random/path',
|
||||
'/opt/some/gentl/provider',
|
||||
'/opt/Vimba_4_0', # This should be selected
|
||||
'/opt/another/gentl/provider',
|
||||
'/another/incorrect/path']
|
||||
expected = '/opt/Vimba_4_0'
|
||||
self.assertEqual(expected, _select_vimba_home(candidates))
|
||||
|
||||
def test_multiple_vimba_home_directories(self):
|
||||
# If multiple VIMBA_HOME directories are found an error should be raised
|
||||
candidates = ['/some/random/path',
|
||||
'/opt/some/gentl/provider',
|
||||
'/opt/Vimba_4_0', # first installation
|
||||
'/home/username/Vimba_4_0', # second installation
|
||||
'/opt/another/gentl/provider',
|
||||
'/another/incorrect/path']
|
||||
with self.assertRaises(VimbaSystemError):
|
||||
_select_vimba_home(candidates)
|
||||
126
Vimba_6_0/VimbaPython/Source/Tests/basic_tests/vimba_test.py
Normal file
126
Vimba_6_0/VimbaPython/Source/Tests/basic_tests/vimba_test.py
Normal file
@@ -0,0 +1,126 @@
|
||||
"""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 unittest
|
||||
from vimba import *
|
||||
|
||||
|
||||
class VimbaTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.vimba = Vimba.get_instance()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_singleton(self):
|
||||
# Expected behavior: Multiple calls to Vimba.get_instance() return the same object.
|
||||
self.assertEqual(self.vimba, Vimba.get_instance())
|
||||
|
||||
def test_get_version(self):
|
||||
# Expectation: Returned Version is not empty and does not raise any exceptions.
|
||||
self.assertNotEqual(self.vimba.get_version(), "")
|
||||
|
||||
def test_get_camera_by_id_failure(self):
|
||||
# Expected behavior: Lookup of a currently unavailable camera must throw an
|
||||
# VimbaCameraError
|
||||
with self.vimba:
|
||||
self.assertRaises(VimbaCameraError, self.vimba.get_camera_by_id, 'Invalid ID')
|
||||
|
||||
def test_get_interface_by_id_failure(self):
|
||||
# Expected behavior: Lookup of a currently unavailable interface must throw an
|
||||
# VimbaInterfaceError
|
||||
with self.vimba:
|
||||
self.assertRaises(VimbaInterfaceError, self.vimba.get_interface_by_id, 'Invalid ID')
|
||||
|
||||
def test_get_feature_by_name_failure(self):
|
||||
# Expected behavior: Lookup of a currently unavailable feature must throw an
|
||||
# VimbaFeatureError
|
||||
with self.vimba:
|
||||
self.assertRaises(VimbaFeatureError, self.vimba.get_feature_by_name, 'Invalid ID')
|
||||
|
||||
def test_runtime_check_failure(self):
|
||||
self.assertRaises(TypeError, self.vimba.set_network_discovery, 0.0)
|
||||
|
||||
with self.vimba:
|
||||
# All functions with RuntimeTypeCheckEnable must return a TypeError on Failure
|
||||
self.assertRaises(TypeError, self.vimba.get_camera_by_id, 0)
|
||||
self.assertRaises(TypeError, self.vimba.get_interface_by_id, 1)
|
||||
self.assertRaises(TypeError, self.vimba.get_feature_by_name, 0)
|
||||
self.assertRaises(TypeError, self.vimba.enable_log, '-1')
|
||||
|
||||
self.assertRaises(TypeError, self.vimba.get_features_affected_by, '-1')
|
||||
self.assertRaises(TypeError, self.vimba.get_features_selected_by, '-1')
|
||||
self.assertRaises(TypeError, self.vimba.get_features_by_type, [])
|
||||
self.assertRaises(TypeError, self.vimba.register_camera_change_handler, 0)
|
||||
self.assertRaises(TypeError, self.vimba.unregister_camera_change_handler, 0)
|
||||
self.assertRaises(TypeError, self.vimba.register_interface_change_handler, 0)
|
||||
self.assertRaises(TypeError, self.vimba.unregister_interface_change_handler, 0)
|
||||
|
||||
def test_vimba_context_manager_reentrancy(self):
|
||||
# Expectation: Implemented Context Manager must be reentrant, not causing
|
||||
# multiple starts of the Vimba API (would cause C-Errors)
|
||||
|
||||
with self.vimba:
|
||||
with self.vimba:
|
||||
with self.vimba:
|
||||
pass
|
||||
|
||||
def test_vimba_api_context_sensitity_outside_context(self):
|
||||
# Expectation: Vimba has functions that shall only be callable outside the Context and
|
||||
# calling within the context must cause a runtime error.
|
||||
|
||||
self.assertNoRaise(self.vimba.set_network_discovery, True)
|
||||
|
||||
with self.vimba:
|
||||
self.assertRaises(RuntimeError, self.vimba.set_network_discovery, True)
|
||||
|
||||
self.assertNoRaise(self.vimba.set_network_discovery, True)
|
||||
|
||||
def test_vimba_api_context_sensitity_inside_context(self):
|
||||
# Expectation: Vimba has functions that shall only be callable inside the Context and
|
||||
# calling outside must cause a runtime error. This test check only if the RuntimeErrors
|
||||
# are triggered then called Outside of the with block.
|
||||
self.assertRaises(RuntimeError, self.vimba.read_memory, 0, 0)
|
||||
self.assertRaises(RuntimeError, self.vimba.write_memory, 0, b'foo')
|
||||
self.assertRaises(RuntimeError, self.vimba.read_registers, ())
|
||||
self.assertRaises(RuntimeError, self.vimba.write_registers, {0: 0})
|
||||
self.assertRaises(RuntimeError, self.vimba.get_all_interfaces)
|
||||
self.assertRaises(RuntimeError, self.vimba.get_interface_by_id, 'id')
|
||||
self.assertRaises(RuntimeError, self.vimba.get_all_cameras)
|
||||
self.assertRaises(RuntimeError, self.vimba.get_camera_by_id, 'id')
|
||||
self.assertRaises(RuntimeError, self.vimba.get_all_features)
|
||||
|
||||
# Enter scope to get handle on Features as valid parameters for the test:
|
||||
# Don't to this in production code because the feature will be invalid if use.
|
||||
with self.vimba:
|
||||
feat = self.vimba.get_all_features()[0]
|
||||
|
||||
self.assertRaises(RuntimeError, self.vimba.get_features_affected_by, feat)
|
||||
self.assertRaises(RuntimeError, self.vimba.get_features_selected_by, feat)
|
||||
self.assertRaises(RuntimeError, self.vimba.get_features_by_type, IntFeature)
|
||||
self.assertRaises(RuntimeError, self.vimba.get_features_by_category, 'foo')
|
||||
self.assertRaises(RuntimeError, self.vimba.get_feature_by_name, 'foo')
|
||||
@@ -0,0 +1,155 @@
|
||||
"""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 unittest
|
||||
|
||||
from vimba import *
|
||||
|
||||
|
||||
class CamAncillaryDataTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.vimba = Vimba.get_instance()
|
||||
self.vimba._startup()
|
||||
|
||||
try:
|
||||
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to lookup Camera.') from e
|
||||
|
||||
try:
|
||||
self.cam._open()
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to open Camera.') from e
|
||||
|
||||
try:
|
||||
self.chunk_mode = self.cam.get_feature_by_name('ChunkModeActive')
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
self.skipTest('Required Feature \'ChunkModeActive\' not available.')
|
||||
|
||||
def tearDown(self):
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
|
||||
def test_ancillary_data_access(self):
|
||||
# Expectation: Ancillary Data is None if ChunkMode is disable.
|
||||
# If ChunkMode is enabled Ancillary Data shall not be None.
|
||||
|
||||
old_state = self.chunk_mode.get()
|
||||
|
||||
try:
|
||||
# Disable ChunkMode, acquire frame: Ancillary Data must be None
|
||||
self.chunk_mode.set(False)
|
||||
self.assertIsNone(self.cam.get_frame().get_ancillary_data())
|
||||
|
||||
# Enable ChunkMode, acquire frame: Ancillary Data must not be None
|
||||
self.chunk_mode.set(True)
|
||||
self.assertIsNotNone(self.cam.get_frame().get_ancillary_data())
|
||||
|
||||
finally:
|
||||
self.chunk_mode.set(old_state)
|
||||
|
||||
def test_ancillary_data_context_manager_reentrancy(self):
|
||||
# Expectation: Ancillary Data Context Manager must be reentrant.
|
||||
old_state = self.chunk_mode.get()
|
||||
|
||||
try:
|
||||
self.chunk_mode.set(True)
|
||||
frame = self.cam.get_frame()
|
||||
anc_data = frame.get_ancillary_data()
|
||||
|
||||
with anc_data:
|
||||
with anc_data:
|
||||
with anc_data:
|
||||
pass
|
||||
|
||||
finally:
|
||||
self.chunk_mode.set(old_state)
|
||||
|
||||
def test_ancillary_data_api_context_sensitity(self):
|
||||
# Expectation: Ancillary Data implements a Context Manager, outside of with-scope
|
||||
# a runtime error should be raised on all feature related methods accessed outside of the
|
||||
# context.
|
||||
|
||||
old_state = self.chunk_mode.get()
|
||||
|
||||
try:
|
||||
self.chunk_mode.set(True)
|
||||
frame = self.cam.get_frame()
|
||||
anc_data = frame.get_ancillary_data()
|
||||
|
||||
# Check Access Outside Context
|
||||
self.assertRaises(RuntimeError, anc_data.get_all_features)
|
||||
self.assertRaises(RuntimeError, anc_data.get_features_by_type, IntFeature)
|
||||
self.assertRaises(RuntimeError, anc_data.get_features_by_category, '/ChunkData')
|
||||
self.assertRaises(RuntimeError, anc_data.get_feature_by_name, 'ChunkExposureTime')
|
||||
|
||||
with anc_data:
|
||||
# Check Access after Context entry
|
||||
self.assertNoRaise(anc_data.get_all_features)
|
||||
self.assertNoRaise(anc_data.get_features_by_type, IntFeature)
|
||||
self.assertNoRaise(anc_data.get_features_by_category, '/ChunkData')
|
||||
self.assertNoRaise(anc_data.get_feature_by_name, 'ChunkExposureTime')
|
||||
|
||||
# Check Access after Context leaving
|
||||
self.assertRaises(RuntimeError, anc_data.get_all_features)
|
||||
self.assertRaises(RuntimeError, anc_data.get_features_by_type, IntFeature)
|
||||
self.assertRaises(RuntimeError, anc_data.get_features_by_category, '/ChunkData')
|
||||
self.assertRaises(RuntimeError, anc_data.get_feature_by_name, 'ChunkExposureTime')
|
||||
|
||||
finally:
|
||||
self.chunk_mode.set(old_state)
|
||||
|
||||
def test_ancillary_data_removed_attrs(self):
|
||||
# Expectation: Ancillary Data are lightweight features. Calling most Feature-Methods that
|
||||
# call VimbaC Features would cause an internal error. Those error prone methods
|
||||
# shall raise a RuntimeError on call.
|
||||
|
||||
old_state = self.chunk_mode.get()
|
||||
|
||||
try:
|
||||
self.chunk_mode.set(True)
|
||||
frame = self.cam.get_frame()
|
||||
anc_data = frame.get_ancillary_data()
|
||||
|
||||
with anc_data:
|
||||
for feat in anc_data.get_all_features():
|
||||
self.assertRaises(RuntimeError, feat.get_access_mode)
|
||||
self.assertRaises(RuntimeError, feat.is_readable)
|
||||
self.assertRaises(RuntimeError, feat.is_writeable)
|
||||
self.assertRaises(RuntimeError, feat.register_change_handler)
|
||||
self.assertRaises(RuntimeError, feat.get_range)
|
||||
self.assertRaises(RuntimeError, feat.get_increment)
|
||||
self.assertRaises(RuntimeError, feat.set)
|
||||
finally:
|
||||
self.chunk_mode.set(old_state)
|
||||
422
Vimba_6_0/VimbaPython/Source/Tests/real_cam_tests/camera_test.py
Normal file
422
Vimba_6_0/VimbaPython/Source/Tests/real_cam_tests/camera_test.py
Normal file
@@ -0,0 +1,422 @@
|
||||
"""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 unittest
|
||||
import threading
|
||||
import os
|
||||
|
||||
from vimba import *
|
||||
from vimba.frame import *
|
||||
|
||||
|
||||
def dummy_frame_handler(cam: Camera, frame: Frame):
|
||||
pass
|
||||
|
||||
|
||||
class CamCameraTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.vimba = Vimba.get_instance()
|
||||
self.vimba._startup()
|
||||
|
||||
try:
|
||||
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to lookup Camera.') from e
|
||||
|
||||
self.cam.set_access_mode(AccessMode.Full)
|
||||
|
||||
def tearDown(self):
|
||||
self.cam.set_access_mode(AccessMode.Full)
|
||||
self.vimba._shutdown()
|
||||
|
||||
def test_camera_context_manager_access_mode(self):
|
||||
# Expectation: Entering Context must not throw in cases where the current access mode is
|
||||
# within get_permitted_access_modes()
|
||||
|
||||
permitted_modes = self.cam.get_permitted_access_modes()
|
||||
|
||||
for mode in permitted_modes:
|
||||
self.cam.set_access_mode(mode)
|
||||
|
||||
try:
|
||||
with self.cam:
|
||||
pass
|
||||
|
||||
except BaseException:
|
||||
self.fail()
|
||||
|
||||
def test_camera_context_manager_feature_discovery(self):
|
||||
# Expectation: Outside of context, all features must be cleared,
|
||||
# inside of context all features must be detected.
|
||||
with self.cam:
|
||||
self.assertNotEqual(self.cam.get_all_features(), ())
|
||||
|
||||
def test_camera_access_mode(self):
|
||||
# Expectation: set/get access mode
|
||||
self.cam.set_access_mode(AccessMode.None_)
|
||||
self.assertEqual(self.cam.get_access_mode(), AccessMode.None_)
|
||||
self.cam.set_access_mode(AccessMode.Full)
|
||||
self.assertEqual(self.cam.get_access_mode(), AccessMode.Full)
|
||||
self.cam.set_access_mode(AccessMode.Read)
|
||||
self.assertEqual(self.cam.get_access_mode(), AccessMode.Read)
|
||||
|
||||
def test_camera_get_id(self):
|
||||
# Expectation: get decoded camera id
|
||||
self.assertTrue(self.cam.get_id())
|
||||
|
||||
def test_camera_get_name(self):
|
||||
# Expectation: get decoded camera name
|
||||
self.assertTrue(self.cam.get_name())
|
||||
|
||||
def test_camera_get_model(self):
|
||||
# Expectation: get decoded camera model
|
||||
self.assertTrue(self.cam.get_model())
|
||||
|
||||
def test_camera_get_serial(self):
|
||||
# Expectation: get decoded camera serial
|
||||
self.assertTrue(self.cam.get_serial())
|
||||
|
||||
def test_camera_get_permitted_access_modes(self):
|
||||
# Expectation: get currently permitted access modes
|
||||
expected = (AccessMode.None_, AccessMode.Full, AccessMode.Read, AccessMode.Config)
|
||||
|
||||
for mode in self.cam.get_permitted_access_modes():
|
||||
self.assertIn(mode, expected)
|
||||
|
||||
def test_camera_get_interface_id(self):
|
||||
# Expectation: get interface Id this camera is connected to
|
||||
self.assertTrue(self.cam.get_interface_id())
|
||||
|
||||
def test_camera_get_features_affected(self):
|
||||
# Expectation: Features that affect other features shall return a set of affected feature
|
||||
# Features that don't affect other features shall return (). If a Feature is supplied that
|
||||
# is not associated with that camera, a TypeError must be raised.
|
||||
|
||||
with self.cam:
|
||||
try:
|
||||
affect = self.cam.get_feature_by_name('Height')
|
||||
|
||||
except VimbaFeatureError as e:
|
||||
raise unittest.SkipTest('Failed to lookup Feature Height') from e
|
||||
|
||||
try:
|
||||
not_affect = self.cam.get_feature_by_name('AcquisitionFrameCount')
|
||||
|
||||
except VimbaFeatureError as e:
|
||||
raise unittest.SkipTest('Failed to lookup Feature AcquisitionFrameCount') from e
|
||||
|
||||
self.assertEqual(self.cam.get_features_affected_by(not_affect), ())
|
||||
|
||||
try:
|
||||
payload_size = self.cam.get_feature_by_name('PayloadSize')
|
||||
|
||||
except VimbaFeatureError as e:
|
||||
raise unittest.SkipTest('Failed to lookup Feature PayloadSize') from e
|
||||
|
||||
self.assertIn(payload_size, self.cam.get_features_affected_by(affect))
|
||||
|
||||
def test_camera_frame_generator_limit_set(self):
|
||||
# Expectation: The Frame generator fetches the given number of images.
|
||||
with self.cam:
|
||||
self.assertEqual(len([i for i in self.cam.get_frame_generator(0)]), 0)
|
||||
self.assertEqual(len([i for i in self.cam.get_frame_generator(1)]), 1)
|
||||
self.assertEqual(len([i for i in self.cam.get_frame_generator(7)]), 7)
|
||||
self.assertEqual(len([i for i in self.cam.get_frame_generator(11)]), 11)
|
||||
|
||||
def test_camera_frame_generator_error(self):
|
||||
# Expectation: The Frame generator raises a ValueError on a
|
||||
# negative limit and the camera raises an ValueError
|
||||
# if the camera is not opened.
|
||||
|
||||
# generator execution must throw if streaming is enabled
|
||||
with self.cam:
|
||||
# Check limits
|
||||
self.assertRaises(ValueError, self.cam.get_frame_generator, -1)
|
||||
self.assertRaises(ValueError, self.cam.get_frame_generator, 1, 0)
|
||||
self.assertRaises(ValueError, self.cam.get_frame_generator, 1, -1)
|
||||
|
||||
self.cam.start_streaming(dummy_frame_handler, 5)
|
||||
|
||||
self.assertRaises(VimbaCameraError, self.cam.get_frame)
|
||||
self.assertRaises(VimbaCameraError, next, self.cam.get_frame_generator(1))
|
||||
|
||||
# Stop Streaming: Everything should be fine.
|
||||
self.cam.stop_streaming()
|
||||
self.assertNoRaise(self.cam.get_frame)
|
||||
self.assertNoRaise(next, self.cam.get_frame_generator(1))
|
||||
|
||||
def test_camera_get_frame(self):
|
||||
# Expectation: Gets single Frame without any exception. Image data must be set.
|
||||
# If a zero or negative timeouts must lead to a ValueError.
|
||||
with self.cam:
|
||||
self.assertRaises(ValueError, self.cam.get_frame, 0)
|
||||
self.assertRaises(ValueError, self.cam.get_frame, -1)
|
||||
|
||||
self.assertNoRaise(self.cam.get_frame)
|
||||
self.assertEqual(type(self.cam.get_frame()), Frame)
|
||||
|
||||
def test_camera_capture_error_outside_vimba_scope(self):
|
||||
# Expectation: Camera access outside of Vimba scope must lead to a RuntimeError
|
||||
gener = None
|
||||
|
||||
with self.cam:
|
||||
gener = self.cam.get_frame_generator(1)
|
||||
|
||||
# Shutdown API
|
||||
self.vimba._shutdown()
|
||||
|
||||
# Access invalid Iterator
|
||||
self.assertRaises(RuntimeError, next, gener)
|
||||
|
||||
def test_camera_capture_error_outside_camera_scope(self):
|
||||
# Expectation: Camera access outside of Camera scope must lead to a RuntimeError
|
||||
gener = None
|
||||
|
||||
with self.cam:
|
||||
gener = self.cam.get_frame_generator(1)
|
||||
|
||||
self.assertRaises(RuntimeError, next, gener)
|
||||
|
||||
def test_camera_capture_timeout(self):
|
||||
# Expectation: Camera access outside of Camera scope must lead to a VimbaTimeout
|
||||
with self.cam:
|
||||
self.assertRaises(VimbaTimeout, self.cam.get_frame, 1)
|
||||
|
||||
def test_camera_is_streaming(self):
|
||||
# Expectation: After start_streaming() is_streaming() must return true. After stop it must
|
||||
# return false. If the camera context is left without stop_streaming(), leaving
|
||||
# the context must stop streaming.
|
||||
|
||||
# Normal Operation
|
||||
self.assertEqual(self.cam.is_streaming(), False)
|
||||
with self.cam:
|
||||
self.cam.start_streaming(dummy_frame_handler)
|
||||
self.assertEqual(self.cam.is_streaming(), True)
|
||||
|
||||
self.cam.stop_streaming()
|
||||
self.assertEqual(self.cam.is_streaming(), False)
|
||||
|
||||
# Missing the stream stop. Close must stop streaming
|
||||
with self.cam:
|
||||
self.cam.start_streaming(dummy_frame_handler, 5)
|
||||
self.assertEqual(self.cam.is_streaming(), True)
|
||||
|
||||
self.assertEqual(self.cam.is_streaming(), False)
|
||||
|
||||
def test_camera_streaming_error_frame_count(self):
|
||||
# Expectation: A negative or zero frame_count must lead to an value error
|
||||
with self.cam:
|
||||
self.assertRaises(ValueError, self.cam.start_streaming, dummy_frame_handler, 0)
|
||||
self.assertRaises(ValueError, self.cam.start_streaming, dummy_frame_handler, -1)
|
||||
|
||||
def test_camera_streaming(self):
|
||||
# Expectation: A given frame_handler must be executed for each buffered frame.
|
||||
|
||||
class FrameHandler:
|
||||
def __init__(self, frame_count):
|
||||
self.cnt = 0
|
||||
self.frame_count = frame_count
|
||||
self.event = threading.Event()
|
||||
|
||||
def __call__(self, cam: Camera, frame: Frame):
|
||||
self.cnt += 1
|
||||
|
||||
if self.cnt == self.frame_count:
|
||||
self.event.set()
|
||||
|
||||
timeout = 5.0
|
||||
frame_count = 10
|
||||
handler = FrameHandler(frame_count)
|
||||
|
||||
with self.cam:
|
||||
try:
|
||||
self.cam.start_streaming(handler, frame_count)
|
||||
|
||||
# Wait until the FrameHandler has been executed for each queued frame
|
||||
self.assertTrue(handler.event.wait(timeout))
|
||||
|
||||
finally:
|
||||
self.cam.stop_streaming()
|
||||
|
||||
def test_camera_streaming_queue(self):
|
||||
# Expectation: A given frame must be reused if it is enqueued again.
|
||||
|
||||
class FrameHandler:
|
||||
def __init__(self, frame_count):
|
||||
self.cnt = 0
|
||||
self.frame_count = frame_count
|
||||
self.event = threading.Event()
|
||||
|
||||
def __call__(self, cam: Camera, frame: Frame):
|
||||
self.cnt += 1
|
||||
|
||||
if self.cnt == self.frame_count:
|
||||
self.event.set()
|
||||
|
||||
cam.queue_frame(frame)
|
||||
|
||||
timeout = 5.0
|
||||
frame_count = 5
|
||||
frame_reuse = 2
|
||||
handler = FrameHandler(frame_count * frame_reuse)
|
||||
|
||||
with self.cam:
|
||||
try:
|
||||
self.cam.start_streaming(handler, frame_count)
|
||||
|
||||
# Wait until the FrameHandler has been executed for each queued frame
|
||||
self.assertTrue(handler.event.wait(timeout))
|
||||
|
||||
finally:
|
||||
self.cam.stop_streaming()
|
||||
|
||||
def test_camera_runtime_type_check(self):
|
||||
def valid_handler(cam, frame):
|
||||
pass
|
||||
|
||||
def invalid_handler_1(cam):
|
||||
pass
|
||||
|
||||
def invalid_handler_2(cam, frame, extra):
|
||||
pass
|
||||
|
||||
self.assertRaises(TypeError, self.cam.set_access_mode, -1)
|
||||
|
||||
with self.cam:
|
||||
# Expectation: raise TypeError on passing invalid parameters
|
||||
self.assertRaises(TypeError, self.cam.get_frame, 'hi')
|
||||
self.assertRaises(TypeError, self.cam.get_features_affected_by, 'No Feature')
|
||||
self.assertRaises(TypeError, self.cam.get_features_selected_by, 'No Feature')
|
||||
self.assertRaises(TypeError, self.cam.get_features_by_type, 0.0)
|
||||
self.assertRaises(TypeError, self.cam.get_feature_by_name, 0)
|
||||
self.assertRaises(TypeError, self.cam.get_frame_generator, '3')
|
||||
self.assertRaises(TypeError, self.cam.get_frame_generator, 0, 'foo')
|
||||
self.assertRaises(TypeError, self.cam.start_streaming, valid_handler, 'no int')
|
||||
self.assertRaises(TypeError, self.cam.start_streaming, invalid_handler_1)
|
||||
self.assertRaises(TypeError, self.cam.start_streaming, invalid_handler_2)
|
||||
self.assertRaises(TypeError, self.cam.save_settings, 0, PersistType.All)
|
||||
self.assertRaises(TypeError, self.cam.save_settings, 'foo.xml', 'false type')
|
||||
|
||||
def test_camera_save_load_settings(self):
|
||||
# Expectation: After settings export a settings change must be reverted by loading a
|
||||
# Previously saved configuration.
|
||||
|
||||
file_name = 'test_save_load_settings.xml'
|
||||
|
||||
with self.cam:
|
||||
feat_height = self.cam.get_feature_by_name('Height')
|
||||
old_val = feat_height.get()
|
||||
|
||||
self.cam.save_settings(file_name, PersistType.All)
|
||||
|
||||
min_, max_ = feat_height.get_range()
|
||||
inc = feat_height.get_increment()
|
||||
|
||||
feat_height.set(max_ - min_ - inc)
|
||||
|
||||
self.cam.load_settings(file_name, PersistType.All)
|
||||
os.remove(file_name)
|
||||
|
||||
self.assertEqual(old_val, feat_height.get())
|
||||
|
||||
def test_camera_save_settings_verify_path(self):
|
||||
# Expectation: Valid files end with .xml and can be either a absolute path or relative
|
||||
# path to the given File. Everything else is a ValueError.
|
||||
|
||||
valid_paths = (
|
||||
'valid1.xml',
|
||||
os.path.join('.', 'valid2.xml'),
|
||||
os.path.join('Tests', 'valid3.xml'),
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'valid4.xml'),
|
||||
)
|
||||
|
||||
with self.cam:
|
||||
self.assertRaises(ValueError, self.cam.save_settings, 'inval.xm', PersistType.All)
|
||||
|
||||
for path in valid_paths:
|
||||
self.assertNoRaise(self.cam.save_settings, path, PersistType.All)
|
||||
os.remove(path)
|
||||
|
||||
def test_camera_load_settings_verify_path(self):
|
||||
# Expectation: Valid files end with .xml and must exist before before any execution.
|
||||
valid_paths = (
|
||||
'valid1.xml',
|
||||
os.path.join('.', 'valid2.xml'),
|
||||
os.path.join('Tests', 'valid3.xml'),
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'valid4.xml'),
|
||||
)
|
||||
|
||||
with self.cam:
|
||||
self.assertRaises(ValueError, self.cam.load_settings, 'inval.xm', PersistType.All)
|
||||
|
||||
for path in valid_paths:
|
||||
self.assertRaises(ValueError, self.cam.load_settings, path, PersistType.All)
|
||||
|
||||
for path in valid_paths:
|
||||
self.cam.save_settings(path, PersistType.All)
|
||||
|
||||
self.assertNoRaise(self.cam.load_settings, path, PersistType.All)
|
||||
os.remove(path)
|
||||
|
||||
def test_camera_context_manager_reentrancy(self):
|
||||
# Expectation: Camera Context Manager must be reentrant. Multiple calls to _open
|
||||
# must be prevented (would cause VimbaC - Error)
|
||||
with self.cam:
|
||||
with self.cam:
|
||||
with self.cam:
|
||||
pass
|
||||
|
||||
def test_camera_api_context_sensitity_outside_context(self):
|
||||
# Expectation: Call set_access_mode withing with scope must raise a RuntimeError
|
||||
with self.cam:
|
||||
self.assertRaises(RuntimeError, self.cam.set_access_mode)
|
||||
|
||||
def test_camera_api_context_sensitity_inside_context(self):
|
||||
# Expectation: Most Camera related functions are only valid then called within the given
|
||||
# Context. If called from Outside a runtime error must be raised.
|
||||
self.assertRaises(RuntimeError, self.cam.read_memory)
|
||||
self.assertRaises(RuntimeError, self.cam.write_memory)
|
||||
self.assertRaises(RuntimeError, self.cam.read_registers)
|
||||
self.assertRaises(RuntimeError, self.cam.write_registers)
|
||||
self.assertRaises(RuntimeError, self.cam.get_all_features)
|
||||
self.assertRaises(RuntimeError, self.cam.get_features_affected_by)
|
||||
self.assertRaises(RuntimeError, self.cam.get_features_selected_by)
|
||||
self.assertRaises(RuntimeError, self.cam.get_features_by_type)
|
||||
self.assertRaises(RuntimeError, self.cam.get_features_by_category)
|
||||
self.assertRaises(RuntimeError, self.cam.get_feature_by_name)
|
||||
self.assertRaises(RuntimeError, self.cam.get_frame_generator)
|
||||
self.assertRaises(RuntimeError, self.cam.get_frame)
|
||||
self.assertRaises(RuntimeError, self.cam.start_streaming)
|
||||
self.assertRaises(RuntimeError, self.cam.stop_streaming)
|
||||
self.assertRaises(RuntimeError, self.cam.queue_frame)
|
||||
self.assertRaises(RuntimeError, self.cam.get_pixel_formats)
|
||||
self.assertRaises(RuntimeError, self.cam.get_pixel_format)
|
||||
self.assertRaises(RuntimeError, self.cam.set_pixel_format)
|
||||
self.assertRaises(RuntimeError, self.cam.save_settings)
|
||||
self.assertRaises(RuntimeError, self.cam.load_settings)
|
||||
@@ -0,0 +1,831 @@
|
||||
"""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 unittest
|
||||
import threading
|
||||
|
||||
from vimba import *
|
||||
from vimba.feature import *
|
||||
|
||||
|
||||
class CamBaseFeatureTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.vimba = Vimba.get_instance()
|
||||
self.vimba._startup()
|
||||
|
||||
try:
|
||||
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to lookup Camera.') from e
|
||||
|
||||
try:
|
||||
self.cam._open()
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to open Camera.') from e
|
||||
|
||||
try:
|
||||
self.height = self.cam.get_feature_by_name('Height')
|
||||
|
||||
except VimbaCameraError:
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
self.skipTest('Required Feature \'Height\' not available.')
|
||||
|
||||
def tearDown(self):
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
|
||||
def test_get_name(self):
|
||||
# Expectation: Return decoded FeatureName
|
||||
self.assertEqual(self.height.get_name(), 'Height')
|
||||
|
||||
def test_get_flags(self):
|
||||
# Expectation: Return decoded FeatureFlags
|
||||
self.assertEqual(self.height.get_flags(), (FeatureFlags.Read, FeatureFlags.Write))
|
||||
|
||||
def test_get_category(self):
|
||||
# Expectation: Return decoded category
|
||||
self.assertNotEqual(self.height.get_category(), '')
|
||||
|
||||
def test_get_display_name(self):
|
||||
# Expectation: Return decoded category
|
||||
self.assertEqual(self.height.get_display_name(), 'Height')
|
||||
|
||||
def test_get_polling_time(self):
|
||||
# Expectation: Return polling time. Only volatile features return
|
||||
# anything other than zero.
|
||||
self.assertEqual(self.height.get_polling_time(), 0)
|
||||
|
||||
def test_get_unit(self):
|
||||
# Expectation: If Unit exists, return unit else return ''
|
||||
self.assertEqual(self.height.get_unit(), '')
|
||||
|
||||
def test_get_representation(self):
|
||||
# Expectation: Get numeric representation if existing else ''
|
||||
self.assertEqual(self.height.get_representation(), '')
|
||||
|
||||
def test_get_visibility(self):
|
||||
# Expectation: Get UI Visibility
|
||||
self.assertEqual(self.height.get_visibility(), FeatureVisibility.Beginner)
|
||||
|
||||
def test_get_tooltip(self):
|
||||
# Expectation: Shall not raise anything
|
||||
self.assertNoRaise(self.height.get_tooltip)
|
||||
|
||||
def test_get_description(self):
|
||||
# Expectation: Get decoded description
|
||||
self.assertNotEqual(self.height.get_description(), '')
|
||||
|
||||
def test_get_sfnc_namespace(self):
|
||||
# Expectation: Get decoded sfnc namespace
|
||||
self.assertNotEqual(self.height.get_sfnc_namespace(), '')
|
||||
|
||||
def test_is_streamable(self):
|
||||
# Expectation: Streamable features shall return True, others False
|
||||
self.assertNoRaise(self.height.is_streamable)
|
||||
|
||||
def test_has_affected_features(self):
|
||||
# Expectation:Features that affect features shall return True, others False
|
||||
self.assertTrue(self.height.has_affected_features())
|
||||
|
||||
def test_has_selected_features(self):
|
||||
# Expectation:Features that select features shall return True, others False
|
||||
self.assertFalse(self.height.has_selected_features())
|
||||
|
||||
def test_get_access_mode(self):
|
||||
# Expectation: Read/Write Features return (True, True), ReadOnly return (True, False)
|
||||
self.assertEqual(self.height.get_access_mode(), (True, True))
|
||||
|
||||
def test_is_readable(self):
|
||||
# Expectation: True if feature grant read access else False
|
||||
self.assertTrue(self.height.is_readable())
|
||||
|
||||
def test_is_writeable(self):
|
||||
# Expectation: True if feature grant write access else False
|
||||
self.assertTrue(self.height.is_writeable())
|
||||
|
||||
def test_change_handler(self):
|
||||
# Expectation: A given change handler is executed on value change.
|
||||
# Adding the same handler multiple times shall not lead to multiple executions.
|
||||
# The same goes for double unregister.
|
||||
|
||||
class Handler:
|
||||
def __init__(self):
|
||||
self.event = threading.Event()
|
||||
self.call_cnt = 0
|
||||
|
||||
def __call__(self, feat):
|
||||
self.call_cnt += 1
|
||||
self.event.set()
|
||||
|
||||
handler = Handler()
|
||||
|
||||
self.height.register_change_handler(handler)
|
||||
self.height.register_change_handler(handler)
|
||||
|
||||
tmp = self.height.get()
|
||||
|
||||
min_, _ = self.height.get_range()
|
||||
inc = self.height.get_increment()
|
||||
|
||||
if min_ <= tmp - inc:
|
||||
self.height.set(tmp - inc)
|
||||
|
||||
else:
|
||||
self.height.set(tmp + inc)
|
||||
|
||||
handler.event.wait()
|
||||
|
||||
self.height.unregister_change_handler(handler)
|
||||
self.height.unregister_change_handler(handler)
|
||||
|
||||
self.height.set(tmp)
|
||||
|
||||
self.assertEqual(handler.call_cnt, 1)
|
||||
|
||||
def test_stringify_features(self):
|
||||
# Expectation: Each Feature must have a __str__ method. Depending on the Feature
|
||||
# current Values are queried, this can fail. In those cases, all exceptions are
|
||||
# fetched -> all features must be strinify able without raising any exception
|
||||
for feat in self.vimba.get_all_features():
|
||||
self.assertNoRaise(str, feat)
|
||||
|
||||
for feat in self.cam.get_all_features():
|
||||
self.assertNoRaise(str, feat)
|
||||
|
||||
|
||||
class CamBoolFeatureTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.vimba = Vimba.get_instance()
|
||||
self.vimba._startup()
|
||||
|
||||
try:
|
||||
self.feat = self.vimba.get_feature_by_name('UsbTLIsPresent')
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.vimba._shutdown()
|
||||
self.skipTest('Required Feature \'UsbTLIsPresent\' not available.')
|
||||
|
||||
def tearDown(self):
|
||||
self.vimba._shutdown()
|
||||
|
||||
def test_get_type(self):
|
||||
# Expectation: BoolFeature must return BoolFeature on get_type
|
||||
self.assertEqual(self.feat.get_type(), BoolFeature)
|
||||
|
||||
def test_get(self):
|
||||
# Expectation: returns current boolean value.
|
||||
self.assertNoRaise(self.feat.get)
|
||||
|
||||
def test_set(self):
|
||||
# Expectation: Raises invalid Access on non-writeable features.
|
||||
self.assertRaises(VimbaFeatureError, self.feat.set, True)
|
||||
|
||||
|
||||
class CamCommandFeatureTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.vimba = Vimba.get_instance()
|
||||
self.vimba._startup()
|
||||
|
||||
try:
|
||||
self.feat = self.vimba.get_feature_by_name('ActionCommand')
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.vimba._shutdown()
|
||||
self.skipTest('Required Feature \'ActionCommand\' not available.')
|
||||
|
||||
def tearDown(self):
|
||||
self.vimba._shutdown()
|
||||
|
||||
def test_get_type(self):
|
||||
# Expectation: CommandFeature must return CommandFeature on get_type
|
||||
self.assertEqual(self.feat.get_type(), CommandFeature)
|
||||
|
||||
|
||||
class CamEnumFeatureTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.vimba = Vimba.get_instance()
|
||||
self.vimba._startup()
|
||||
|
||||
try:
|
||||
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to lookup Camera.') from e
|
||||
|
||||
try:
|
||||
self.cam._open()
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to open Camera.') from e
|
||||
|
||||
try:
|
||||
self.feat_r = self.cam.get_feature_by_name('DeviceScanType')
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
self.skipTest('Required Feature \'DeviceScanType\' not available.')
|
||||
|
||||
try:
|
||||
self.feat_rw = self.cam.get_feature_by_name('AcquisitionMode')
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
self.skipTest('Required Feature \'AcquisitionMode\' not available.')
|
||||
|
||||
def tearDown(self):
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
|
||||
def test_get_type(self):
|
||||
# Expectation: EnumFeature must return EnumFeature on get_type
|
||||
self.assertEqual(self.feat_r.get_type(), EnumFeature)
|
||||
self.assertEqual(self.feat_rw.get_type(), EnumFeature)
|
||||
|
||||
def test_entry_as_bytes(self):
|
||||
# Expectation: Get EnumEntry as encoded byte sequence
|
||||
expected = b'MultiFrame'
|
||||
entry = self.feat_rw.get_entry('MultiFrame')
|
||||
|
||||
self.assertEqual(bytes(entry), expected)
|
||||
|
||||
def test_entry_as_tuple(self):
|
||||
# Expectation: Get EnumEntry as (str, int)
|
||||
entry = self.feat_rw.get_entry('MultiFrame')
|
||||
self.assertEqual(entry.as_tuple(), self.feat_rw.get_entry(int(entry)).as_tuple())
|
||||
|
||||
def test_get_all_entries(self):
|
||||
# Expectation: Get all possible enum entries regardless of the availability
|
||||
expected = (self.feat_r.get_entry('Areascan'),)
|
||||
|
||||
for e in expected:
|
||||
self.assertIn(e, self.feat_r.get_all_entries())
|
||||
|
||||
expected = (
|
||||
self.feat_rw.get_entry('SingleFrame'),
|
||||
self.feat_rw.get_entry('MultiFrame'),
|
||||
self.feat_rw.get_entry('Continuous')
|
||||
)
|
||||
|
||||
for e in expected:
|
||||
self.assertIn(e, self.feat_rw.get_all_entries())
|
||||
|
||||
def test_get_avail_entries(self):
|
||||
# Expectation: All returned enum entries must be available
|
||||
for e in self.feat_r.get_available_entries():
|
||||
self.assertTrue(e.is_available())
|
||||
|
||||
for e in self.feat_rw.get_available_entries():
|
||||
self.assertTrue(e.is_available())
|
||||
|
||||
def test_get_entry_int(self):
|
||||
# Expectation: Lookup a given entry by using an int as key.
|
||||
# Invalid keys must return VimbaFeatureError.
|
||||
|
||||
expected = self.feat_r.get_all_entries()[0]
|
||||
self.assertEqual(self.feat_r.get_entry(int(expected)), expected)
|
||||
|
||||
expected = self.feat_rw.get_all_entries()[1]
|
||||
self.assertEqual(self.feat_rw.get_entry(int(expected)), expected)
|
||||
|
||||
self.assertRaises(VimbaFeatureError, self.feat_r.get_entry, -1)
|
||||
self.assertRaises(VimbaFeatureError, self.feat_rw.get_entry, -1)
|
||||
|
||||
def test_get_entry_str(self):
|
||||
# Expectation: Lookup a given entry by using a str as key.
|
||||
# Invalid keys must return VimbaFeatureError.
|
||||
|
||||
expected = self.feat_r.get_all_entries()[0]
|
||||
self.assertEqual(self.feat_r.get_entry(str(expected)), expected)
|
||||
|
||||
expected = self.feat_rw.get_all_entries()[1]
|
||||
self.assertEqual(self.feat_rw.get_entry(str(expected)), expected)
|
||||
|
||||
self.assertRaises(VimbaFeatureError, self.feat_r.get_entry, 'Should be invalid')
|
||||
self.assertRaises(VimbaFeatureError, self.feat_rw.get_entry, 'Should be invalid')
|
||||
|
||||
def test_get(self):
|
||||
# Expectation: Get must return the current value.
|
||||
self.assertNoRaise(self.feat_r.get)
|
||||
self.assertNoRaise(self.feat_rw.get)
|
||||
|
||||
def test_set_entry(self):
|
||||
# Expectation: Set given enum entry if feature is writable.
|
||||
# Raises:
|
||||
# - VimbaFeatureError if enum entry is from other enum feature.
|
||||
# - VimbaFeatureError if feature is read only
|
||||
|
||||
# Read Only Feature
|
||||
entry = self.feat_r.get_all_entries()[0]
|
||||
self.assertRaises(VimbaFeatureError, self.feat_r.set, entry)
|
||||
|
||||
# Read/Write Feature
|
||||
old_entry = self.feat_rw.get()
|
||||
|
||||
try:
|
||||
# Normal operation
|
||||
self.assertNoRaise(self.feat_rw.set, self.feat_rw.get_entry(2))
|
||||
self.assertEqual(self.feat_rw.get(), self.feat_rw.get_entry(2))
|
||||
|
||||
# Provoke FeatureError by setting the feature from the ReadOnly entry.
|
||||
self.assertRaises(VimbaFeatureError, self.feat_rw.set, entry)
|
||||
|
||||
finally:
|
||||
self.feat_rw.set(old_entry)
|
||||
|
||||
def test_set_str(self):
|
||||
# Expectation: Set given enum entry string value if feature is writable.
|
||||
# Raises:
|
||||
# - VimbaFeatureError if given string is not associated with this feature.
|
||||
# - VimbaFeatureError if feature is read only
|
||||
|
||||
# Read Only Feature
|
||||
self.assertRaises(VimbaFeatureError, self.feat_r.set, str(self.feat_r.get_entry(0)))
|
||||
|
||||
# Read/Write Feature
|
||||
old_entry = self.feat_rw.get()
|
||||
|
||||
try:
|
||||
# Normal operation
|
||||
self.assertNoRaise(self.feat_rw.set, str(self.feat_rw.get_entry(2)))
|
||||
self.assertEqual(self.feat_rw.get(), self.feat_rw.get_entry(2))
|
||||
|
||||
# Provoke FeatureError by an invalid enum value
|
||||
self.assertRaises(VimbaFeatureError, self.feat_rw.set, 'Hopefully invalid')
|
||||
|
||||
finally:
|
||||
self.feat_rw.set(old_entry)
|
||||
|
||||
def test_set_int(self):
|
||||
# Expectation: Set given enum entry int value if feature is writable.
|
||||
# Raises:
|
||||
# - VimbaFeatureError if given int is not associated with this feature.
|
||||
# - VimbaFeatureError if feature is read only
|
||||
|
||||
# Read Only Feature
|
||||
self.assertRaises(VimbaFeatureError, self.feat_r.set, int(self.feat_r.get_entry(0)))
|
||||
|
||||
# Read/Write Feature
|
||||
old_entry = self.feat_rw.get()
|
||||
|
||||
try:
|
||||
# Normal operation
|
||||
self.assertNoRaise(self.feat_rw.set, int(self.feat_rw.get_entry(2)))
|
||||
self.assertEqual(self.feat_rw.get(), self.feat_rw.get_entry(2))
|
||||
|
||||
# Provoke FeatureError by an invalid enum value
|
||||
self.assertRaises(VimbaFeatureError, self.feat_rw.set, -23)
|
||||
|
||||
finally:
|
||||
self.feat_rw.set(old_entry)
|
||||
|
||||
def test_set_in_callback(self):
|
||||
# Expected behavior: A set operation within a change handler must
|
||||
# Raise a VimbaFeatureError to prevent an endless handler execution.
|
||||
|
||||
class Handler:
|
||||
def __init__(self):
|
||||
self.raised = False
|
||||
self.event = threading.Event()
|
||||
|
||||
def __call__(self, feat):
|
||||
try:
|
||||
feat.set(feat.get())
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.raised = True
|
||||
|
||||
self.event.set()
|
||||
|
||||
old_entry = self.feat_rw.get()
|
||||
|
||||
try:
|
||||
handler = Handler()
|
||||
self.feat_rw.register_change_handler(handler)
|
||||
|
||||
# Trigger change handler and wait for callback execution.
|
||||
self.feat_rw.set(self.feat_rw.get())
|
||||
handler.event.wait()
|
||||
|
||||
self.assertTrue(handler.raised)
|
||||
|
||||
finally:
|
||||
self.feat_rw.unregister_change_handler(handler)
|
||||
self.feat_rw.set(old_entry)
|
||||
|
||||
|
||||
class CamFloatFeatureTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.vimba = Vimba.get_instance()
|
||||
self.vimba._startup()
|
||||
|
||||
try:
|
||||
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to lookup Camera.') from e
|
||||
|
||||
try:
|
||||
self.cam._open()
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to open Camera.') from e
|
||||
|
||||
try:
|
||||
self.feat_r = self.vimba.get_feature_by_name('Elapsed')
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
self.skipTest('Required Feature \'Elapsed\' not available.')
|
||||
|
||||
try:
|
||||
self.feat_rw = self.cam.get_feature_by_name('ExposureTime')
|
||||
|
||||
except VimbaFeatureError:
|
||||
# Some Cameras name ExposureTime as ExposureTimeAbs
|
||||
try:
|
||||
self.feat_rw = self.cam.get_feature_by_name('ExposureTimeAbs')
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
self.skipTest('Required Feature \'ExposureTime\' not available.')
|
||||
|
||||
def tearDown(self):
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
|
||||
def test_get_type(self):
|
||||
# Expectation: FloatFeature returns FloatFeature on get_type.
|
||||
self.assertEqual(self.feat_r.get_type(), FloatFeature)
|
||||
self.assertEqual(self.feat_rw.get_type(), FloatFeature)
|
||||
|
||||
def test_get(self):
|
||||
# Expectation: Get current value.
|
||||
|
||||
self.assertNoRaise(self.feat_r.get)
|
||||
self.assertNoRaise(self.feat_rw.get)
|
||||
|
||||
def test_get_range(self):
|
||||
# Expectation: Get value range. Raise VimbaFeatureError on non-read access.
|
||||
self.assertNoRaise(self.feat_r.get_range)
|
||||
self.assertNoRaise(self.feat_rw.get_range)
|
||||
|
||||
def test_get_increment(self):
|
||||
# Expectation: Get value increment if existing. If this Feature has no
|
||||
# increment, None is returned.
|
||||
|
||||
self.assertNoRaise(self.feat_r.get_increment)
|
||||
self.assertNoRaise(self.feat_rw.get_increment)
|
||||
|
||||
def test_set(self):
|
||||
# Expectation: Set value. Errors:
|
||||
# VimbaFeatureError if access right are not writable
|
||||
# VimbaFeatureError if value is out of bounds
|
||||
|
||||
# Read only feature
|
||||
self.assertRaises(VimbaFeatureError, self.feat_r.set, 0.0)
|
||||
|
||||
# Read/Write Feature
|
||||
old_value = self.feat_rw.get()
|
||||
|
||||
try:
|
||||
delta = 0.1
|
||||
|
||||
# Range test
|
||||
min_, max_ = self.feat_rw.get_range()
|
||||
|
||||
# Within bounds (no error)
|
||||
self.assertNoRaise(self.feat_rw.set, min_)
|
||||
self.assertAlmostEqual(self.feat_rw.get(), min_)
|
||||
self.assertNoRaise(self.feat_rw.set, max_)
|
||||
self.assertAlmostEqual(self.feat_rw.get(), max_)
|
||||
|
||||
# Out of bounds (must raise)
|
||||
self.assertRaises(VimbaFeatureError, self.feat_rw.set, min_ - delta)
|
||||
self.assertRaises(VimbaFeatureError, self.feat_rw.set, max_ + delta)
|
||||
|
||||
finally:
|
||||
self.feat_rw.set(old_value)
|
||||
|
||||
def test_set_in_callback(self):
|
||||
# Expectation: Calling set within change_handler must raise an VimbaFeatureError
|
||||
|
||||
class Handler:
|
||||
def __init__(self):
|
||||
self.raised = False
|
||||
self.event = threading.Event()
|
||||
|
||||
def __call__(self, feat):
|
||||
try:
|
||||
feat.set(feat.get())
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.raised = True
|
||||
|
||||
self.event.set()
|
||||
|
||||
old_entry = self.feat_rw.get()
|
||||
|
||||
try:
|
||||
handler = Handler()
|
||||
self.feat_rw.register_change_handler(handler)
|
||||
|
||||
# Trigger change handler and wait for callback execution.
|
||||
self.feat_rw.set(self.feat_rw.get())
|
||||
handler.event.wait()
|
||||
|
||||
self.assertTrue(handler.raised)
|
||||
|
||||
finally:
|
||||
self.feat_rw.unregister_change_handler(handler)
|
||||
self.feat_rw.set(old_entry)
|
||||
|
||||
|
||||
class CamIntFeatureTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.vimba = Vimba.get_instance()
|
||||
self.vimba._startup()
|
||||
|
||||
try:
|
||||
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to lookup Camera.') from e
|
||||
|
||||
try:
|
||||
self.cam._open()
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to open Camera.') from e
|
||||
|
||||
try:
|
||||
self.feat_r = self.cam.get_feature_by_name('HeightMax')
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
self.skipTest('Required Feature \'HeightMax\' not available.')
|
||||
|
||||
try:
|
||||
self.feat_rw = self.cam.get_feature_by_name('Height')
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
self.skipTest('Required Feature \'Height\' not available.')
|
||||
|
||||
def tearDown(self):
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
|
||||
def test_get_type(self):
|
||||
# Expectation: IntFeature must return IntFeature on get_type
|
||||
self.assertEqual(self.feat_r.get_type(), IntFeature)
|
||||
self.assertEqual(self.feat_rw.get_type(), IntFeature)
|
||||
|
||||
def test_get(self):
|
||||
# Expectation: Get current value
|
||||
|
||||
self.assertNoRaise(self.feat_r.get)
|
||||
self.assertNoRaise(self.feat_rw.get)
|
||||
|
||||
def test_get_range(self):
|
||||
# Expectation: Get range of accepted values
|
||||
self.assertNoRaise(self.feat_r.get_range)
|
||||
self.assertNoRaise(self.feat_rw.get_range)
|
||||
|
||||
def test_get_increment(self):
|
||||
# Expectation: Get step between valid values
|
||||
self.assertNoRaise(self.feat_r.get_increment)
|
||||
self.assertNoRaise(self.feat_rw.get_increment)
|
||||
|
||||
def test_set(self):
|
||||
# Expectation: Set value or raise VimbaFeatureError under the following conditions.
|
||||
# 1) Invalid Access Rights
|
||||
# 2) Misaligned value.
|
||||
# 3) Out-of-bounds Access
|
||||
|
||||
# Read only feature
|
||||
self.assertRaises(VimbaFeatureError, self.feat_r.set, 0)
|
||||
|
||||
# Writable feature
|
||||
old_value = self.feat_rw.get()
|
||||
|
||||
try:
|
||||
inc = self.feat_rw.get_increment()
|
||||
min_, max_ = self.feat_rw.get_range()
|
||||
|
||||
# Normal usage
|
||||
self.assertNoRaise(self.feat_rw.set, min_)
|
||||
self.assertEqual(self.feat_rw.get(), min_)
|
||||
self.assertNoRaise(self.feat_rw.set, max_)
|
||||
self.assertEqual(self.feat_rw.get(), max_)
|
||||
|
||||
# Out of bounds access.
|
||||
self.assertRaises(VimbaFeatureError, self.feat_rw.set, min_ - inc)
|
||||
self.assertRaises(VimbaFeatureError, self.feat_rw.set, max_ + inc)
|
||||
|
||||
finally:
|
||||
self.feat_rw.set(old_value)
|
||||
|
||||
def test_set_in_callback(self):
|
||||
# Expectation: Setting a value within a Callback must raise a VimbaFeatureError
|
||||
|
||||
class Handler:
|
||||
def __init__(self):
|
||||
self.raised = False
|
||||
self.event = threading.Event()
|
||||
|
||||
def __call__(self, feat):
|
||||
try:
|
||||
feat.set(feat.get())
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.raised = True
|
||||
|
||||
self.event.set()
|
||||
|
||||
old_entry = self.feat_rw.get()
|
||||
|
||||
try:
|
||||
handler = Handler()
|
||||
self.feat_rw.register_change_handler(handler)
|
||||
|
||||
# Trigger change handler and wait for callback execution.
|
||||
min_, _ = self.feat_rw.get_range()
|
||||
inc = self.feat_rw.get_increment()
|
||||
|
||||
if min_ <= (old_entry - inc):
|
||||
self.feat_rw.set(old_entry - inc)
|
||||
|
||||
else:
|
||||
self.feat_rw.set(old_entry + inc)
|
||||
|
||||
handler.event.wait()
|
||||
|
||||
self.assertTrue(handler.raised)
|
||||
|
||||
finally:
|
||||
self.feat_rw.unregister_change_handler(handler)
|
||||
self.feat_rw.set(old_entry)
|
||||
|
||||
|
||||
class CamStringFeatureTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.vimba = Vimba.get_instance()
|
||||
self.vimba._startup()
|
||||
|
||||
try:
|
||||
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to lookup Camera.') from e
|
||||
|
||||
try:
|
||||
self.cam._open()
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to open Camera.') from e
|
||||
|
||||
self.feat_r = None
|
||||
feats = self.cam.get_features_by_type(StringFeature)
|
||||
|
||||
for feat in feats:
|
||||
if feat.get_access_mode() == (True, False):
|
||||
self.feat_r = feat
|
||||
|
||||
if self.feat_r is None:
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
self.skipTest('Test requires read only StringFeature.')
|
||||
|
||||
self.feat_rw = None
|
||||
feats = self.cam.get_features_by_type(StringFeature)
|
||||
|
||||
for feat in feats:
|
||||
if feat.get_access_mode() == (True, True):
|
||||
self.feat_rw = feat
|
||||
|
||||
if self.feat_rw is None:
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
self.skipTest('Test requires read/write StringFeature.')
|
||||
|
||||
def tearDown(self):
|
||||
self.cam._close()
|
||||
self.vimba._shutdown()
|
||||
|
||||
def test_get_type(self):
|
||||
# Expectation: StringFeature must return StringFeature on get_type
|
||||
self.assertEqual(self.feat_r.get_type(), StringFeature)
|
||||
self.assertEqual(self.feat_rw.get_type(), StringFeature)
|
||||
|
||||
def test_get(self):
|
||||
# Expectation: Get current value without raising an exception
|
||||
self.assertNoRaise(self.feat_r.get)
|
||||
self.assertNoRaise(self.feat_rw.get)
|
||||
|
||||
def test_get_max_length(self):
|
||||
# Expectation: Get maximum string length
|
||||
self.assertNoRaise(self.feat_r.get_max_length)
|
||||
self.assertNoRaise(self.feat_rw.get_max_length)
|
||||
|
||||
def test_set(self):
|
||||
# Expectation:
|
||||
# 1) Setting a read only feature must raise a VimbaFeatureError
|
||||
# 2) Setting a read/wrtie must raise VimbaFeatureError if the string is
|
||||
# longer than max length
|
||||
# 3) Setting a read/write feature must work if string is long enough
|
||||
|
||||
# Ensure Expectation 1
|
||||
self.assertRaises(VimbaFeatureError, self.feat_r.set, self.feat_r.get())
|
||||
self.assertNoRaise(self.feat_rw.set, self.feat_rw.get())
|
||||
|
||||
# Ensure Expectation 2
|
||||
old_val = self.feat_rw.get()
|
||||
|
||||
try:
|
||||
invalid = 'a' * self.feat_rw.get_max_length()
|
||||
self.assertRaises(VimbaFeatureError, self.feat_rw.set, invalid)
|
||||
|
||||
finally:
|
||||
self.feat_rw.set(old_val)
|
||||
|
||||
# Ensure Expectation 3
|
||||
try:
|
||||
valid = 'a' * (self.feat_rw.get_max_length() - 1)
|
||||
self.assertNoRaise(self.feat_rw.set, valid)
|
||||
self.assertEqual(valid, self.feat_rw.get())
|
||||
|
||||
finally:
|
||||
self.feat_rw.set(old_val)
|
||||
|
||||
def test_set_in_callback(self):
|
||||
# Expectation: Setting a value within a Callback must raise a VimbaFeatureError
|
||||
|
||||
class Handler:
|
||||
def __init__(self):
|
||||
self.raised = False
|
||||
self.event = threading.Event()
|
||||
|
||||
def __call__(self, feat):
|
||||
try:
|
||||
feat.set(feat.get())
|
||||
|
||||
except VimbaFeatureError:
|
||||
self.raised = True
|
||||
|
||||
self.event.set()
|
||||
|
||||
try:
|
||||
handler = Handler()
|
||||
self.feat_rw.register_change_handler(handler)
|
||||
|
||||
self.feat_rw.set(self.feat_rw.get())
|
||||
|
||||
handler.event.wait()
|
||||
|
||||
self.assertTrue(handler.raised)
|
||||
|
||||
finally:
|
||||
self.feat_rw.unregister_change_handler(handler)
|
||||
206
Vimba_6_0/VimbaPython/Source/Tests/real_cam_tests/frame_test.py
Normal file
206
Vimba_6_0/VimbaPython/Source/Tests/real_cam_tests/frame_test.py
Normal file
@@ -0,0 +1,206 @@
|
||||
"""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 unittest
|
||||
import copy
|
||||
import ctypes
|
||||
|
||||
from vimba import *
|
||||
from vimba.frame import *
|
||||
|
||||
|
||||
class CamFrameTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.vimba = Vimba.get_instance()
|
||||
self.vimba._startup()
|
||||
|
||||
try:
|
||||
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
|
||||
|
||||
except VimbaCameraError as e:
|
||||
self.vimba._shutdown()
|
||||
raise Exception('Failed to lookup Camera.') from e
|
||||
|
||||
def tearDown(self):
|
||||
self.vimba._shutdown()
|
||||
|
||||
def test_verify_buffer(self):
|
||||
# Expectation: A Frame buffer shall have exactly the specified size on construction.
|
||||
# Allocation is performed by VimbaPython
|
||||
self.assertEqual(Frame(0, AllocationMode.AnnounceFrame).get_buffer_size(), 0)
|
||||
self.assertEqual(Frame(1024, AllocationMode.AnnounceFrame).get_buffer_size(), 1024)
|
||||
self.assertEqual(Frame(1024 * 1024, AllocationMode.AnnounceFrame).get_buffer_size(),
|
||||
1024 * 1024)
|
||||
|
||||
def test_verify_no_copy_empty_buffer_access(self):
|
||||
# Expectation: Accessing the internal buffer must not create a copy
|
||||
# frame._buffer is only set on construction if buffer is allocated by VimbaPython
|
||||
frame = Frame(10, AllocationMode.AnnounceFrame)
|
||||
self.assertEqual(id(frame._buffer), id(frame.get_buffer()))
|
||||
|
||||
def test_verify_no_copy_filled_buffer_access(self):
|
||||
# Expectation: Accessing the internal buffer must not create a copy
|
||||
for allocation_mode in AllocationMode:
|
||||
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
|
||||
with self.cam:
|
||||
frame = self.cam.get_frame(allocation_mode=allocation_mode)
|
||||
self.assertEqual(id(frame._buffer), id(frame.get_buffer()))
|
||||
|
||||
def test_get_id(self):
|
||||
# Expectation: get_id() must return None if Its locally constructed
|
||||
# else it must return the frame id.
|
||||
for allocation_mode in AllocationMode:
|
||||
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
|
||||
self.assertIsNone(Frame(0, allocation_mode).get_id())
|
||||
|
||||
with self.cam:
|
||||
self.assertIsNotNone(
|
||||
self.cam.get_frame(allocation_mode=allocation_mode).get_id())
|
||||
|
||||
def test_get_timestamp(self):
|
||||
# Expectation: get_timestamp() must return None if Its locally constructed
|
||||
# else it must return the timestamp.
|
||||
for allocation_mode in AllocationMode:
|
||||
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
|
||||
self.assertIsNone(Frame(0, allocation_mode).get_timestamp())
|
||||
|
||||
with self.cam:
|
||||
self.assertIsNotNone(
|
||||
self.cam.get_frame(allocation_mode=allocation_mode).get_timestamp())
|
||||
|
||||
def test_get_offset(self):
|
||||
# Expectation: get_offset_x() must return None if Its locally constructed
|
||||
# else it must return the offset as int. Same goes for get_offset_y()
|
||||
for allocation_mode in AllocationMode:
|
||||
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
|
||||
self.assertIsNone(Frame(0, allocation_mode).get_offset_x())
|
||||
self.assertIsNone(Frame(0, allocation_mode).get_offset_y())
|
||||
|
||||
with self.cam:
|
||||
frame = self.cam.get_frame(allocation_mode=allocation_mode)
|
||||
self.assertIsNotNone(frame.get_offset_x())
|
||||
self.assertIsNotNone(frame.get_offset_y())
|
||||
|
||||
def test_get_dimension(self):
|
||||
# Expectation: get_width() must return None if Its locally constructed
|
||||
# else it must return the offset as int. Same goes for get_height()
|
||||
for allocation_mode in AllocationMode:
|
||||
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
|
||||
self.assertIsNone(Frame(0, allocation_mode).get_width())
|
||||
self.assertIsNone(Frame(0, allocation_mode).get_height())
|
||||
|
||||
with self.cam:
|
||||
frame = self.cam.get_frame(allocation_mode=allocation_mode)
|
||||
self.assertIsNotNone(frame.get_width())
|
||||
self.assertIsNotNone(frame.get_height())
|
||||
|
||||
def test_get_image_size(self):
|
||||
# Expectation: get_image_size() must return 0 if locally constructed
|
||||
# else it must return the image_size as int.
|
||||
for allocation_mode in AllocationMode:
|
||||
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
|
||||
self.assertEquals(Frame(0, allocation_mode).get_image_size(), 0)
|
||||
|
||||
with self.cam:
|
||||
self.assertNotEquals(
|
||||
self.cam.get_frame(allocation_mode=allocation_mode).get_image_size(), 0)
|
||||
|
||||
def test_deepcopy(self):
|
||||
# Expectation: a deepcopy must clone the frame buffer with its contents an
|
||||
# update the internally store pointer in VmbFrame struct.
|
||||
for allocation_mode in AllocationMode:
|
||||
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
|
||||
with self.cam:
|
||||
frame = self.cam.get_frame(allocation_mode=allocation_mode)
|
||||
|
||||
frame_cpy = copy.deepcopy(frame)
|
||||
|
||||
# Ensure frames and their members are not the same object
|
||||
self.assertNotEquals(id(frame), id(frame_cpy))
|
||||
self.assertNotEquals(id(frame._buffer), id(frame_cpy._buffer))
|
||||
self.assertNotEquals(id(frame._frame), id(frame_cpy._frame))
|
||||
|
||||
# Ensure that both buffers have the same size and contain the same data.
|
||||
self.assertEquals(frame.get_buffer_size(), frame_cpy.get_buffer_size())
|
||||
self.assertTrue(all(a == b for a, b in zip(frame.get_buffer(),
|
||||
frame_cpy.get_buffer())))
|
||||
|
||||
# Ensure that internal Frame Pointer points to correct buffer.
|
||||
self.assertEquals(frame._frame.buffer,
|
||||
ctypes.cast(frame._buffer, ctypes.c_void_p).value)
|
||||
|
||||
self.assertEquals(frame_cpy._frame.buffer,
|
||||
ctypes.cast(frame_cpy._buffer, ctypes.c_void_p).value)
|
||||
|
||||
self.assertEquals(frame._frame.bufferSize, frame_cpy._frame.bufferSize)
|
||||
|
||||
def test_get_pixel_format(self):
|
||||
# Expectation: Frames have an image format set after acquisition
|
||||
for allocation_mode in AllocationMode:
|
||||
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
|
||||
with self.cam:
|
||||
self.assertNotEquals(
|
||||
self.cam.get_frame(allocation_mode=allocation_mode).get_pixel_format(), 0)
|
||||
|
||||
def test_incompatible_formats_value_error(self):
|
||||
# Expectation: Conversion into incompatible formats must lead to an value error
|
||||
for allocation_mode in AllocationMode:
|
||||
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
|
||||
with self.cam:
|
||||
frame = self.cam.get_frame(allocation_mode=allocation_mode)
|
||||
|
||||
current_fmt = frame.get_pixel_format()
|
||||
convertable_fmt = current_fmt.get_convertible_formats()
|
||||
|
||||
for fmt in PixelFormat.__members__.values():
|
||||
if (fmt != current_fmt) and (fmt not in convertable_fmt):
|
||||
self.assertRaises(ValueError, frame.convert_pixel_format, fmt)
|
||||
|
||||
def test_convert_to_all_given_formats(self):
|
||||
# Expectation: A Series of Frame, each acquired with a different Pixel format
|
||||
# Must be convertible to all formats the given format claims its convertible to without any
|
||||
# errors.
|
||||
|
||||
test_frames = []
|
||||
|
||||
with self.cam:
|
||||
for fmt in self.cam.get_pixel_formats():
|
||||
self.cam.set_pixel_format(fmt)
|
||||
|
||||
frame = self.cam.get_frame()
|
||||
|
||||
self.assertEqual(fmt, frame.get_pixel_format())
|
||||
test_frames.append(frame)
|
||||
|
||||
for frame in test_frames:
|
||||
|
||||
# The test shall work on a copy to keep the original Frame untouched
|
||||
for expected_fmt in frame.get_pixel_format().get_convertible_formats():
|
||||
cpy_frame = copy.deepcopy(frame)
|
||||
cpy_frame.convert_pixel_format(expected_fmt)
|
||||
|
||||
self.assertEquals(expected_fmt, cpy_frame.get_pixel_format())
|
||||
149
Vimba_6_0/VimbaPython/Source/Tests/real_cam_tests/vimba_test.py
Normal file
149
Vimba_6_0/VimbaPython/Source/Tests/real_cam_tests/vimba_test.py
Normal file
@@ -0,0 +1,149 @@
|
||||
"""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 unittest
|
||||
import ipaddress
|
||||
import struct
|
||||
|
||||
from vimba import *
|
||||
|
||||
|
||||
class CamVimbaTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.vimba = Vimba.get_instance()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_context_entry_exit(self):
|
||||
# Expected Behavior:
|
||||
# On entering the context features, cameras and interfaces shall
|
||||
# be detected and after leaving the context, everything should be reverted.
|
||||
|
||||
self.assertRaises(RuntimeError, self.vimba.get_all_features)
|
||||
self.assertRaises(RuntimeError, self.vimba.get_all_interfaces)
|
||||
self.assertRaises(RuntimeError, self.vimba.get_all_cameras)
|
||||
|
||||
with self.vimba:
|
||||
self.assertNotEqual(self.vimba.get_all_features(), ())
|
||||
self.assertNotEqual(self.vimba.get_all_interfaces(), ())
|
||||
self.assertNotEqual(self.vimba.get_all_cameras(), ())
|
||||
|
||||
self.assertRaises(RuntimeError, self.vimba.get_all_features)
|
||||
self.assertRaises(RuntimeError, self.vimba.get_all_interfaces)
|
||||
self.assertRaises(RuntimeError, self.vimba.get_all_cameras)
|
||||
|
||||
def test_get_all_interfaces(self):
|
||||
# Expected Behavior: get_all_interfaces() must raise an RuntimeError in closed state and
|
||||
# be non-empty then opened.
|
||||
self.assertRaises(RuntimeError, self.vimba.get_all_interfaces)
|
||||
|
||||
with self.vimba:
|
||||
self.assertTrue(self.vimba.get_all_interfaces())
|
||||
|
||||
def test_get_interface_by_id(self):
|
||||
# Expected Behavior: All detected Interfaces must be lookup able by their Id.
|
||||
# If outside of given scope, an error must be returned
|
||||
with self.vimba:
|
||||
ids = [inter.get_id() for inter in self.vimba.get_all_interfaces()]
|
||||
|
||||
for id_ in ids:
|
||||
self.assertNoRaise(self.vimba.get_interface_by_id, id_)
|
||||
|
||||
for id_ in ids:
|
||||
self.assertRaises(RuntimeError, self.vimba.get_interface_by_id, id_)
|
||||
|
||||
def test_get_all_cameras(self):
|
||||
# Expected Behavior: get_all_cameras() must only return camera handles on a open camera.
|
||||
self.assertRaises(RuntimeError, self.vimba.get_all_cameras)
|
||||
|
||||
with self.vimba:
|
||||
self.assertTrue(self.vimba.get_all_cameras())
|
||||
|
||||
def test_get_camera_by_id(self):
|
||||
# Expected Behavior: Lookup of test camera must not fail after system opening
|
||||
camera_id = self.get_test_camera_id()
|
||||
|
||||
with self.vimba:
|
||||
self.assertNoRaise(self.vimba.get_camera_by_id, camera_id)
|
||||
|
||||
def test_get_camera_by_ip(self):
|
||||
# Expected Behavior: get_camera_by_id() must work with a valid ipv4 address.
|
||||
# A with lookup of an invalid ipv4 address (no Camera attached)
|
||||
# must raise a VimbaCameraError, a lookup with an ipv6 address must raise a
|
||||
# VimbaCameraError in general (VimbaC doesn't support ipv6)
|
||||
with self.vimba:
|
||||
# Verify that the Test Camera is a GigE - Camera
|
||||
cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
|
||||
inter = self.vimba.get_interface_by_id(cam.get_interface_id())
|
||||
|
||||
if inter.get_type() != InterfaceType.Ethernet:
|
||||
raise self.skipTest('Test requires GigE - Camera.')
|
||||
|
||||
# Lookup test cameras IP address.
|
||||
with cam:
|
||||
ip_as_number = cam.get_feature_by_name('GevCurrentIPAddress').get()
|
||||
|
||||
# Swap byte order, the raw value does not seem to follow network byte order.
|
||||
ip_as_number = struct.pack('<L', ip_as_number)
|
||||
|
||||
# Verify that lookup with IPv4 Address returns the same Camera Object
|
||||
ip_addr = str(ipaddress.IPv4Address(ip_as_number))
|
||||
self.assertEqual(self.vimba.get_camera_by_id(ip_addr), cam)
|
||||
|
||||
# Verify that a lookup with an invalid IPv4 Address raises a VimbaCameraError
|
||||
ip_addr = str(ipaddress.IPv4Address('127.0.0.1'))
|
||||
self.assertRaises(VimbaCameraError, self.vimba.get_camera_by_id, ip_addr)
|
||||
|
||||
# Verify that a lookup with an IPv6 Address raises a VimbaCameraError
|
||||
ip_addr = str(ipaddress.IPv6Address('FD00::DEAD:BEEF'))
|
||||
self.assertRaises(VimbaCameraError, self.vimba.get_camera_by_id, ip_addr)
|
||||
|
||||
def test_get_camera_by_mac(self):
|
||||
# Expected Behavior: get_feature_by_id must be usable with a given MAC Address.
|
||||
with self.vimba:
|
||||
# Verify that the Test Camera is a GigE - Camera
|
||||
cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
|
||||
inter = self.vimba.get_interface_by_id(cam.get_interface_id())
|
||||
|
||||
if inter.get_type() != InterfaceType.Ethernet:
|
||||
raise self.skipTest('Test requires GigE - Camera.')
|
||||
|
||||
# Lookup test cameras MAC Address.
|
||||
with cam:
|
||||
# Construct MAC Address from raw value.
|
||||
mac_as_number = cam.get_feature_by_name('GevDeviceMACAddress').get()
|
||||
|
||||
mac_as_bytes = mac_as_number.to_bytes(6, byteorder='big')
|
||||
mac_as_str = ''.join(format(s, '02x') for s in mac_as_bytes).upper()
|
||||
|
||||
# Verify that lookup with MAC Address returns the same Camera Object
|
||||
self.assertEqual(self.vimba.get_camera_by_id(mac_as_str), cam)
|
||||
|
||||
# Verify that a lookup with an invalid MAC Address raises a VimbaCameraError
|
||||
invalid_mac = 'ffffffff'
|
||||
self.assertRaises(VimbaCameraError, self.vimba.get_camera_by_id, invalid_mac)
|
||||
177
Vimba_6_0/VimbaPython/Source/Tests/runner.py
Normal file
177
Vimba_6_0/VimbaPython/Source/Tests/runner.py
Normal file
@@ -0,0 +1,177 @@
|
||||
"""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 unittest
|
||||
import docopt
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add local directory to search path for test module import in this script.
|
||||
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
|
||||
|
||||
|
||||
# Add vimba module at the start of the search path. The tests should run against the
|
||||
# local VimbaPython sources regardless of any existing installations.
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
|
||||
# Inject 'assertNotRaise' to default test module. Tests are derived from this class.
|
||||
def _assertNoRaise(self, func, *args, **kwargs):
|
||||
try:
|
||||
func(*args, **kwargs)
|
||||
|
||||
except BaseException as e:
|
||||
self.fail('Function raised: {}'.format(e))
|
||||
|
||||
|
||||
# Inject shared test camera id into the base TestCase
|
||||
def _get_test_camera_id(self) -> str:
|
||||
return unittest.TestCase.test_cam_id
|
||||
|
||||
|
||||
def _set_test_camera_id(test_cam_id) -> str:
|
||||
unittest.TestCase.test_cam_id = test_cam_id
|
||||
|
||||
|
||||
unittest.TestCase.assertNoRaise = _assertNoRaise
|
||||
unittest.TestCase.set_test_camera_id = _set_test_camera_id
|
||||
unittest.TestCase.get_test_camera_id = _get_test_camera_id
|
||||
|
||||
|
||||
def _blacklist_tests(test_suite, blacklist):
|
||||
for test in test_suite:
|
||||
# Process TestSuites recursively
|
||||
if type(test) == unittest.TestSuite:
|
||||
_blacklist_tests(test, blacklist)
|
||||
|
||||
# Test is actually a TestCase. Add skip decorator to test
|
||||
# function if the test is blacklisted.
|
||||
else:
|
||||
name = test._testMethodName
|
||||
if name in blacklist:
|
||||
setattr(test, name, unittest.skip('Blacklisted')(getattr(test, name)))
|
||||
|
||||
return test_suite
|
||||
|
||||
|
||||
def main():
|
||||
CLI = """VimbaPython test runner.
|
||||
Usage:
|
||||
runner.py -h
|
||||
runner.py -s basic -o console [BLACKLIST...]
|
||||
runner.py -s basic -o junit_xml REPORT_DIR [BLACKLIST...]
|
||||
runner.py -s (real_cam | all) -c CAMERA_ID -o console [BLACKLIST...]
|
||||
runner.py -s (real_cam | all) -c CAMERA_ID -o junit_xml REPORT_DIR [BLACKLIST...]
|
||||
|
||||
Arguments:
|
||||
CAMERA_ID Camera Id from Camera that shall be used during testing
|
||||
REPORT_DIR Directory used for junit_export.
|
||||
BLACKLIST Optional sequence of unittest functions to skip.
|
||||
|
||||
Options:
|
||||
-h Show this screen.
|
||||
-s Testsuite to execute. real_cam and all require a camera to
|
||||
run tests against, therefore -c is mandatory.
|
||||
-c Camera Id used while testing.
|
||||
-o Test output: Either console or junit_xml.
|
||||
"""
|
||||
|
||||
args = docopt.docopt(CLI)
|
||||
loader = unittest.TestLoader()
|
||||
|
||||
if args['CAMERA_ID']:
|
||||
unittest.TestCase.set_test_camera_id(args['CAMERA_ID'])
|
||||
|
||||
else:
|
||||
unittest.TestCase.set_test_camera_id(None)
|
||||
|
||||
# Select TestRunner
|
||||
if args['console']:
|
||||
runner = unittest.TextTestRunner(verbosity=2)
|
||||
|
||||
elif args['junit_xml']:
|
||||
import xmlrunner
|
||||
runner = xmlrunner.XMLTestRunner(output=args['REPORT_DIR'])
|
||||
|
||||
# Import tests cases
|
||||
import basic_tests.c_binding_test
|
||||
import basic_tests.util_runtime_type_check_test
|
||||
import basic_tests.util_tracer_test
|
||||
import basic_tests.util_context_decorator_test
|
||||
import basic_tests.vimba_common_test
|
||||
import basic_tests.vimba_test
|
||||
import basic_tests.interface_test
|
||||
|
||||
import real_cam_tests.vimba_test
|
||||
import real_cam_tests.feature_test
|
||||
import real_cam_tests.camera_test
|
||||
import real_cam_tests.frame_test
|
||||
import real_cam_tests.ancillary_data_test
|
||||
|
||||
# Assign test cases to test suites
|
||||
BASIC_TEST_MODS = [
|
||||
basic_tests.c_binding_test,
|
||||
basic_tests.util_runtime_type_check_test,
|
||||
basic_tests.util_tracer_test,
|
||||
basic_tests.util_context_decorator_test,
|
||||
basic_tests.vimba_common_test,
|
||||
basic_tests.vimba_test,
|
||||
basic_tests.interface_test
|
||||
]
|
||||
|
||||
REAL_CAM_TEST_MODS = [
|
||||
real_cam_tests.vimba_test,
|
||||
real_cam_tests.feature_test,
|
||||
real_cam_tests.camera_test,
|
||||
real_cam_tests.frame_test,
|
||||
real_cam_tests.ancillary_data_test
|
||||
]
|
||||
|
||||
# Prepare TestSuites
|
||||
suite_basic = unittest.TestSuite()
|
||||
suite_cam = unittest.TestSuite()
|
||||
|
||||
for mod in BASIC_TEST_MODS:
|
||||
suite_basic.addTests(_blacklist_tests(loader.loadTestsFromModule(mod), args['BLACKLIST']))
|
||||
|
||||
for mod in REAL_CAM_TEST_MODS:
|
||||
suite_cam.addTests(_blacklist_tests(loader.loadTestsFromModule(mod), args['BLACKLIST']))
|
||||
|
||||
# Execute TestSuites
|
||||
if args['basic']:
|
||||
runner.run(suite_basic)
|
||||
|
||||
elif args['real_cam']:
|
||||
runner.run(suite_cam)
|
||||
|
||||
elif args['all']:
|
||||
runner.run(suite_basic)
|
||||
runner.run(suite_cam)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
172
Vimba_6_0/VimbaPython/Source/run_tests.py
Normal file
172
Vimba_6_0/VimbaPython/Source/run_tests.py
Normal file
@@ -0,0 +1,172 @@
|
||||
"""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 os
|
||||
import shutil
|
||||
import subprocess
|
||||
import docopt
|
||||
|
||||
|
||||
def fprint(line):
|
||||
print(line, flush=True)
|
||||
|
||||
|
||||
def stringify_list(l):
|
||||
list_str = ''
|
||||
for e in l:
|
||||
list_str += e + ' '
|
||||
|
||||
return list_str
|
||||
|
||||
|
||||
def static_test():
|
||||
fprint('Execute Static Test: flake8')
|
||||
subprocess.run('flake8 vimba', shell=True)
|
||||
subprocess.run('flake8 Examples --ignore=F405,F403', shell=True)
|
||||
subprocess.run('flake8 Tests --ignore=F405,F403', shell=True)
|
||||
fprint('')
|
||||
|
||||
fprint('Execute Static Test: mypy')
|
||||
subprocess.run('mypy vimba', shell=True, check=True)
|
||||
fprint('')
|
||||
|
||||
|
||||
def unit_test(testsuite, testcamera, blacklist):
|
||||
blacklist = " ".join(blacklist)
|
||||
|
||||
fprint('Execute Unit tests and measure coverage:')
|
||||
if testsuite == 'basic':
|
||||
cmd = 'coverage run Tests/runner.py -s basic -o console {}'.format(blacklist)
|
||||
|
||||
else:
|
||||
cmd = 'coverage run Tests/runner.py -s {} -c {} -o console {}'
|
||||
cmd = cmd.format(testsuite, testcamera, blacklist)
|
||||
|
||||
subprocess.run(cmd, shell=True, check=True)
|
||||
fprint('')
|
||||
|
||||
fprint('Coverage during test execution:')
|
||||
subprocess.run('coverage report -m', shell=True, check=True)
|
||||
fprint('')
|
||||
|
||||
coverage_file = '.coverage'
|
||||
if os.path.exists(coverage_file):
|
||||
os.remove(coverage_file)
|
||||
|
||||
|
||||
def setup_junit(report_dir):
|
||||
if os.path.exists(report_dir):
|
||||
shutil.rmtree(report_dir, ignore_errors=True)
|
||||
|
||||
os.mkdir(report_dir)
|
||||
|
||||
|
||||
def static_test_junit(report_dir):
|
||||
fprint('Execute Static Test: flake8')
|
||||
cmd = 'flake8 vimba --output-file=' + report_dir + '/flake8.txt'
|
||||
subprocess.run(cmd, shell=True, check=True)
|
||||
|
||||
cmd = 'flake8_junit ' + report_dir + '/flake8.txt ' + report_dir + '/flake8_junit.xml'
|
||||
subprocess.run(cmd, shell=True, check=True)
|
||||
fprint('')
|
||||
|
||||
fprint('Execute Static Test: mypy')
|
||||
cmd = 'mypy vimba --junit-xml ' + report_dir + '/mypy_junit.xml'
|
||||
subprocess.run(cmd, shell=True, check=True)
|
||||
fprint('')
|
||||
|
||||
|
||||
def unit_test_junit(report_dir, testsuite, testcamera, blacklist):
|
||||
fprint('Execute Unit tests and measure coverage:')
|
||||
|
||||
blacklist = " ".join(blacklist)
|
||||
if testsuite == 'basic':
|
||||
cmd = 'coverage run --branch Tests/runner.py -s basic -o junit_xml {} {}'
|
||||
cmd = cmd.format(report_dir, blacklist)
|
||||
|
||||
else:
|
||||
cmd = 'coverage run --branch Tests/runner.py -s {} -c {} -o junit_xml {} {}'
|
||||
cmd = cmd.format(testsuite, testcamera, report_dir, blacklist)
|
||||
|
||||
subprocess.run(cmd, shell=True, check=True)
|
||||
fprint('')
|
||||
|
||||
fprint('Generate Coverage reports:')
|
||||
subprocess.run('coverage report -m', shell=True, check=True)
|
||||
subprocess.run('coverage xml -o ' + report_dir + '/coverage.xml', shell=True, check=True)
|
||||
fprint('')
|
||||
|
||||
coverage_file = '.coverage'
|
||||
if os.path.exists(coverage_file):
|
||||
os.remove(coverage_file)
|
||||
|
||||
|
||||
def test(testsuite, testcamera, blacklist):
|
||||
static_test()
|
||||
unit_test(testsuite, testcamera, blacklist)
|
||||
|
||||
|
||||
def test_junit(report_dir, testsuite, testcamera, blacklist):
|
||||
setup_junit(report_dir)
|
||||
static_test_junit(report_dir)
|
||||
unit_test_junit(report_dir, testsuite, testcamera, blacklist)
|
||||
|
||||
|
||||
def main():
|
||||
CLI = """VimbaPython tests script.
|
||||
Usage:
|
||||
run_tests.py -h
|
||||
run_tests.py test -s basic [BLACKLIST...]
|
||||
run_tests.py test -s (real_cam | all) -c CAMERA_ID [BLACKLIST...]
|
||||
run_tests.py test_junit -s basic [BLACKLIST...]
|
||||
run_tests.py test_junit -s (real_cam | all) -c CAMERA_ID [BLACKLIST...]
|
||||
|
||||
Arguments:
|
||||
CAMERA_ID Camera Id from Camera that shall be used during testing
|
||||
BLACKLIST Optional sequence of unittest functions to skip.
|
||||
|
||||
Options:
|
||||
-h Show this screen.
|
||||
-s Unittestsuite. Can be 'basic', 'real_cam' or 'all'. The last two require a
|
||||
Camera Id to test against.
|
||||
-c Camera Id used in testing.
|
||||
"""
|
||||
|
||||
args = docopt.docopt(CLI)
|
||||
|
||||
suite = 'basic' if args['basic'] else 'real_cam' if args['real_cam'] else 'all'
|
||||
|
||||
if args['test']:
|
||||
test(suite, args['CAMERA_ID'], args['BLACKLIST'])
|
||||
|
||||
elif args['test_junit']:
|
||||
report_dir = 'Test_Reports'
|
||||
test_junit(report_dir, suite, args['CAMERA_ID'], args['BLACKLIST'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
11
Vimba_6_0/VimbaPython/Source/setup.cfg
Normal file
11
Vimba_6_0/VimbaPython/Source/setup.cfg
Normal file
@@ -0,0 +1,11 @@
|
||||
[flake8]
|
||||
max-line-length = 100
|
||||
|
||||
[coverage:run]
|
||||
source = vimba
|
||||
|
||||
[coverage:report]
|
||||
exclude_lines =
|
||||
coverage: skip
|
||||
def __repr__
|
||||
def __str__
|
||||
82
Vimba_6_0/VimbaPython/Source/setup.py
Normal file
82
Vimba_6_0/VimbaPython/Source/setup.py
Normal file
@@ -0,0 +1,82 @@
|
||||
"""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 setuptools
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
def read_file(name):
|
||||
with open(file=name, mode='r', encoding='utf-8') as file:
|
||||
return file.read()
|
||||
|
||||
|
||||
def get_version(file_content):
|
||||
result = re.search(r'{}\s*=\s*[\'"]([^\'"]*)[\'"]'.format('__version__'),
|
||||
file_content)
|
||||
return result.group(1)
|
||||
|
||||
|
||||
name = 'VimbaPython'
|
||||
version = get_version(read_file(os.path.join('.', 'vimba', '__init__.py')))
|
||||
author = 'Allied Vision Technologies GmbH'
|
||||
description = 'Python Bindings for Allied Visions VimbaSDK'
|
||||
long_description = read_file('README.txt')
|
||||
long_description_type = 'text/markdown'
|
||||
license = 'BSD-2-Clause'
|
||||
packages = [
|
||||
'vimba',
|
||||
'vimba.c_binding',
|
||||
'vimba.util'
|
||||
]
|
||||
python_requires = '>=3.7'
|
||||
tests_require = [
|
||||
'xmlrunner',
|
||||
'flake8',
|
||||
'flake8-junit-report',
|
||||
'mypy',
|
||||
'coverage',
|
||||
'docopt'
|
||||
]
|
||||
extras_require = {
|
||||
'numpy-export': ['numpy'],
|
||||
'opencv-export': ['opencv-python'],
|
||||
'test': tests_require
|
||||
}
|
||||
|
||||
setuptools.setup(
|
||||
name=name,
|
||||
version=version,
|
||||
author=author,
|
||||
description=description,
|
||||
long_description=long_description,
|
||||
long_description_content_type=long_description_type,
|
||||
license=license,
|
||||
packages=packages,
|
||||
python_requires=python_requires,
|
||||
extras_require=extras_require
|
||||
)
|
||||
128
Vimba_6_0/VimbaPython/Source/vimba/__init__.py
Normal file
128
Vimba_6_0/VimbaPython/Source/vimba/__init__.py
Normal file
@@ -0,0 +1,128 @@
|
||||
"""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.
|
||||
"""
|
||||
|
||||
# Suppress 'imported but unused' - Error from static style checker.
|
||||
# flake8: noqa: F401
|
||||
|
||||
__version__ = '1.2.1'
|
||||
|
||||
__all__ = [
|
||||
'Vimba',
|
||||
'Camera',
|
||||
'CameraChangeHandler',
|
||||
'CameraEvent',
|
||||
'AccessMode',
|
||||
'PersistType',
|
||||
'Interface',
|
||||
'InterfaceType',
|
||||
'InterfaceChangeHandler',
|
||||
'InterfaceEvent',
|
||||
'PixelFormat',
|
||||
'Frame',
|
||||
'FeatureTypes',
|
||||
'FrameHandler',
|
||||
'FrameStatus',
|
||||
'AllocationMode',
|
||||
'Debayer',
|
||||
'intersect_pixel_formats',
|
||||
'MONO_PIXEL_FORMATS',
|
||||
'BAYER_PIXEL_FORMATS',
|
||||
'RGB_PIXEL_FORMATS',
|
||||
'RGBA_PIXEL_FORMATS',
|
||||
'BGR_PIXEL_FORMATS',
|
||||
'BGRA_PIXEL_FORMATS',
|
||||
'YUV_PIXEL_FORMATS',
|
||||
'YCBCR_PIXEL_FORMATS',
|
||||
'COLOR_PIXEL_FORMATS',
|
||||
'OPENCV_PIXEL_FORMATS',
|
||||
|
||||
'VimbaSystemError',
|
||||
'VimbaCameraError',
|
||||
'VimbaInterfaceError',
|
||||
'VimbaFeatureError',
|
||||
'VimbaFrameError',
|
||||
'VimbaTimeout',
|
||||
|
||||
'IntFeature',
|
||||
'FloatFeature',
|
||||
'StringFeature',
|
||||
'BoolFeature',
|
||||
'EnumEntry',
|
||||
'EnumFeature',
|
||||
'CommandFeature',
|
||||
'RawFeature',
|
||||
|
||||
'LogLevel',
|
||||
'LogConfig',
|
||||
'Log',
|
||||
'LOG_CONFIG_TRACE_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_TRACE_FILE_ONLY',
|
||||
'LOG_CONFIG_TRACE',
|
||||
'LOG_CONFIG_INFO_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_INFO_FILE_ONLY',
|
||||
'LOG_CONFIG_INFO',
|
||||
'LOG_CONFIG_WARNING_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_WARNING_FILE_ONLY',
|
||||
'LOG_CONFIG_WARNING',
|
||||
'LOG_CONFIG_ERROR_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_ERROR_FILE_ONLY',
|
||||
'LOG_CONFIG_ERROR',
|
||||
'LOG_CONFIG_CRITICAL_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_CRITICAL_FILE_ONLY',
|
||||
'LOG_CONFIG_CRITICAL',
|
||||
|
||||
'TraceEnable',
|
||||
'ScopedLogEnable',
|
||||
'RuntimeTypeCheckEnable'
|
||||
]
|
||||
|
||||
# Import everything exported from the top level module
|
||||
from .vimba import Vimba
|
||||
|
||||
from .camera import AccessMode, PersistType, Camera, CameraChangeHandler, CameraEvent, FrameHandler
|
||||
|
||||
from .interface import Interface, InterfaceType, InterfaceChangeHandler, InterfaceEvent
|
||||
|
||||
from .frame import PixelFormat, Frame, Debayer, intersect_pixel_formats, MONO_PIXEL_FORMATS, \
|
||||
BAYER_PIXEL_FORMATS, RGB_PIXEL_FORMATS, RGBA_PIXEL_FORMATS, BGR_PIXEL_FORMATS, \
|
||||
BGRA_PIXEL_FORMATS, YUV_PIXEL_FORMATS, YCBCR_PIXEL_FORMATS, \
|
||||
COLOR_PIXEL_FORMATS, OPENCV_PIXEL_FORMATS, FrameStatus, FeatureTypes, \
|
||||
AllocationMode
|
||||
|
||||
from .error import VimbaSystemError, VimbaCameraError, VimbaInterfaceError, VimbaFeatureError, \
|
||||
VimbaFrameError, VimbaTimeout
|
||||
|
||||
from .feature import IntFeature, FloatFeature, StringFeature, BoolFeature, EnumEntry, EnumFeature, \
|
||||
CommandFeature, RawFeature
|
||||
|
||||
from .util import Log, LogLevel, LogConfig, LOG_CONFIG_TRACE_CONSOLE_ONLY, \
|
||||
LOG_CONFIG_TRACE_FILE_ONLY, LOG_CONFIG_TRACE, LOG_CONFIG_INFO_CONSOLE_ONLY, \
|
||||
LOG_CONFIG_INFO_FILE_ONLY, LOG_CONFIG_INFO, LOG_CONFIG_WARNING_CONSOLE_ONLY, \
|
||||
LOG_CONFIG_WARNING_FILE_ONLY, LOG_CONFIG_WARNING, LOG_CONFIG_ERROR_CONSOLE_ONLY, \
|
||||
LOG_CONFIG_ERROR_FILE_ONLY, LOG_CONFIG_ERROR, LOG_CONFIG_CRITICAL_CONSOLE_ONLY, \
|
||||
LOG_CONFIG_CRITICAL_FILE_ONLY, LOG_CONFIG_CRITICAL, ScopedLogEnable, \
|
||||
TraceEnable, RuntimeTypeCheckEnable
|
||||
120
Vimba_6_0/VimbaPython/Source/vimba/c_binding/__init__.py
Normal file
120
Vimba_6_0/VimbaPython/Source/vimba/c_binding/__init__.py
Normal file
@@ -0,0 +1,120 @@
|
||||
"""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.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
NOTE: Vimba/Vmb naming convention.
|
||||
VimbaPython is based heavily on VimbaC, this submodule contains all wrapped types and functions
|
||||
of VimbaC. All VimbaC Types and Functions are prefixed with 'Vmb', this convention is kept for
|
||||
all python types interfacing with the C - Layer. VimbaC developers should be able to understand
|
||||
the interface to VimbaC and keeping the name convention helps a lot in that regard.
|
||||
|
||||
However prefixing everything with 'Vmb' is not required in VimbaPython, therefore most Types
|
||||
of the public API have no prefix.
|
||||
"""
|
||||
|
||||
# Suppress 'imported but unused' - Error from static style checker.
|
||||
# flake8: noqa: F401
|
||||
|
||||
__all__ = [
|
||||
# Exports from vimba_common
|
||||
'VmbInt8',
|
||||
'VmbUint8',
|
||||
'VmbInt16',
|
||||
'VmbUint16',
|
||||
'VmbInt32',
|
||||
'VmbUint32',
|
||||
'VmbInt64',
|
||||
'VmbUint64',
|
||||
'VmbHandle',
|
||||
'VmbBool',
|
||||
'VmbUchar',
|
||||
'VmbDouble',
|
||||
'VmbError',
|
||||
'VimbaCError',
|
||||
'VmbPixelFormat',
|
||||
'decode_cstr',
|
||||
'decode_flags',
|
||||
|
||||
# Exports from vimba_c
|
||||
'VmbInterface',
|
||||
'VmbAccessMode',
|
||||
'VmbFeatureData',
|
||||
'VmbFeaturePersist',
|
||||
'VmbFeatureVisibility',
|
||||
'VmbFeatureFlags',
|
||||
'VmbFrameStatus',
|
||||
'VmbFrameFlags',
|
||||
'VmbVersionInfo',
|
||||
'VmbInterfaceInfo',
|
||||
'VmbCameraInfo',
|
||||
'VmbFeatureInfo',
|
||||
'VmbFeatureEnumEntry',
|
||||
'VmbFrame',
|
||||
'VmbFeaturePersistSettings',
|
||||
'G_VIMBA_C_HANDLE',
|
||||
'VIMBA_C_VERSION',
|
||||
'EXPECTED_VIMBA_C_VERSION',
|
||||
'call_vimba_c',
|
||||
'build_callback_type',
|
||||
|
||||
# Exports from vimba_image_transform
|
||||
'VmbImage',
|
||||
'VmbImageInfo',
|
||||
'VmbDebayerMode',
|
||||
'VmbTransformInfo',
|
||||
'VIMBA_IMAGE_TRANSFORM_VERSION',
|
||||
'EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION',
|
||||
'call_vimba_image_transform',
|
||||
'PIXEL_FORMAT_TO_LAYOUT',
|
||||
'LAYOUT_TO_PIXEL_FORMAT',
|
||||
'PIXEL_FORMAT_CONVERTIBILITY_MAP',
|
||||
|
||||
# Exports from ctypes
|
||||
'byref',
|
||||
'sizeof',
|
||||
'create_string_buffer'
|
||||
]
|
||||
|
||||
from .vimba_common import VmbInt8, VmbUint8, VmbInt16, VmbUint16, VmbInt32, VmbUint32, \
|
||||
VmbInt64, VmbUint64, VmbHandle, VmbBool, VmbUchar, VmbDouble, VmbError, \
|
||||
VimbaCError, VmbPixelFormat, decode_cstr, decode_flags, \
|
||||
_select_vimba_home
|
||||
|
||||
from .vimba_c import VmbInterface, VmbAccessMode, VmbFeatureData, \
|
||||
VmbFeaturePersist, VmbFeatureVisibility, VmbFeatureFlags, VmbFrameStatus, \
|
||||
VmbFrameFlags, VmbVersionInfo, VmbInterfaceInfo, VmbCameraInfo, VmbFeatureInfo, \
|
||||
VmbFeatureEnumEntry, VmbFrame, VmbFeaturePersistSettings, \
|
||||
G_VIMBA_C_HANDLE, EXPECTED_VIMBA_C_VERSION, VIMBA_C_VERSION, call_vimba_c, \
|
||||
build_callback_type
|
||||
|
||||
from .vimba_image_transform import VmbImage, VmbImageInfo, VmbDebayerMode, \
|
||||
VIMBA_IMAGE_TRANSFORM_VERSION, \
|
||||
EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION, VmbTransformInfo, \
|
||||
call_vimba_image_transform, PIXEL_FORMAT_TO_LAYOUT, \
|
||||
LAYOUT_TO_PIXEL_FORMAT, PIXEL_FORMAT_CONVERTIBILITY_MAP
|
||||
|
||||
from ctypes import byref, sizeof, create_string_buffer
|
||||
772
Vimba_6_0/VimbaPython/Source/vimba/c_binding/vimba_c.py
Normal file
772
Vimba_6_0/VimbaPython/Source/vimba/c_binding/vimba_c.py
Normal file
@@ -0,0 +1,772 @@
|
||||
"""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 copy
|
||||
import ctypes
|
||||
from typing import Callable, Any, Tuple
|
||||
from ctypes import c_void_p, c_char_p, byref, sizeof, POINTER as c_ptr, c_char_p as c_str
|
||||
from ..util import TraceEnable
|
||||
from ..error import VimbaSystemError
|
||||
from .vimba_common import Uint32Enum, Int32Enum, VmbInt32, VmbUint32, VmbInt64, VmbUint64, \
|
||||
VmbHandle, VmbBool, VmbDouble, VmbError, VimbaCError, VmbPixelFormat, \
|
||||
fmt_enum_repr, fmt_repr, fmt_flags_repr, load_vimba_lib
|
||||
|
||||
__version__ = None
|
||||
|
||||
__all__ = [
|
||||
'VmbPixelFormat',
|
||||
'VmbInterface',
|
||||
'VmbAccessMode',
|
||||
'VmbFeatureData',
|
||||
'VmbFeaturePersist',
|
||||
'VmbFeatureVisibility',
|
||||
'VmbFeatureFlags',
|
||||
'VmbFrameStatus',
|
||||
'VmbFrameFlags',
|
||||
'VmbVersionInfo',
|
||||
'VmbInterfaceInfo',
|
||||
'VmbCameraInfo',
|
||||
'VmbFeatureInfo',
|
||||
'VmbFeatureEnumEntry',
|
||||
'VmbFrame',
|
||||
'VmbFeaturePersistSettings',
|
||||
'G_VIMBA_C_HANDLE',
|
||||
'VIMBA_C_VERSION',
|
||||
'EXPECTED_VIMBA_C_VERSION',
|
||||
'call_vimba_c',
|
||||
'build_callback_type'
|
||||
]
|
||||
|
||||
|
||||
# Types
|
||||
class VmbInterface(Uint32Enum):
|
||||
"""
|
||||
Camera Interface Types:
|
||||
Unknown - Interface is not known to this version of the API
|
||||
Firewire - 1394
|
||||
Ethernet - GigE
|
||||
Usb - USB 3.0
|
||||
CL - Camera Link
|
||||
CSI2 - CSI-2
|
||||
"""
|
||||
Unknown = 0
|
||||
Firewire = 1
|
||||
Ethernet = 2
|
||||
Usb = 3
|
||||
CL = 4
|
||||
CSI2 = 5
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbAccessMode(Uint32Enum):
|
||||
"""
|
||||
Camera Access Mode:
|
||||
None_ - No access
|
||||
Full - Read and write access
|
||||
Read - Read-only access
|
||||
Config - Configuration access (GeV)
|
||||
Lite - Read and write access without feature access (only addresses)
|
||||
"""
|
||||
None_ = 0
|
||||
Full = 1
|
||||
Read = 2
|
||||
Config = 4
|
||||
Lite = 8
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbFeatureData(Uint32Enum):
|
||||
"""
|
||||
Feature Data Types
|
||||
Unknown - Unknown feature type
|
||||
Int - 64 bit integer feature
|
||||
Float - 64 bit floating point feature
|
||||
Enum - Enumeration feature
|
||||
String - String feature
|
||||
Bool - Boolean feature
|
||||
Command - Command feature
|
||||
Raw - Raw (direct register access) feature
|
||||
None_ - Feature with no data
|
||||
"""
|
||||
Unknown = 0
|
||||
Int = 1
|
||||
Float = 2
|
||||
Enum = 3
|
||||
String = 4
|
||||
Bool = 5
|
||||
Command = 6
|
||||
Raw = 7
|
||||
None_ = 8
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbFeaturePersist(Uint32Enum):
|
||||
"""
|
||||
Type of features that are to be saved (persisted) to the XML file
|
||||
when using VmbCameraSettingsSave
|
||||
|
||||
All - Save all features to XML, including look-up tables
|
||||
Streamable - Save only features marked as streamable, excluding
|
||||
look-up tables
|
||||
NoLUT - Save all features except look-up tables (default)
|
||||
"""
|
||||
All = 0
|
||||
Streamable = 1
|
||||
NoLUT = 2
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbFeatureVisibility(Uint32Enum):
|
||||
"""
|
||||
Feature Visibility
|
||||
Unknown - Feature visibility is not known
|
||||
Beginner - Feature is visible in feature list (beginner level)
|
||||
Expert - Feature is visible in feature list (expert level)
|
||||
Guru - Feature is visible in feature list (guru level)
|
||||
Invisible - Feature is not visible in feature list
|
||||
"""
|
||||
Unknown = 0
|
||||
Beginner = 1
|
||||
Expert = 2
|
||||
Guru = 3
|
||||
Invisible = 4
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbFeatureFlags(Uint32Enum):
|
||||
"""
|
||||
Feature Flags
|
||||
None_ - No additional information is provided
|
||||
Read - Static info about read access.
|
||||
Current status depends on access mode, check with
|
||||
VmbFeatureAccessQuery()
|
||||
Write - Static info about write access.
|
||||
Current status depends on access mode, check with
|
||||
VmbFeatureAccessQuery()
|
||||
Volatile - Value may change at any time
|
||||
ModifyWrite - Value may change after a write
|
||||
"""
|
||||
None_ = 0
|
||||
Read = 1
|
||||
Write = 2
|
||||
Undocumented = 4
|
||||
Volatile = 8
|
||||
ModifyWrite = 16
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbFrameStatus(Int32Enum):
|
||||
"""
|
||||
Frame transfer status
|
||||
Complete - Frame has been completed without errors
|
||||
Incomplete - Frame could not be filled to the end
|
||||
TooSmall - Frame buffer was too small
|
||||
Invalid - Frame buffer was invalid
|
||||
"""
|
||||
Complete = 0
|
||||
Incomplete = -1
|
||||
TooSmall = -2
|
||||
Invalid = -3
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbFrameFlags(Uint32Enum):
|
||||
"""
|
||||
Frame Flags
|
||||
None_ - No additional information is provided
|
||||
Dimension - Frame's dimension is provided
|
||||
Offset - Frame's offset is provided (ROI)
|
||||
FrameID - Frame's ID is provided
|
||||
Timestamp - Frame's timestamp is provided
|
||||
"""
|
||||
None_ = 0
|
||||
Dimension = 1
|
||||
Offset = 2
|
||||
FrameID = 4
|
||||
Timestamp = 8
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbVersionInfo(ctypes.Structure):
|
||||
"""
|
||||
Version Information
|
||||
Fields:
|
||||
major - Type: VmbUint32, Info: Major version number
|
||||
minor - Type: VmbUint32, Info: Minor version number
|
||||
patch - Type: VmbUint32, Info: Patch version number
|
||||
"""
|
||||
_fields_ = [
|
||||
("major", VmbUint32),
|
||||
("minor", VmbUint32),
|
||||
("patch", VmbUint32)
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return '{}.{}.{}'.format(self.major, self.minor, self.patch)
|
||||
|
||||
def __repr__(self):
|
||||
rep = 'VmbVersionInfo'
|
||||
rep += '(major=' + repr(self.major)
|
||||
rep += ',minor=' + repr(self.minor)
|
||||
rep += ',patch=' + repr(self.patch)
|
||||
rep += ')'
|
||||
return rep
|
||||
|
||||
|
||||
class VmbInterfaceInfo(ctypes.Structure):
|
||||
"""
|
||||
Interface information. Holds read-only information about an interface.
|
||||
Fields:
|
||||
interfaceIdString - Type: c_char_p
|
||||
Info: Unique identifier for each interface
|
||||
interfaceType - Type: VmbInterface (VmbUint32)
|
||||
Info: Interface type, see VmbInterface
|
||||
interfaceName - Type: c_char_p
|
||||
Info: Interface name, given by transport layer
|
||||
serialString - Type: c_char_p
|
||||
Info: Serial number
|
||||
permittedAccess - Type: VmbAccessMode (VmbUint32)
|
||||
Info: Used access mode, see VmbAccessMode
|
||||
"""
|
||||
_fields_ = [
|
||||
("interfaceIdString", c_char_p),
|
||||
("interfaceType", VmbUint32),
|
||||
("interfaceName", c_char_p),
|
||||
("serialString", c_char_p),
|
||||
("permittedAccess", VmbUint32)
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
rep = 'VmbInterfaceInfo'
|
||||
rep += fmt_repr('(interfaceIdString={}', self.interfaceIdString)
|
||||
rep += fmt_enum_repr(',interfaceType={}', VmbInterface, self.interfaceType)
|
||||
rep += fmt_repr(',interfaceName={}', self.interfaceName)
|
||||
rep += fmt_repr(',serialString={}', self.serialString)
|
||||
rep += fmt_flags_repr(',permittedAccess={}', VmbAccessMode, self.permittedAccess)
|
||||
rep += ')'
|
||||
return rep
|
||||
|
||||
|
||||
class VmbCameraInfo(ctypes.Structure):
|
||||
"""
|
||||
Camera information. Holds read-only information about a camera.
|
||||
Fields:
|
||||
cameraIdString - Type: c_char_p
|
||||
Info: Unique identifier for each camera
|
||||
cameraName - Type: c_char_p
|
||||
Info: Name of the camera
|
||||
modelName - Type: c_char_p
|
||||
Info: Model name
|
||||
serialString - Type: c_char_p
|
||||
Info: Serial number
|
||||
permittedAccess - Type: VmbAccessMode (VmbUint32)
|
||||
Info: Used access mode, see VmbAccessMode
|
||||
interfaceIdString - Type: c_char_p
|
||||
Info: Unique value for each interface or bus
|
||||
"""
|
||||
_fields_ = [
|
||||
("cameraIdString", c_char_p),
|
||||
("cameraName", c_char_p),
|
||||
("modelName", c_char_p),
|
||||
("serialString", c_char_p),
|
||||
("permittedAccess", VmbUint32),
|
||||
("interfaceIdString", c_char_p)
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
rep = 'VmbCameraInfo'
|
||||
rep += fmt_repr('(cameraIdString={}', self.cameraIdString)
|
||||
rep += fmt_repr(',cameraName={}', self.cameraName)
|
||||
rep += fmt_repr(',modelName={}', self.modelName)
|
||||
rep += fmt_repr(',serialString={}', self.serialString)
|
||||
rep += fmt_flags_repr(',permittedAccess={}', VmbAccessMode, self.permittedAccess)
|
||||
rep += fmt_repr(',interfaceIdString={}', self.interfaceIdString)
|
||||
rep += ')'
|
||||
return rep
|
||||
|
||||
|
||||
class VmbFeatureInfo(ctypes.Structure):
|
||||
"""
|
||||
Feature information. Holds read-only information about a feature.
|
||||
Fields:
|
||||
name - Type: c_char_p
|
||||
Info: Name used in the API
|
||||
featureDataType - Type: VmbFeatureData (VmbUint32)
|
||||
Info: Data type of this feature
|
||||
featureFlags - Type: VmbFeatureFlags (VmbUint32)
|
||||
Info: Access flags for this feature
|
||||
category - Type: c_char_p
|
||||
Info: Category this feature can be found in
|
||||
displayName - Type: c_char_p
|
||||
Info: Feature name to be used in GUIs
|
||||
pollingTime - Type: VmbUint32
|
||||
Info: Predefined polling time for volatile
|
||||
features
|
||||
unit - Type: c_char_p
|
||||
Info: Measuring unit as given in the XML file
|
||||
representation - Type: c_char_p
|
||||
Info: Representation of a numeric feature
|
||||
visibility - Type: VmbFeatureVisibility (VmbUint32)
|
||||
Info: GUI visibility
|
||||
tooltip - Type: c_char_p
|
||||
Info: Short description, e.g. for a tooltip
|
||||
description - Type: c_char_p
|
||||
Info: Longer description
|
||||
sfncNamespace - Type: c_char_p
|
||||
Info: Namespace this feature resides in
|
||||
isStreamable - Type: VmbBool
|
||||
Info: Indicates if a feature can be stored
|
||||
to / loaded from a file
|
||||
hasAffectedFeatures - Type: VmbBool
|
||||
Info: Indicates if the feature potentially
|
||||
affects other features
|
||||
hasSelectedFeatures - Type: VmbBool
|
||||
Info: Indicates if the feature selects other
|
||||
features
|
||||
"""
|
||||
_fields_ = [
|
||||
("name", c_char_p),
|
||||
("featureDataType", VmbUint32),
|
||||
("featureFlags", VmbUint32),
|
||||
("category", c_char_p),
|
||||
("displayName", c_char_p),
|
||||
("pollingTime", VmbUint32),
|
||||
("unit", c_char_p),
|
||||
("representation", c_char_p),
|
||||
("visibility", VmbUint32),
|
||||
("tooltip", c_char_p),
|
||||
("description", c_char_p),
|
||||
("sfncNamespace", c_char_p),
|
||||
("isStreamable", VmbBool),
|
||||
("hasAffectedFeatures", VmbBool),
|
||||
("hasSelectedFeatures", VmbBool)
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
rep = 'VmbFeatureInfo'
|
||||
rep += fmt_repr('(name={}', self.name)
|
||||
rep += fmt_enum_repr(',featureDataType={}', VmbFeatureData, self.featureDataType)
|
||||
rep += fmt_flags_repr(',featureFlags={}', VmbFeatureFlags, self.featureFlags)
|
||||
rep += fmt_repr(',category={}', self.category)
|
||||
rep += fmt_repr(',displayName={}', self.displayName)
|
||||
rep += fmt_repr(',pollingTime={}', self.pollingTime)
|
||||
rep += fmt_repr(',unit={}', self.unit)
|
||||
rep += fmt_repr(',representation={}', self.representation)
|
||||
rep += fmt_enum_repr(',visibility={}', VmbFeatureVisibility, self.visibility)
|
||||
rep += fmt_repr(',tooltip={}', self.tooltip)
|
||||
rep += fmt_repr(',description={}', self.description)
|
||||
rep += fmt_repr(',sfncNamespace={}', self.sfncNamespace)
|
||||
rep += fmt_repr(',isStreamable={}', self.isStreamable)
|
||||
rep += fmt_repr(',hasAffectedFeatures={}', self.hasAffectedFeatures)
|
||||
rep += fmt_repr(',hasSelectedFeatures={}', self.hasSelectedFeatures)
|
||||
rep += ')'
|
||||
return rep
|
||||
|
||||
|
||||
class VmbFeatureEnumEntry(ctypes.Structure):
|
||||
"""
|
||||
Info about possible entries of an enumeration feature:
|
||||
Fields:
|
||||
name - Type: c_char_p
|
||||
Info: Name used in the API
|
||||
displayName - Type: c_char_p
|
||||
Info: Enumeration entry name to be used in GUIs
|
||||
visibility - Type: VmbFeatureVisibility (VmbUint32)
|
||||
Info: GUI visibility
|
||||
tooltip - Type: c_char_p
|
||||
Info: Short description, e.g. for a tooltip
|
||||
description - Type: c_char_p
|
||||
Info: Longer description
|
||||
sfncNamespace - Type: c_char_p
|
||||
Info: Namespace this feature resides in
|
||||
intValue - Type: VmbInt64
|
||||
Info: Integer value of this enumeration entry
|
||||
"""
|
||||
_fields_ = [
|
||||
("name", c_char_p),
|
||||
("displayName", c_char_p),
|
||||
("visibility", VmbUint32),
|
||||
("tooltip", c_char_p),
|
||||
("description", c_char_p),
|
||||
("sfncNamespace", c_char_p),
|
||||
("intValue", VmbInt64)
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
rep = 'VmbFeatureEnumEntry'
|
||||
rep += fmt_repr('(name={}', self.name)
|
||||
rep += fmt_repr(',displayName={}', self.displayName)
|
||||
rep += fmt_enum_repr(',visibility={}', VmbFeatureVisibility, self.visibility)
|
||||
rep += fmt_repr(',tooltip={}', self.tooltip)
|
||||
rep += fmt_repr(',description={}', self.description)
|
||||
rep += fmt_repr(',sfncNamespace={}', self.sfncNamespace)
|
||||
rep += fmt_repr(',intValue={},', self.intValue)
|
||||
rep += ')'
|
||||
return rep
|
||||
|
||||
|
||||
class VmbFrame(ctypes.Structure):
|
||||
"""
|
||||
Frame delivered by Camera
|
||||
Fields (in):
|
||||
buffer - Type: c_void_p
|
||||
Info: Comprises image and ancillary data
|
||||
bufferSize - Type: VmbUint32_t
|
||||
Info: Size of the data buffer
|
||||
context - Type: c_void_p[4]
|
||||
Info: 4 void pointers that can be employed by the user
|
||||
(e.g. for storing handles)
|
||||
|
||||
Fields (out):
|
||||
receiveStatus - Type: VmbFrameStatus (VmbInt32)
|
||||
Info: Resulting status of the receive operation
|
||||
receiveFlags - Type: VmbFrameFlags (VmbUint32)
|
||||
Info: Flags indicating which additional frame
|
||||
information is available
|
||||
imageSize - Type: VmbUint32
|
||||
Info: Size of the image data inside the data buffer
|
||||
ancillarySize - Type: VmbUint32
|
||||
Info: Size of the ancillary data inside the
|
||||
data buffer
|
||||
pixelFormat - Type: VmbPixelFormat (VmbUint32)
|
||||
Info: Pixel format of the image
|
||||
width - Type: VmbUint32
|
||||
Info: Width of an image
|
||||
height - Type: VmbUint32
|
||||
Info: Height of an image
|
||||
offsetX - Type: VmbUint32
|
||||
Info: Horizontal offset of an image
|
||||
offsetY - Type: VmbUint32
|
||||
Info: Vertical offset of an image
|
||||
frameID - Type: VmbUint64
|
||||
Info: Unique ID of this frame in this stream
|
||||
timestamp - Type: VmbUint64
|
||||
Info: Timestamp set by the camera
|
||||
"""
|
||||
_fields_ = [
|
||||
("buffer", c_void_p),
|
||||
("bufferSize", VmbUint32),
|
||||
("context", c_void_p * 4),
|
||||
("receiveStatus", VmbInt32),
|
||||
("receiveFlags", VmbUint32),
|
||||
("imageSize", VmbUint32),
|
||||
("ancillarySize", VmbUint32),
|
||||
("pixelFormat", VmbUint32),
|
||||
("width", VmbUint32),
|
||||
("height", VmbUint32),
|
||||
("offsetX", VmbUint32),
|
||||
("offsetY", VmbUint32),
|
||||
("frameID", VmbUint64),
|
||||
("timestamp", VmbUint64)
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
rep = 'VmbFrame'
|
||||
rep += fmt_repr('(buffer={}', self.buffer)
|
||||
rep += fmt_repr(',bufferSize={}', self.bufferSize)
|
||||
rep += fmt_repr(',context={}', self.context)
|
||||
rep += fmt_enum_repr('receiveStatus: {}', VmbFrameStatus, self.receiveStatus)
|
||||
rep += fmt_flags_repr(',receiveFlags={}', VmbFrameFlags, self.receiveFlags)
|
||||
rep += fmt_repr(',imageSize={}', self.imageSize)
|
||||
rep += fmt_repr(',ancillarySize={}', self.ancillarySize)
|
||||
rep += fmt_enum_repr(',pixelFormat={}', VmbPixelFormat, self.pixelFormat)
|
||||
rep += fmt_repr(',width={}', self.width)
|
||||
rep += fmt_repr(',height={}', self.height)
|
||||
rep += fmt_repr(',offsetX={}', self.offsetX)
|
||||
rep += fmt_repr(',offsetY={}', self.offsetY)
|
||||
rep += fmt_repr(',frameID={}', self.frameID)
|
||||
rep += fmt_repr(',timestamp={}', self.timestamp)
|
||||
rep += ')'
|
||||
return rep
|
||||
|
||||
def deepcopy_skip_ptr(self, memo):
|
||||
result = VmbFrame()
|
||||
memo[id(self)] = result
|
||||
|
||||
result.buffer = None
|
||||
result.bufferSize = 0
|
||||
result.context = (None, None, None, None)
|
||||
|
||||
setattr(result, 'receiveStatus', copy.deepcopy(self.receiveStatus, memo))
|
||||
setattr(result, 'receiveFlags', copy.deepcopy(self.receiveFlags, memo))
|
||||
setattr(result, 'imageSize', copy.deepcopy(self.imageSize, memo))
|
||||
setattr(result, 'ancillarySize', copy.deepcopy(self.ancillarySize, memo))
|
||||
setattr(result, 'pixelFormat', copy.deepcopy(self.pixelFormat, memo))
|
||||
setattr(result, 'width', copy.deepcopy(self.width, memo))
|
||||
setattr(result, 'height', copy.deepcopy(self.height, memo))
|
||||
setattr(result, 'offsetX', copy.deepcopy(self.offsetX, memo))
|
||||
setattr(result, 'offsetY', copy.deepcopy(self.offsetY, memo))
|
||||
setattr(result, 'frameID', copy.deepcopy(self.frameID, memo))
|
||||
setattr(result, 'timestamp', copy.deepcopy(self.timestamp, memo))
|
||||
return result
|
||||
|
||||
|
||||
class VmbFeaturePersistSettings(ctypes.Structure):
|
||||
"""
|
||||
Parameters determining the operation mode of VmbCameraSettingsSave
|
||||
and VmbCameraSettingsLoad
|
||||
Fields:
|
||||
persistType - Type: VmbFeaturePersist (VmbUint32)
|
||||
Info: Type of features that are to be saved
|
||||
maxIterations - Type: VmbUint32
|
||||
Info: Number of iterations when loading settings
|
||||
loggingLevel - Type: VmbUint32
|
||||
Info: Determines level of detail for load/save
|
||||
settings logging
|
||||
"""
|
||||
_fields_ = [
|
||||
("persistType", VmbUint32),
|
||||
("maxIterations", VmbUint32),
|
||||
("loggingLevel", VmbUint32)
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
rep = 'VmbFrame'
|
||||
rep += fmt_enum_repr('(persistType={}', VmbFeaturePersist, self.persistType)
|
||||
rep += fmt_repr(',maxIterations={}', self.maxIterations)
|
||||
rep += fmt_repr(',loggingLevel={}', self.loggingLevel)
|
||||
rep += ')'
|
||||
return rep
|
||||
|
||||
|
||||
G_VIMBA_C_HANDLE = VmbHandle(1)
|
||||
|
||||
VIMBA_C_VERSION = None
|
||||
EXPECTED_VIMBA_C_VERSION = '1.9.0'
|
||||
|
||||
# For detailed information on the signatures see "VimbaC.h"
|
||||
# To improve readability, suppress 'E501 line too long (> 100 characters)'
|
||||
# check of flake8
|
||||
_SIGNATURES = {
|
||||
'VmbVersionQuery': (VmbError, [c_ptr(VmbVersionInfo), VmbUint32]),
|
||||
'VmbStartup': (VmbError, None),
|
||||
'VmbShutdown': (None, None),
|
||||
'VmbCamerasList': (VmbError, [c_ptr(VmbCameraInfo), VmbUint32, c_ptr(VmbUint32), VmbUint32]),
|
||||
'VmbCameraInfoQuery': (VmbError, [c_str, c_ptr(VmbCameraInfo), VmbUint32]),
|
||||
'VmbCameraOpen': (VmbError, [c_str, VmbAccessMode, c_ptr(VmbHandle)]),
|
||||
'VmbCameraClose': (VmbError, [VmbHandle]),
|
||||
'VmbFeaturesList': (VmbError, [VmbHandle, c_ptr(VmbFeatureInfo), VmbUint32, c_ptr(VmbUint32), VmbUint32]), # noqa: E501
|
||||
'VmbFeatureInfoQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbFeatureInfo), VmbUint32]),
|
||||
'VmbFeatureListAffected': (VmbError, [VmbHandle, c_str, c_ptr(VmbFeatureInfo), VmbUint32, c_ptr(VmbUint32), VmbUint32]), # noqa: E501
|
||||
'VmbFeatureListSelected': (VmbError, [VmbHandle, c_str, c_ptr(VmbFeatureInfo), VmbUint32, c_ptr(VmbUint32), VmbUint32]), # noqa: E501
|
||||
'VmbFeatureAccessQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbBool), c_ptr(VmbBool)]),
|
||||
'VmbFeatureIntGet': (VmbError, [VmbHandle, c_str, c_ptr(VmbInt64)]),
|
||||
'VmbFeatureIntSet': (VmbError, [VmbHandle, c_str, VmbInt64]),
|
||||
'VmbFeatureIntRangeQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbInt64), c_ptr(VmbInt64)]), # noqa: E501
|
||||
'VmbFeatureIntIncrementQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbInt64)]),
|
||||
'VmbFeatureFloatGet': (VmbError, [VmbHandle, c_str, c_ptr(VmbDouble)]),
|
||||
'VmbFeatureFloatSet': (VmbError, [VmbHandle, c_str, VmbDouble]),
|
||||
'VmbFeatureFloatRangeQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbDouble), c_ptr(VmbDouble)]),
|
||||
'VmbFeatureFloatIncrementQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbBool), c_ptr(VmbDouble)]), # noqa: E501
|
||||
'VmbFeatureEnumGet': (VmbError, [VmbHandle, c_str, c_ptr(c_str)]),
|
||||
'VmbFeatureEnumSet': (VmbError, [VmbHandle, c_str, c_str]),
|
||||
'VmbFeatureEnumRangeQuery': (VmbError, [VmbHandle, c_str, c_ptr(c_str), VmbUint32, c_ptr(VmbUint32)]), # noqa: E501
|
||||
'VmbFeatureEnumIsAvailable': (VmbError, [VmbHandle, c_str, c_str, c_ptr(VmbBool)]),
|
||||
'VmbFeatureEnumAsInt': (VmbError, [VmbHandle, c_str, c_str, c_ptr(VmbInt64)]),
|
||||
'VmbFeatureEnumAsString': (VmbError, [VmbHandle, c_str, VmbInt64, c_ptr(c_str)]),
|
||||
'VmbFeatureEnumEntryGet': (VmbError, [VmbHandle, c_str, c_str, c_ptr(VmbFeatureEnumEntry), VmbUint32]), # noqa: E501
|
||||
'VmbFeatureStringGet': (VmbError, [VmbHandle, c_str, c_str, VmbUint32, c_ptr(VmbUint32)]), # noqa: E501
|
||||
'VmbFeatureStringSet': (VmbError, [VmbHandle, c_str, c_str]),
|
||||
'VmbFeatureStringMaxlengthQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbUint32)]),
|
||||
'VmbFeatureBoolGet': (VmbError, [VmbHandle, c_str, c_ptr(VmbBool)]),
|
||||
'VmbFeatureBoolSet': (VmbError, [VmbHandle, c_str, VmbBool]),
|
||||
'VmbFeatureCommandRun': (VmbError, [VmbHandle, c_str]),
|
||||
'VmbFeatureCommandIsDone': (VmbError, [VmbHandle, c_str, c_ptr(VmbBool)]),
|
||||
'VmbFeatureRawGet': (VmbError, [VmbHandle, c_str, c_str, VmbUint32, c_ptr(VmbUint32)]),
|
||||
'VmbFeatureRawSet': (VmbError, [VmbHandle, c_str, c_str, VmbUint32]),
|
||||
'VmbFeatureRawLengthQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbUint32)]),
|
||||
'VmbFeatureInvalidationRegister': (VmbError, [VmbHandle, c_str, c_void_p, c_void_p]), # noqa: E501
|
||||
'VmbFeatureInvalidationUnregister': (VmbError, [VmbHandle, c_str, c_void_p]),
|
||||
'VmbFrameAnnounce': (VmbError, [VmbHandle, c_ptr(VmbFrame), VmbUint32]),
|
||||
'VmbFrameRevoke': (VmbError, [VmbHandle, c_ptr(VmbFrame)]),
|
||||
'VmbFrameRevokeAll': (VmbError, [VmbHandle]),
|
||||
'VmbCaptureStart': (VmbError, [VmbHandle]),
|
||||
'VmbCaptureEnd': (VmbError, [VmbHandle]),
|
||||
'VmbCaptureFrameQueue': (VmbError, [VmbHandle, c_ptr(VmbFrame), c_void_p]),
|
||||
'VmbCaptureFrameWait': (VmbError, [VmbHandle, c_ptr(VmbFrame), VmbUint32]),
|
||||
'VmbCaptureQueueFlush': (VmbError, [VmbHandle]),
|
||||
'VmbInterfacesList': (VmbError, [c_ptr(VmbInterfaceInfo), VmbUint32, c_ptr(VmbUint32), VmbUint32]), # noqa: E501
|
||||
'VmbInterfaceOpen': (VmbError, [c_str, c_ptr(VmbHandle)]),
|
||||
'VmbInterfaceClose': (VmbError, [VmbHandle]),
|
||||
'VmbAncillaryDataOpen': (VmbError, [c_ptr(VmbFrame), c_ptr(VmbHandle)]),
|
||||
'VmbAncillaryDataClose': (VmbError, [VmbHandle]),
|
||||
'VmbMemoryRead': (VmbError, [VmbHandle, VmbUint64, VmbUint32, c_str, c_ptr(VmbUint32)]),
|
||||
'VmbMemoryWrite': (VmbError, [VmbHandle, VmbUint64, VmbUint32, c_str, c_ptr(VmbUint32)]),
|
||||
'VmbRegistersRead': (VmbError, [VmbHandle, VmbUint32, c_ptr(VmbUint64), c_ptr(VmbUint64), c_ptr(VmbUint32)]), # noqa: E501
|
||||
'VmbRegistersWrite': (VmbError, [VmbHandle, VmbUint32, c_ptr(VmbUint64), c_ptr(VmbUint64), c_ptr(VmbUint32)]), # noqa: E501
|
||||
'VmbCameraSettingsSave': (VmbError, [VmbHandle, c_str, c_ptr(VmbFeaturePersistSettings), VmbUint32]), # noqa: E501
|
||||
'VmbCameraSettingsLoad': (VmbError, [VmbHandle, c_str, c_ptr(VmbFeaturePersistSettings), VmbUint32]) # noqa: E501
|
||||
}
|
||||
|
||||
|
||||
def _attach_signatures(lib_handle):
|
||||
global _SIGNATURES
|
||||
|
||||
for function_name, signature in _SIGNATURES.items():
|
||||
fn = getattr(lib_handle, function_name)
|
||||
fn.restype, fn.argtypes = signature
|
||||
fn.errcheck = _eval_vmberror
|
||||
|
||||
return lib_handle
|
||||
|
||||
|
||||
def _check_version(lib_handle):
|
||||
global EXPECTED_VIMBA_C_VERSION
|
||||
global VIMBA_C_VERSION
|
||||
|
||||
v = VmbVersionInfo()
|
||||
lib_handle.VmbVersionQuery(byref(v), sizeof(v))
|
||||
|
||||
VIMBA_C_VERSION = str(v)
|
||||
|
||||
loaded_version = (v.major, v.minor, v.patch)
|
||||
expected_version = tuple(map(int, EXPECTED_VIMBA_C_VERSION.split(".")))
|
||||
# major and minor version must be equal, patch version may be equal or greater
|
||||
if not(loaded_version[0:2] == expected_version[0:2] and
|
||||
loaded_version[2] >= expected_version[2]):
|
||||
msg = 'Invalid VimbaC Version: Expected: {}, Found:{}'
|
||||
raise VimbaSystemError(msg.format(EXPECTED_VIMBA_C_VERSION, VIMBA_C_VERSION))
|
||||
|
||||
return lib_handle
|
||||
|
||||
|
||||
def _eval_vmberror(result: VmbError, func: Callable[..., Any], *args: Tuple[Any, ...]):
|
||||
if result not in (VmbError.Success, None):
|
||||
raise VimbaCError(result)
|
||||
|
||||
|
||||
_lib_instance = _check_version(_attach_signatures(load_vimba_lib('VimbaC')))
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
def call_vimba_c(func_name: str, *args):
|
||||
"""This function encapsulates the entire VimbaC access.
|
||||
|
||||
For Details on valid function signatures see the 'VimbaC.h'.
|
||||
|
||||
Arguments:
|
||||
func_name: The function name from VimbaC to be called.
|
||||
args: Varargs passed directly to the underlaying C-Function.
|
||||
|
||||
Raises:
|
||||
TypeError if given are do not match the signature of the function.
|
||||
AttributeError if func with name 'func_name' does not exist.
|
||||
VimbaCError if the function call is valid but neither None or VmbError.Success was returned.
|
||||
|
||||
The following functions of VimbaC can be executed:
|
||||
VmbVersionQuery
|
||||
VmbStartup
|
||||
VmbShutdown
|
||||
VmbCamerasList
|
||||
VmbCameraInfoQuery
|
||||
VmbCameraOpen
|
||||
VmbCameraClose
|
||||
VmbFeaturesList
|
||||
VmbFeatureInfoQuery
|
||||
VmbFeatureListAffected
|
||||
VmbFeatureListSelected
|
||||
VmbFeatureAccessQuery
|
||||
VmbFeatureIntGet
|
||||
VmbFeatureIntSet
|
||||
VmbFeatureIntRangeQuery
|
||||
VmbFeatureIntIncrementQuery
|
||||
VmbFeatureFloatGet
|
||||
VmbFeatureFloatSet
|
||||
VmbFeatureFloatRangeQuery
|
||||
VmbFeatureFloatIncrementQuery
|
||||
VmbFeatureEnumGet
|
||||
VmbFeatureEnumSet
|
||||
VmbFeatureEnumRangeQuery
|
||||
VmbFeatureEnumIsAvailable
|
||||
VmbFeatureEnumAsInt
|
||||
VmbFeatureEnumAsString
|
||||
VmbFeatureEnumEntryGet
|
||||
VmbFeatureStringGet
|
||||
VmbFeatureStringSet
|
||||
VmbFeatureStringMaxlengthQuery
|
||||
VmbFeatureBoolGet
|
||||
VmbFeatureBoolSet
|
||||
VmbFeatureCommandRun
|
||||
VmbFeatureCommandIsDone
|
||||
VmbFeatureRawGet
|
||||
VmbFeatureRawSet
|
||||
VmbFeatureRawLengthQuery
|
||||
VmbFeatureInvalidationRegister
|
||||
VmbFeatureInvalidationUnregister
|
||||
VmbFrameAnnounce
|
||||
VmbFrameRevoke
|
||||
VmbFrameRevokeAll
|
||||
VmbCaptureStart
|
||||
VmbCaptureEnd
|
||||
VmbCaptureFrameQueue
|
||||
VmbCaptureFrameWait
|
||||
VmbCaptureQueueFlush
|
||||
VmbInterfacesList
|
||||
VmbInterfaceOpen
|
||||
VmbInterfaceClose
|
||||
VmbAncillaryDataOpen
|
||||
VmbAncillaryDataClose
|
||||
VmbMemoryRead
|
||||
VmbMemoryWrite
|
||||
VmbRegistersRead
|
||||
VmbRegistersWrite
|
||||
VmbCameraSettingsSave
|
||||
VmbCameraSettingsLoad
|
||||
"""
|
||||
global _lib_instance
|
||||
getattr(_lib_instance, func_name)(*args)
|
||||
|
||||
|
||||
def build_callback_type(*args):
|
||||
global _lib_instance
|
||||
|
||||
lib_type = type(_lib_instance)
|
||||
|
||||
if lib_type == ctypes.CDLL:
|
||||
return ctypes.CFUNCTYPE(*args)
|
||||
|
||||
elif lib_type == ctypes.WinDLL:
|
||||
return ctypes.WINFUNCTYPE(*args)
|
||||
|
||||
else:
|
||||
raise VimbaSystemError('Unknown Library Type. Abort.')
|
||||
611
Vimba_6_0/VimbaPython/Source/vimba/c_binding/vimba_common.py
Normal file
611
Vimba_6_0/VimbaPython/Source/vimba/c_binding/vimba_common.py
Normal file
@@ -0,0 +1,611 @@
|
||||
"""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 ctypes
|
||||
import enum
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import functools
|
||||
from typing import Tuple, List
|
||||
from ..error import VimbaSystemError
|
||||
|
||||
|
||||
__all__ = [
|
||||
'Int32Enum',
|
||||
'Uint32Enum',
|
||||
'VmbInt8',
|
||||
'VmbUint8',
|
||||
'VmbInt16',
|
||||
'VmbUint16',
|
||||
'VmbInt32',
|
||||
'VmbUint32',
|
||||
'VmbInt64',
|
||||
'VmbUint64',
|
||||
'VmbHandle',
|
||||
'VmbBool',
|
||||
'VmbUchar',
|
||||
'VmbFloat',
|
||||
'VmbDouble',
|
||||
'VmbError',
|
||||
'VimbaCError',
|
||||
'VmbPixelFormat',
|
||||
'decode_cstr',
|
||||
'decode_flags',
|
||||
'fmt_repr',
|
||||
'fmt_enum_repr',
|
||||
'fmt_flags_repr',
|
||||
'load_vimba_lib'
|
||||
]
|
||||
|
||||
|
||||
# Types
|
||||
class Int32Enum(enum.IntEnum):
|
||||
@classmethod
|
||||
def from_param(cls, obj):
|
||||
return ctypes.c_int(obj)
|
||||
|
||||
|
||||
class Uint32Enum(enum.IntEnum):
|
||||
@classmethod
|
||||
def from_param(cls, obj):
|
||||
return ctypes.c_uint(obj)
|
||||
|
||||
|
||||
# Aliases for vmb base types
|
||||
VmbInt8 = ctypes.c_byte
|
||||
VmbUint8 = ctypes.c_ubyte
|
||||
VmbInt16 = ctypes.c_short
|
||||
VmbUint16 = ctypes.c_ushort
|
||||
VmbInt32 = ctypes.c_int
|
||||
VmbUint32 = ctypes.c_uint
|
||||
VmbInt64 = ctypes.c_longlong
|
||||
VmbUint64 = ctypes.c_ulonglong
|
||||
VmbHandle = ctypes.c_void_p
|
||||
VmbBool = ctypes.c_bool
|
||||
VmbUchar = ctypes.c_char
|
||||
VmbFloat = ctypes.c_float
|
||||
VmbDouble = ctypes.c_double
|
||||
|
||||
|
||||
class VmbError(Int32Enum):
|
||||
"""
|
||||
Enum containing error types returned
|
||||
Success - No error
|
||||
InternalFault - Unexpected fault in VimbaC or driver
|
||||
ApiNotStarted - VmbStartup() was not called before the current
|
||||
command
|
||||
NotFound - The designated instance (camera, feature etc.)
|
||||
cannot be found
|
||||
BadHandle - The given handle is not valid
|
||||
DeviceNotOpen - Device was not opened for usage
|
||||
InvalidAccess - Operation is invalid with the current access mode
|
||||
BadParameter - One of the parameters is invalid (usually an illegal
|
||||
pointer)
|
||||
StructSize - The given struct size is not valid for this version
|
||||
of the API
|
||||
MoreData - More data available in a string/list than space is
|
||||
provided
|
||||
WrongType - Wrong feature type for this access function
|
||||
InvalidValue - The value is not valid; Either out of bounds or not
|
||||
an increment of the minimum
|
||||
Timeout - Timeout during wait
|
||||
Other - Other error
|
||||
Resources - Resources not available (e.g. memory)
|
||||
InvalidCall - Call is invalid in the current context (callback)
|
||||
NoTL - No transport layers are found
|
||||
NotImplemented_ - API feature is not implemented
|
||||
NotSupported - API feature is not supported
|
||||
Incomplete - A multiple registers read or write is partially
|
||||
completed
|
||||
IO - low level IO error in transport layer
|
||||
"""
|
||||
Success = 0
|
||||
InternalFault = -1
|
||||
ApiNotStarted = -2
|
||||
NotFound = -3
|
||||
BadHandle = -4
|
||||
DeviceNotOpen = -5
|
||||
InvalidAccess = -6
|
||||
BadParameter = -7
|
||||
StructSize = -8
|
||||
MoreData = -9
|
||||
WrongType = -10
|
||||
InvalidValue = -11
|
||||
Timeout = -12
|
||||
Other = -13
|
||||
Resources = -14
|
||||
InvalidCall = -15
|
||||
NoTL = -16
|
||||
NotImplemented_ = -17
|
||||
NotSupported = -18
|
||||
Incomplete = -19
|
||||
IO = -20
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class _VmbPixel(Uint32Enum):
|
||||
Mono = 0x01000000
|
||||
Color = 0x02000000
|
||||
|
||||
|
||||
class _VmbPixelOccupy(Uint32Enum):
|
||||
Bit8 = 0x00080000
|
||||
Bit10 = 0x000A0000
|
||||
Bit12 = 0x000C0000
|
||||
Bit14 = 0x000E0000
|
||||
Bit16 = 0x00100000
|
||||
Bit24 = 0x00180000
|
||||
Bit32 = 0x00200000
|
||||
Bit48 = 0x00300000
|
||||
Bit64 = 0x00400000
|
||||
|
||||
|
||||
class VmbPixelFormat(Uint32Enum):
|
||||
"""
|
||||
Enum containing Pixelformats
|
||||
Mono formats:
|
||||
Mono8 - Monochrome, 8 bits (PFNC:Mono8)
|
||||
Mono10 - Monochrome, 10 bits in 16 bits (PFNC:Mono10)
|
||||
Mono10p - Monochrome, 4x10 bits continuously packed in 40 bits
|
||||
(PFNC:Mono10p)
|
||||
Mono12 - Monochrome, 12 bits in 16 bits (PFNC:Mono12)
|
||||
Mono12Packed - Monochrome, 2x12 bits in 24 bits (GEV:Mono12Packed)
|
||||
Mono12p - Monochrome, 2x12 bits continuously packed in 24 bits
|
||||
(PFNC:Mono12p)
|
||||
Mono14 - Monochrome, 14 bits in 16 bits (PFNC:Mono14)
|
||||
Mono16 - Monochrome, 16 bits (PFNC:Mono16)
|
||||
|
||||
Bayer formats:
|
||||
BayerGR8 - Bayer-color, 8 bits, starting with GR line
|
||||
(PFNC:BayerGR8)
|
||||
BayerRG8 - Bayer-color, 8 bits, starting with RG line
|
||||
(PFNC:BayerRG8)
|
||||
BayerGB8 - Bayer-color, 8 bits, starting with GB line
|
||||
(PFNC:BayerGB8)
|
||||
BayerBG8 - Bayer-color, 8 bits, starting with BG line
|
||||
(PFNC:BayerBG8)
|
||||
BayerGR10 - Bayer-color, 10 bits in 16 bits, starting with GR
|
||||
line (PFNC:BayerGR10)
|
||||
BayerRG10 - Bayer-color, 10 bits in 16 bits, starting with RG
|
||||
line (PFNC:BayerRG10)
|
||||
BayerGB10 - Bayer-color, 10 bits in 16 bits, starting with GB
|
||||
line (PFNC:BayerGB10)
|
||||
BayerBG10 - Bayer-color, 10 bits in 16 bits, starting with BG
|
||||
line (PFNC:BayerBG10)
|
||||
BayerGR12 - Bayer-color, 12 bits in 16 bits, starting with GR
|
||||
line (PFNC:BayerGR12)
|
||||
BayerRG12 - Bayer-color, 12 bits in 16 bits, starting with RG
|
||||
line (PFNC:BayerRG12)
|
||||
BayerGB12 - Bayer-color, 12 bits in 16 bits, starting with GB
|
||||
line (PFNC:BayerGB12)
|
||||
BayerBG12 - Bayer-color, 12 bits in 16 bits, starting with BG
|
||||
line (PFNC:BayerBG12)
|
||||
BayerGR12Packed - Bayer-color, 2x12 bits in 24 bits, starting with GR
|
||||
line (GEV:BayerGR12Packed)
|
||||
BayerRG12Packed - Bayer-color, 2x12 bits in 24 bits, starting with RG
|
||||
line (GEV:BayerRG12Packed)
|
||||
BayerGB12Packed - Bayer-color, 2x12 bits in 24 bits, starting with GB
|
||||
line (GEV:BayerGB12Packed)
|
||||
BayerBG12Packed - Bayer-color, 2x12 bits in 24 bits, starting with BG
|
||||
line (GEV:BayerBG12Packed)
|
||||
BayerGR10p - Bayer-color, 4x10 bits continuously packed in 40
|
||||
bits, starting with GR line (PFNC:BayerGR10p)
|
||||
BayerRG10p - Bayer-color, 4x10 bits continuously packed in 40
|
||||
bits, starting with RG line (PFNC:BayerRG10p)
|
||||
BayerGB10p - Bayer-color, 4x10 bits continuously packed in 40
|
||||
bits, starting with GB line (PFNC:BayerGB10p)
|
||||
BayerBG10p - Bayer-color, 4x10 bits continuously packed in 40
|
||||
bits, starting with BG line (PFNC:BayerBG10p)
|
||||
BayerGR12p - Bayer-color, 2x12 bits continuously packed in 24
|
||||
bits, starting with GR line (PFNC:BayerGR12p)
|
||||
BayerRG12p - Bayer-color, 2x12 bits continuously packed in 24
|
||||
bits, starting with RG line (PFNC:BayerRG12p)
|
||||
BayerGB12p - Bayer-color, 2x12 bits continuously packed in 24
|
||||
bits, starting with GB line (PFNC:BayerGB12p)
|
||||
BayerBG12p - Bayer-color, 2x12 bits continuously packed in 24
|
||||
bits, starting with BG line (PFNC:BayerBG12p)
|
||||
BayerGR16 - Bayer-color, 16 bits, starting with GR line
|
||||
(PFNC:BayerGR16)
|
||||
BayerRG16 - Bayer-color, 16 bits, starting with RG line
|
||||
(PFNC:BayerRG16)
|
||||
BayerGB16 - Bayer-color, 16 bits, starting with GB line
|
||||
(PFNC:BayerGB16)
|
||||
BayerBG16 - Bayer-color, 16 bits, starting with BG line
|
||||
(PFNC:BayerBG16)
|
||||
|
||||
RGB formats:
|
||||
Rgb8 - RGB, 8 bits x 3 (PFNC:RGB8)
|
||||
Bgr8 - BGR, 8 bits x 3 (PFNC:Bgr8)
|
||||
Rgb10 - RGB, 10 bits in 16 bits x 3 (PFNC:RGB10)
|
||||
Bgr10 - BGR, 10 bits in 16 bits x 3 (PFNC:BGR10)
|
||||
Rgb12 - RGB, 12 bits in 16 bits x 3 (PFNC:RGB12)
|
||||
Bgr12 - BGR, 12 bits in 16 bits x 3 (PFNC:BGR12)
|
||||
Rgb14 - RGB, 14 bits in 16 bits x 3 (PFNC:RGB14)
|
||||
Bgr14 - BGR, 14 bits in 16 bits x 3 (PFNC:BGR14)
|
||||
Rgb16 - RGB, 16 bits x 3 (PFNC:RGB16)
|
||||
Bgr16 - BGR, 16 bits x 3 (PFNC:BGR16)
|
||||
|
||||
RGBA formats:
|
||||
Argb8 - ARGB, 8 bits x 4 (PFNC:RGBa8)
|
||||
Rgba8 - RGBA, 8 bits x 4, legacy name
|
||||
Bgra8 - BGRA, 8 bits x 4 (PFNC:BGRa8)
|
||||
Rgba10 - RGBA, 10 bits in 16 bits x 4
|
||||
Bgra10 - BGRA, 10 bits in 16 bits x 4
|
||||
Rgba12 - RGBA, 12 bits in 16 bits x 4
|
||||
Bgra12 - BGRA, 12 bits in 16 bits x 4
|
||||
Rgba14 - RGBA, 14 bits in 16 bits x 4
|
||||
Bgra14 - BGRA, 14 bits in 16 bits x 4
|
||||
Rgba16 - RGBA, 16 bits x 4
|
||||
Bgra16 - BGRA, 16 bits x 4
|
||||
|
||||
YUV/YCbCr formats:
|
||||
Yuv411 - YUV 411 with 8 bits (GEV:YUV411Packed)
|
||||
Yuv422 - YUV 422 with 8 bits (GEV:YUV422Packed)
|
||||
Yuv444 - YUV 444 with 8 bits (GEV:YUV444Packed)
|
||||
YCbCr411_8_CbYYCrYY - Y´CbCr 411 with 8 bits
|
||||
(PFNC:YCbCr411_8_CbYYCrYY) - identical to Yuv411
|
||||
YCbCr422_8_CbYCrY - Y´CbCr 422 with 8 bits
|
||||
(PFNC:YCbCr422_8_CbYCrY) - identical to Yuv422
|
||||
YCbCr8_CbYCr - Y´CbCr 444 with 8 bits
|
||||
(PFNC:YCbCr8_CbYCr) - identical to Yuv444
|
||||
"""
|
||||
None_ = 0
|
||||
Mono8 = _VmbPixel.Mono | _VmbPixelOccupy.Bit8 | 0x0001
|
||||
Mono10 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0003
|
||||
Mono10p = _VmbPixel.Mono | _VmbPixelOccupy.Bit10 | 0x0046
|
||||
Mono12 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0005
|
||||
Mono12Packed = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x0006
|
||||
Mono12p = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x0047
|
||||
Mono14 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0025
|
||||
Mono16 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0007
|
||||
BayerGR8 = _VmbPixel.Mono | _VmbPixelOccupy.Bit8 | 0x0008
|
||||
BayerRG8 = _VmbPixel.Mono | _VmbPixelOccupy.Bit8 | 0x0009
|
||||
BayerGB8 = _VmbPixel.Mono | _VmbPixelOccupy.Bit8 | 0x000A
|
||||
BayerBG8 = _VmbPixel.Mono | _VmbPixelOccupy.Bit8 | 0x000B
|
||||
BayerGR10 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x000C
|
||||
BayerRG10 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x000D
|
||||
BayerGB10 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x000E
|
||||
BayerBG10 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x000F
|
||||
BayerGR12 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0010
|
||||
BayerRG12 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0011
|
||||
BayerGB12 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0012
|
||||
BayerBG12 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0013
|
||||
BayerGR12Packed = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x002A
|
||||
BayerRG12Packed = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x002B
|
||||
BayerGB12Packed = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x002C
|
||||
BayerBG12Packed = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x002D
|
||||
BayerGR10p = _VmbPixel.Mono | _VmbPixelOccupy.Bit10 | 0x0056
|
||||
BayerRG10p = _VmbPixel.Mono | _VmbPixelOccupy.Bit10 | 0x0058
|
||||
BayerGB10p = _VmbPixel.Mono | _VmbPixelOccupy.Bit10 | 0x0054
|
||||
BayerBG10p = _VmbPixel.Mono | _VmbPixelOccupy.Bit10 | 0x0052
|
||||
BayerGR12p = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x0057
|
||||
BayerRG12p = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x0059
|
||||
BayerGB12p = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x0055
|
||||
BayerBG12p = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x0053
|
||||
BayerGR16 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x002E
|
||||
BayerRG16 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x002F
|
||||
BayerGB16 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0030
|
||||
BayerBG16 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0031
|
||||
Rgb8 = _VmbPixel.Color | _VmbPixelOccupy.Bit24 | 0x0014
|
||||
Bgr8 = _VmbPixel.Color | _VmbPixelOccupy.Bit24 | 0x0015
|
||||
Rgb10 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x0018
|
||||
Bgr10 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x0019
|
||||
Rgb12 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x001A
|
||||
Bgr12 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x001B
|
||||
Rgb14 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x005E
|
||||
Bgr14 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x004A
|
||||
Rgb16 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x0033
|
||||
Bgr16 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x004B
|
||||
Argb8 = _VmbPixel.Color | _VmbPixelOccupy.Bit32 | 0x0016
|
||||
Rgba8 = Argb8
|
||||
Bgra8 = _VmbPixel.Color | _VmbPixelOccupy.Bit32 | 0x0017
|
||||
Rgba10 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x005F
|
||||
Bgra10 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x004C
|
||||
Rgba12 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x0061
|
||||
Bgra12 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x004E
|
||||
Rgba14 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x0063
|
||||
Bgra14 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x0050
|
||||
Rgba16 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x0064
|
||||
Bgra16 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x0051
|
||||
Yuv411 = _VmbPixel.Color | _VmbPixelOccupy.Bit12 | 0x001E
|
||||
Yuv422 = _VmbPixel.Color | _VmbPixelOccupy.Bit16 | 0x001F
|
||||
Yuv444 = _VmbPixel.Color | _VmbPixelOccupy.Bit24 | 0x0020
|
||||
YCbCr411_8_CbYYCrYY = _VmbPixel.Color | _VmbPixelOccupy.Bit12 | 0x003C
|
||||
YCbCr422_8_CbYCrY = _VmbPixel.Color | _VmbPixelOccupy.Bit16 | 0x0043
|
||||
YCbCr8_CbYCr = _VmbPixel.Color | _VmbPixelOccupy.Bit24 | 0x003A
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VimbaCError(Exception):
|
||||
"""Error Type containing an error code from the C-Layer. This error code is highly context
|
||||
sensitive. All wrapped C-Functions that do not return VmbError.Success or None must
|
||||
raise a VimbaCError and the surrounding code must deal if the Error is possible.
|
||||
"""
|
||||
|
||||
def __init__(self, c_error: VmbError):
|
||||
super().__init__(repr(c_error))
|
||||
self.__c_error = c_error
|
||||
|
||||
def __str__(self):
|
||||
return repr(self)
|
||||
|
||||
def __repr__(self):
|
||||
return 'VimbaCError({})'.format(repr(self.__c_error))
|
||||
|
||||
def get_error_code(self) -> VmbError:
|
||||
""" Get contained Error Code """
|
||||
return self.__c_error
|
||||
|
||||
|
||||
# Utility Functions
|
||||
def _split_into_powers_of_two(num: int) -> Tuple[int, ...]:
|
||||
result = []
|
||||
for mask in [1 << i for i in range(32)]:
|
||||
if mask & num:
|
||||
result.append(mask)
|
||||
|
||||
if not result:
|
||||
result.append(0)
|
||||
|
||||
return tuple(result)
|
||||
|
||||
|
||||
def _split_flags_into_enum(num: int, enum_type):
|
||||
return [enum_type(val) for val in _split_into_powers_of_two(num)]
|
||||
|
||||
|
||||
def _repr_flags_list(enum_type, flag_val: int):
|
||||
values = _split_flags_into_enum(flag_val, enum_type)
|
||||
|
||||
if values:
|
||||
def fold_func(acc, arg):
|
||||
return '{} {}'.format(acc, repr(arg))
|
||||
|
||||
return functools.reduce(fold_func, values, '')
|
||||
|
||||
else:
|
||||
return '{}'.format(repr(enum_type(0)))
|
||||
|
||||
|
||||
def decode_cstr(val: bytes) -> str:
|
||||
"""Converts c_char_p stored in interface structures to a str.
|
||||
|
||||
Arguments:
|
||||
val - Byte sequence to convert into str.
|
||||
|
||||
Returns:
|
||||
str represented by 'val'
|
||||
"""
|
||||
return val.decode() if val else ''
|
||||
|
||||
|
||||
def decode_flags(enum_type, enum_val: int):
|
||||
"""Splits C-styled bit mask into a set of flags from a given Enumeration.
|
||||
|
||||
Arguments:
|
||||
enum_val - Bit mask to decode.
|
||||
enum_type - Enum Type represented within 'enum_val'
|
||||
|
||||
Returns:
|
||||
A set of all values of enum_type occurring in enum_val.
|
||||
|
||||
Raises:
|
||||
Attribute error a set value is not within the given 'enum_type'.
|
||||
"""
|
||||
|
||||
return tuple(_split_flags_into_enum(enum_val, enum_type))
|
||||
|
||||
|
||||
def fmt_repr(fmt: str, val):
|
||||
"""Append repr to a format string."""
|
||||
return fmt.format(repr(val))
|
||||
|
||||
|
||||
def fmt_enum_repr(fmt: str, enum_type, enum_val):
|
||||
"""Append repr of a given enum type to a format string.
|
||||
|
||||
Arguments:
|
||||
fmt - Format string
|
||||
enum_type - Enum Type to construct.
|
||||
enum_val - Enum value.
|
||||
|
||||
Returns:
|
||||
formatted string
|
||||
"""
|
||||
return fmt.format(repr(enum_type(enum_val)))
|
||||
|
||||
|
||||
def fmt_flags_repr(fmt: str, enum_type, enum_val):
|
||||
"""Append repr of a c-style flag value in the form of a set containing
|
||||
all bits set from a given enum_type.
|
||||
|
||||
Arguments:
|
||||
fmt - Format string
|
||||
enum_type - Enum Type to construct.
|
||||
enum_val - Enum value.
|
||||
|
||||
Returns:
|
||||
formatted string
|
||||
"""
|
||||
return fmt.format(_repr_flags_list(enum_type, enum_val))
|
||||
|
||||
|
||||
def load_vimba_lib(vimba_project: str):
|
||||
""" Load shared library shipped with the Vimba installation
|
||||
|
||||
Arguments:
|
||||
vimba_project - Library name without prefix or extension
|
||||
|
||||
Return:
|
||||
CDLL or WinDLL Handle on loaded library
|
||||
|
||||
Raises:
|
||||
VimbaSystemError if given library could not be loaded.
|
||||
"""
|
||||
|
||||
platform_handlers = {
|
||||
'linux': _load_under_linux,
|
||||
'win32': _load_under_windows
|
||||
}
|
||||
|
||||
if sys.platform not in platform_handlers:
|
||||
msg = 'Abort. Unsupported Platform ({}) detected.'
|
||||
raise VimbaSystemError(msg.format(sys.platform))
|
||||
|
||||
return platform_handlers[sys.platform](vimba_project)
|
||||
|
||||
|
||||
def _load_under_linux(vimba_project: str):
|
||||
# Construct VimbaHome based on TL installation paths
|
||||
path_list: List[str] = []
|
||||
tl32_path = os.environ.get('GENICAM_GENTL32_PATH', "")
|
||||
if tl32_path:
|
||||
path_list += tl32_path.split(':')
|
||||
tl64_path = os.environ.get('GENICAM_GENTL64_PATH', "")
|
||||
if tl64_path:
|
||||
path_list += tl64_path.split(':')
|
||||
|
||||
# Remove empty strings from path_list if there are any.
|
||||
# Necessary because the GENICAM_GENTLXX_PATH variable might start with a :
|
||||
path_list = [path for path in path_list if path]
|
||||
|
||||
# Early return if required variables are not set.
|
||||
if not path_list:
|
||||
raise VimbaSystemError('No TL detected. Please verify Vimba installation.')
|
||||
|
||||
vimba_home_candidates: List[str] = []
|
||||
for path in path_list:
|
||||
vimba_home = os.path.dirname(os.path.dirname(os.path.dirname(path)))
|
||||
|
||||
if vimba_home not in vimba_home_candidates:
|
||||
vimba_home_candidates.append(vimba_home)
|
||||
|
||||
# Select the most likely directory from the candidates
|
||||
vimba_home = _select_vimba_home(vimba_home_candidates)
|
||||
|
||||
arch = platform.machine()
|
||||
|
||||
# Linux x86 64 Bit (Requires additional interpreter version check)
|
||||
if arch == 'x86_64':
|
||||
dir_ = 'x86_64bit' if _is_python_64_bit() else 'x86_32bit'
|
||||
|
||||
# Linux x86 32 Bit
|
||||
elif arch in ('i386', 'i686'):
|
||||
dir_ = 'x86_32bit'
|
||||
|
||||
# Linux arm 64 Bit (Requires additional interpreter version check)
|
||||
elif arch == 'aarch64':
|
||||
dir_ = 'arm_64bit' if _is_python_64_bit() else 'arm_32bit'
|
||||
|
||||
# Linux arm 32 Bit:
|
||||
elif arch == 'armv7l':
|
||||
dir_ = 'arm_32bit'
|
||||
|
||||
else:
|
||||
raise VimbaSystemError('Unknown Architecture \'{}\'. Abort'.format(arch))
|
||||
|
||||
lib_name = 'lib{}.so'.format(vimba_project)
|
||||
lib_path = os.path.join(vimba_home, vimba_project, 'DynamicLib', dir_, lib_name)
|
||||
|
||||
try:
|
||||
lib = ctypes.cdll.LoadLibrary(lib_path)
|
||||
|
||||
except OSError as e:
|
||||
msg = 'Failed to load library \'{}\'. Please verify Vimba installation.'
|
||||
raise VimbaSystemError(msg.format(lib_path)) from e
|
||||
|
||||
return lib
|
||||
|
||||
|
||||
def _load_under_windows(vimba_project: str):
|
||||
vimba_home = os.environ.get('VIMBA_HOME')
|
||||
|
||||
if vimba_home is None:
|
||||
raise VimbaSystemError('Variable VIMBA_HOME not set. Please verify Vimba installation.')
|
||||
|
||||
load_64bit = True if (platform.machine() == 'AMD64') and _is_python_64_bit() else False
|
||||
lib_name = '{}.dll'.format(vimba_project)
|
||||
lib_path = os.path.join(vimba_home, vimba_project, 'Bin', 'Win64' if load_64bit else 'Win32',
|
||||
lib_name)
|
||||
|
||||
try:
|
||||
# Load Library with 64 Bit and use cdecl call convention
|
||||
if load_64bit:
|
||||
lib = ctypes.cdll.LoadLibrary(lib_path)
|
||||
|
||||
# Load Library with 32 Bit and use stdcall call convention
|
||||
else:
|
||||
# Tell mypy to ignore this line to allow type checking on both windows and linux as
|
||||
# windll is not available on linux and would therefore produce an error there
|
||||
lib = ctypes.windll.LoadLibrary(lib_path) # type: ignore
|
||||
|
||||
except OSError as e:
|
||||
msg = 'Failed to load library \'{}\'. Please verify Vimba installation.'
|
||||
raise VimbaSystemError(msg.format(lib_path)) from e
|
||||
|
||||
return lib
|
||||
|
||||
|
||||
def _select_vimba_home(candidates: List[str]) -> str:
|
||||
"""
|
||||
Select the most likely candidate for VIMBA_HOME from the given list of
|
||||
candidates
|
||||
|
||||
Arguments:
|
||||
candidates - List of strings pointing to possible vimba home directories
|
||||
|
||||
Return:
|
||||
Path that represents the most likely VIMBA_HOME directory
|
||||
|
||||
Raises:
|
||||
VimbaSystemError if multiple VIMBA_HOME directories were found in candidates
|
||||
"""
|
||||
most_likely_candidates = []
|
||||
for candidate in candidates:
|
||||
if 'vimba' in candidate.lower():
|
||||
most_likely_candidates.append(candidate)
|
||||
|
||||
if len(most_likely_candidates) == 0:
|
||||
raise VimbaSystemError('No suitable Vimba installation found. The following paths '
|
||||
'were considered: {}'.format(candidates))
|
||||
elif len(most_likely_candidates) > 1:
|
||||
raise VimbaSystemError('Multiple Vimba installations found. Can\'t decide which to select: '
|
||||
'{}'.format(most_likely_candidates))
|
||||
|
||||
return most_likely_candidates[0]
|
||||
|
||||
|
||||
def _is_python_64_bit() -> bool:
|
||||
# Query if the currently running python interpreter is build as 64 bit binary.
|
||||
# The default method of getting this information seems to be rather hacky
|
||||
# (check if maxint > 2^32) but it seems to be the way to do this....
|
||||
return True if sys.maxsize > 2**32 else False
|
||||
@@ -0,0 +1,569 @@
|
||||
"""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 ctypes
|
||||
import sys
|
||||
from ctypes import byref, sizeof, c_char_p, POINTER as c_ptr
|
||||
from typing import Callable, Any, Tuple, Dict, List
|
||||
|
||||
from ..error import VimbaSystemError
|
||||
from ..util import TraceEnable
|
||||
from .vimba_common import Uint32Enum, VmbUint32, VmbInt32, VmbError, VmbFloat, VimbaCError, \
|
||||
VmbPixelFormat, load_vimba_lib, fmt_repr, fmt_enum_repr
|
||||
|
||||
|
||||
__all__ = [
|
||||
'VmbBayerPattern',
|
||||
'VmbEndianness',
|
||||
'VmbAligment',
|
||||
'VmbAPIInfo',
|
||||
'VmbPixelLayout',
|
||||
'VmbDebayerMode',
|
||||
'VmbImage',
|
||||
'VmbImageInfo',
|
||||
'VmbTransformInfo',
|
||||
'VIMBA_IMAGE_TRANSFORM_VERSION',
|
||||
'EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION',
|
||||
'call_vimba_image_transform',
|
||||
'PIXEL_FORMAT_TO_LAYOUT',
|
||||
'LAYOUT_TO_PIXEL_FORMAT',
|
||||
'PIXEL_FORMAT_CONVERTIBILITY_MAP'
|
||||
]
|
||||
|
||||
|
||||
class VmbBayerPattern(Uint32Enum):
|
||||
"""Enum defining BayerPatterns
|
||||
Values:
|
||||
RGGB - RGGB pattern, red pixel comes first
|
||||
GBRG - RGGB pattern, green pixel of blue row comes first
|
||||
GRBG - RGGB pattern, green pixel of red row comes first
|
||||
BGGR - RGGB pattern, blue pixel comes first
|
||||
CYGM - CYGM pattern, cyan pixel comes first in the first row, green in the second row
|
||||
GMCY - CYGM pattern, green pixel comes first in the first row, cyan in the second row
|
||||
CYMG - CYGM pattern, cyan pixel comes first in the first row, magenta in the second row
|
||||
MGCY - CYGM pattern, magenta pixel comes first in the first row, cyan in the second row
|
||||
LAST - Indicator for end of defined range
|
||||
"""
|
||||
RGGB = 0
|
||||
GBRG = 1
|
||||
GRBG = 2
|
||||
BGGR = 3
|
||||
CYGM = 128
|
||||
GMCY = 129
|
||||
CYMG = 130
|
||||
MGCY = 131
|
||||
LAST = 255
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbEndianness(Uint32Enum):
|
||||
"""Enum defining Endian Formats
|
||||
Values:
|
||||
LITTLE - Little Endian
|
||||
BIG - Big Endian
|
||||
LAST - Indicator for end of defined range
|
||||
"""
|
||||
LITTLE = 0
|
||||
BIG = 1
|
||||
LAST = 255
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbAligment(Uint32Enum):
|
||||
"""Enum defining image alignment
|
||||
Values:
|
||||
MSB - Alignment (pppp pppp pppp ....)
|
||||
LSB - Alignment (.... pppp pppp pppp)
|
||||
LAST - Indicator for end of defined range
|
||||
"""
|
||||
MSB = 0
|
||||
LSB = 1
|
||||
LAST = 255
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbAPIInfo(Uint32Enum):
|
||||
"""API Info Types
|
||||
Values:
|
||||
ALL - All Infos
|
||||
PLATFORM - Platform the API was built for
|
||||
BUILD - Build Types (debug or release)
|
||||
TECHNOLOGY - Special technology info
|
||||
LAST - Indicator for end of defined range
|
||||
"""
|
||||
ALL = 0
|
||||
PLATFORM = 1
|
||||
BUILD = 2
|
||||
TECHNOLOGY = 3
|
||||
LAST = 4
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbPixelLayout(Uint32Enum):
|
||||
"""Image Pixel Layout Information. C Header offers no further documentation."""
|
||||
Mono = 0
|
||||
MonoPacked = 1
|
||||
Raw = 2
|
||||
RawPacked = 3
|
||||
RGB = 4
|
||||
BGR = 5
|
||||
RGBA = 6
|
||||
BGRA = 7
|
||||
YUV411 = 8
|
||||
YUV422 = 9
|
||||
YUV444 = 10
|
||||
MonoP = 11
|
||||
MonoPl = 12
|
||||
RawP = 13
|
||||
RawPl = 14
|
||||
YYCbYYCr411 = 15
|
||||
CbYYCrYY411 = YUV411,
|
||||
YCbYCr422 = 16
|
||||
CbYCrY422 = YUV422
|
||||
YCbCr444 = 17
|
||||
CbYCr444 = YUV444
|
||||
LAST = 19
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbColorSpace(Uint32Enum):
|
||||
"""Image Color space. C Header offers no further documentation."""
|
||||
Undefined = 0
|
||||
ITU_BT709 = 1
|
||||
ITU_BT601 = 2
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbDebayerMode(Uint32Enum):
|
||||
"""Debayer Mode. C Header offers no further documentation."""
|
||||
Mode_2x2 = 0
|
||||
Mode_3x3 = 1
|
||||
Mode_LCAA = 2
|
||||
Mode_LCAAV = 3
|
||||
Mode_YUV422 = 4
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbTransformType(Uint32Enum):
|
||||
"""TransformType Mode. C Header offers no further documentation."""
|
||||
None_ = 0
|
||||
DebayerMode = 1
|
||||
ColorCorrectionMatrix = 2
|
||||
GammaCorrection = 3
|
||||
Offset = 4
|
||||
Gain = 5
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
|
||||
class VmbPixelInfo(ctypes.Structure):
|
||||
"""Structure containing pixel information. Sadly c_header contains no more documentation"""
|
||||
_fields_ = [
|
||||
('BitsPerPixel', VmbUint32),
|
||||
('BitsUsed', VmbUint32),
|
||||
('Alignment', VmbUint32),
|
||||
('Endianness', VmbUint32),
|
||||
('PixelLayout', VmbUint32),
|
||||
('BayerPattern', VmbUint32),
|
||||
('Reserved', VmbUint32)
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
rep = 'VmbPixelInfo'
|
||||
rep += fmt_repr('(BitsPerPixel={}', self.BitsPerPixel)
|
||||
rep += fmt_repr(',BitsUsed={}', self.BitsUsed)
|
||||
rep += fmt_enum_repr(',Alignment={}', VmbAligment, self.Alignment)
|
||||
rep += fmt_enum_repr(',Endianness={}', VmbEndianness, self.Endianness)
|
||||
rep += fmt_enum_repr(',PixelLayout={}', VmbPixelLayout, self.PixelLayout)
|
||||
rep += fmt_enum_repr(',BayerPattern={}', VmbBayerPattern, self.BayerPattern)
|
||||
rep += fmt_enum_repr(',Reserved={}', VmbColorSpace, self.Reserved)
|
||||
rep += ')'
|
||||
return rep
|
||||
|
||||
|
||||
class VmbImageInfo(ctypes.Structure):
|
||||
"""Structure containing image information. Sadly c_header contains no more documentation"""
|
||||
_fields_ = [
|
||||
('Width', VmbUint32),
|
||||
('Height', VmbUint32),
|
||||
('Stride', VmbInt32),
|
||||
('PixelInfo', VmbPixelInfo)
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
rep = 'VmbImageInfo'
|
||||
rep += fmt_repr('(Width={}', self.Width)
|
||||
rep += fmt_repr(',Height={}', self.Height)
|
||||
rep += fmt_repr(',Stride={}', self.Stride)
|
||||
rep += fmt_repr(',PixelInfo={}', self.PixelInfo)
|
||||
rep += ')'
|
||||
return rep
|
||||
|
||||
|
||||
class VmbImage(ctypes.Structure):
|
||||
"""Structure containing image. Sadly c_header contains no more documentation"""
|
||||
_fields_ = [
|
||||
('Size', VmbUint32),
|
||||
('Data', ctypes.c_void_p),
|
||||
('ImageInfo', VmbImageInfo)
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
rep = 'VmbImage'
|
||||
rep += fmt_repr('(Size={}', self.Size)
|
||||
rep += fmt_repr(',Data={}', self.Data)
|
||||
rep += fmt_repr(',ImageInfo={}', self.ImageInfo)
|
||||
rep += ')'
|
||||
return rep
|
||||
|
||||
|
||||
class VmbTransformParameterMatrix3x3(ctypes.Structure):
|
||||
"""Sadly c_header contains no more documentation"""
|
||||
_fields_ = [
|
||||
('Matrix', VmbFloat * 9)
|
||||
]
|
||||
|
||||
|
||||
class VmbTransformParameterGamma(ctypes.Structure):
|
||||
"""Sadly c_header contains no more documentation"""
|
||||
_fields_ = [
|
||||
('Gamma', VmbFloat)
|
||||
]
|
||||
|
||||
|
||||
class VmbTransformParameterDebayer(ctypes.Structure):
|
||||
"""Sadly c_header contains no more documentation"""
|
||||
_fields_ = [
|
||||
('Method', VmbUint32)
|
||||
]
|
||||
|
||||
|
||||
class VmbTransformParameterOffset(ctypes.Structure):
|
||||
"""Sadly c_header contains no more documentation"""
|
||||
_fields_ = [
|
||||
('Offset', VmbInt32)
|
||||
]
|
||||
|
||||
|
||||
class VmbTransformParameterGain(ctypes.Structure):
|
||||
"""Sadly c_header contains no more documentation"""
|
||||
_fields_ = [
|
||||
('Gain', VmbUint32)
|
||||
]
|
||||
|
||||
|
||||
class VmbTransformParameter(ctypes.Union):
|
||||
"""Sadly c_header contains no more documentation"""
|
||||
_fields_ = [
|
||||
('Matrix3x3', VmbTransformParameterMatrix3x3),
|
||||
('Debayer', VmbTransformParameterDebayer),
|
||||
('Gamma', VmbTransformParameterGamma),
|
||||
('Offset', VmbTransformParameterOffset),
|
||||
('Gain', VmbTransformParameterGain)
|
||||
]
|
||||
|
||||
|
||||
class VmbTransformInfo(ctypes.Structure):
|
||||
"""Struct holding transformation information"""
|
||||
_fields_ = [
|
||||
('TransformType', VmbUint32),
|
||||
('Parameter', VmbTransformParameter)
|
||||
]
|
||||
|
||||
|
||||
# API
|
||||
VIMBA_IMAGE_TRANSFORM_VERSION = None
|
||||
if sys.platform == 'linux':
|
||||
EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION = '1.0'
|
||||
|
||||
else:
|
||||
EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION = '1.6'
|
||||
|
||||
# For detailed information on the signatures see "VimbaImageTransform.h"
|
||||
# To improve readability, suppress 'E501 line too long (> 100 characters)'
|
||||
# check of flake8
|
||||
_SIGNATURES = {
|
||||
'VmbGetVersion': (VmbError, [c_ptr(VmbUint32)]),
|
||||
'VmbGetErrorInfo': (VmbError, [VmbError, c_char_p, VmbUint32]),
|
||||
'VmbGetApiInfoString': (VmbError, [VmbAPIInfo, c_char_p, VmbUint32]),
|
||||
'VmbSetDebayerMode': (VmbError, [VmbDebayerMode, c_ptr(VmbTransformInfo)]),
|
||||
'VmbSetColorCorrectionMatrix3x3': (VmbError, [c_ptr(VmbFloat), c_ptr(VmbTransformInfo)]),
|
||||
'VmbSetGammaCorrection': (VmbError, [VmbFloat, c_ptr(VmbTransformInfo)]),
|
||||
'VmbSetImageInfoFromPixelFormat': (VmbError, [VmbPixelFormat, VmbUint32, VmbUint32, c_ptr(VmbImage)]), # noqa: E501
|
||||
'VmbSetImageInfoFromString': (VmbError, [c_char_p, VmbUint32, VmbUint32, VmbUint32, c_ptr(VmbImage)]), # noqa: E501
|
||||
'VmbSetImageInfoFromInputParameters': (VmbError, [VmbPixelFormat, VmbUint32, VmbUint32, VmbPixelLayout, VmbUint32, c_ptr(VmbImage)]), # noqa: E501
|
||||
'VmbSetImageInfoFromInputImage': (VmbError, [c_ptr(VmbImage), VmbPixelLayout, VmbUint32, c_ptr(VmbImage)]), # noqa: E501
|
||||
'VmbImageTransform': (VmbError, [c_ptr(VmbImage), c_ptr(VmbImage), c_ptr(VmbTransformInfo), VmbUint32]) # noqa: E501
|
||||
}
|
||||
|
||||
|
||||
def _attach_signatures(lib_handle):
|
||||
global _SIGNATURES
|
||||
|
||||
for function_name, signature in _SIGNATURES.items():
|
||||
fn = getattr(lib_handle, function_name)
|
||||
fn.restype, fn.argtypes = signature
|
||||
fn.errcheck = _eval_vmberror
|
||||
|
||||
return lib_handle
|
||||
|
||||
|
||||
def _check_version(lib_handle):
|
||||
global EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION
|
||||
global VIMBA_IMAGE_TRANSFORM_VERSION
|
||||
|
||||
v = VmbUint32()
|
||||
lib_handle.VmbGetVersion(byref(v))
|
||||
|
||||
VIMBA_IMAGE_TRANSFORM_VERSION = '{}.{}'.format((v.value >> 24) & 0xff, (v.value >> 16) & 0xff)
|
||||
|
||||
loaded_version = tuple(map(int, VIMBA_IMAGE_TRANSFORM_VERSION.split(".")))
|
||||
expected_version = tuple(map(int, EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION.split(".")))
|
||||
# Major version must match. minor version may be equal or greater
|
||||
if not(loaded_version[0] == expected_version[0] and
|
||||
loaded_version[1] >= expected_version[1]):
|
||||
msg = 'Invalid VimbaImageTransform Version: Expected: {}, Found:{}'
|
||||
raise VimbaSystemError(msg.format(EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION,
|
||||
VIMBA_IMAGE_TRANSFORM_VERSION))
|
||||
|
||||
return lib_handle
|
||||
|
||||
|
||||
def _eval_vmberror(result: VmbError, func: Callable[..., Any], *args: Tuple[Any, ...]):
|
||||
if result not in (VmbError.Success, None):
|
||||
raise VimbaCError(result)
|
||||
|
||||
|
||||
_lib_instance = _check_version(_attach_signatures(load_vimba_lib('VimbaImageTransform')))
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
def call_vimba_image_transform(func_name: str, *args):
|
||||
"""This function encapsulates the entire VimbaImageTransform access.
|
||||
|
||||
For Details on valid function signatures see the 'VimbaImageTransform.h'.
|
||||
|
||||
Arguments:
|
||||
func_name: The function name from VimbaImageTransform to be called.
|
||||
args: Varargs passed directly to the underlaying C-Function.
|
||||
|
||||
Raises:
|
||||
TypeError if given are do not match the signature of the function.
|
||||
AttributeError if func with name 'func_name' does not exist.
|
||||
VimbaCError if the function call is valid but neither None or VmbError.Success was returned.
|
||||
|
||||
The following functions of VimbaImageTransform can be executed:
|
||||
VmbGetVersion
|
||||
VmbGetTechnoInfo
|
||||
VmbGetErrorInfo
|
||||
VmbGetApiInfoString
|
||||
VmbSetDebayerMode
|
||||
VmbSetColorCorrectionMatrix3x3
|
||||
VmbSetGammaCorrection
|
||||
VmbSetImageInfoFromPixelFormat
|
||||
VmbSetImageInfoFromString
|
||||
VmbSetImageInfoFromInputParameters
|
||||
VmbSetImageInfoFromInputImage
|
||||
VmbImageTransform
|
||||
"""
|
||||
|
||||
global _lib_instance
|
||||
getattr(_lib_instance, func_name)(*args)
|
||||
|
||||
|
||||
PIXEL_FORMAT_TO_LAYOUT: Dict[VmbPixelFormat, Tuple[VmbPixelLayout, int]] = {
|
||||
VmbPixelFormat.Mono8: (VmbPixelLayout.Mono, 8),
|
||||
VmbPixelFormat.Mono10: (VmbPixelLayout.Mono, 16),
|
||||
VmbPixelFormat.Mono12: (VmbPixelLayout.Mono, 16),
|
||||
VmbPixelFormat.Mono14: (VmbPixelLayout.Mono, 16),
|
||||
VmbPixelFormat.Mono16: (VmbPixelLayout.Mono, 16),
|
||||
VmbPixelFormat.BayerGR8: (VmbPixelLayout.Raw, 8),
|
||||
VmbPixelFormat.BayerRG8: (VmbPixelLayout.Raw, 8),
|
||||
VmbPixelFormat.BayerGB8: (VmbPixelLayout.Raw, 8),
|
||||
VmbPixelFormat.BayerBG8: (VmbPixelLayout.Raw, 8),
|
||||
VmbPixelFormat.BayerGR10: (VmbPixelLayout.Raw, 16),
|
||||
VmbPixelFormat.BayerRG10: (VmbPixelLayout.Raw, 16),
|
||||
VmbPixelFormat.BayerGB10: (VmbPixelLayout.Raw, 16),
|
||||
VmbPixelFormat.BayerBG10: (VmbPixelLayout.Raw, 16),
|
||||
VmbPixelFormat.BayerGR12: (VmbPixelLayout.Raw, 16),
|
||||
VmbPixelFormat.BayerRG12: (VmbPixelLayout.Raw, 16),
|
||||
VmbPixelFormat.BayerGB12: (VmbPixelLayout.Raw, 16),
|
||||
VmbPixelFormat.BayerBG12: (VmbPixelLayout.Raw, 16),
|
||||
VmbPixelFormat.BayerGR16: (VmbPixelLayout.Raw, 16),
|
||||
VmbPixelFormat.BayerRG16: (VmbPixelLayout.Raw, 16),
|
||||
VmbPixelFormat.BayerGB16: (VmbPixelLayout.Raw, 16),
|
||||
VmbPixelFormat.BayerBG16: (VmbPixelLayout.Raw, 16),
|
||||
VmbPixelFormat.Rgb8: (VmbPixelLayout.RGB, 8),
|
||||
VmbPixelFormat.Rgb10: (VmbPixelLayout.RGB, 16),
|
||||
VmbPixelFormat.Rgb12: (VmbPixelLayout.RGB, 16),
|
||||
VmbPixelFormat.Rgb14: (VmbPixelLayout.RGB, 16),
|
||||
VmbPixelFormat.Rgb16: (VmbPixelLayout.RGB, 16),
|
||||
VmbPixelFormat.Bgr8: (VmbPixelLayout.BGR, 8),
|
||||
VmbPixelFormat.Bgr10: (VmbPixelLayout.BGR, 16),
|
||||
VmbPixelFormat.Bgr12: (VmbPixelLayout.BGR, 16),
|
||||
VmbPixelFormat.Bgr14: (VmbPixelLayout.BGR, 16),
|
||||
VmbPixelFormat.Bgr16: (VmbPixelLayout.BGR, 16),
|
||||
VmbPixelFormat.Rgba8: (VmbPixelLayout.RGBA, 8),
|
||||
VmbPixelFormat.Rgba10: (VmbPixelLayout.RGBA, 16),
|
||||
VmbPixelFormat.Rgba12: (VmbPixelLayout.RGBA, 16),
|
||||
VmbPixelFormat.Rgba14: (VmbPixelLayout.RGBA, 16),
|
||||
VmbPixelFormat.Rgba16: (VmbPixelLayout.RGBA, 16),
|
||||
VmbPixelFormat.Bgra8: (VmbPixelLayout.BGRA, 8),
|
||||
VmbPixelFormat.Bgra10: (VmbPixelLayout.BGRA, 16),
|
||||
VmbPixelFormat.Bgra12: (VmbPixelLayout.BGRA, 16),
|
||||
VmbPixelFormat.Bgra14: (VmbPixelLayout.BGRA, 16),
|
||||
VmbPixelFormat.Bgra16: (VmbPixelLayout.BGRA, 16)
|
||||
}
|
||||
|
||||
LAYOUT_TO_PIXEL_FORMAT = dict([(v, k) for k, v in PIXEL_FORMAT_TO_LAYOUT.items()])
|
||||
|
||||
|
||||
def _query_compatibility(pixel_format: VmbPixelFormat) -> Tuple[VmbPixelFormat, ...]:
|
||||
global LAYOUT_TO_PIXEL_FORMAT
|
||||
|
||||
# Query compatible formats from ImageTransform
|
||||
output_pixel_layouts = (VmbPixelLayout.Mono, VmbPixelLayout.MonoPacked, VmbPixelLayout.Raw,
|
||||
VmbPixelLayout.RawPacked, VmbPixelLayout.RGB, VmbPixelLayout.BGR,
|
||||
VmbPixelLayout.RGBA, VmbPixelLayout.BGRA)
|
||||
|
||||
output_bits_per_pixel = (8, 16)
|
||||
output_layouts = tuple([(layouts, bits)
|
||||
for layouts in output_pixel_layouts
|
||||
for bits in output_bits_per_pixel])
|
||||
|
||||
result: List[VmbPixelFormat] = []
|
||||
|
||||
src_image = VmbImage()
|
||||
src_image.Size = sizeof(src_image)
|
||||
|
||||
call_vimba_image_transform('VmbSetImageInfoFromPixelFormat', pixel_format, 0, 0,
|
||||
byref(src_image))
|
||||
|
||||
dst_image = VmbImage()
|
||||
dst_image.Size = sizeof(dst_image)
|
||||
|
||||
for layout, bits in output_layouts:
|
||||
|
||||
try:
|
||||
call_vimba_image_transform('VmbSetImageInfoFromInputImage', byref(src_image), layout,
|
||||
bits, byref(dst_image))
|
||||
|
||||
fmt = LAYOUT_TO_PIXEL_FORMAT[(layout, bits)]
|
||||
|
||||
if fmt not in result:
|
||||
result.append(fmt)
|
||||
|
||||
except VimbaCError as e:
|
||||
if e.get_error_code() not in (VmbError.NotImplemented_, VmbError.BadParameter):
|
||||
raise e
|
||||
|
||||
return tuple(result)
|
||||
|
||||
|
||||
PIXEL_FORMAT_CONVERTIBILITY_MAP: Dict[VmbPixelFormat, Tuple[VmbPixelFormat, ...]] = {
|
||||
VmbPixelFormat.Mono8: _query_compatibility(VmbPixelFormat.Mono8),
|
||||
VmbPixelFormat.Mono10: _query_compatibility(VmbPixelFormat.Mono10),
|
||||
VmbPixelFormat.Mono10p: _query_compatibility(VmbPixelFormat.Mono10p),
|
||||
VmbPixelFormat.Mono12: _query_compatibility(VmbPixelFormat.Mono12),
|
||||
VmbPixelFormat.Mono12Packed: _query_compatibility(VmbPixelFormat.Mono12Packed),
|
||||
VmbPixelFormat.Mono12p: _query_compatibility(VmbPixelFormat.Mono12p),
|
||||
VmbPixelFormat.Mono14: _query_compatibility(VmbPixelFormat.Mono14),
|
||||
VmbPixelFormat.Mono16: _query_compatibility(VmbPixelFormat.Mono16),
|
||||
|
||||
VmbPixelFormat.BayerGR8: _query_compatibility(VmbPixelFormat.BayerGR8),
|
||||
VmbPixelFormat.BayerRG8: _query_compatibility(VmbPixelFormat.BayerRG8),
|
||||
VmbPixelFormat.BayerGB8: _query_compatibility(VmbPixelFormat.BayerGB8),
|
||||
VmbPixelFormat.BayerBG8: _query_compatibility(VmbPixelFormat.BayerBG8),
|
||||
VmbPixelFormat.BayerGR10: _query_compatibility(VmbPixelFormat.BayerGR10),
|
||||
VmbPixelFormat.BayerRG10: _query_compatibility(VmbPixelFormat.BayerRG10),
|
||||
VmbPixelFormat.BayerGB10: _query_compatibility(VmbPixelFormat.BayerGB10),
|
||||
VmbPixelFormat.BayerBG10: _query_compatibility(VmbPixelFormat.BayerBG10),
|
||||
VmbPixelFormat.BayerGR12: _query_compatibility(VmbPixelFormat.BayerGR12),
|
||||
VmbPixelFormat.BayerRG12: _query_compatibility(VmbPixelFormat.BayerRG12),
|
||||
VmbPixelFormat.BayerGB12: _query_compatibility(VmbPixelFormat.BayerGB12),
|
||||
VmbPixelFormat.BayerBG12: _query_compatibility(VmbPixelFormat.BayerBG12),
|
||||
VmbPixelFormat.BayerGR12Packed: _query_compatibility(VmbPixelFormat.BayerGR12Packed),
|
||||
VmbPixelFormat.BayerRG12Packed: _query_compatibility(VmbPixelFormat.BayerRG12Packed),
|
||||
VmbPixelFormat.BayerGB12Packed: _query_compatibility(VmbPixelFormat.BayerGB12Packed),
|
||||
VmbPixelFormat.BayerBG12Packed: _query_compatibility(VmbPixelFormat.BayerBG12Packed),
|
||||
VmbPixelFormat.BayerGR10p: _query_compatibility(VmbPixelFormat.BayerGR10p),
|
||||
VmbPixelFormat.BayerRG10p: _query_compatibility(VmbPixelFormat.BayerRG10p),
|
||||
VmbPixelFormat.BayerGB10p: _query_compatibility(VmbPixelFormat.BayerGB10p),
|
||||
VmbPixelFormat.BayerBG10p: _query_compatibility(VmbPixelFormat.BayerBG10p),
|
||||
VmbPixelFormat.BayerGR12p: _query_compatibility(VmbPixelFormat.BayerGR12p),
|
||||
VmbPixelFormat.BayerRG12p: _query_compatibility(VmbPixelFormat.BayerRG12p),
|
||||
VmbPixelFormat.BayerGB12p: _query_compatibility(VmbPixelFormat.BayerGB12p),
|
||||
VmbPixelFormat.BayerBG12p: _query_compatibility(VmbPixelFormat.BayerBG12p),
|
||||
VmbPixelFormat.BayerGR16: _query_compatibility(VmbPixelFormat.BayerGR16),
|
||||
VmbPixelFormat.BayerRG16: _query_compatibility(VmbPixelFormat.BayerRG16),
|
||||
VmbPixelFormat.BayerGB16: _query_compatibility(VmbPixelFormat.BayerGB16),
|
||||
VmbPixelFormat.BayerBG16: _query_compatibility(VmbPixelFormat.BayerBG16),
|
||||
|
||||
VmbPixelFormat.Rgb8: _query_compatibility(VmbPixelFormat.Rgb8),
|
||||
VmbPixelFormat.Bgr8: _query_compatibility(VmbPixelFormat.Bgr8),
|
||||
VmbPixelFormat.Rgb10: _query_compatibility(VmbPixelFormat.Rgb10),
|
||||
VmbPixelFormat.Bgr10: _query_compatibility(VmbPixelFormat.Bgr10),
|
||||
VmbPixelFormat.Rgb12: _query_compatibility(VmbPixelFormat.Rgb12),
|
||||
VmbPixelFormat.Bgr12: _query_compatibility(VmbPixelFormat.Bgr12),
|
||||
VmbPixelFormat.Rgb14: _query_compatibility(VmbPixelFormat.Rgb14),
|
||||
VmbPixelFormat.Bgr14: _query_compatibility(VmbPixelFormat.Bgr14),
|
||||
VmbPixelFormat.Rgb16: _query_compatibility(VmbPixelFormat.Rgb16),
|
||||
VmbPixelFormat.Bgr16: _query_compatibility(VmbPixelFormat.Bgr16),
|
||||
VmbPixelFormat.Argb8: _query_compatibility(VmbPixelFormat.Argb8),
|
||||
VmbPixelFormat.Rgba8: _query_compatibility(VmbPixelFormat.Rgba8),
|
||||
VmbPixelFormat.Bgra8: _query_compatibility(VmbPixelFormat.Bgra8),
|
||||
VmbPixelFormat.Rgba10: _query_compatibility(VmbPixelFormat.Rgba10),
|
||||
VmbPixelFormat.Bgra10: _query_compatibility(VmbPixelFormat.Bgra10),
|
||||
VmbPixelFormat.Rgba12: _query_compatibility(VmbPixelFormat.Rgba12),
|
||||
VmbPixelFormat.Bgra12: _query_compatibility(VmbPixelFormat.Bgra12),
|
||||
VmbPixelFormat.Rgba14: _query_compatibility(VmbPixelFormat.Rgba14),
|
||||
VmbPixelFormat.Bgra14: _query_compatibility(VmbPixelFormat.Bgra14),
|
||||
VmbPixelFormat.Rgba16: _query_compatibility(VmbPixelFormat.Rgba16),
|
||||
VmbPixelFormat.Bgra16: _query_compatibility(VmbPixelFormat.Bgra16),
|
||||
|
||||
VmbPixelFormat.Yuv411: _query_compatibility(VmbPixelFormat.Yuv411),
|
||||
VmbPixelFormat.Yuv422: _query_compatibility(VmbPixelFormat.Yuv422),
|
||||
VmbPixelFormat.Yuv444: _query_compatibility(VmbPixelFormat.Yuv444),
|
||||
VmbPixelFormat.YCbCr411_8_CbYYCrYY: _query_compatibility(VmbPixelFormat.YCbCr411_8_CbYYCrYY),
|
||||
VmbPixelFormat.YCbCr422_8_CbYCrY: _query_compatibility(VmbPixelFormat.YCbCr422_8_CbYCrY),
|
||||
VmbPixelFormat.YCbCr8_CbYCr: _query_compatibility(VmbPixelFormat.YCbCr8_CbYCr)
|
||||
}
|
||||
1078
Vimba_6_0/VimbaPython/Source/vimba/camera.py
Normal file
1078
Vimba_6_0/VimbaPython/Source/vimba/camera.py
Normal file
File diff suppressed because it is too large
Load Diff
95
Vimba_6_0/VimbaPython/Source/vimba/error.py
Normal file
95
Vimba_6_0/VimbaPython/Source/vimba/error.py
Normal file
@@ -0,0 +1,95 @@
|
||||
"""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.
|
||||
"""
|
||||
|
||||
from .util import Log
|
||||
|
||||
__all__ = [
|
||||
'VimbaSystemError',
|
||||
'VimbaCameraError',
|
||||
'VimbaInterfaceError',
|
||||
'VimbaFeatureError',
|
||||
'VimbaFrameError',
|
||||
'VimbaTimeout'
|
||||
]
|
||||
|
||||
|
||||
class _LoggedError(Exception):
|
||||
def __init__(self, msg: str):
|
||||
super().__init__(msg)
|
||||
Log.get_instance().error(msg)
|
||||
|
||||
|
||||
class VimbaSystemError(_LoggedError):
|
||||
"""Errors related to the underlying Vimba System
|
||||
|
||||
Error type to indicate system-wide errors like:
|
||||
- Incomplete Vimba installation
|
||||
- Incompatible version of the underlying C-Layer
|
||||
- An unsupported OS
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class VimbaCameraError(_LoggedError):
|
||||
"""Errors related to cameras
|
||||
|
||||
Error Type to indicated camera-related errors like:
|
||||
- Access of a disconnected Camera object
|
||||
- Lookup of non-existing cameras
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class VimbaInterfaceError(_LoggedError):
|
||||
"""Errors related to Interfaces
|
||||
|
||||
Error Type to indicated interface-related errors like:
|
||||
- Access on a disconnected Interface object
|
||||
- Lookup of a non-existing Interface
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class VimbaFeatureError(_LoggedError):
|
||||
"""Error related to Feature access.
|
||||
|
||||
Error type to indicate invalid Feature access like:
|
||||
- Invalid access mode on Feature access.
|
||||
- Out of range values upon setting a value.
|
||||
- Failed lookup of features.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class VimbaFrameError(_LoggedError):
|
||||
"""Error related to Frame data"""
|
||||
pass
|
||||
|
||||
|
||||
class VimbaTimeout(_LoggedError):
|
||||
"""Indicates that an operation timed out."""
|
||||
pass
|
||||
1273
Vimba_6_0/VimbaPython/Source/vimba/feature.py
Normal file
1273
Vimba_6_0/VimbaPython/Source/vimba/feature.py
Normal file
File diff suppressed because it is too large
Load Diff
923
Vimba_6_0/VimbaPython/Source/vimba/frame.py
Normal file
923
Vimba_6_0/VimbaPython/Source/vimba/frame.py
Normal file
@@ -0,0 +1,923 @@
|
||||
"""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
|
||||
import ctypes
|
||||
import copy
|
||||
import functools
|
||||
|
||||
from typing import Optional, Tuple
|
||||
from .c_binding import byref, sizeof, decode_flags
|
||||
from .c_binding import call_vimba_c, call_vimba_image_transform, VmbFrameStatus, VmbFrameFlags, \
|
||||
VmbFrame, VmbHandle, VmbPixelFormat, VmbImage, VmbDebayerMode, \
|
||||
VmbTransformInfo, PIXEL_FORMAT_CONVERTIBILITY_MAP, PIXEL_FORMAT_TO_LAYOUT
|
||||
from .feature import FeaturesTuple, FeatureTypes, FeatureTypeTypes, discover_features
|
||||
from .shared import filter_features_by_name, filter_features_by_type, filter_features_by_category, \
|
||||
attach_feature_accessors, remove_feature_accessors
|
||||
from .util import TraceEnable, RuntimeTypeCheckEnable, EnterContextOnCall, LeaveContextOnCall, \
|
||||
RaiseIfOutsideContext
|
||||
from .error import VimbaFrameError, VimbaFeatureError
|
||||
|
||||
try:
|
||||
import numpy # type: ignore
|
||||
|
||||
except ModuleNotFoundError:
|
||||
numpy = None # type: ignore
|
||||
|
||||
|
||||
__all__ = [
|
||||
'PixelFormat',
|
||||
'MONO_PIXEL_FORMATS',
|
||||
'BAYER_PIXEL_FORMATS',
|
||||
'RGB_PIXEL_FORMATS',
|
||||
'RGBA_PIXEL_FORMATS',
|
||||
'BGR_PIXEL_FORMATS',
|
||||
'BGRA_PIXEL_FORMATS',
|
||||
'YUV_PIXEL_FORMATS',
|
||||
'YCBCR_PIXEL_FORMATS',
|
||||
'COLOR_PIXEL_FORMATS',
|
||||
'OPENCV_PIXEL_FORMATS',
|
||||
'FrameStatus',
|
||||
'Debayer',
|
||||
'Frame',
|
||||
'FrameTuple',
|
||||
'FormatTuple',
|
||||
'intersect_pixel_formats'
|
||||
]
|
||||
|
||||
|
||||
# Forward declarations
|
||||
FrameTuple = Tuple['Frame', ...]
|
||||
FormatTuple = Tuple['PixelFormat', ...]
|
||||
|
||||
|
||||
class PixelFormat(enum.IntEnum):
|
||||
"""Enum specifying all PixelFormats. Note: Not all Cameras support all Pixelformats.
|
||||
|
||||
Mono formats:
|
||||
Mono8 - Monochrome, 8 bits (PFNC:Mono8)
|
||||
Mono10 - Monochrome, 10 bits in 16 bits (PFNC:Mono10)
|
||||
Mono10p - Monochrome, 4x10 bits continuously packed in 40 bits
|
||||
(PFNC:Mono10p)
|
||||
Mono12 - Monochrome, 12 bits in 16 bits (PFNC:Mono12)
|
||||
Mono12Packed - Monochrome, 2x12 bits in 24 bits (GEV:Mono12Packed)
|
||||
Mono12p - Monochrome, 2x12 bits continuously packed in 24 bits
|
||||
(PFNC:Mono12p)
|
||||
Mono14 - Monochrome, 14 bits in 16 bits (PFNC:Mono14)
|
||||
Mono16 - Monochrome, 16 bits (PFNC:Mono16)
|
||||
|
||||
Bayer formats:
|
||||
BayerGR8 - Bayer-color, 8 bits, starting with GR line
|
||||
(PFNC:BayerGR8)
|
||||
BayerRG8 - Bayer-color, 8 bits, starting with RG line
|
||||
(PFNC:BayerRG8)
|
||||
BayerGB8 - Bayer-color, 8 bits, starting with GB line
|
||||
(PFNC:BayerGB8)
|
||||
BayerBG8 - Bayer-color, 8 bits, starting with BG line
|
||||
(PFNC:BayerBG8)
|
||||
BayerGR10 - Bayer-color, 10 bits in 16 bits, starting with GR
|
||||
line (PFNC:BayerGR10)
|
||||
BayerRG10 - Bayer-color, 10 bits in 16 bits, starting with RG
|
||||
line (PFNC:BayerRG10)
|
||||
BayerGB10 - Bayer-color, 10 bits in 16 bits, starting with GB
|
||||
line (PFNC:BayerGB10)
|
||||
BayerBG10 - Bayer-color, 10 bits in 16 bits, starting with BG
|
||||
line (PFNC:BayerBG10)
|
||||
BayerGR12 - Bayer-color, 12 bits in 16 bits, starting with GR
|
||||
line (PFNC:BayerGR12)
|
||||
BayerRG12 - Bayer-color, 12 bits in 16 bits, starting with RG
|
||||
line (PFNC:BayerRG12)
|
||||
BayerGB12 - Bayer-color, 12 bits in 16 bits, starting with GB
|
||||
line (PFNC:BayerGB12)
|
||||
BayerBG12 - Bayer-color, 12 bits in 16 bits, starting with BG
|
||||
line (PFNC:BayerBG12)
|
||||
BayerGR12Packed - Bayer-color, 2x12 bits in 24 bits, starting with GR
|
||||
line (GEV:BayerGR12Packed)
|
||||
BayerRG12Packed - Bayer-color, 2x12 bits in 24 bits, starting with RG
|
||||
line (GEV:BayerRG12Packed)
|
||||
BayerGB12Packed - Bayer-color, 2x12 bits in 24 bits, starting with GB
|
||||
line (GEV:BayerGB12Packed)
|
||||
BayerBG12Packed - Bayer-color, 2x12 bits in 24 bits, starting with BG
|
||||
line (GEV:BayerBG12Packed)
|
||||
BayerGR10p - Bayer-color, 4x10 bits continuously packed in 40
|
||||
bits, starting with GR line (PFNC:BayerGR10p)
|
||||
BayerRG10p - Bayer-color, 4x10 bits continuously packed in 40
|
||||
bits, starting with RG line (PFNC:BayerRG10p)
|
||||
BayerGB10p - Bayer-color, 4x10 bits continuously packed in 40
|
||||
bits, starting with GB line (PFNC:BayerGB10p)
|
||||
BayerBG10p - Bayer-color, 4x10 bits continuously packed in 40
|
||||
bits, starting with BG line (PFNC:BayerBG10p)
|
||||
BayerGR12p - Bayer-color, 2x12 bits continuously packed in 24
|
||||
bits, starting with GR line (PFNC:BayerGR12p)
|
||||
BayerRG12p - Bayer-color, 2x12 bits continuously packed in 24
|
||||
bits, starting with RG line (PFNC:BayerRG12p)
|
||||
BayerGB12p - Bayer-color, 2x12 bits continuously packed in 24
|
||||
bits, starting with GB line (PFNC:BayerGB12p)
|
||||
BayerBG12p - Bayer-color, 2x12 bits continuously packed in 24
|
||||
bits, starting with BG line (PFNC:BayerBG12p)
|
||||
BayerGR16 - Bayer-color, 16 bits, starting with GR line
|
||||
(PFNC:BayerGR16)
|
||||
BayerRG16 - Bayer-color, 16 bits, starting with RG line
|
||||
(PFNC:BayerRG16)
|
||||
BayerGB16 - Bayer-color, 16 bits, starting with GB line
|
||||
(PFNC:BayerGB16)
|
||||
BayerBG16 - Bayer-color, 16 bits, starting with BG line
|
||||
(PFNC:BayerBG16)
|
||||
|
||||
RGB formats:
|
||||
Rgb8 - RGB, 8 bits x 3 (PFNC:RGB8)
|
||||
Bgr8 - BGR, 8 bits x 3 (PFNC:Bgr8)
|
||||
Rgb10 - RGB, 10 bits in 16 bits x 3 (PFNC:RGB10)
|
||||
Bgr10 - BGR, 10 bits in 16 bits x 3 (PFNC:BGR10)
|
||||
Rgb12 - RGB, 12 bits in 16 bits x 3 (PFNC:RGB12)
|
||||
Bgr12 - BGR, 12 bits in 16 bits x 3 (PFNC:BGR12)
|
||||
Rgb14 - RGB, 14 bits in 16 bits x 3 (PFNC:RGB14)
|
||||
Bgr14 - BGR, 14 bits in 16 bits x 3 (PFNC:BGR14)
|
||||
Rgb16 - RGB, 16 bits x 3 (PFNC:RGB16)
|
||||
Bgr16 - BGR, 16 bits x 3 (PFNC:BGR16)
|
||||
|
||||
RGBA formats:
|
||||
Argb8 - ARGB, 8 bits x 4 (PFNC:RGBa8)
|
||||
Rgba8 - RGBA, 8 bits x 4, legacy name
|
||||
Bgra8 - BGRA, 8 bits x 4 (PFNC:BGRa8)
|
||||
Rgba10 - RGBA, 10 bits in 16 bits x 4
|
||||
Bgra10 - BGRA, 10 bits in 16 bits x 4
|
||||
Rgba12 - RGBA, 12 bits in 16 bits x 4
|
||||
Bgra12 - BGRA, 12 bits in 16 bits x 4
|
||||
Rgba14 - RGBA, 14 bits in 16 bits x 4
|
||||
Bgra14 - BGRA, 14 bits in 16 bits x 4
|
||||
Rgba16 - RGBA, 16 bits x 4
|
||||
Bgra16 - BGRA, 16 bits x 4
|
||||
|
||||
YUV/YCbCr formats:
|
||||
Yuv411 - YUV 411 with 8 bits (GEV:YUV411Packed)
|
||||
Yuv422 - YUV 422 with 8 bits (GEV:YUV422Packed)
|
||||
Yuv444 - YUV 444 with 8 bits (GEV:YUV444Packed)
|
||||
YCbCr411_8_CbYYCrYY - Y´CbCr 411 with 8 bits
|
||||
(PFNC:YCbCr411_8_CbYYCrYY) - identical to Yuv411
|
||||
YCbCr422_8_CbYCrY - Y´CbCr 422 with 8 bits
|
||||
(PFNC:YCbCr422_8_CbYCrY) - identical to Yuv422
|
||||
YCbCr8_CbYCr - Y´CbCr 444 with 8 bits
|
||||
(PFNC:YCbCr8_CbYCr) - identical to Yuv444
|
||||
"""
|
||||
# Mono Formats
|
||||
Mono8 = VmbPixelFormat.Mono8
|
||||
Mono10 = VmbPixelFormat.Mono10
|
||||
Mono10p = VmbPixelFormat.Mono10p
|
||||
Mono12 = VmbPixelFormat.Mono12
|
||||
Mono12Packed = VmbPixelFormat.Mono12Packed
|
||||
Mono12p = VmbPixelFormat.Mono12p
|
||||
Mono14 = VmbPixelFormat.Mono14
|
||||
Mono16 = VmbPixelFormat.Mono16
|
||||
|
||||
# Bayer Formats
|
||||
BayerGR8 = VmbPixelFormat.BayerGR8
|
||||
BayerRG8 = VmbPixelFormat.BayerRG8
|
||||
BayerGB8 = VmbPixelFormat.BayerGB8
|
||||
BayerBG8 = VmbPixelFormat.BayerBG8
|
||||
BayerGR10 = VmbPixelFormat.BayerGR10
|
||||
BayerRG10 = VmbPixelFormat.BayerRG10
|
||||
BayerGB10 = VmbPixelFormat.BayerGB10
|
||||
BayerBG10 = VmbPixelFormat.BayerBG10
|
||||
BayerGR12 = VmbPixelFormat.BayerGR12
|
||||
BayerRG12 = VmbPixelFormat.BayerRG12
|
||||
BayerGB12 = VmbPixelFormat.BayerGB12
|
||||
BayerBG12 = VmbPixelFormat.BayerBG12
|
||||
BayerGR12Packed = VmbPixelFormat.BayerGR12Packed
|
||||
BayerRG12Packed = VmbPixelFormat.BayerRG12Packed
|
||||
BayerGB12Packed = VmbPixelFormat.BayerGB12Packed
|
||||
BayerBG12Packed = VmbPixelFormat.BayerBG12Packed
|
||||
BayerGR10p = VmbPixelFormat.BayerGR10p
|
||||
BayerRG10p = VmbPixelFormat.BayerRG10p
|
||||
BayerGB10p = VmbPixelFormat.BayerGB10p
|
||||
BayerBG10p = VmbPixelFormat.BayerBG10p
|
||||
BayerGR12p = VmbPixelFormat.BayerGR12p
|
||||
BayerRG12p = VmbPixelFormat.BayerRG12p
|
||||
BayerGB12p = VmbPixelFormat.BayerGB12p
|
||||
BayerBG12p = VmbPixelFormat.BayerBG12p
|
||||
BayerGR16 = VmbPixelFormat.BayerGR16
|
||||
BayerRG16 = VmbPixelFormat.BayerRG16
|
||||
BayerGB16 = VmbPixelFormat.BayerGB16
|
||||
BayerBG16 = VmbPixelFormat.BayerBG16
|
||||
|
||||
# RGB Formats
|
||||
Rgb8 = VmbPixelFormat.Rgb8
|
||||
Bgr8 = VmbPixelFormat.Bgr8
|
||||
Rgb10 = VmbPixelFormat.Rgb10
|
||||
Bgr10 = VmbPixelFormat.Bgr10
|
||||
Rgb12 = VmbPixelFormat.Rgb12
|
||||
Bgr12 = VmbPixelFormat.Bgr12
|
||||
Rgb14 = VmbPixelFormat.Rgb14
|
||||
Bgr14 = VmbPixelFormat.Bgr14
|
||||
Rgb16 = VmbPixelFormat.Rgb16
|
||||
Bgr16 = VmbPixelFormat.Bgr16
|
||||
|
||||
# RGBA Formats
|
||||
Rgba8 = VmbPixelFormat.Rgba8
|
||||
Bgra8 = VmbPixelFormat.Bgra8
|
||||
Argb8 = VmbPixelFormat.Argb8
|
||||
Rgba10 = VmbPixelFormat.Rgba10
|
||||
Bgra10 = VmbPixelFormat.Bgra10
|
||||
Rgba12 = VmbPixelFormat.Rgba12
|
||||
Bgra12 = VmbPixelFormat.Bgra12
|
||||
Rgba14 = VmbPixelFormat.Rgba14
|
||||
Bgra14 = VmbPixelFormat.Bgra14
|
||||
Rgba16 = VmbPixelFormat.Rgba16
|
||||
Bgra16 = VmbPixelFormat.Bgra16
|
||||
Yuv411 = VmbPixelFormat.Yuv411
|
||||
Yuv422 = VmbPixelFormat.Yuv422
|
||||
Yuv444 = VmbPixelFormat.Yuv444
|
||||
|
||||
# YCbCr Formats
|
||||
YCbCr411_8_CbYYCrYY = VmbPixelFormat.YCbCr411_8_CbYYCrYY
|
||||
YCbCr422_8_CbYCrY = VmbPixelFormat.YCbCr422_8_CbYCrY
|
||||
YCbCr8_CbYCr = VmbPixelFormat.YCbCr8_CbYCr
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
def __repr__(self):
|
||||
return 'PixelFormat.{}'.format(str(self))
|
||||
|
||||
def get_convertible_formats(self) -> Tuple['PixelFormat', ...]:
|
||||
formats = PIXEL_FORMAT_CONVERTIBILITY_MAP[VmbPixelFormat(self)]
|
||||
return tuple([PixelFormat(fmt) for fmt in formats])
|
||||
|
||||
|
||||
MONO_PIXEL_FORMATS = (
|
||||
PixelFormat.Mono8,
|
||||
PixelFormat.Mono10,
|
||||
PixelFormat.Mono10p,
|
||||
PixelFormat.Mono12,
|
||||
PixelFormat.Mono12Packed,
|
||||
PixelFormat.Mono12p,
|
||||
PixelFormat.Mono14,
|
||||
PixelFormat.Mono16
|
||||
)
|
||||
|
||||
|
||||
BAYER_PIXEL_FORMATS = (
|
||||
PixelFormat.BayerGR8,
|
||||
PixelFormat.BayerRG8,
|
||||
PixelFormat.BayerGB8,
|
||||
PixelFormat.BayerBG8,
|
||||
PixelFormat.BayerGR10,
|
||||
PixelFormat.BayerRG10,
|
||||
PixelFormat.BayerGB10,
|
||||
PixelFormat.BayerBG10,
|
||||
PixelFormat.BayerGR12,
|
||||
PixelFormat.BayerRG12,
|
||||
PixelFormat.BayerGB12,
|
||||
PixelFormat.BayerBG12,
|
||||
PixelFormat.BayerGR12Packed,
|
||||
PixelFormat.BayerRG12Packed,
|
||||
PixelFormat.BayerGB12Packed,
|
||||
PixelFormat.BayerBG12Packed,
|
||||
PixelFormat.BayerGR10p,
|
||||
PixelFormat.BayerRG10p,
|
||||
PixelFormat.BayerGB10p,
|
||||
PixelFormat.BayerBG10p,
|
||||
PixelFormat.BayerGR12p,
|
||||
PixelFormat.BayerRG12p,
|
||||
PixelFormat.BayerGB12p,
|
||||
PixelFormat.BayerBG12p,
|
||||
PixelFormat.BayerGR16,
|
||||
PixelFormat.BayerRG16,
|
||||
PixelFormat.BayerGB16,
|
||||
PixelFormat.BayerBG16
|
||||
)
|
||||
|
||||
|
||||
RGB_PIXEL_FORMATS = (
|
||||
PixelFormat.Rgb8,
|
||||
PixelFormat.Rgb10,
|
||||
PixelFormat.Rgb12,
|
||||
PixelFormat.Rgb14,
|
||||
PixelFormat.Rgb16
|
||||
)
|
||||
|
||||
|
||||
RGBA_PIXEL_FORMATS = (
|
||||
PixelFormat.Rgba8,
|
||||
PixelFormat.Argb8,
|
||||
PixelFormat.Rgba10,
|
||||
PixelFormat.Rgba12,
|
||||
PixelFormat.Rgba14,
|
||||
PixelFormat.Rgba16
|
||||
)
|
||||
|
||||
|
||||
BGR_PIXEL_FORMATS = (
|
||||
PixelFormat.Bgr8,
|
||||
PixelFormat.Bgr10,
|
||||
PixelFormat.Bgr12,
|
||||
PixelFormat.Bgr14,
|
||||
PixelFormat.Bgr16
|
||||
)
|
||||
|
||||
|
||||
BGRA_PIXEL_FORMATS = (
|
||||
PixelFormat.Bgra8,
|
||||
PixelFormat.Bgra10,
|
||||
PixelFormat.Bgra12,
|
||||
PixelFormat.Bgra14,
|
||||
PixelFormat.Bgra16
|
||||
)
|
||||
|
||||
|
||||
YUV_PIXEL_FORMATS = (
|
||||
PixelFormat.Yuv411,
|
||||
PixelFormat.Yuv422,
|
||||
PixelFormat.Yuv444
|
||||
)
|
||||
|
||||
|
||||
YCBCR_PIXEL_FORMATS = (
|
||||
PixelFormat.YCbCr411_8_CbYYCrYY,
|
||||
PixelFormat.YCbCr422_8_CbYCrY,
|
||||
PixelFormat.YCbCr8_CbYCr
|
||||
)
|
||||
|
||||
|
||||
COLOR_PIXEL_FORMATS = BAYER_PIXEL_FORMATS + RGB_PIXEL_FORMATS + RGBA_PIXEL_FORMATS + \
|
||||
BGR_PIXEL_FORMATS + BGRA_PIXEL_FORMATS + YUV_PIXEL_FORMATS + \
|
||||
YCBCR_PIXEL_FORMATS
|
||||
|
||||
|
||||
OPENCV_PIXEL_FORMATS = (
|
||||
PixelFormat.Mono8,
|
||||
PixelFormat.Bgr8,
|
||||
PixelFormat.Bgra8,
|
||||
PixelFormat.Mono16,
|
||||
PixelFormat.Bgr16,
|
||||
PixelFormat.Bgra16
|
||||
)
|
||||
|
||||
|
||||
class Debayer(enum.IntEnum):
|
||||
"""Enum specifying debayer modes.
|
||||
|
||||
Enum values:
|
||||
Mode2x2 - 2x2 with green averaging (this is the default if no debayering algorithm
|
||||
is added as transformation option).
|
||||
Mode3x3 - 3x3 with equal green weighting per line (8-bit images only).
|
||||
ModeLCAA - Debayering with horizontal local color anti-aliasing (8-bit images only).
|
||||
ModeLCAAV - Debayering with horizontal and vertical local color anti-aliasing
|
||||
( 8-bit images only).
|
||||
ModeYuv422 - Debayering with YUV422-alike sub-sampling (8-bit images only).
|
||||
"""
|
||||
Mode2x2 = VmbDebayerMode.Mode_2x2
|
||||
Mode3x3 = VmbDebayerMode.Mode_3x3
|
||||
ModeLCAA = VmbDebayerMode.Mode_LCAA
|
||||
ModeLCAAV = VmbDebayerMode.Mode_LCAAV
|
||||
ModeYuv422 = VmbDebayerMode.Mode_YUV422
|
||||
|
||||
def __str__(self):
|
||||
return 'DebayerMode.{}'.format(self._name_)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
class FrameStatus(enum.IntEnum):
|
||||
"""Enum specifying the current status of internal Frame data.
|
||||
|
||||
Enum values:
|
||||
Complete - Frame data is complete without errors.
|
||||
Incomplete - Frame could not be filled to the end.
|
||||
TooSmall - Frame buffer was too small.
|
||||
Invalid - Frame buffer was invalid.
|
||||
"""
|
||||
|
||||
Complete = VmbFrameStatus.Complete
|
||||
Incomplete = VmbFrameStatus.Incomplete
|
||||
TooSmall = VmbFrameStatus.TooSmall
|
||||
Invalid = VmbFrameStatus.Invalid
|
||||
|
||||
|
||||
class AllocationMode(enum.IntEnum):
|
||||
"""Enum specifying the supported frame allocation modes.
|
||||
|
||||
Enum values:
|
||||
AnnounceFrame - The buffer is allocated by VimbaPython
|
||||
AllocAndAnnounceFrame - The buffer is allocated by the Transport Layer
|
||||
"""
|
||||
AnnounceFrame = 0
|
||||
AllocAndAnnounceFrame = 1
|
||||
|
||||
|
||||
class AncillaryData:
|
||||
"""Ancillary Data are created after enabling a Cameras 'ChunkModeActive' Feature.
|
||||
Ancillary Data are Features stored within a Frame.
|
||||
"""
|
||||
@TraceEnable()
|
||||
@LeaveContextOnCall()
|
||||
def __init__(self, handle: VmbFrame):
|
||||
"""Do not call directly. Get Object via Frame access method"""
|
||||
self.__handle: VmbFrame = handle
|
||||
self.__data_handle: VmbHandle = VmbHandle()
|
||||
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()
|
||||
|
||||
@RaiseIfOutsideContext()
|
||||
def get_all_features(self) -> FeaturesTuple:
|
||||
"""Get all features in ancillary data.
|
||||
|
||||
Returns:
|
||||
A set of all currently features stored in Ancillary Data.
|
||||
|
||||
Raises:
|
||||
RuntimeError then called outside of "with" - statement.
|
||||
"""
|
||||
return self.__feats
|
||||
|
||||
@RaiseIfOutsideContext()
|
||||
@RuntimeTypeCheckEnable()
|
||||
def get_features_by_type(self, feat_type: FeatureTypeTypes) -> FeaturesTuple:
|
||||
"""Get all features in ancillary data of a specific type.
|
||||
|
||||
Valid FeatureTypes are: IntFeature, FloatFeature, StringFeature, BoolFeature,
|
||||
EnumFeature, CommandFeature, RawFeature
|
||||
|
||||
Arguments:
|
||||
feat_type - FeatureType used find features of that type.
|
||||
|
||||
Returns:
|
||||
A all features of type 'feat_type'.
|
||||
|
||||
Raises:
|
||||
RuntimeError then called outside of "with" - statement.
|
||||
TypeError if parameters do not match their type hint.
|
||||
"""
|
||||
return filter_features_by_type(self.__feats, feat_type)
|
||||
|
||||
@RaiseIfOutsideContext()
|
||||
@RuntimeTypeCheckEnable()
|
||||
def get_features_by_category(self, category: str) -> FeaturesTuple:
|
||||
"""Get all features in ancillary data of a specific category.
|
||||
|
||||
Arguments:
|
||||
category - Category that should be used for filtering.
|
||||
|
||||
Returns:
|
||||
A all features of category 'category'.
|
||||
|
||||
Raises:
|
||||
RuntimeError then called outside of "with" - statement.
|
||||
TypeError if parameters do not match their type hint.
|
||||
"""
|
||||
return filter_features_by_category(self.__feats, category)
|
||||
|
||||
@RaiseIfOutsideContext()
|
||||
@RuntimeTypeCheckEnable()
|
||||
def get_feature_by_name(self, feat_name: str) -> FeatureTypes:
|
||||
"""Get a features in ancillary data by its name.
|
||||
|
||||
Arguments:
|
||||
feat_name - Name used to find a feature.
|
||||
|
||||
Returns:
|
||||
Feature with the associated name.
|
||||
|
||||
Raises:
|
||||
RuntimeError then called outside of "with" - statement.
|
||||
TypeError if parameters do not match their type hint.
|
||||
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('VmbAncillaryDataOpen', byref(self.__handle), byref(self.__data_handle))
|
||||
|
||||
self.__feats = _replace_invalid_feature_calls(discover_features(self.__data_handle))
|
||||
attach_feature_accessors(self, self.__feats)
|
||||
|
||||
@TraceEnable()
|
||||
@LeaveContextOnCall()
|
||||
def _close(self):
|
||||
remove_feature_accessors(self, self.__feats)
|
||||
self.__feats = ()
|
||||
|
||||
call_vimba_c('VmbAncillaryDataClose', self.__data_handle)
|
||||
self.__data_handle = VmbHandle()
|
||||
|
||||
|
||||
def _replace_invalid_feature_calls(feats: FeaturesTuple) -> FeaturesTuple:
|
||||
# AncillaryData are basically "lightweight" features. Calling most feature related
|
||||
# Functions with a AncillaryData - Handle leads to VimbaC Errors. This method decorates
|
||||
# all Methods that are unsafe to call with a decorator raising a RuntimeError.
|
||||
to_wrap = [
|
||||
'get_access_mode',
|
||||
'is_readable',
|
||||
'is_writeable',
|
||||
'register_change_handler',
|
||||
'get_increment',
|
||||
'get_range',
|
||||
'set'
|
||||
]
|
||||
|
||||
# Decorator raising a RuntimeError instead of delegating call to inner function.
|
||||
def invalid_call(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
msg = 'Calling \'{}\' is invalid for AncillaryData Features.'
|
||||
raise RuntimeError(msg.format(func.__name__))
|
||||
|
||||
return wrapper
|
||||
|
||||
# Replace original implementation by injecting a surrounding decorator and
|
||||
# binding the resulting function as a method to the Feature instance.
|
||||
for f, a in [(f, a) for f in feats for a in to_wrap]:
|
||||
try:
|
||||
fn = invalid_call(getattr(f, a))
|
||||
setattr(f, a, fn.__get__(f))
|
||||
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return feats
|
||||
|
||||
|
||||
class Frame:
|
||||
"""This class allows access to Frames acquired by a camera. The Frame is basically
|
||||
a buffer that wraps image data and some metadata.
|
||||
"""
|
||||
def __init__(self, buffer_size: int, allocation_mode: AllocationMode):
|
||||
"""Do not call directly. Create Frames via Camera methods instead."""
|
||||
self._allocation_mode = allocation_mode
|
||||
|
||||
# Allocation is not necessary for the AllocAndAnnounce case. In that case the Transport
|
||||
# Layer will take care of buffer allocation. The self._buffer variable will be updated after
|
||||
# the frame is announced and memory has been allocated.
|
||||
if self._allocation_mode == AllocationMode.AnnounceFrame:
|
||||
self._buffer = (ctypes.c_ubyte * buffer_size)()
|
||||
self._frame: VmbFrame = VmbFrame()
|
||||
|
||||
# Setup underlaying Frame
|
||||
if self._allocation_mode == AllocationMode.AnnounceFrame:
|
||||
self._frame.buffer = ctypes.cast(self._buffer, ctypes.c_void_p)
|
||||
self._frame.bufferSize = sizeof(self._buffer)
|
||||
elif self._allocation_mode == AllocationMode.AllocAndAnnounceFrame:
|
||||
# Set buffer pointer to NULL and inform Transport Layer of size it should allocate
|
||||
self._frame.buffer = None
|
||||
self._frame.bufferSize = buffer_size
|
||||
|
||||
def __str__(self):
|
||||
msg = 'Frame(id={}, status={}, buffer={})'
|
||||
return msg.format(self._frame.frameID, str(FrameStatus(self._frame.receiveStatus)),
|
||||
hex(self._frame.buffer))
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
cls = self.__class__
|
||||
result = cls.__new__(cls)
|
||||
memo[id(self)] = result
|
||||
|
||||
# VmbFrame contains Pointers and ctypes.Structure with Pointers can't be copied.
|
||||
# As a workaround VmbFrame contains a deepcopy-like Method performing deep copy of all
|
||||
# Attributes except PointerTypes. Those must be set manually after the copy operation.
|
||||
setattr(result, '_buffer', copy.deepcopy(self._buffer, memo))
|
||||
setattr(result, '_frame', self._frame.deepcopy_skip_ptr(memo))
|
||||
|
||||
result._frame.buffer = ctypes.cast(result._buffer, ctypes.c_void_p)
|
||||
result._frame.bufferSize = sizeof(result._buffer)
|
||||
|
||||
return result
|
||||
|
||||
def _set_buffer(self, buffer: ctypes.c_void_p):
|
||||
"""Set self._buffer to memory pointed to by passed buffer pointer
|
||||
|
||||
Useful if frames were allocated with AllocationMode.AllocAndAnnounce
|
||||
"""
|
||||
self._buffer = ctypes.cast(buffer,
|
||||
ctypes.POINTER(ctypes.c_ubyte * self._frame.bufferSize)).contents
|
||||
|
||||
def get_buffer(self) -> ctypes.Array:
|
||||
"""Get internal buffer object containing image data."""
|
||||
return self._buffer
|
||||
|
||||
def get_buffer_size(self) -> int:
|
||||
"""Get byte size of internal buffer."""
|
||||
return self._frame.bufferSize
|
||||
|
||||
def get_image_size(self) -> int:
|
||||
"""Get byte size of image data stored in buffer."""
|
||||
return self._frame.imageSize
|
||||
|
||||
def get_ancillary_data(self) -> Optional[AncillaryData]:
|
||||
"""Get AncillaryData.
|
||||
|
||||
Frames acquired with cameras where Feature ChunkModeActive is enabled can contain
|
||||
ancillary data within the image data.
|
||||
|
||||
Returns:
|
||||
None if Frame contains no ancillary data.
|
||||
AncillaryData if Frame contains ancillary data.
|
||||
"""
|
||||
if not self._frame.ancillarySize:
|
||||
return None
|
||||
|
||||
return AncillaryData(self._frame)
|
||||
|
||||
def get_status(self) -> FrameStatus:
|
||||
"""Returns current frame status."""
|
||||
return FrameStatus(self._frame.receiveStatus)
|
||||
|
||||
def get_pixel_format(self) -> PixelFormat:
|
||||
"""Get format of the acquired image data"""
|
||||
return PixelFormat(self._frame.pixelFormat)
|
||||
|
||||
def get_height(self) -> Optional[int]:
|
||||
"""Get image height in pixels.
|
||||
|
||||
Returns:
|
||||
Image height in pixels if dimension data is provided by the camera.
|
||||
None if dimension data is not provided by the camera.
|
||||
"""
|
||||
flags = decode_flags(VmbFrameFlags, self._frame.receiveFlags)
|
||||
|
||||
if VmbFrameFlags.Dimension not in flags:
|
||||
return None
|
||||
|
||||
return self._frame.height
|
||||
|
||||
def get_width(self) -> Optional[int]:
|
||||
"""Get image width in pixels.
|
||||
|
||||
Returns:
|
||||
Image width in pixels if dimension data is provided by the camera.
|
||||
None if dimension data is not provided by the camera.
|
||||
"""
|
||||
flags = decode_flags(VmbFrameFlags, self._frame.receiveFlags)
|
||||
|
||||
if VmbFrameFlags.Dimension not in flags:
|
||||
return None
|
||||
|
||||
return self._frame.width
|
||||
|
||||
def get_offset_x(self) -> Optional[int]:
|
||||
"""Get horizontal offset in pixels.
|
||||
|
||||
Returns:
|
||||
Horizontal offset in pixel if offset data is provided by the camera.
|
||||
None if offset data is not provided by the camera.
|
||||
"""
|
||||
flags = decode_flags(VmbFrameFlags, self._frame.receiveFlags)
|
||||
|
||||
if VmbFrameFlags.Offset not in flags:
|
||||
return None
|
||||
|
||||
return self._frame.offsetX
|
||||
|
||||
def get_offset_y(self) -> Optional[int]:
|
||||
"""Get vertical offset in pixels.
|
||||
|
||||
Returns:
|
||||
Vertical offset in pixels if offset data is provided by the camera.
|
||||
None if offset data is not provided by the camera.
|
||||
"""
|
||||
flags = decode_flags(VmbFrameFlags, self._frame.receiveFlags)
|
||||
|
||||
if VmbFrameFlags.Offset not in flags:
|
||||
return None
|
||||
|
||||
return self._frame.offsetY
|
||||
|
||||
def get_id(self) -> Optional[int]:
|
||||
"""Get Frame ID.
|
||||
|
||||
Returns:
|
||||
Frame ID if the id is provided by the camera.
|
||||
None if frame id is not provided by the camera.
|
||||
"""
|
||||
flags = decode_flags(VmbFrameFlags, self._frame.receiveFlags)
|
||||
|
||||
if VmbFrameFlags.FrameID not in flags:
|
||||
return None
|
||||
|
||||
return self._frame.frameID
|
||||
|
||||
def get_timestamp(self) -> Optional[int]:
|
||||
"""Get Frame timestamp.
|
||||
|
||||
Returns:
|
||||
Timestamp if provided by the camera.
|
||||
None if timestamp is not provided by the camera.
|
||||
"""
|
||||
flags = decode_flags(VmbFrameFlags, self._frame.receiveFlags)
|
||||
|
||||
if VmbFrameFlags.Timestamp not in flags:
|
||||
return None
|
||||
|
||||
return self._frame.timestamp
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def convert_pixel_format(self, target_fmt: PixelFormat,
|
||||
debayer_mode: Optional[Debayer] = None):
|
||||
"""Convert internal pixel format to given format.
|
||||
|
||||
Note: This method allocates a new buffer for internal image data leading to some
|
||||
runtime overhead. For performance reasons, it might be better to set the value
|
||||
of the camera's 'PixelFormat' feature instead. In addition, a non-default debayer mode
|
||||
can be specified.
|
||||
|
||||
Arguments:
|
||||
target_fmt - PixelFormat to convert to.
|
||||
debayer_mode - Non-default algorithm used to debayer images in Bayer Formats. If
|
||||
no mode is specified, default debayering mode 'Mode2x2' is applied. If
|
||||
the current format is no Bayer format, this parameter is silently
|
||||
ignored.
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
ValueError if the current format can't be converted into 'target_fmt'. Convertible
|
||||
Formats can be queried via get_convertible_formats() of PixelFormat.
|
||||
AssertionError if image width or height can't be determined.
|
||||
"""
|
||||
|
||||
global BAYER_PIXEL_FORMATS
|
||||
|
||||
# 1) Perform sanity checking
|
||||
fmt = self.get_pixel_format()
|
||||
|
||||
if fmt == target_fmt:
|
||||
return
|
||||
|
||||
if target_fmt not in fmt.get_convertible_formats():
|
||||
raise ValueError('Current PixelFormat can\'t be converted into given format.')
|
||||
|
||||
# 2) Specify Transformation Input Image
|
||||
height = self._frame.height
|
||||
width = self._frame.width
|
||||
|
||||
c_src_image = VmbImage()
|
||||
c_src_image.Size = sizeof(c_src_image)
|
||||
c_src_image.Data = ctypes.cast(self._buffer, ctypes.c_void_p)
|
||||
|
||||
call_vimba_image_transform('VmbSetImageInfoFromPixelFormat', fmt, width, height,
|
||||
byref(c_src_image))
|
||||
|
||||
# 3) Specify Transformation Output Image
|
||||
c_dst_image = VmbImage()
|
||||
c_dst_image.Size = sizeof(c_dst_image)
|
||||
|
||||
layout, bits = PIXEL_FORMAT_TO_LAYOUT[VmbPixelFormat(target_fmt)]
|
||||
|
||||
call_vimba_image_transform('VmbSetImageInfoFromInputImage', byref(c_src_image), layout,
|
||||
bits, byref(c_dst_image))
|
||||
|
||||
# 4) Allocate Buffer and perform transformation
|
||||
img_size = int(height * width * c_dst_image.ImageInfo.PixelInfo.BitsPerPixel / 8)
|
||||
anc_size = self._frame.ancillarySize
|
||||
|
||||
buf = (ctypes.c_ubyte * (img_size + anc_size))()
|
||||
c_dst_image.Data = ctypes.cast(buf, ctypes.c_void_p)
|
||||
|
||||
# 5) Setup Debayering mode if given.
|
||||
transform_info = VmbTransformInfo()
|
||||
if debayer_mode and (fmt in BAYER_PIXEL_FORMATS):
|
||||
call_vimba_image_transform('VmbSetDebayerMode', VmbDebayerMode(debayer_mode),
|
||||
byref(transform_info))
|
||||
|
||||
# 6) Perform Transformation
|
||||
call_vimba_image_transform('VmbImageTransform', byref(c_src_image), byref(c_dst_image),
|
||||
byref(transform_info), 1)
|
||||
|
||||
# 7) Copy ancillary data if existing
|
||||
if anc_size:
|
||||
src = ctypes.addressof(self._buffer) + self._frame.imageSize
|
||||
dst = ctypes.addressof(buf) + img_size
|
||||
|
||||
ctypes.memmove(dst, src, anc_size)
|
||||
|
||||
# 8) Update frame metadata
|
||||
self._buffer = buf
|
||||
self._frame.buffer = ctypes.cast(self._buffer, ctypes.c_void_p)
|
||||
self._frame.bufferSize = sizeof(self._buffer)
|
||||
self._frame.imageSize = img_size
|
||||
self._frame.pixelFormat = target_fmt
|
||||
|
||||
def as_numpy_ndarray(self) -> 'numpy.ndarray':
|
||||
"""Construct numpy.ndarray view on VimbaFrame.
|
||||
|
||||
Returns:
|
||||
numpy.ndarray on internal image buffer.
|
||||
|
||||
Raises:
|
||||
ImportError if numpy is not installed.
|
||||
VimbaFrameError if current PixelFormat can't be converted to a numpy.ndarray.
|
||||
"""
|
||||
if numpy is None:
|
||||
raise ImportError('\'Frame.as_opencv_image()\' requires module \'numpy\'.')
|
||||
|
||||
# Construct numpy overlay on underlaying image buffer
|
||||
height = self._frame.height
|
||||
width = self._frame.width
|
||||
fmt = self._frame.pixelFormat
|
||||
|
||||
c_image = VmbImage()
|
||||
c_image.Size = sizeof(c_image)
|
||||
|
||||
call_vimba_image_transform('VmbSetImageInfoFromPixelFormat', fmt, width, height,
|
||||
byref(c_image))
|
||||
|
||||
layout = PIXEL_FORMAT_TO_LAYOUT.get(fmt)
|
||||
|
||||
if not layout:
|
||||
msg = 'Can\'t construct numpy.ndarray for Pixelformat {}. ' \
|
||||
'Use \'frame.convert_pixel_format()\' to convert to a different Pixelformat.'
|
||||
raise VimbaFrameError(msg.format(str(self.get_pixel_format())))
|
||||
|
||||
bits_per_channel = layout[1]
|
||||
channels_per_pixel = c_image.ImageInfo.PixelInfo.BitsPerPixel // bits_per_channel
|
||||
|
||||
return numpy.ndarray(shape=(height, width, channels_per_pixel),
|
||||
buffer=self._buffer, # type: ignore
|
||||
dtype=numpy.uint8 if bits_per_channel == 8 else numpy.uint16)
|
||||
|
||||
def as_opencv_image(self) -> 'numpy.ndarray':
|
||||
"""Construct OpenCV compatible view on VimbaFrame.
|
||||
|
||||
Returns:
|
||||
OpenCV compatible numpy.ndarray
|
||||
|
||||
Raises:
|
||||
ImportError if numpy is not installed.
|
||||
ValueError if current pixel format is not compatible with opencv. Compatible
|
||||
formats are in OPENCV_PIXEL_FORMATS.
|
||||
"""
|
||||
global OPENCV_PIXEL_FORMATS
|
||||
|
||||
if numpy is None:
|
||||
raise ImportError('\'Frame.as_opencv_image()\' requires module \'numpy\'.')
|
||||
|
||||
fmt = self._frame.pixelFormat
|
||||
|
||||
if fmt not in OPENCV_PIXEL_FORMATS:
|
||||
raise ValueError('Current Format \'{}\' is not in OPENCV_PIXEL_FORMATS'.format(
|
||||
str(PixelFormat(self._frame.pixelFormat))))
|
||||
|
||||
return self.as_numpy_ndarray()
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
@RuntimeTypeCheckEnable()
|
||||
def intersect_pixel_formats(fmts1: FormatTuple, fmts2: FormatTuple) -> FormatTuple:
|
||||
"""Build intersection of two sets containing PixelFormat.
|
||||
|
||||
Arguments:
|
||||
fmts1 - PixelFormats to intersect with fmts2
|
||||
fmts2 - PixelFormats to intersect with fmts1
|
||||
|
||||
Returns:
|
||||
Set of PixelFormats that occur in fmts1 and fmts2
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
"""
|
||||
return tuple(set(fmts1).intersection(set(fmts2)))
|
||||
391
Vimba_6_0/VimbaPython/Source/vimba/interface.py
Normal file
391
Vimba_6_0/VimbaPython/Source/vimba/interface.py
Normal file
@@ -0,0 +1,391 @@
|
||||
"""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()
|
||||
357
Vimba_6_0/VimbaPython/Source/vimba/shared.py
Normal file
357
Vimba_6_0/VimbaPython/Source/vimba/shared.py
Normal file
@@ -0,0 +1,357 @@
|
||||
"""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 itertools
|
||||
|
||||
from typing import Dict, Tuple
|
||||
from .c_binding import VmbUint32, VmbUint64, VmbHandle, VmbFeatureInfo
|
||||
from .c_binding import call_vimba_c, byref, sizeof, create_string_buffer, VimbaCError
|
||||
from .feature import FeaturesTuple, FeatureTypes, FeatureTypeTypes
|
||||
from .error import VimbaFeatureError
|
||||
from .util import TraceEnable
|
||||
|
||||
__all__ = [
|
||||
'filter_affected_features',
|
||||
'filter_selected_features',
|
||||
'filter_features_by_name',
|
||||
'filter_features_by_type',
|
||||
'filter_features_by_category',
|
||||
'attach_feature_accessors',
|
||||
'remove_feature_accessors',
|
||||
'read_memory',
|
||||
'write_memory',
|
||||
'read_registers',
|
||||
'write_registers'
|
||||
]
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
def filter_affected_features(feats: FeaturesTuple, feat: FeatureTypes) -> FeaturesTuple:
|
||||
"""Search for all Features affected by a given feature within a feature set.
|
||||
|
||||
Arguments:
|
||||
feats: Feature set to search in.
|
||||
feat: Feature that might affect Features within 'feats'.
|
||||
|
||||
Returns:
|
||||
A set of all features that are affected by 'feat'.
|
||||
|
||||
Raises:
|
||||
VimbaFeatureError if 'feat' is not stored within 'feats'.
|
||||
"""
|
||||
|
||||
if feat not in feats:
|
||||
raise VimbaFeatureError('Feature \'{}\' not in given Features'.format(feat.get_name()))
|
||||
|
||||
result = []
|
||||
|
||||
if feat.has_affected_features():
|
||||
feats_count = VmbUint32()
|
||||
feats_handle = feat._handle
|
||||
feats_name = feat._info.name
|
||||
|
||||
# Query affected features from given Feature
|
||||
call_vimba_c('VmbFeatureListAffected', feats_handle, feats_name, None, 0,
|
||||
byref(feats_count), sizeof(VmbFeatureInfo))
|
||||
|
||||
feats_found = VmbUint32(0)
|
||||
feats_infos = (VmbFeatureInfo * feats_count.value)()
|
||||
|
||||
call_vimba_c('VmbFeatureListAffected', feats_handle, feats_name, feats_infos, feats_count,
|
||||
byref(feats_found), sizeof(VmbFeatureInfo))
|
||||
|
||||
# Search affected features in given feature set
|
||||
for info, feature in itertools.product(feats_infos[:feats_found.value], feats):
|
||||
if info.name == feature._info.name:
|
||||
result.append(feature)
|
||||
|
||||
return tuple(result)
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
def filter_selected_features(feats: FeaturesTuple, feat: FeatureTypes) -> FeaturesTuple:
|
||||
"""Search for all Features selected by a given feature within a feature set.
|
||||
|
||||
Arguments:
|
||||
feats: Feature set to search in.
|
||||
feat: Feature that might select Features within 'feats'.
|
||||
|
||||
Returns:
|
||||
A set of all features that are selected by 'feat'.
|
||||
|
||||
Raises:
|
||||
VimbaFeatureError if 'feat' is not stored within 'feats'.
|
||||
"""
|
||||
if feat not in feats:
|
||||
raise VimbaFeatureError('Feature \'{}\' not in given Features'.format(feat.get_name()))
|
||||
|
||||
result = []
|
||||
|
||||
if feat.has_selected_features():
|
||||
feats_count = VmbUint32()
|
||||
feats_handle = feat._handle
|
||||
feats_name = feat._info.name
|
||||
|
||||
# Query selected features from given feature
|
||||
call_vimba_c('VmbFeatureListSelected', feats_handle, feats_name, None, 0,
|
||||
byref(feats_count), sizeof(VmbFeatureInfo))
|
||||
|
||||
feats_found = VmbUint32(0)
|
||||
feats_infos = (VmbFeatureInfo * feats_count.value)()
|
||||
|
||||
call_vimba_c('VmbFeatureListSelected', feats_handle, feats_name, feats_infos, feats_count,
|
||||
byref(feats_found), sizeof(VmbFeatureInfo))
|
||||
|
||||
# Search selected features in given feature set
|
||||
for info, feature in itertools.product(feats_infos[:feats_found.value], feats):
|
||||
if info.name == feature._info.name:
|
||||
result.append(feature)
|
||||
|
||||
return tuple(result)
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
def filter_features_by_name(feats: FeaturesTuple, feat_name: str):
|
||||
"""Search for a feature with a specific name within a feature set.
|
||||
|
||||
Arguments:
|
||||
feats: Feature set to search in.
|
||||
feat_name: Feature name to look for.
|
||||
|
||||
Returns:
|
||||
The Feature with the name 'feat_name' or None if lookup failed
|
||||
"""
|
||||
filtered = [feat for feat in feats if feat_name == feat.get_name()]
|
||||
return filtered.pop() if filtered else None
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
def filter_features_by_type(feats: FeaturesTuple, feat_type: FeatureTypeTypes) -> FeaturesTuple:
|
||||
"""Search for all features with a specific type within a given feature set.
|
||||
|
||||
Arguments:
|
||||
feats: Feature set to search in.
|
||||
feat_type: Feature Type to search for
|
||||
|
||||
Returns:
|
||||
A set of all features of type 'feat_type' in 'feats'. If no matching type is found an
|
||||
empty set is returned.
|
||||
"""
|
||||
return tuple([feat for feat in feats if type(feat) == feat_type])
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
def filter_features_by_category(feats: FeaturesTuple, category: str) -> FeaturesTuple:
|
||||
"""Search for all features of a given category.
|
||||
|
||||
Arguments:
|
||||
feats: Feature set to search in.
|
||||
category: Category to filter for
|
||||
|
||||
Returns:
|
||||
A set of all features of category 'category' in 'feats'. If no matching type is found an
|
||||
empty set is returned.
|
||||
"""
|
||||
return tuple([feat for feat in feats if feat.get_category() == category])
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
def attach_feature_accessors(obj, feats: FeaturesTuple):
|
||||
"""Attach all Features in feats to obj under the feature name.
|
||||
|
||||
Arguments:
|
||||
obj: Object feats should be attached on.
|
||||
feats: Features to attach.
|
||||
"""
|
||||
BLACKLIST = (
|
||||
'PixelFormat', # PixelFormats have special access methods.
|
||||
)
|
||||
|
||||
for feat in feats:
|
||||
feat_name = feat.get_name()
|
||||
if feat_name not in BLACKLIST:
|
||||
setattr(obj, feat_name, feat)
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
def remove_feature_accessors(obj, feats: FeaturesTuple):
|
||||
"""Remove all Features in feats from obj.
|
||||
|
||||
Arguments:
|
||||
obj: Object, feats should be removed from.
|
||||
feats: Features to remove.
|
||||
"""
|
||||
for feat in feats:
|
||||
try:
|
||||
delattr(obj, feat.get_name())
|
||||
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
def read_memory(handle: VmbHandle, addr: int, max_bytes: int) -> bytes: # coverage: skip
|
||||
"""Read a byte sequence from a given memory address.
|
||||
|
||||
Arguments:
|
||||
handle: Handle on entity that allows raw memory access.
|
||||
addr: Starting address to read from.
|
||||
max_bytes: Maximum number of bytes to read from addr.
|
||||
|
||||
Returns:
|
||||
Read memory contents as bytes.
|
||||
|
||||
Raises:
|
||||
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.
|
||||
_verify_addr(addr)
|
||||
_verify_size(max_bytes)
|
||||
|
||||
buf = create_string_buffer(max_bytes)
|
||||
bytesRead = VmbUint32()
|
||||
|
||||
try:
|
||||
call_vimba_c('VmbMemoryRead', handle, addr, max_bytes, buf, byref(bytesRead))
|
||||
|
||||
except VimbaCError as e:
|
||||
msg = 'Memory read access at {} failed with C-Error: {}.'
|
||||
raise ValueError(msg.format(hex(addr), repr(e.get_error_code()))) from e
|
||||
|
||||
return buf.raw[:bytesRead.value]
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
def write_memory(handle: VmbHandle, addr: int, data: bytes): # coverage: skip
|
||||
""" Write a byte sequence to a given memory address.
|
||||
|
||||
Arguments:
|
||||
handle: Handle on entity that allows raw memory access.
|
||||
addr: Address to write the content of 'data' too.
|
||||
data: Byte sequence to write at address 'addr'.
|
||||
|
||||
Raises:
|
||||
ValueError if addr is negative.
|
||||
ValueError if the memory access was invalid.
|
||||
"""
|
||||
# Note: Coverage is skipped. Function is untestable in a generic way.
|
||||
_verify_addr(addr)
|
||||
|
||||
bytesWrite = VmbUint32()
|
||||
|
||||
try:
|
||||
call_vimba_c('VmbMemoryWrite', handle, addr, len(data), data, byref(bytesWrite))
|
||||
|
||||
except VimbaCError as e:
|
||||
msg = 'Memory write access at {} failed with C-Error: {}.'
|
||||
raise ValueError(msg.format(hex(addr), repr(e.get_error_code()))) from e
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
def read_registers(handle: VmbHandle, addrs: Tuple[int, ...]) -> Dict[int, int]: # coverage: skip
|
||||
"""Read contents of multiple registers.
|
||||
|
||||
Arguments:
|
||||
handle: Handle on entity providing registers to access.
|
||||
addrs: Sequence of addresses that should be read iteratively.
|
||||
|
||||
Return:
|
||||
Dictionary containing a mapping from given address to the read register values.
|
||||
|
||||
Raises:
|
||||
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.
|
||||
for addr in addrs:
|
||||
_verify_addr(addr)
|
||||
|
||||
size = len(addrs)
|
||||
valid_reads = VmbUint32()
|
||||
|
||||
c_addrs = (VmbUint64 * size)()
|
||||
c_values = (VmbUint64 * size)()
|
||||
|
||||
for i, addr in enumerate(addrs):
|
||||
c_addrs[i] = addr
|
||||
|
||||
try:
|
||||
call_vimba_c('VmbRegistersRead', handle, size, c_addrs, c_values, byref(valid_reads))
|
||||
|
||||
except VimbaCError as e:
|
||||
msg = 'Register read access failed with C-Error: {}.'
|
||||
raise ValueError(msg.format(repr(e.get_error_code()))) from e
|
||||
|
||||
return dict(zip(c_addrs, c_values))
|
||||
|
||||
|
||||
@TraceEnable()
|
||||
def write_registers(handle: VmbHandle, addrs_values: Dict[int, int]): # coverage: skip
|
||||
"""Write data to multiple Registers.
|
||||
|
||||
Arguments:
|
||||
handle: Handle on entity providing registers to access.
|
||||
addrs_values: Mapping between Register addresses and the data to write.
|
||||
|
||||
Raises:
|
||||
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.
|
||||
for addr in addrs_values:
|
||||
_verify_addr(addr)
|
||||
|
||||
size = len(addrs_values)
|
||||
valid_writes = VmbUint32()
|
||||
|
||||
addrs = (VmbUint64 * size)()
|
||||
values = (VmbUint64 * size)()
|
||||
|
||||
for i, addr in enumerate(addrs_values):
|
||||
addrs[i] = addr
|
||||
values[i] = addrs_values[addr]
|
||||
|
||||
try:
|
||||
call_vimba_c('VmbRegistersWrite', handle, size, addrs, values, byref(valid_writes))
|
||||
|
||||
except VimbaCError as e:
|
||||
msg = 'Register write access failed with C-Error: {}.'
|
||||
raise ValueError(msg.format(repr(e.get_error_code()))) from e
|
||||
|
||||
|
||||
def _verify_addr(addr: int): # coverage: skip
|
||||
# Note: Coverage is skipped. Function is untestable in a generic way.
|
||||
if addr < 0:
|
||||
raise ValueError('Given Address {} is negative'.format(addr))
|
||||
|
||||
|
||||
def _verify_size(size: int): # coverage: skip
|
||||
# Note: Coverage is skipped. Function is untestable in a generic way.
|
||||
if size < 0:
|
||||
raise ValueError('Given size {} is negative'.format(size))
|
||||
72
Vimba_6_0/VimbaPython/Source/vimba/util/__init__.py
Normal file
72
Vimba_6_0/VimbaPython/Source/vimba/util/__init__.py
Normal file
@@ -0,0 +1,72 @@
|
||||
"""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.
|
||||
"""
|
||||
|
||||
# Suppress 'imported but unused' - Error from static style checker.
|
||||
# flake8: noqa: F401
|
||||
|
||||
__all__ = [
|
||||
'LogLevel',
|
||||
'LogConfig',
|
||||
'Log',
|
||||
'LOG_CONFIG_TRACE_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_TRACE_FILE_ONLY',
|
||||
'LOG_CONFIG_TRACE',
|
||||
'LOG_CONFIG_INFO_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_INFO_FILE_ONLY',
|
||||
'LOG_CONFIG_INFO',
|
||||
'LOG_CONFIG_WARNING_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_WARNING_FILE_ONLY',
|
||||
'LOG_CONFIG_WARNING',
|
||||
'LOG_CONFIG_ERROR_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_ERROR_FILE_ONLY',
|
||||
'LOG_CONFIG_ERROR',
|
||||
'LOG_CONFIG_CRITICAL_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_CRITICAL_FILE_ONLY',
|
||||
'LOG_CONFIG_CRITICAL',
|
||||
|
||||
# Decorators
|
||||
'TraceEnable',
|
||||
'ScopedLogEnable',
|
||||
'RuntimeTypeCheckEnable',
|
||||
'EnterContextOnCall',
|
||||
'LeaveContextOnCall',
|
||||
'RaiseIfInsideContext',
|
||||
'RaiseIfOutsideContext'
|
||||
]
|
||||
|
||||
from .log import Log, LogLevel, LogConfig, LOG_CONFIG_TRACE_CONSOLE_ONLY, \
|
||||
LOG_CONFIG_TRACE_FILE_ONLY, LOG_CONFIG_TRACE, LOG_CONFIG_INFO_CONSOLE_ONLY, \
|
||||
LOG_CONFIG_INFO_FILE_ONLY, LOG_CONFIG_INFO, LOG_CONFIG_WARNING_CONSOLE_ONLY, \
|
||||
LOG_CONFIG_WARNING_FILE_ONLY, LOG_CONFIG_WARNING, LOG_CONFIG_ERROR_CONSOLE_ONLY, \
|
||||
LOG_CONFIG_ERROR_FILE_ONLY, LOG_CONFIG_ERROR, LOG_CONFIG_CRITICAL_CONSOLE_ONLY, \
|
||||
LOG_CONFIG_CRITICAL_FILE_ONLY, LOG_CONFIG_CRITICAL
|
||||
|
||||
from .tracer import TraceEnable
|
||||
from .scoped_log import ScopedLogEnable
|
||||
from .runtime_type_check import RuntimeTypeCheckEnable
|
||||
from .context_decorator import EnterContextOnCall, LeaveContextOnCall, RaiseIfInsideContext, \
|
||||
RaiseIfOutsideContext
|
||||
96
Vimba_6_0/VimbaPython/Source/vimba/util/context_decorator.py
Normal file
96
Vimba_6_0/VimbaPython/Source/vimba/util/context_decorator.py
Normal file
@@ -0,0 +1,96 @@
|
||||
"""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 functools
|
||||
|
||||
__all__ = [
|
||||
'EnterContextOnCall',
|
||||
'LeaveContextOnCall',
|
||||
'RaiseIfInsideContext',
|
||||
'RaiseIfOutsideContext'
|
||||
]
|
||||
|
||||
|
||||
class EnterContextOnCall:
|
||||
"""Decorator setting/injecting flag used for checking the context."""
|
||||
def __call__(self, func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
args[0]._context_entered = True
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class LeaveContextOnCall:
|
||||
"""Decorator clearing/injecting flag used for checking the context."""
|
||||
def __call__(self, func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
result = func(*args, **kwargs)
|
||||
args[0]._context_entered = False
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class RaiseIfInsideContext:
|
||||
"""Raising RuntimeError is decorated Method is called inside with-statement.
|
||||
|
||||
Note This Decorator shall work only on Object implementing a Context Manger.
|
||||
For this to work object must offer a boolean attribute called _context_entered
|
||||
"""
|
||||
def __call__(self, func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if args[0]._context_entered:
|
||||
msg = 'Called \'{}()\' inside of \'with\' - statement scope.'
|
||||
msg = msg.format('{}'.format(func.__qualname__))
|
||||
raise RuntimeError(msg)
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class RaiseIfOutsideContext:
|
||||
"""Raising RuntimeError is decorated Method is called outside with-statement.
|
||||
|
||||
Note This Decorator shall work only on Object implementing a Context Manger.
|
||||
For this to work object must offer a boolean attribute called __context_entered
|
||||
"""
|
||||
def __call__(self, func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if not args[0]._context_entered:
|
||||
msg = 'Called \'{}()\' outside of \'with\' - statement scope.'
|
||||
msg = msg.format('{}'.format(func.__qualname__))
|
||||
raise RuntimeError(msg)
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
295
Vimba_6_0/VimbaPython/Source/vimba/util/log.py
Normal file
295
Vimba_6_0/VimbaPython/Source/vimba/util/log.py
Normal file
@@ -0,0 +1,295 @@
|
||||
"""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 os
|
||||
import enum
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
__all__ = [
|
||||
'LogLevel',
|
||||
'LogConfig',
|
||||
'Log',
|
||||
'LOG_CONFIG_TRACE_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_TRACE_FILE_ONLY',
|
||||
'LOG_CONFIG_TRACE',
|
||||
'LOG_CONFIG_INFO_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_INFO_FILE_ONLY',
|
||||
'LOG_CONFIG_INFO',
|
||||
'LOG_CONFIG_WARNING_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_WARNING_FILE_ONLY',
|
||||
'LOG_CONFIG_WARNING',
|
||||
'LOG_CONFIG_ERROR_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_ERROR_FILE_ONLY',
|
||||
'LOG_CONFIG_ERROR',
|
||||
'LOG_CONFIG_CRITICAL_CONSOLE_ONLY',
|
||||
'LOG_CONFIG_CRITICAL_FILE_ONLY',
|
||||
'LOG_CONFIG_CRITICAL'
|
||||
]
|
||||
|
||||
|
||||
class LogLevel(enum.IntEnum):
|
||||
"""Enum containing all LogLevels.
|
||||
|
||||
Enum values are:
|
||||
Trace - Show Tracing information. Show all messages.
|
||||
Info - Show Informational, Warning, Error, and Critical Events.
|
||||
Warning - Show Warning, Error, and Critical Events.
|
||||
Error - Show Errors and Critical Events.
|
||||
Critical - Show Critical Events only.
|
||||
"""
|
||||
Trace = logging.DEBUG
|
||||
Info = logging.INFO
|
||||
Warning = logging.WARNING
|
||||
Error = logging.ERROR
|
||||
Critical = logging.CRITICAL
|
||||
|
||||
def __str__(self):
|
||||
return self._name_
|
||||
|
||||
def as_equal_len_str(self) -> str:
|
||||
return _LEVEL_TO_EQUAL_LEN_STR[self]
|
||||
|
||||
|
||||
_LEVEL_TO_EQUAL_LEN_STR = {
|
||||
LogLevel.Trace: 'Trace ',
|
||||
LogLevel.Info: 'Info ',
|
||||
LogLevel.Warning: 'Warning ',
|
||||
LogLevel.Error: 'Error ',
|
||||
LogLevel.Critical: 'Critical'
|
||||
}
|
||||
|
||||
|
||||
class LogConfig:
|
||||
"""The LogConfig is a builder to configure various specialized logging configurations.
|
||||
The constructed LogConfig must set via vimba.Vimba or the ScopedLogEnable Decorator
|
||||
to start logging.
|
||||
"""
|
||||
|
||||
__ENTRY_FORMAT = logging.Formatter('%(asctime)s | %(message)s')
|
||||
|
||||
def __init__(self):
|
||||
self.__handlers: List[logging.Handler] = []
|
||||
self.__max_msg_length: Optional[int] = None
|
||||
|
||||
def add_file_log(self, level: LogLevel) -> 'LogConfig':
|
||||
"""Add a new Log file to the Config Builder.
|
||||
|
||||
Arguments:
|
||||
level: LogLevel of the added log file.
|
||||
|
||||
Returns:
|
||||
Reference to the LogConfig instance (builder pattern).
|
||||
"""
|
||||
log_ts = datetime.datetime.today().strftime('%Y-%m-%d_%H-%M-%S')
|
||||
log_file = 'VimbaPython_{}_{}.log'.format(log_ts, str(level))
|
||||
log_file = os.path.join(os.getcwd(), log_file)
|
||||
|
||||
handler = logging.FileHandler(log_file, delay=True)
|
||||
handler.setLevel(level)
|
||||
handler.setFormatter(LogConfig.__ENTRY_FORMAT)
|
||||
|
||||
self.__handlers.append(handler)
|
||||
return self
|
||||
|
||||
def add_console_log(self, level: LogLevel) -> 'LogConfig':
|
||||
"""Add a new Console Log to the Config Builder.
|
||||
|
||||
Arguments:
|
||||
level: LogLevel of the added console log file.
|
||||
|
||||
Returns:
|
||||
Reference to the LogConfig instance (builder pattern).
|
||||
"""
|
||||
handler = logging.StreamHandler()
|
||||
handler.setLevel(level)
|
||||
handler.setFormatter(LogConfig.__ENTRY_FORMAT)
|
||||
|
||||
self.__handlers.append(handler)
|
||||
return self
|
||||
|
||||
def set_max_msg_length(self, max_msg_length: int):
|
||||
"""Set max length of a log entry. Messages longer than this entry will be cut off."""
|
||||
self.__max_msg_length = max_msg_length
|
||||
|
||||
def get_max_msg_length(self) -> Optional[int]:
|
||||
"""Get configured max message length"""
|
||||
return self.__max_msg_length
|
||||
|
||||
def get_handlers(self) -> List[logging.Handler]:
|
||||
"""Get all configured log handlers"""
|
||||
return self.__handlers
|
||||
|
||||
|
||||
class Log:
|
||||
class __Impl:
|
||||
"""This class is wraps the logging Facility. Since this is as Singleton
|
||||
Use Log.get_instace(), to access the log.
|
||||
"""
|
||||
def __init__(self):
|
||||
"""Do not call directly. Use Log.get_instance() instead."""
|
||||
self.__logger: Optional[logging.Logger] = None
|
||||
self.__config: Optional[LogConfig] = None
|
||||
self._test_buffer: Optional[List[str]] = None
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self.__logger)
|
||||
|
||||
def enable(self, config: LogConfig):
|
||||
"""Enable global VimbaPython logging mechanism.
|
||||
|
||||
Arguments:
|
||||
config: The configuration to apply.
|
||||
"""
|
||||
self.disable()
|
||||
|
||||
logger = logging.getLogger('VimbaPythonLog')
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
for handler in config.get_handlers():
|
||||
logger.addHandler(handler)
|
||||
|
||||
self.__config = config
|
||||
self.__logger = logger
|
||||
|
||||
def disable(self):
|
||||
"""Disable global VimbaPython logging mechanism."""
|
||||
if self.__logger and self.__config:
|
||||
for handler in self.__config.get_handlers():
|
||||
handler.close()
|
||||
self.__logger.removeHandler(handler)
|
||||
|
||||
self.__logger = None
|
||||
self.__config = None
|
||||
|
||||
def get_config(self) -> Optional[LogConfig]:
|
||||
""" Get log configuration
|
||||
|
||||
Returns:
|
||||
Configuration if the log is enabled. In case the log is disabled return None.
|
||||
"""
|
||||
return self.__config
|
||||
|
||||
def trace(self, msg: str):
|
||||
"""Add an entry of LogLevel.Trace to the log. Does nothing is the log is disabled.
|
||||
|
||||
Arguments:
|
||||
msg - The message that should be added to the Log.
|
||||
"""
|
||||
if self.__logger:
|
||||
self.__logger.debug(self.__build_msg(LogLevel.Trace, msg))
|
||||
|
||||
def info(self, msg: str):
|
||||
"""Add an entry of LogLevel.Info to the log. Does nothing is the log is disabled.
|
||||
|
||||
Arguments:
|
||||
msg - The message that should be added to the Log.
|
||||
"""
|
||||
if self.__logger:
|
||||
self.__logger.info(self.__build_msg(LogLevel.Info, msg))
|
||||
|
||||
def warning(self, msg: str):
|
||||
"""Add an entry of LogLevel.Warning to the log. Does nothing is the log is disabled.
|
||||
|
||||
Arguments:
|
||||
msg - The message that should be added to the Log.
|
||||
"""
|
||||
if self.__logger:
|
||||
self.__logger.warning(self.__build_msg(LogLevel.Warning, msg))
|
||||
|
||||
def error(self, msg: str):
|
||||
"""Add an entry of LogLevel.Error to the log. Does nothing is the log is disabled.
|
||||
|
||||
Arguments:
|
||||
msg - The message that should be added to the Log.
|
||||
"""
|
||||
if self.__logger:
|
||||
self.__logger.error(self.__build_msg(LogLevel.Error, msg))
|
||||
|
||||
def critical(self, msg: str):
|
||||
"""Add an entry of LogLevel.Critical to the log. Does nothing is the log is disabled.
|
||||
|
||||
Arguments:
|
||||
msg - The message that should be added to the Log.
|
||||
"""
|
||||
if self.__logger:
|
||||
self.__logger.critical(self.__build_msg(LogLevel.Critical, msg))
|
||||
|
||||
def __build_msg(self, loglevel: LogLevel, msg: str) -> str:
|
||||
msg = '{} | {}'.format(loglevel.as_equal_len_str(), msg)
|
||||
max_len = self.__config.get_max_msg_length() if self.__config else None
|
||||
|
||||
if max_len and (max_len < len(msg)):
|
||||
suffix = ' ...'
|
||||
msg = msg[:max_len - len(suffix)] + suffix
|
||||
|
||||
if self._test_buffer is not None:
|
||||
self._test_buffer.append(msg)
|
||||
|
||||
return msg
|
||||
|
||||
__instance = __Impl()
|
||||
|
||||
@staticmethod
|
||||
def get_instance() -> '__Impl':
|
||||
"""Get Log instance."""
|
||||
return Log.__instance
|
||||
|
||||
|
||||
def _build_cfg(console_level: Optional[LogLevel], file_level: Optional[LogLevel]) -> LogConfig:
|
||||
cfg = LogConfig()
|
||||
|
||||
cfg.set_max_msg_length(200)
|
||||
|
||||
if console_level:
|
||||
cfg.add_console_log(console_level)
|
||||
|
||||
if file_level:
|
||||
cfg.add_file_log(file_level)
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
# Exported Default Log configurations.
|
||||
LOG_CONFIG_TRACE_CONSOLE_ONLY = _build_cfg(LogLevel.Trace, None)
|
||||
LOG_CONFIG_TRACE_FILE_ONLY = _build_cfg(None, LogLevel.Trace)
|
||||
LOG_CONFIG_TRACE = _build_cfg(LogLevel.Trace, LogLevel.Trace)
|
||||
LOG_CONFIG_INFO_CONSOLE_ONLY = _build_cfg(LogLevel.Info, None)
|
||||
LOG_CONFIG_INFO_FILE_ONLY = _build_cfg(None, LogLevel.Info)
|
||||
LOG_CONFIG_INFO = _build_cfg(LogLevel.Info, LogLevel.Info)
|
||||
LOG_CONFIG_WARNING_CONSOLE_ONLY = _build_cfg(LogLevel.Warning, None)
|
||||
LOG_CONFIG_WARNING_FILE_ONLY = _build_cfg(None, LogLevel.Warning)
|
||||
LOG_CONFIG_WARNING = _build_cfg(LogLevel.Warning, LogLevel.Warning)
|
||||
LOG_CONFIG_ERROR_CONSOLE_ONLY = _build_cfg(LogLevel.Error, None)
|
||||
LOG_CONFIG_ERROR_FILE_ONLY = _build_cfg(None, LogLevel.Error)
|
||||
LOG_CONFIG_ERROR = _build_cfg(LogLevel.Error, LogLevel.Error)
|
||||
LOG_CONFIG_CRITICAL_CONSOLE_ONLY = _build_cfg(LogLevel.Critical, None)
|
||||
LOG_CONFIG_CRITICAL_FILE_ONLY = _build_cfg(None, LogLevel.Critical)
|
||||
LOG_CONFIG_CRITICAL = _build_cfg(LogLevel.Critical, LogLevel.Critical)
|
||||
223
Vimba_6_0/VimbaPython/Source/vimba/util/runtime_type_check.py
Normal file
223
Vimba_6_0/VimbaPython/Source/vimba/util/runtime_type_check.py
Normal file
@@ -0,0 +1,223 @@
|
||||
"""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 collections.abc
|
||||
|
||||
from inspect import isfunction, ismethod, signature
|
||||
from functools import wraps
|
||||
from typing import get_type_hints, Union
|
||||
from .log import Log
|
||||
|
||||
|
||||
__all__ = [
|
||||
'RuntimeTypeCheckEnable'
|
||||
]
|
||||
|
||||
|
||||
class RuntimeTypeCheckEnable:
|
||||
"""Decorator adding runtime type checking to the wrapped callable.
|
||||
|
||||
Each time the callable is executed, all arguments are checked if they match with the given
|
||||
type hints. If all checks are passed, the wrapped function is executed, if the given
|
||||
arguments to not match a TypeError is raised.
|
||||
Note: This decorator is no replacement for a feature complete TypeChecker. It supports only
|
||||
a subset of all types expressible by type hints.
|
||||
"""
|
||||
_log = Log.get_instance()
|
||||
|
||||
def __call__(self, func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
full_args, hints = self.__dismantle_sig(func, *args, **kwargs)
|
||||
|
||||
for arg_name in hints:
|
||||
self.__verify_arg(func, hints[arg_name], (arg_name, full_args[arg_name]))
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
def __dismantle_sig(self, func, *args, **kwargs):
|
||||
# Get merge args, kwargs and defaults to complete argument list.
|
||||
full_args = signature(func).bind(*args, **kwargs)
|
||||
full_args.apply_defaults()
|
||||
|
||||
# Get available type hints, remove return value.
|
||||
hints = get_type_hints(func)
|
||||
hints.pop('return', None)
|
||||
|
||||
return (full_args.arguments, hints)
|
||||
|
||||
def __verify_arg(self, func, type_hint, arg_spec):
|
||||
arg_name, arg = arg_spec
|
||||
|
||||
if (self.__matches(type_hint, arg)):
|
||||
return
|
||||
|
||||
msg = '\'{}\' called with unexpected argument type. Argument\'{}\'. Expected type: {}.'
|
||||
msg = msg.format(func.__qualname__, arg_name, type_hint)
|
||||
|
||||
RuntimeTypeCheckEnable._log.error(msg)
|
||||
raise TypeError(msg)
|
||||
|
||||
def __matches(self, type_hint, arg) -> bool:
|
||||
if self.__matches_base_types(type_hint, arg):
|
||||
return True
|
||||
|
||||
elif self.__matches_type_types(type_hint, arg):
|
||||
return True
|
||||
|
||||
elif self.__matches_union_types(type_hint, arg):
|
||||
return True
|
||||
|
||||
elif self.__matches_tuple_types(type_hint, arg):
|
||||
return True
|
||||
|
||||
elif self.__matches_dict_types(type_hint, arg):
|
||||
return True
|
||||
|
||||
else:
|
||||
return self.__matches_callable(type_hint, arg)
|
||||
|
||||
def __matches_base_types(self, type_hint, arg) -> bool:
|
||||
return type_hint == type(arg)
|
||||
|
||||
def __matches_type_types(self, type_hint, arg) -> bool:
|
||||
try:
|
||||
if not type_hint.__origin__ == type:
|
||||
return False
|
||||
|
||||
hint_args = type_hint.__args__
|
||||
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
return arg in hint_args
|
||||
|
||||
def __matches_union_types(self, type_hint, arg) -> bool:
|
||||
try:
|
||||
if not type_hint.__origin__ == Union:
|
||||
return False
|
||||
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
# If Matches if true for an Union hint:
|
||||
for hint in type_hint.__args__:
|
||||
if self.__matches(hint, arg):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def __matches_tuple_types(self, type_hint, arg) -> bool:
|
||||
try:
|
||||
if not (type_hint.__origin__ == tuple and type(arg) == tuple):
|
||||
return False
|
||||
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
if arg == ():
|
||||
return True
|
||||
|
||||
if Ellipsis in type_hint.__args__:
|
||||
fn = self.__matches_var_length_tuple
|
||||
|
||||
else:
|
||||
fn = self.__matches_fixed_size_tuple
|
||||
|
||||
return fn(type_hint, arg)
|
||||
|
||||
def __matches_fixed_size_tuple(self, type_hint, arg) -> bool:
|
||||
# To pass, the entire tuple must match in length and all types
|
||||
expand_hint = type_hint.__args__
|
||||
|
||||
if len(expand_hint) != len(arg):
|
||||
return False
|
||||
|
||||
for hint, value in zip(expand_hint, arg):
|
||||
if not self.__matches(hint, value):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def __matches_var_length_tuple(self, type_hint, arg) -> bool:
|
||||
# To pass a tuple can be empty or all contents must match the given type.
|
||||
hint, _ = type_hint.__args__
|
||||
|
||||
for value in arg:
|
||||
if not self.__matches(hint, value):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def __matches_dict_types(self, type_hint, arg) -> bool:
|
||||
# To pass the hint must be a Dictionary and arg must match the given types.
|
||||
try:
|
||||
if not (type_hint.__origin__ == dict and type(arg) == dict):
|
||||
return False
|
||||
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
key_type, val_type = type_hint.__args__
|
||||
|
||||
for k, v in arg.items():
|
||||
if type(k) != key_type or type(v) != val_type:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def __matches_callable(self, type_hint, arg) -> bool:
|
||||
# Return if the given hint is no callable
|
||||
try:
|
||||
if not type_hint.__origin__ == collections.abc.Callable:
|
||||
return False
|
||||
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
# Verify that are is some form of callable.:
|
||||
# 1) Check if it is either a function or a method
|
||||
# 2) If it is an object, check if it has a __call__ method. If so use call for checks.
|
||||
if not (isfunction(arg) or ismethod(arg)):
|
||||
|
||||
try:
|
||||
arg = getattr(arg, '__call__')
|
||||
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
# Examine signature of given callable
|
||||
sig_args = signature(arg).parameters
|
||||
hint_args = type_hint.__args__
|
||||
|
||||
# Verify Parameter list length
|
||||
if len(sig_args) != len(hint_args[:-1]):
|
||||
return False
|
||||
|
||||
return True
|
||||
80
Vimba_6_0/VimbaPython/Source/vimba/util/scoped_log.py
Normal file
80
Vimba_6_0/VimbaPython/Source/vimba/util/scoped_log.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""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.
|
||||
"""
|
||||
|
||||
from functools import wraps
|
||||
from typing import Any, Callable, Tuple, Optional
|
||||
from .log import LogConfig, Log
|
||||
|
||||
|
||||
__all__ = [
|
||||
'ScopedLogEnable'
|
||||
]
|
||||
|
||||
|
||||
class _ScopedLog:
|
||||
__log = Log.get_instance()
|
||||
|
||||
def __init__(self, config: LogConfig):
|
||||
self.__config: LogConfig = config
|
||||
self.__old_config: Optional[LogConfig] = None
|
||||
|
||||
def __enter__(self):
|
||||
self.__old_config = _ScopedLog.__log.get_config()
|
||||
_ScopedLog.__log.enable(self.__config)
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, exc_traceback):
|
||||
if self.__old_config:
|
||||
_ScopedLog.__log.enable(self.__old_config)
|
||||
|
||||
else:
|
||||
_ScopedLog.__log.disable()
|
||||
|
||||
|
||||
class ScopedLogEnable:
|
||||
"""Decorator: Enables logging facility before execution of the wrapped function
|
||||
and disables logging after exiting the wrapped function. This allows more specific
|
||||
logging of a code section compared to enabling or disabling the global logging mechanism.
|
||||
|
||||
Arguments:
|
||||
config: The configuration the log should be enabled with.
|
||||
"""
|
||||
def __init__(self, config: LogConfig):
|
||||
"""Add scoped logging to a Callable.
|
||||
|
||||
Arguments:
|
||||
config: The configuration the log should be enabled with.
|
||||
"""
|
||||
self.__config = config
|
||||
|
||||
def __call__(self, func: Callable[..., Any]):
|
||||
@wraps(func)
|
||||
def wrapper(*args: Tuple[Any, ...]):
|
||||
with _ScopedLog(self.__config):
|
||||
return func(*args)
|
||||
|
||||
return wrapper
|
||||
136
Vimba_6_0/VimbaPython/Source/vimba/util/tracer.py
Normal file
136
Vimba_6_0/VimbaPython/Source/vimba/util/tracer.py
Normal file
@@ -0,0 +1,136 @@
|
||||
"""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.
|
||||
"""
|
||||
|
||||
from functools import reduce, wraps
|
||||
from inspect import signature
|
||||
from .log import Log
|
||||
|
||||
|
||||
__all__ = [
|
||||
'TraceEnable'
|
||||
]
|
||||
|
||||
|
||||
_FMT_MSG_ENTRY: str = 'Enter | {}'
|
||||
_FMT_MSG_LEAVE: str = 'Leave | {}'
|
||||
_FMT_MSG_RAISE: str = 'Raise | {}, {}'
|
||||
_FMT_ERROR: str = 'ErrorType: {}, ErrorValue: {}'
|
||||
_INDENT_PER_LEVEL: str = ' '
|
||||
|
||||
|
||||
def _args_to_str(func, *args, **kwargs) -> str:
|
||||
# Expand function signature
|
||||
sig = signature(func).bind(*args, **kwargs)
|
||||
sig.apply_defaults()
|
||||
full_args = sig.arguments
|
||||
|
||||
# Early return if there is nothing to print
|
||||
if not full_args:
|
||||
return '(None)'
|
||||
|
||||
def fold(args_as_str: str, arg):
|
||||
name, value = arg
|
||||
|
||||
if name == 'self':
|
||||
arg_str = 'self'
|
||||
|
||||
else:
|
||||
arg_str = str(value)
|
||||
|
||||
return '{}{}, '.format(args_as_str, arg_str)
|
||||
|
||||
return '({})'.format(reduce(fold, full_args.items(), '')[:-2])
|
||||
|
||||
|
||||
def _get_indent(level: int) -> str:
|
||||
return _INDENT_PER_LEVEL * level
|
||||
|
||||
|
||||
def _create_enter_msg(name: str, level: int, args_str: str) -> str:
|
||||
msg = '{}{}{}'.format(_get_indent(level), name, args_str)
|
||||
return _FMT_MSG_ENTRY.format(msg)
|
||||
|
||||
|
||||
def _create_leave_msg(name: str, level: int, ) -> str:
|
||||
msg = '{}{}'.format(_get_indent(level), name)
|
||||
return _FMT_MSG_LEAVE.format(msg)
|
||||
|
||||
|
||||
def _create_raise_msg(name: str, level: int, exc_type: Exception, exc_value: str) -> str:
|
||||
msg = '{}{}'.format(_get_indent(level), name)
|
||||
exc = _FMT_ERROR.format(exc_type, exc_value)
|
||||
return _FMT_MSG_RAISE.format(msg, exc)
|
||||
|
||||
|
||||
class _Tracer:
|
||||
__log = Log.get_instance()
|
||||
__level: int = 0
|
||||
|
||||
@staticmethod
|
||||
def is_log_enabled() -> bool:
|
||||
return bool(_Tracer.__log)
|
||||
|
||||
def __init__(self, func, *args, **kwargs):
|
||||
self.__full_name: str = '{}.{}'.format(func.__module__, func.__qualname__)
|
||||
self.__full_args: str = _args_to_str(func, *args, **kwargs)
|
||||
|
||||
def __enter__(self):
|
||||
msg = _create_enter_msg(self.__full_name, _Tracer.__level, self.__full_args)
|
||||
|
||||
_Tracer.__log.trace(msg)
|
||||
_Tracer.__level += 1
|
||||
|
||||
def __exit__(self, exc_type, exc_value, exc_traceback):
|
||||
_Tracer.__level -= 1
|
||||
|
||||
if exc_type:
|
||||
msg = _create_raise_msg(self.__full_name, _Tracer.__level, exc_type, exc_value)
|
||||
|
||||
else:
|
||||
msg = _create_leave_msg(self.__full_name, _Tracer.__level)
|
||||
|
||||
_Tracer.__log.trace(msg)
|
||||
|
||||
|
||||
class TraceEnable:
|
||||
"""Decorator: Adds an entry of LogLevel. Trace on entry and exit of the wrapped function.
|
||||
On exit, the log entry contains information if the function was left normally or with an
|
||||
exception.
|
||||
"""
|
||||
def __call__(self, func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if _Tracer.is_log_enabled():
|
||||
with _Tracer(func, *args, **kwargs):
|
||||
result = func(*args, **kwargs)
|
||||
|
||||
return result
|
||||
|
||||
else:
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
600
Vimba_6_0/VimbaPython/Source/vimba/vimba.py
Normal file
600
Vimba_6_0/VimbaPython/Source/vimba/vimba.py
Normal file
@@ -0,0 +1,600 @@
|
||||
"""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 threading
|
||||
from typing import List, Dict, Tuple
|
||||
from .c_binding import call_vimba_c, VIMBA_C_VERSION, VIMBA_IMAGE_TRANSFORM_VERSION, \
|
||||
G_VIMBA_C_HANDLE
|
||||
from .feature import discover_features, FeatureTypes, FeaturesTuple, FeatureTypeTypes, EnumFeature
|
||||
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 .interface import Interface, InterfaceChangeHandler, InterfaceEvent, InterfacesTuple, \
|
||||
InterfacesList, discover_interfaces, discover_interface
|
||||
from .camera import Camera, CamerasList, CameraChangeHandler, CameraEvent, CamerasTuple, \
|
||||
discover_cameras, discover_camera
|
||||
from .util import Log, LogConfig, TraceEnable, RuntimeTypeCheckEnable, EnterContextOnCall, \
|
||||
LeaveContextOnCall, RaiseIfInsideContext, RaiseIfOutsideContext
|
||||
from .error import VimbaCameraError, VimbaInterfaceError, VimbaFeatureError
|
||||
from . import __version__ as VIMBA_PYTHON_VERSION
|
||||
|
||||
|
||||
__all__ = [
|
||||
'Vimba',
|
||||
]
|
||||
|
||||
|
||||
class Vimba:
|
||||
class __Impl:
|
||||
"""This class allows access to the entire Vimba System.
|
||||
Vimba is meant be used in conjunction with the "with" - Statement, upon
|
||||
entering the context, all system features, connected cameras and interfaces are detected
|
||||
and can be used.
|
||||
"""
|
||||
|
||||
@TraceEnable()
|
||||
@LeaveContextOnCall()
|
||||
def __init__(self):
|
||||
"""Do not call directly. Use Vimba.get_instance() instead."""
|
||||
self.__feats: FeaturesTuple = ()
|
||||
|
||||
self.__inters: InterfacesList = ()
|
||||
self.__inters_lock: threading.Lock = threading.Lock()
|
||||
self.__inters_handlers: List[InterfaceChangeHandler] = []
|
||||
self.__inters_handlers_lock: threading.Lock = threading.Lock()
|
||||
|
||||
self.__cams: CamerasList = ()
|
||||
self.__cams_lock: threading.Lock = threading.Lock()
|
||||
self.__cams_handlers: List[CameraChangeHandler] = []
|
||||
self.__cams_handlers_lock: threading.Lock = threading.Lock()
|
||||
|
||||
self.__nw_discover: bool = True
|
||||
self.__context_cnt: int = 0
|
||||
|
||||
@TraceEnable()
|
||||
def __enter__(self):
|
||||
if not self.__context_cnt:
|
||||
self._startup()
|
||||
|
||||
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._shutdown()
|
||||
|
||||
def get_version(self) -> str:
|
||||
""" Returns version string of VimbaPython and underlaying dependencies."""
|
||||
msg = 'VimbaPython: {} (using VimbaC: {}, VimbaImageTransform: {})'
|
||||
return msg.format(VIMBA_PYTHON_VERSION, VIMBA_C_VERSION, VIMBA_IMAGE_TRANSFORM_VERSION)
|
||||
|
||||
@RaiseIfInsideContext()
|
||||
@RuntimeTypeCheckEnable()
|
||||
def set_network_discovery(self, enable: bool):
|
||||
"""Enable/Disable network camera discovery.
|
||||
|
||||
Arguments:
|
||||
enable - If 'True' VimbaPython tries to detect cameras connected via Ethernet
|
||||
on entering the 'with' statement. If set to 'False', no network
|
||||
discover occurs.
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
RuntimeError if called inside with-statement.
|
||||
"""
|
||||
self.__nw_discover = enable
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def enable_log(self, config: LogConfig):
|
||||
"""Enable VimbaPython's logging mechanism.
|
||||
|
||||
Arguments:
|
||||
config - Configuration for the logging mechanism.
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
"""
|
||||
Log.get_instance().enable(config)
|
||||
|
||||
def disable_log(self):
|
||||
"""Disable VimbaPython's logging mechanism."""
|
||||
Log.get_instance().disable()
|
||||
|
||||
@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 then called outside of "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(G_VIMBA_C_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' too.
|
||||
data: Byte sequence to write at address 'addr'.
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
RuntimeError then called outside of "with" - statement.
|
||||
ValueError if addr is negative.
|
||||
"""
|
||||
# Note: Coverage is skipped. Function is untestable in a generic way.
|
||||
return write_memory(G_VIMBA_C_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.
|
||||
|
||||
Return:
|
||||
Dictionary containing a mapping from given address to the read register values.
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
RuntimeError then called outside of "with" - statement.
|
||||
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 read_registers(G_VIMBA_C_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.
|
||||
RuntimeError then called outside of "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 write_registers(G_VIMBA_C_HANDLE, addrs_values)
|
||||
|
||||
@RaiseIfOutsideContext()
|
||||
def get_all_interfaces(self) -> InterfacesTuple:
|
||||
"""Get access to all discovered Interfaces:
|
||||
|
||||
Returns:
|
||||
A set of all currently detected Interfaces.
|
||||
|
||||
Raises:
|
||||
RuntimeError then called outside of "with" - statement.
|
||||
"""
|
||||
with self.__inters_lock:
|
||||
return tuple(self.__inters)
|
||||
|
||||
@RaiseIfOutsideContext()
|
||||
@RuntimeTypeCheckEnable()
|
||||
def get_interface_by_id(self, id_: str) -> Interface:
|
||||
"""Lookup Interface with given ID.
|
||||
|
||||
Arguments:
|
||||
id_ - Interface Id to search for.
|
||||
|
||||
Returns:
|
||||
Interface associated with given Id.
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
RuntimeError then called outside of "with" - statement.
|
||||
VimbaInterfaceError if interface with id_ can't be found.
|
||||
"""
|
||||
with self.__inters_lock:
|
||||
inter = [inter for inter in self.__inters if id_ == inter.get_id()]
|
||||
|
||||
if not inter:
|
||||
raise VimbaInterfaceError('Interface with ID \'{}\' not found.'.format(id_))
|
||||
|
||||
return inter.pop()
|
||||
|
||||
@RaiseIfOutsideContext()
|
||||
def get_all_cameras(self) -> CamerasTuple:
|
||||
"""Get access to all discovered Cameras.
|
||||
|
||||
Returns:
|
||||
A set of all currently detected Cameras.
|
||||
|
||||
Raises:
|
||||
RuntimeError then called outside of "with" - statement.
|
||||
"""
|
||||
with self.__cams_lock:
|
||||
return tuple(self.__cams)
|
||||
|
||||
@RaiseIfOutsideContext()
|
||||
@RuntimeTypeCheckEnable()
|
||||
def get_camera_by_id(self, id_: str) -> Camera:
|
||||
"""Lookup Camera with given ID.
|
||||
|
||||
Arguments:
|
||||
id_ - Camera Id to search for. For GigE - Cameras, the IP and MAC-Address
|
||||
can be used to Camera lookup
|
||||
|
||||
Returns:
|
||||
Camera associated with given Id.
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
RuntimeError then called outside of "with" - statement.
|
||||
VimbaCameraError if camera with id_ can't be found.
|
||||
"""
|
||||
with self.__cams_lock:
|
||||
# Search for given Camera Id in all currently detected cameras.
|
||||
for cam in self.__cams:
|
||||
if id_ == cam.get_id():
|
||||
return cam
|
||||
|
||||
# If a search by ID fails, the given id_ is almost certain an IP or MAC - Address.
|
||||
# Try to query this Camera.
|
||||
try:
|
||||
cam_info = discover_camera(id_)
|
||||
|
||||
# Since cam_info is newly constructed, search in existing cameras for a Camera
|
||||
for cam in self.__cams:
|
||||
if cam_info.get_id() == cam.get_id():
|
||||
return cam
|
||||
|
||||
except VimbaCameraError:
|
||||
pass
|
||||
|
||||
raise VimbaCameraError('No Camera with Id \'{}\' available.'.format(id_))
|
||||
|
||||
@RaiseIfOutsideContext()
|
||||
def get_all_features(self) -> FeaturesTuple:
|
||||
"""Get access to all discovered system features:
|
||||
|
||||
Returns:
|
||||
A set of all currently detected Features.
|
||||
|
||||
Raises:
|
||||
RuntimeError then called outside of "with" - statement.
|
||||
"""
|
||||
return self.__feats
|
||||
|
||||
@TraceEnable()
|
||||
@RaiseIfOutsideContext()
|
||||
@RuntimeTypeCheckEnable()
|
||||
def get_features_affected_by(self, feat: FeatureTypes) -> FeaturesTuple:
|
||||
"""Get all system features affected by a specific system feature.
|
||||
|
||||
Arguments:
|
||||
feat - Feature used 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 then called outside of "with" - statement.
|
||||
VimbaFeatureError if 'feat' is not a system feature.
|
||||
"""
|
||||
return filter_affected_features(self.__feats, feat)
|
||||
|
||||
@TraceEnable()
|
||||
@RaiseIfOutsideContext()
|
||||
@RuntimeTypeCheckEnable()
|
||||
def get_features_selected_by(self, feat: FeatureTypes) -> FeaturesTuple:
|
||||
"""Get all system features selected by a specific system feature.
|
||||
|
||||
Arguments:
|
||||
feat - Feature used find features that are selected by feat.
|
||||
|
||||
Returns:
|
||||
A set of features selected by 'feat'.
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
RuntimeError then called outside of "with" - statement.
|
||||
VimbaFeatureError if 'feat' is not a system feature.
|
||||
"""
|
||||
return filter_selected_features(self.__feats, feat)
|
||||
|
||||
@RaiseIfOutsideContext()
|
||||
@RuntimeTypeCheckEnable()
|
||||
def get_features_by_type(self, feat_type: FeatureTypeTypes) -> FeaturesTuple:
|
||||
"""Get all system 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 then called outside of "with" - statement.
|
||||
"""
|
||||
return filter_features_by_type(self.__feats, feat_type)
|
||||
|
||||
@RaiseIfOutsideContext()
|
||||
@RuntimeTypeCheckEnable()
|
||||
def get_features_by_category(self, category: str) -> FeaturesTuple:
|
||||
"""Get all system features of a specific category.
|
||||
|
||||
Arguments:
|
||||
category - Category that should be used for filtering.
|
||||
|
||||
Returns:
|
||||
A set of features of category 'category'.
|
||||
|
||||
Returns:
|
||||
TypeError if parameters do not match their type hint.
|
||||
RuntimeError then called outside of "with" - statement.
|
||||
"""
|
||||
return filter_features_by_category(self.__feats, category)
|
||||
|
||||
@RaiseIfOutsideContext()
|
||||
@RuntimeTypeCheckEnable()
|
||||
def get_feature_by_name(self, feat_name: str) -> FeatureTypes:
|
||||
"""Get a system feature by its name.
|
||||
|
||||
Arguments:
|
||||
feat_name - Name used to find a feature.
|
||||
|
||||
Returns:
|
||||
Feature with the associated name.
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
RuntimeError then called outside of "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
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def register_camera_change_handler(self, handler: CameraChangeHandler):
|
||||
"""Add Callable what is executed on camera connect/disconnect
|
||||
|
||||
Arguments:
|
||||
handler - The change handler that shall be added.
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
"""
|
||||
with self.__cams_handlers_lock:
|
||||
if handler not in self.__cams_handlers:
|
||||
self.__cams_handlers.append(handler)
|
||||
|
||||
def unregister_all_camera_change_handlers(self):
|
||||
"""Remove all currently registered camera change handlers"""
|
||||
with self.__cams_handlers_lock:
|
||||
if self.__cams_handlers:
|
||||
self.__cams_handlers.clear()
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def unregister_camera_change_handler(self, handler: CameraChangeHandler):
|
||||
"""Remove previously registered camera change handler
|
||||
|
||||
Arguments:
|
||||
handler - The change handler that shall be removed.
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
"""
|
||||
with self.__cams_handlers_lock:
|
||||
if handler in self.__cams_handlers:
|
||||
self.__cams_handlers.remove(handler)
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def register_interface_change_handler(self, handler: InterfaceChangeHandler):
|
||||
"""Add Callable what is executed on interface connect/disconnect
|
||||
|
||||
Arguments:
|
||||
handler - The change handler that shall be added.
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
"""
|
||||
with self.__inters_handlers_lock:
|
||||
if handler not in self.__inters_handlers:
|
||||
self.__inters_handlers.append(handler)
|
||||
|
||||
def unregister_all_interface_change_handlers(self):
|
||||
"""Remove all currently registered interface change handlers"""
|
||||
with self.__inters_handlers_lock:
|
||||
if self.__inters_handlers:
|
||||
self.__inters_handlers.clear()
|
||||
|
||||
@RuntimeTypeCheckEnable()
|
||||
def unregister_interface_change_handler(self, handler: InterfaceChangeHandler):
|
||||
"""Remove previously registered interface change handler
|
||||
|
||||
Arguments:
|
||||
handler - The change handler that shall be removed.
|
||||
|
||||
Raises:
|
||||
TypeError if parameters do not match their type hint.
|
||||
"""
|
||||
with self.__inters_handlers_lock:
|
||||
if handler in self.__inters_handlers:
|
||||
self.__inters_handlers.remove(handler)
|
||||
|
||||
@TraceEnable()
|
||||
@EnterContextOnCall()
|
||||
def _startup(self):
|
||||
Log.get_instance().info('Starting {}'.format(self.get_version()))
|
||||
|
||||
call_vimba_c('VmbStartup')
|
||||
|
||||
self.__inters = discover_interfaces()
|
||||
self.__cams = discover_cameras(self.__nw_discover)
|
||||
self.__feats = discover_features(G_VIMBA_C_HANDLE)
|
||||
attach_feature_accessors(self, self.__feats)
|
||||
|
||||
feat = self.get_feature_by_name('DiscoveryInterfaceEvent')
|
||||
feat.register_change_handler(self.__inter_cb_wrapper)
|
||||
|
||||
feat = self.get_feature_by_name('DiscoveryCameraEvent')
|
||||
feat.register_change_handler(self.__cam_cb_wrapper)
|
||||
|
||||
@TraceEnable()
|
||||
@LeaveContextOnCall()
|
||||
def _shutdown(self):
|
||||
self.unregister_all_camera_change_handlers()
|
||||
self.unregister_all_interface_change_handlers()
|
||||
|
||||
for feat in self.__feats:
|
||||
feat.unregister_all_change_handlers()
|
||||
|
||||
remove_feature_accessors(self, self.__feats)
|
||||
self.__feats = ()
|
||||
self.__cams_handlers = []
|
||||
self.__cams = ()
|
||||
self.__inters_handlers = []
|
||||
self.__inters = ()
|
||||
|
||||
call_vimba_c('VmbShutdown')
|
||||
|
||||
def __cam_cb_wrapper(self, cam_event: EnumFeature): # coverage: skip
|
||||
# Skip coverage because it can't be measured. This is called from C-Context
|
||||
event = CameraEvent(int(cam_event.get()))
|
||||
cam = None
|
||||
cam_id = self.get_feature_by_name('DiscoveryCameraIdent').get()
|
||||
log = Log.get_instance()
|
||||
|
||||
# New camera found: Add it to camera list
|
||||
if event == CameraEvent.Detected:
|
||||
cam = discover_camera(cam_id)
|
||||
|
||||
with self.__cams_lock:
|
||||
self.__cams.append(cam)
|
||||
|
||||
log.info('Added camera \"{}\" to active cameras'.format(cam_id))
|
||||
|
||||
# Existing camera lost. Remove it from active cameras
|
||||
elif event == CameraEvent.Missing:
|
||||
with self.__cams_lock:
|
||||
cam = [c for c in self.__cams if cam_id == c.get_id()].pop()
|
||||
cam._disconnected = True
|
||||
self.__cams.remove(cam)
|
||||
|
||||
log.info('Removed camera \"{}\" from active cameras'.format(cam_id))
|
||||
|
||||
else:
|
||||
cam = self.get_camera_by_id(cam_id)
|
||||
|
||||
with self.__cams_handlers_lock:
|
||||
for handler in self.__cams_handlers:
|
||||
try:
|
||||
handler(cam, event)
|
||||
|
||||
except Exception as e:
|
||||
msg = 'Caught Exception in handler: '
|
||||
msg += 'Type: {}, '.format(type(e))
|
||||
msg += 'Value: {}, '.format(e)
|
||||
msg += 'raised by: {}'.format(handler)
|
||||
Log.get_instance().error(msg)
|
||||
raise e
|
||||
|
||||
def __inter_cb_wrapper(self, inter_event: EnumFeature): # coverage: skip
|
||||
# Skip coverage because it can't be measured. This is called from C-Context
|
||||
event = InterfaceEvent(int(inter_event.get()))
|
||||
inter = None
|
||||
inter_id = self.get_feature_by_name('DiscoveryInterfaceIdent').get()
|
||||
log = Log.get_instance()
|
||||
|
||||
# New interface found: Add it to interface list
|
||||
if event == InterfaceEvent.Detected:
|
||||
inter = discover_interface(inter_id)
|
||||
|
||||
with self.__inters_lock:
|
||||
self.__inters.append(inter)
|
||||
|
||||
log.info('Added interface \"{}\" to active interfaces'.format(inter_id))
|
||||
|
||||
# Existing interface lost. Remove it from active interfaces
|
||||
elif event == InterfaceEvent.Missing:
|
||||
with self.__inters_lock:
|
||||
inter = [i for i in self.__inters if inter_id == i.get_id()].pop()
|
||||
self.__inters.remove(inter)
|
||||
|
||||
log.info('Removed interface \"{}\" from active interfaces'.format(inter_id))
|
||||
|
||||
else:
|
||||
inter = self.get_interface_by_id(inter_id)
|
||||
|
||||
with self.__inters_handlers_lock:
|
||||
for handler in self.__inters_handlers:
|
||||
try:
|
||||
handler(inter, event)
|
||||
|
||||
except Exception as e:
|
||||
msg = 'Caught Exception in handler: '
|
||||
msg += 'Type: {}, '.format(type(e))
|
||||
msg += 'Value: {}, '.format(e)
|
||||
msg += 'raised by: {}'.format(handler)
|
||||
Log.get_instance().error(msg)
|
||||
raise e
|
||||
|
||||
__instance = __Impl()
|
||||
|
||||
@staticmethod
|
||||
@TraceEnable()
|
||||
def get_instance() -> '__Impl':
|
||||
"""Get VimbaSystem Singleton."""
|
||||
return Vimba.__instance
|
||||
Reference in New Issue
Block a user