viva-genicam 0.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. viva_genicam-0.2.4/PKG-INFO +64 -0
  2. viva_genicam-0.2.4/README.md +33 -0
  3. viva_genicam-0.2.4/pyproject.toml +49 -0
  4. viva_genicam-0.2.4/python/viva_genicam/__init__.py +59 -0
  5. viva_genicam-0.2.4/python/viva_genicam/_native.pyi +102 -0
  6. viva_genicam-0.2.4/python/viva_genicam/camera.py +126 -0
  7. viva_genicam-0.2.4/python/viva_genicam/discovery.py +110 -0
  8. viva_genicam-0.2.4/python/viva_genicam/errors.py +26 -0
  9. viva_genicam-0.2.4/python/viva_genicam/frame.py +73 -0
  10. viva_genicam-0.2.4/python/viva_genicam/node.py +72 -0
  11. viva_genicam-0.2.4/python/viva_genicam/py.typed +0 -0
  12. viva_genicam-0.2.4/python/viva_genicam/stream.py +56 -0
  13. viva_genicam-0.2.4/python/viva_genicam/testing.py +88 -0
  14. viva_genicam-0.2.4/viva-fake-gige/Cargo.toml +22 -0
  15. viva_genicam-0.2.4/viva-fake-gige/README.md +36 -0
  16. viva_genicam-0.2.4/viva-fake-gige/src/gvcp_server.rs +361 -0
  17. viva_genicam-0.2.4/viva-fake-gige/src/gvsp_sender.rs +298 -0
  18. viva_genicam-0.2.4/viva-fake-gige/src/lib.rs +210 -0
  19. viva_genicam-0.2.4/viva-fake-gige/src/main.rs +93 -0
  20. viva_genicam-0.2.4/viva-fake-gige/src/registers.rs +815 -0
  21. viva_genicam-0.2.4/viva-genapi/Cargo.toml +22 -0
  22. viva_genicam-0.2.4/viva-genapi/README.md +48 -0
  23. viva_genicam-0.2.4/viva-genapi/src/bitops.rs +194 -0
  24. viva_genicam-0.2.4/viva-genapi/src/conversions.rs +314 -0
  25. viva_genicam-0.2.4/viva-genapi/src/error.rs +65 -0
  26. viva_genicam-0.2.4/viva-genapi/src/io.rs +28 -0
  27. viva_genicam-0.2.4/viva-genapi/src/lib.rs +1019 -0
  28. viva_genicam-0.2.4/viva-genapi/src/nodemap.rs +1749 -0
  29. viva_genicam-0.2.4/viva-genapi/src/nodes.rs +426 -0
  30. viva_genicam-0.2.4/viva-genapi/src/swissknife.rs +1169 -0
  31. viva_genicam-0.2.4/viva-genapi-xml/Cargo.toml +29 -0
  32. viva_genicam-0.2.4/viva-genapi-xml/README.md +44 -0
  33. viva_genicam-0.2.4/viva-genapi-xml/src/builders.rs +406 -0
  34. viva_genicam-0.2.4/viva-genapi-xml/src/fetch.rs +176 -0
  35. viva_genicam-0.2.4/viva-genapi-xml/src/lib.rs +1280 -0
  36. viva_genicam-0.2.4/viva-genapi-xml/src/parsers/control.rs +179 -0
  37. viva_genicam-0.2.4/viva-genapi-xml/src/parsers/converter.rs +359 -0
  38. viva_genicam-0.2.4/viva-genapi-xml/src/parsers/mod.rs +356 -0
  39. viva_genicam-0.2.4/viva-genapi-xml/src/parsers/numeric.rs +489 -0
  40. viva_genicam-0.2.4/viva-genapi-xml/src/parsers/struct_reg.rs +163 -0
  41. viva_genicam-0.2.4/viva-genapi-xml/src/parsers/swissknife.rs +126 -0
  42. viva_genicam-0.2.4/viva-genapi-xml/src/parsers/symbolic.rs +509 -0
  43. viva_genicam-0.2.4/viva-genapi-xml/src/util.rs +133 -0
  44. viva_genicam-0.2.4/viva-gencp/Cargo.toml +24 -0
  45. viva_genicam-0.2.4/viva-gencp/README.md +45 -0
  46. viva_genicam-0.2.4/viva-gencp/src/lib.rs +378 -0
  47. viva_genicam-0.2.4/viva-genicam/Cargo.toml +46 -0
  48. viva_genicam-0.2.4/viva-genicam/README.md +56 -0
  49. viva_genicam-0.2.4/viva-genicam/examples/action_trigger.rs +148 -0
  50. viva_genicam-0.2.4/viva-genicam/examples/bitfields_demo.rs +145 -0
  51. viva_genicam-0.2.4/viva-genicam/examples/demo_fake_camera.rs +170 -0
  52. viva_genicam-0.2.4/viva-genicam/examples/enum_pvalue_demo.rs +208 -0
  53. viva_genicam-0.2.4/viva-genicam/examples/events_gige.rs +168 -0
  54. viva_genicam-0.2.4/viva-genicam/examples/features_demo.rs +203 -0
  55. viva_genicam-0.2.4/viva-genicam/examples/get_set_feature.rs +66 -0
  56. viva_genicam-0.2.4/viva-genicam/examples/grab_gige.rs +264 -0
  57. viva_genicam-0.2.4/viva-genicam/examples/grab_with_chunks.rs +335 -0
  58. viva_genicam-0.2.4/viva-genicam/examples/list_cameras.rs +60 -0
  59. viva_genicam-0.2.4/viva-genicam/examples/multicast_grab.rs +395 -0
  60. viva_genicam-0.2.4/viva-genicam/examples/paddress_demo.rs +216 -0
  61. viva_genicam-0.2.4/viva-genicam/examples/selectors_demo.rs +201 -0
  62. viva_genicam-0.2.4/viva-genicam/examples/soak_bench.rs +421 -0
  63. viva_genicam-0.2.4/viva-genicam/examples/stats.rs +36 -0
  64. viva_genicam-0.2.4/viva-genicam/examples/swissknife_demo.rs +166 -0
  65. viva_genicam-0.2.4/viva-genicam/examples/time_sync.rs +166 -0
  66. viva_genicam-0.2.4/viva-genicam/src/chunks.rs +222 -0
  67. viva_genicam-0.2.4/viva-genicam/src/events.rs +174 -0
  68. viva_genicam-0.2.4/viva-genicam/src/frame.rs +308 -0
  69. viva_genicam-0.2.4/viva-genicam/src/lib.rs +988 -0
  70. viva_genicam-0.2.4/viva-genicam/src/stream.rs +931 -0
  71. viva_genicam-0.2.4/viva-genicam/src/time.rs +7 -0
  72. viva_genicam-0.2.4/viva-genicam/tests/common/mod.rs +59 -0
  73. viva_genicam-0.2.4/viva-genicam/tests/fake_camera.rs +527 -0
  74. viva_genicam-0.2.4/viva-genicam/tests/fake_u3v_camera.rs +95 -0
  75. viva_genicam-0.2.4/viva-genicam/tests/predicates.rs +188 -0
  76. viva_genicam-0.2.4/viva-gige/Cargo.toml +35 -0
  77. viva_genicam-0.2.4/viva-gige/README.md +47 -0
  78. viva_genicam-0.2.4/viva-gige/src/action.rs +211 -0
  79. viva_genicam-0.2.4/viva-gige/src/gvcp.rs +1115 -0
  80. viva_genicam-0.2.4/viva-gige/src/gvsp.rs +729 -0
  81. viva_genicam-0.2.4/viva-gige/src/lib.rs +15 -0
  82. viva_genicam-0.2.4/viva-gige/src/message.rs +248 -0
  83. viva_genicam-0.2.4/viva-gige/src/nic.rs +431 -0
  84. viva_genicam-0.2.4/viva-gige/src/stats.rs +403 -0
  85. viva_genicam-0.2.4/viva-gige/src/time.rs +432 -0
  86. viva_genicam-0.2.4/viva-pfnc/Cargo.toml +24 -0
  87. viva_genicam-0.2.4/viva-pfnc/README.md +43 -0
  88. viva_genicam-0.2.4/viva-pfnc/src/lib.rs +211 -0
  89. viva_genicam-0.2.4/viva-pygenicam/Cargo.lock +1062 -0
  90. viva_genicam-0.2.4/viva-pygenicam/Cargo.toml +34 -0
  91. viva_genicam-0.2.4/viva-pygenicam/README.md +33 -0
  92. viva_genicam-0.2.4/viva-pygenicam/examples/README.md +33 -0
  93. viva_genicam-0.2.4/viva-pygenicam/examples/demo_fake_camera.py +56 -0
  94. viva_genicam-0.2.4/viva-pygenicam/examples/discover.py +46 -0
  95. viva_genicam-0.2.4/viva-pygenicam/examples/get_set_feature.py +64 -0
  96. viva_genicam-0.2.4/viva-pygenicam/examples/grab_frame.py +62 -0
  97. viva_genicam-0.2.4/viva-pygenicam/examples/node_browser.py +65 -0
  98. viva_genicam-0.2.4/viva-pygenicam/src/camera.rs +342 -0
  99. viva_genicam-0.2.4/viva-pygenicam/src/discovery.rs +141 -0
  100. viva_genicam-0.2.4/viva-pygenicam/src/errors.rs +60 -0
  101. viva_genicam-0.2.4/viva-pygenicam/src/frame.rs +147 -0
  102. viva_genicam-0.2.4/viva-pygenicam/src/lib.rs +28 -0
  103. viva_genicam-0.2.4/viva-pygenicam/src/nodemap.rs +67 -0
  104. viva_genicam-0.2.4/viva-pygenicam/src/runtime.rs +16 -0
  105. viva_genicam-0.2.4/viva-pygenicam/src/stream.rs +143 -0
  106. viva_genicam-0.2.4/viva-pygenicam/src/testing.rs +187 -0
  107. viva_genicam-0.2.4/viva-pygenicam/tests/conftest.py +38 -0
  108. viva_genicam-0.2.4/viva-pygenicam/tests/test_api_shape.py +57 -0
  109. viva_genicam-0.2.4/viva-pygenicam/tests/test_discovery.py +26 -0
  110. viva_genicam-0.2.4/viva-pygenicam/tests/test_features.py +64 -0
  111. viva_genicam-0.2.4/viva-pygenicam/tests/test_streaming.py +63 -0
  112. viva_genicam-0.2.4/viva-pygenicam/tests/test_testing.py +56 -0
  113. viva_genicam-0.2.4/viva-sfnc/Cargo.toml +20 -0
  114. viva_genicam-0.2.4/viva-sfnc/README.md +40 -0
  115. viva_genicam-0.2.4/viva-sfnc/src/lib.rs +86 -0
  116. viva_genicam-0.2.4/viva-u3v/Cargo.toml +24 -0
  117. viva_genicam-0.2.4/viva-u3v/README.md +48 -0
  118. viva_genicam-0.2.4/viva-u3v/src/bootstrap.rs +438 -0
  119. viva_genicam-0.2.4/viva-u3v/src/control.rs +523 -0
  120. viva_genicam-0.2.4/viva-u3v/src/descriptor.rs +144 -0
  121. viva_genicam-0.2.4/viva-u3v/src/device.rs +386 -0
  122. viva_genicam-0.2.4/viva-u3v/src/discovery.rs +113 -0
  123. viva_genicam-0.2.4/viva-u3v/src/lib.rs +46 -0
  124. viva_genicam-0.2.4/viva-u3v/src/stream.rs +458 -0
  125. viva_genicam-0.2.4/viva-u3v/src/usb.rs +207 -0
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: viva-genicam
3
+ Version: 0.2.4
4
+ Classifier: Development Status :: 4 - Beta
5
+ Classifier: Intended Audience :: Science/Research
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.9
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Rust
14
+ Classifier: Topic :: Multimedia :: Graphics :: Capture
15
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
16
+ Requires-Dist: numpy>=1.23
17
+ Requires-Dist: pytest>=7 ; extra == 'dev'
18
+ Requires-Dist: maturin>=1.7 ; extra == 'dev'
19
+ Provides-Extra: dev
20
+ Summary: Pure-Rust GenICam stack for Python: discover, control, and stream GigE Vision / USB3 Vision cameras
21
+ Keywords: genicam,gige,usb3-vision,camera,vision
22
+ Home-Page: https://github.com/VitalyVorobyev/viva-genicam
23
+ Author: Vitaly Vorobyev
24
+ License: MIT
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
27
+ Project-URL: Documentation, https://vitalyvorobyev.github.io/viva-genicam/
28
+ Project-URL: Homepage, https://github.com/VitalyVorobyev/viva-genicam
29
+ Project-URL: Repository, https://github.com/VitalyVorobyev/viva-genicam
30
+
31
+ # viva-genicam (Python)
32
+
33
+ Pure-Rust GenICam stack with Python bindings. Discover, control, and stream GigE Vision and USB3 Vision cameras from Python — no aravis, no C toolchain, just a wheel.
34
+
35
+ ```bash
36
+ pip install viva-genicam
37
+ ```
38
+
39
+ ```python
40
+ import viva_genicam as vg
41
+
42
+ cams = vg.discover(timeout_ms=500)
43
+ cam = vg.connect_gige(cams[0])
44
+
45
+ print(cam.get("DeviceModelName"))
46
+ cam.set_exposure_time_us(10_000.0)
47
+
48
+ with cam.stream() as frames:
49
+ for frame in frames:
50
+ arr = frame.to_numpy() # NumPy (H, W) or (H, W, 3) uint8
51
+ print(frame.width, frame.height, frame.pixel_format)
52
+ break
53
+ ```
54
+
55
+ See the [documentation](https://vitalyvorobyev.github.io/viva-genicam/python.html) for the full API.
56
+
57
+ ## Build from source
58
+
59
+ ```bash
60
+ uv venv .venv
61
+ uv pip install --python .venv/bin/python maturin numpy pytest
62
+ uv run --python .venv/bin/python maturin develop -m crates/viva-pygenicam/Cargo.toml
63
+ ```
64
+
@@ -0,0 +1,33 @@
1
+ # viva-genicam (Python)
2
+
3
+ Pure-Rust GenICam stack with Python bindings. Discover, control, and stream GigE Vision and USB3 Vision cameras from Python — no aravis, no C toolchain, just a wheel.
4
+
5
+ ```bash
6
+ pip install viva-genicam
7
+ ```
8
+
9
+ ```python
10
+ import viva_genicam as vg
11
+
12
+ cams = vg.discover(timeout_ms=500)
13
+ cam = vg.connect_gige(cams[0])
14
+
15
+ print(cam.get("DeviceModelName"))
16
+ cam.set_exposure_time_us(10_000.0)
17
+
18
+ with cam.stream() as frames:
19
+ for frame in frames:
20
+ arr = frame.to_numpy() # NumPy (H, W) or (H, W, 3) uint8
21
+ print(frame.width, frame.height, frame.pixel_format)
22
+ break
23
+ ```
24
+
25
+ See the [documentation](https://vitalyvorobyev.github.io/viva-genicam/python.html) for the full API.
26
+
27
+ ## Build from source
28
+
29
+ ```bash
30
+ uv venv .venv
31
+ uv pip install --python .venv/bin/python maturin numpy pytest
32
+ uv run --python .venv/bin/python maturin develop -m crates/viva-pygenicam/Cargo.toml
33
+ ```
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["maturin>=1.7,<2.0"]
3
+ build-backend = "maturin"
4
+
5
+ [project]
6
+ name = "viva-genicam"
7
+ version = "0.2.4"
8
+ description = "Pure-Rust GenICam stack for Python: discover, control, and stream GigE Vision / USB3 Vision cameras"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [
12
+ { name = "Vitaly Vorobyev" },
13
+ ]
14
+ requires-python = ">=3.9"
15
+ keywords = ["genicam", "gige", "usb3-vision", "camera", "vision"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Programming Language :: Rust",
27
+ "Topic :: Multimedia :: Graphics :: Capture",
28
+ "Topic :: Scientific/Engineering :: Image Processing",
29
+ ]
30
+ dependencies = [
31
+ "numpy>=1.23",
32
+ ]
33
+
34
+ [project.optional-dependencies]
35
+ dev = [
36
+ "pytest>=7",
37
+ "maturin>=1.7",
38
+ ]
39
+
40
+ [project.urls]
41
+ Homepage = "https://github.com/VitalyVorobyev/viva-genicam"
42
+ Repository = "https://github.com/VitalyVorobyev/viva-genicam"
43
+ Documentation = "https://vitalyvorobyev.github.io/viva-genicam/"
44
+
45
+ [tool.maturin]
46
+ module-name = "viva_genicam._native"
47
+ features = ["pyo3/extension-module"]
48
+ manifest-path = "viva-pygenicam/Cargo.toml"
49
+ python-source = "python"
@@ -0,0 +1,59 @@
1
+ """Python bindings for viva-genicam.
2
+
3
+ Pure-Rust GenICam stack for GigE Vision and USB3 Vision cameras.
4
+
5
+ Quickstart:
6
+
7
+ import viva_genicam as vg
8
+
9
+ cams = vg.discover(timeout_ms=500)
10
+ cam = vg.connect_gige(cams[0])
11
+ print(cam.get("DeviceModelName"))
12
+
13
+ with cam.stream() as frames:
14
+ for frame in frames:
15
+ arr = frame.to_numpy()
16
+ break
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from .camera import Camera
22
+ from .discovery import DeviceInfo, GigeDeviceInfo, U3vDeviceInfo, discover, discover_u3v
23
+ from .errors import (
24
+ GenApiError,
25
+ GenicamError,
26
+ MissingChunkFeatureError,
27
+ ParseError,
28
+ TransportError,
29
+ UnsupportedPixelFormatError,
30
+ )
31
+ from .frame import Frame
32
+ from .node import NodeInfo, NodeKind
33
+ from .stream import FrameStream
34
+
35
+ # Lazy functional wrappers so the public API is a flat module.
36
+ from .camera import connect_gige, connect_u3v
37
+
38
+ __all__ = [
39
+ "Camera",
40
+ "DeviceInfo",
41
+ "GigeDeviceInfo",
42
+ "U3vDeviceInfo",
43
+ "Frame",
44
+ "FrameStream",
45
+ "NodeInfo",
46
+ "NodeKind",
47
+ "GenicamError",
48
+ "GenApiError",
49
+ "TransportError",
50
+ "ParseError",
51
+ "MissingChunkFeatureError",
52
+ "UnsupportedPixelFormatError",
53
+ "discover",
54
+ "discover_u3v",
55
+ "connect_gige",
56
+ "connect_u3v",
57
+ ]
58
+
59
+ __version__ = "0.2.4"
@@ -0,0 +1,102 @@
1
+ """Type stubs for the private native extension.
2
+
3
+ End users should not import from this module directly — use the public
4
+ ``viva_genicam`` package instead.
5
+ """
6
+
7
+ from typing import Any, Optional
8
+
9
+ class GigeDeviceInfo:
10
+ ip: str
11
+ mac: str
12
+ manufacturer: Optional[str]
13
+ model: Optional[str]
14
+
15
+ class U3vDeviceInfo:
16
+ bus: int
17
+ address: int
18
+ vendor_id: int
19
+ product_id: int
20
+ serial: Optional[str]
21
+ manufacturer: Optional[str]
22
+ model: Optional[str]
23
+
24
+ class _Frame:
25
+ width: int
26
+ height: int
27
+ pixel_format: str
28
+ pixel_format_code: int
29
+ ts_dev: Optional[int]
30
+ ts_host: Optional[float]
31
+ def payload(self) -> bytes: ...
32
+ def to_numpy(self) -> Any: ...
33
+ def to_rgb8(self) -> Any: ...
34
+
35
+ class _FrameStream:
36
+ def next_frame(self, timeout_ms: Optional[int] = None) -> Optional[_Frame]: ...
37
+ def close(self) -> None: ...
38
+ def __iter__(self) -> "_FrameStream": ...
39
+ def __next__(self) -> _Frame: ...
40
+
41
+ class _Camera:
42
+ transport: str
43
+ xml: str
44
+ def get(self, name: str) -> str: ...
45
+ def set(self, name: str, value: str) -> None: ...
46
+ def set_exposure_time_us(self, value: float) -> None: ...
47
+ def set_gain_db(self, value: float) -> None: ...
48
+ def enum_entries(self, name: str) -> list[str]: ...
49
+ def nodes(self) -> list[str]: ...
50
+ def node_info(self, name: str) -> Optional[dict]: ...
51
+ def all_node_info(self) -> list[dict]: ...
52
+ def categories(self) -> dict[str, list[str]]: ...
53
+ def acquisition_start(self) -> None: ...
54
+ def acquisition_stop(self) -> None: ...
55
+ def open_stream(
56
+ self,
57
+ iface: Optional[str],
58
+ auto_packet_size: Optional[bool],
59
+ multicast: Optional[str],
60
+ destination_port: Optional[int],
61
+ ) -> _FrameStream: ...
62
+
63
+ def discover_gige(
64
+ timeout_ms: int = 500,
65
+ iface: Optional[str] = None,
66
+ all: bool = False,
67
+ ) -> list[GigeDeviceInfo]: ...
68
+ def discover_u3v() -> list[U3vDeviceInfo]: ...
69
+ def connect_gige(
70
+ device_info: GigeDeviceInfo, iface: Optional[str] = None
71
+ ) -> _Camera: ...
72
+ def connect_u3v(device_info: U3vDeviceInfo) -> _Camera: ...
73
+
74
+ class GenicamError(Exception): ...
75
+ class GenApiError(GenicamError): ...
76
+ class TransportError(GenicamError): ...
77
+ class ParseError(GenicamError): ...
78
+ class MissingChunkFeatureError(GenicamError): ...
79
+ class UnsupportedPixelFormatError(GenicamError): ...
80
+
81
+ class _testing:
82
+ class FakeGigeCamera:
83
+ def __init__(
84
+ self,
85
+ width: int = 640,
86
+ height: int = 480,
87
+ fps: int = 30,
88
+ bind_ip: str = "127.0.0.1",
89
+ port: int = 3956,
90
+ pixel_format: str = "Mono8",
91
+ ) -> None: ...
92
+ @property
93
+ def ip(self) -> str: ...
94
+ @property
95
+ def port(self) -> int: ...
96
+ def start(self) -> None: ...
97
+ def stop(self) -> None: ...
98
+ def device_info(self, timeout_ms: int = 1500) -> GigeDeviceInfo: ...
99
+ def __enter__(self) -> "_testing.FakeGigeCamera": ...
100
+ def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> bool: ...
101
+
102
+ testing: _testing
@@ -0,0 +1,126 @@
1
+ """Camera facade: unified API over GigE and U3V transports."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Optional
6
+
7
+ from . import _native
8
+ from .discovery import DeviceInfo, GigeDeviceInfo, U3vDeviceInfo
9
+ from .node import NodeInfo
10
+ from .stream import FrameStream
11
+
12
+
13
+ class Camera:
14
+ """Unified camera handle.
15
+
16
+ Construct via ``connect_gige(device_info)`` or ``connect_u3v(device_info)``.
17
+ The class itself presents the same method surface regardless of transport.
18
+ """
19
+
20
+ __slots__ = ("_native",)
21
+
22
+ def __init__(self, native: Any) -> None:
23
+ self._native = native
24
+
25
+ # ── class factories ──────────────────────────────────────────────────
26
+
27
+ @classmethod
28
+ def open(cls, device: DeviceInfo, **kwargs) -> "Camera":
29
+ """Connect to ``device`` using the appropriate transport."""
30
+ if isinstance(device, GigeDeviceInfo):
31
+ return connect_gige(device, **kwargs)
32
+ if isinstance(device, U3vDeviceInfo):
33
+ return connect_u3v(device)
34
+ raise TypeError(f"unsupported device type: {type(device).__name__}")
35
+
36
+ # ── metadata ─────────────────────────────────────────────────────────
37
+
38
+ @property
39
+ def transport(self) -> str:
40
+ """``"gige"`` or ``"u3v"``."""
41
+ return self._native.transport
42
+
43
+ @property
44
+ def xml(self) -> str:
45
+ """Raw GenICam XML fetched from the camera."""
46
+ return self._native.xml
47
+
48
+ # ── feature access ───────────────────────────────────────────────────
49
+
50
+ def get(self, name: str) -> str:
51
+ """Read a feature value, formatted as a string."""
52
+ return self._native.get(name)
53
+
54
+ def set(self, name: str, value: str) -> None:
55
+ """Set a feature value; the string is parsed per node type."""
56
+ self._native.set(name, value)
57
+
58
+ def set_exposure_time_us(self, value: float) -> None:
59
+ self._native.set_exposure_time_us(value)
60
+
61
+ def set_gain_db(self, value: float) -> None:
62
+ self._native.set_gain_db(value)
63
+
64
+ def enum_entries(self, name: str) -> list[str]:
65
+ """List allowed entries for an enumeration feature."""
66
+ return self._native.enum_entries(name)
67
+
68
+ # ── introspection ────────────────────────────────────────────────────
69
+
70
+ def nodes(self) -> list[str]:
71
+ """Names of every feature exposed by the camera."""
72
+ return self._native.nodes()
73
+
74
+ def node_info(self, name: str) -> Optional[NodeInfo]:
75
+ """Metadata for one feature, or ``None`` if not present."""
76
+ raw = self._native.node_info(name)
77
+ return NodeInfo.from_dict(raw) if raw is not None else None
78
+
79
+ def all_node_info(self) -> list[NodeInfo]:
80
+ """Metadata for every feature."""
81
+ return [NodeInfo.from_dict(d) for d in self._native.all_node_info()]
82
+
83
+ def categories(self) -> dict[str, list[str]]:
84
+ """Category → feature-name children."""
85
+ return dict(self._native.categories())
86
+
87
+ # ── control ──────────────────────────────────────────────────────────
88
+
89
+ def acquisition_start(self) -> None:
90
+ self._native.acquisition_start()
91
+
92
+ def acquisition_stop(self) -> None:
93
+ self._native.acquisition_stop()
94
+
95
+ # ── streaming ────────────────────────────────────────────────────────
96
+
97
+ def stream(
98
+ self,
99
+ iface: Optional[str] = None,
100
+ auto_packet_size: Optional[bool] = None,
101
+ multicast: Optional[str] = None,
102
+ destination_port: Optional[int] = None,
103
+ ) -> FrameStream:
104
+ """Open a frame stream (context-manager yielding ``Frame`` objects).
105
+
106
+ GigE-only parameters are ignored for U3V cameras.
107
+ """
108
+ native = self._native.open_stream(
109
+ iface, auto_packet_size, multicast, destination_port
110
+ )
111
+ return FrameStream(native, self)
112
+
113
+
114
+ def connect_gige(device_info: GigeDeviceInfo, iface: Optional[str] = None) -> Camera:
115
+ """Connect to a GigE Vision camera."""
116
+ native = _native.connect_gige(device_info._handle, iface)
117
+ return Camera(native)
118
+
119
+
120
+ def connect_u3v(device_info: U3vDeviceInfo) -> Camera:
121
+ """Connect to a USB3 Vision camera."""
122
+ native = _native.connect_u3v(device_info._handle)
123
+ return Camera(native)
124
+
125
+
126
+ __all__ = ["Camera", "connect_gige", "connect_u3v"]
@@ -0,0 +1,110 @@
1
+ """Camera discovery: GigE Vision (network) and USB3 Vision (libusb)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Any, Literal, Optional, Union
7
+
8
+ from . import _native
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class GigeDeviceInfo:
13
+ """A discovered GigE Vision camera."""
14
+
15
+ ip: str
16
+ mac: str
17
+ manufacturer: Optional[str]
18
+ model: Optional[str]
19
+ transport: Literal["gige"] = "gige"
20
+ _handle: Any = field(default=None, repr=False, compare=False)
21
+
22
+ @classmethod
23
+ def _from_native(cls, native: Any) -> "GigeDeviceInfo":
24
+ return cls(
25
+ ip=native.ip,
26
+ mac=native.mac,
27
+ manufacturer=native.manufacturer,
28
+ model=native.model,
29
+ _handle=native,
30
+ )
31
+
32
+ def to_dict(self) -> dict:
33
+ return {
34
+ "ip": self.ip,
35
+ "mac": self.mac,
36
+ "manufacturer": self.manufacturer,
37
+ "model": self.model,
38
+ "transport": self.transport,
39
+ }
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class U3vDeviceInfo:
44
+ """A discovered USB3 Vision camera."""
45
+
46
+ bus: int
47
+ address: int
48
+ vendor_id: int
49
+ product_id: int
50
+ serial: Optional[str]
51
+ manufacturer: Optional[str]
52
+ model: Optional[str]
53
+ transport: Literal["u3v"] = "u3v"
54
+ _handle: Any = field(default=None, repr=False, compare=False)
55
+
56
+ @classmethod
57
+ def _from_native(cls, native: Any) -> "U3vDeviceInfo":
58
+ return cls(
59
+ bus=native.bus,
60
+ address=native.address,
61
+ vendor_id=native.vendor_id,
62
+ product_id=native.product_id,
63
+ serial=native.serial,
64
+ manufacturer=native.manufacturer,
65
+ model=native.model,
66
+ _handle=native,
67
+ )
68
+
69
+ def to_dict(self) -> dict:
70
+ return {
71
+ "bus": self.bus,
72
+ "address": self.address,
73
+ "vendor_id": self.vendor_id,
74
+ "product_id": self.product_id,
75
+ "serial": self.serial,
76
+ "manufacturer": self.manufacturer,
77
+ "model": self.model,
78
+ "transport": self.transport,
79
+ }
80
+
81
+
82
+ DeviceInfo = Union[GigeDeviceInfo, U3vDeviceInfo]
83
+
84
+
85
+ def discover(
86
+ timeout_ms: int = 500,
87
+ iface: Optional[str] = None,
88
+ all: bool = False,
89
+ ) -> list[GigeDeviceInfo]:
90
+ """Discover GigE Vision cameras on the local network.
91
+
92
+ Args:
93
+ timeout_ms: How long to wait for responses.
94
+ iface: If given, restrict discovery to the named NIC (e.g. ``"en0"``).
95
+ all: If True, enumerate all system interfaces and merge results.
96
+
97
+ Returns:
98
+ A list of ``GigeDeviceInfo`` — may be empty.
99
+ """
100
+ natives = _native.discover_gige(timeout_ms, iface, all)
101
+ return [GigeDeviceInfo._from_native(n) for n in natives]
102
+
103
+
104
+ def discover_u3v() -> list[U3vDeviceInfo]:
105
+ """Enumerate USB3 Vision cameras connected to the system."""
106
+ natives = _native.discover_u3v()
107
+ return [U3vDeviceInfo._from_native(n) for n in natives]
108
+
109
+
110
+ __all__ = ["GigeDeviceInfo", "U3vDeviceInfo", "DeviceInfo", "discover", "discover_u3v"]
@@ -0,0 +1,26 @@
1
+ """Exception hierarchy for viva-genicam.
2
+
3
+ All exceptions raised by the native extension subclass ``GenicamError``.
4
+ They are defined on the Rust side and re-exported here so users can do
5
+ ``except vg.TransportError:`` without reaching into ``_native``.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from . import _native
11
+
12
+ GenicamError = _native.GenicamError
13
+ GenApiError = _native.GenApiError
14
+ TransportError = _native.TransportError
15
+ ParseError = _native.ParseError
16
+ MissingChunkFeatureError = _native.MissingChunkFeatureError
17
+ UnsupportedPixelFormatError = _native.UnsupportedPixelFormatError
18
+
19
+ __all__ = [
20
+ "GenicamError",
21
+ "GenApiError",
22
+ "TransportError",
23
+ "ParseError",
24
+ "MissingChunkFeatureError",
25
+ "UnsupportedPixelFormatError",
26
+ ]
@@ -0,0 +1,73 @@
1
+ """Frame wrapper around the native ``_Frame`` handle."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any, Optional
6
+
7
+ if TYPE_CHECKING:
8
+ import numpy as np
9
+
10
+
11
+ class Frame:
12
+ """One image frame produced by the camera.
13
+
14
+ Wraps a native handle — the payload is only materialized into Python
15
+ memory when ``payload()``, ``to_numpy()``, or ``to_rgb8()`` is called.
16
+ """
17
+
18
+ __slots__ = ("_native",)
19
+
20
+ def __init__(self, native: Any) -> None:
21
+ self._native = native
22
+
23
+ @property
24
+ def width(self) -> int:
25
+ return self._native.width
26
+
27
+ @property
28
+ def height(self) -> int:
29
+ return self._native.height
30
+
31
+ @property
32
+ def pixel_format(self) -> str:
33
+ """Human-readable PFNC name, e.g. ``"Mono8"`` or ``"BayerRG8"``."""
34
+ return self._native.pixel_format
35
+
36
+ @property
37
+ def pixel_format_code(self) -> int:
38
+ """Raw PFNC numeric code."""
39
+ return self._native.pixel_format_code
40
+
41
+ @property
42
+ def ts_dev(self) -> Optional[int]:
43
+ """Device tick timestamp (camera-local), if reported."""
44
+ return self._native.ts_dev
45
+
46
+ @property
47
+ def ts_host(self) -> Optional[float]:
48
+ """Host-mapped timestamp as POSIX seconds, if available."""
49
+ return self._native.ts_host
50
+
51
+ def payload(self) -> bytes:
52
+ """Raw payload as ``bytes`` (one copy)."""
53
+ return self._native.payload()
54
+
55
+ def to_numpy(self) -> "np.ndarray":
56
+ """Return a NumPy array shaped for the pixel format.
57
+
58
+ - Mono8 → ``(H, W) uint8``
59
+ - Mono16 → ``(H, W) uint16``
60
+ - RGB8 / BGR8 / Bayer8 → ``(H, W, 3) uint8``
61
+ - unknown → ``(N,) uint8``
62
+ """
63
+ return self._native.to_numpy()
64
+
65
+ def to_rgb8(self) -> "np.ndarray":
66
+ """Always return an ``(H, W, 3) uint8`` RGB array."""
67
+ return self._native.to_rgb8()
68
+
69
+ def __repr__(self) -> str:
70
+ return self._native.__repr__()
71
+
72
+
73
+ __all__ = ["Frame"]