Skip to content

Commit 24bebea

Browse files
committed
port to use pylon parameter API
Instead of direct access to the genicam types pypylon will now _always_ provide the Parameter types with enhanced usability
1 parent 243be91 commit 24bebea

File tree

7 files changed

+2030
-12
lines changed

7 files changed

+2030
-12
lines changed

docs/MODERN_API_PROPOSAL.md

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
# Modern C++ API Compatibility for pypylon
2+
3+
This document outlines the implementation of complete modern C++ API compatibility for pypylon, exactly matching the actual Pylon SDK patterns discovered by examining `/opt/pylon/include/pylon/`.
4+
5+
## Problem Statement
6+
7+
Current pypylon requires verbose API calls:
8+
```python
9+
geni.IsWritable(self.camera.ExposureTimeRaw.Node)
10+
```
11+
12+
Modern C++ Pylon SDK provides:
13+
```cpp
14+
camera.ExposureTimeRaw.IsWritable()
15+
```
16+
17+
**Goal**: Enable identical API in Python:
18+
```python
19+
self.camera.ExposureTimeRaw.IsWritable()
20+
```
21+
22+
## Implementation: Exact SDK Pattern Matching
23+
24+
Based on examination of the actual Pylon SDK headers, we've implemented **exact interface matching** with the C++ SDK.
25+
26+
### Core Interfaces Implemented
27+
28+
#### 1. **IValueEx Interface** (Base for all parameters)
29+
Matches: `/opt/pylon/include/pylon/Parameter.h`
30+
31+
```python
32+
# All parameters inherit these methods
33+
param.IsWritable() # -> bool
34+
param.IsReadable() # -> bool
35+
param.IsValid() # -> bool
36+
param.GetInfo(EParameterInfo.ParameterInfo_Name) # -> string
37+
param.GetInfoOrDefault(info, "default") # -> string
38+
param.ToStringOrDefault("default") # -> string
39+
```
40+
41+
#### 2. **IIntegerEx Interface**
42+
Matches: `/opt/pylon/include/pylon/IntegerParameter.h`
43+
44+
```python
45+
# Integer parameters (ExposureTimeRaw, Width, Height, etc.)
46+
param.TrySetValue(1000) # -> bool
47+
param.GetValueOrDefault(500) # -> int64
48+
param.TrySetValue(1000, EIntegerValueCorrection.IntegerValueCorrection_Nearest) # -> bool
49+
param.SetValue(1000, EIntegerValueCorrection.IntegerValueCorrection_Up)
50+
param.GetValuePercentOfRange() # -> double
51+
param.SetValuePercentOfRange(50.0) # Set to 50% of range
52+
param.TrySetValuePercentOfRange(75.0) # -> bool
53+
param.SetToMaximum() # Set to max value
54+
param.SetToMinimum() # Set to min value
55+
param.TrySetToMaximum() # -> bool
56+
param.TrySetToMinimum() # -> bool
57+
```
58+
59+
#### 3. **IEnumerationEx Interface**
60+
Matches: `/opt/pylon/include/pylon/EnumParameter.h`
61+
62+
```python
63+
# Enumeration parameters (PixelFormat, TriggerMode, etc.)
64+
param.TrySetValue("Mono8") # -> bool
65+
param.CanSetValue("RGB8") # -> bool
66+
param.GetValueOrDefault("Unknown") # -> string
67+
param.SetValue(["BayerGR8", "BayerRG8", "Mono8"]) # Set first available
68+
param.TrySetValue(["BayerGR8", "BayerRG8", "Mono8"]) # -> bool
69+
```
70+
71+
#### 4. **IBooleanEx Interface**
72+
Matches: `/opt/pylon/include/pylon/BooleanParameter.h`
73+
74+
```python
75+
# Boolean parameters (ReverseX, ReverseY, etc.)
76+
param.TrySetValue(True) # -> bool
77+
param.GetValueOrDefault(False) # -> bool
78+
```
79+
80+
#### 5. **ICommandEx Interface**
81+
Matches: `/opt/pylon/include/pylon/CommandParameter.h`
82+
83+
```python
84+
# Command parameters (TriggerSoftware, UserSetLoad, etc.)
85+
param.TryExecute() # -> bool
86+
param.IsDone() # -> bool
87+
```
88+
89+
#### 6. **IFloatEx Interface** (Additional implementation)
90+
```python
91+
# Float parameters (ExposureTime, Gain, etc.)
92+
param.TrySetValue(1.5) # -> bool
93+
param.GetValueOrDefault(1.0) # -> double
94+
```
95+
96+
#### 7. **IStringEx Interface** (Additional implementation)
97+
```python
98+
# String parameters
99+
param.TrySetValue("test") # -> bool
100+
param.GetValueOrDefault("default") # -> string
101+
```
102+
103+
### Value Correction Enums (Exact SDK Match)
104+
105+
```python
106+
from pypylon import pylon as py
107+
108+
# Matches EIntegerValueCorrection from SDK
109+
py.EIntegerValueCorrection.IntegerValueCorrection_None # No correction
110+
py.EIntegerValueCorrection.IntegerValueCorrection_Up # Round up
111+
py.EIntegerValueCorrection.IntegerValueCorrection_Down # Round down
112+
py.EIntegerValueCorrection.IntegerValueCorrection_Nearest # Round to nearest
113+
114+
# Matches EParameterInfo from SDK
115+
py.EParameterInfo.ParameterInfo_Name # Parameter name
116+
py.EParameterInfo.ParameterInfo_DisplayName # Display name
117+
py.EParameterInfo.ParameterInfo_ToolTip # Short description
118+
py.EParameterInfo.ParameterInfo_Description # Long description
119+
```
120+
121+
## Key Implementation Features
122+
123+
### 1. **Type-Specific Parameter Wrappers**
124+
- **Factory Function**: `CreateParameterWrapper(node)` automatically returns the correct type
125+
- **Automatic Detection**: Based on dynamic casting (Integer, Float, Boolean, Enum, Command, String)
126+
- **Fallback Handling**: Unknown types get base `PyValueEx` interface
127+
128+
### 2. **Enhanced InstantCamera Integration**
129+
```python
130+
# Automatic parameter wrapping
131+
exposure = camera.ExposureTimeRaw # Returns PyIntegerEx automatically
132+
width = camera.Width # Returns PyIntegerEx automatically
133+
pixel_format = camera.PixelFormat # Returns PyEnumerationEx automatically
134+
135+
# Helper methods
136+
camera.GetParameter("Width") # Explicit parameter access
137+
camera.HasParameter("CustomParam") # Parameter existence check
138+
camera.IsParameterWritable("Width") # Writability check
139+
camera.TrySetParameter("Width", 640) # Safe parameter setting
140+
```
141+
142+
### 3. **Improved Error Handling with Parameter Names**
143+
**Problem Solved**: The original error "Node not existing" was unhelpful.
144+
145+
**Before:**
146+
```
147+
[ERROR] GenICam exception: Node not existing (file 'genicamPYTHON_wrap.cxx', line 16822)
148+
```
149+
150+
**After:**
151+
```python
152+
# Helpful error messages with parameter names
153+
try:
154+
param = camera.NonExistentParameter
155+
except AttributeError as e:
156+
print(e) # "Camera parameter access failed: Parameter 'NonExistentParameter' does not exist on this camera/device"
157+
158+
# Safe methods that don't throw exceptions
159+
camera.HasParameter("FakeParam") # Returns False
160+
camera.IsParameterWritable("FakeParam") # Returns False
161+
camera.TrySetParameter("FakeParam", 123) # Returns False
162+
163+
# Detailed parameter information
164+
try:
165+
info = camera.GetParameterInfo("FakeParam")
166+
except RuntimeError as e:
167+
print(e) # "Failed to get parameter info for 'FakeParam': Parameter 'FakeParam' does not exist on this camera/device"
168+
```
169+
170+
**Error Handling Features:**
171+
- **Parameter Names in Errors**: All error messages include the requested parameter name
172+
- **Context-Aware Messages**: Different error types (missing, read-only, etc.) have specific messages
173+
- **Safe Fallback Methods**: Try* and Has* methods that return False instead of throwing
174+
- **Detailed Error Context**: Information about what operation failed and why
175+
176+
### 4. **Value Correction Algorithm**
177+
Exact implementation matching SDK logic:
178+
179+
```python
180+
def CorrectIntegerValue(value, min_val, max_val, inc, correction):
181+
# Range clipping
182+
corrected = max(min_val, min(max_val, value))
183+
184+
# Increment alignment
185+
if inc > 1:
186+
remainder = (corrected - min_val) % inc
187+
if remainder != 0:
188+
if correction == IntegerValueCorrection_Up:
189+
corrected += (inc - remainder)
190+
elif correction == IntegerValueCorrection_Down:
191+
corrected -= remainder
192+
elif correction == IntegerValueCorrection_Nearest:
193+
if remainder <= inc // 2:
194+
corrected -= remainder
195+
else:
196+
corrected += (inc - remainder)
197+
198+
return max(min_val, min(max_val, corrected))
199+
```
200+
201+
## Complete Usage Examples
202+
203+
### Basic Parameter Access
204+
```python
205+
from pypylon import pylon as py
206+
207+
camera = py.InstantCamera()
208+
camera.Open()
209+
210+
# Modern C++ API style - exact SDK match
211+
exposure = camera.ExposureTimeRaw
212+
print(f"Writable: {exposure.IsWritable()}")
213+
print(f"Current: {exposure.GetValue()}")
214+
print(f"Range: {exposure.GetMin()} - {exposure.GetMax()}")
215+
```
216+
217+
### Safe Parameter Operations
218+
```python
219+
# Safe setting with error handling
220+
if exposure.TrySetValue(10000):
221+
print("✓ Set exposure successfully")
222+
else:
223+
print("✗ Failed to set exposure")
224+
225+
# Value correction
226+
if exposure.TrySetValue(9999, py.EIntegerValueCorrection.IntegerValueCorrection_Nearest):
227+
print(f"✓ Set exposure with correction: {exposure.GetValue()}")
228+
```
229+
230+
### Range Operations
231+
```python
232+
# Percentage-based setting
233+
exposure.SetValuePercentOfRange(25.0) # Set to 25% of range
234+
print(f"Current range percentage: {exposure.GetValuePercentOfRange():.1f}%")
235+
236+
# Min/max operations
237+
if exposure.TrySetToMinimum():
238+
print(f"✓ Set to minimum: {exposure.GetValue()}")
239+
```
240+
241+
### Enumeration Operations
242+
```python
243+
pixel_format = camera.PixelFormat
244+
245+
# Check available values
246+
formats = ["Mono8", "Mono12", "RGB8", "BayerGR8"]
247+
for fmt in formats:
248+
if pixel_format.CanSetValue(fmt):
249+
print(f"{fmt} is available")
250+
251+
# Safe setting
252+
if pixel_format.TrySetValue("Mono8"):
253+
print("✓ Changed to Mono8")
254+
```
255+
256+
### Command Operations
257+
```python
258+
if hasattr(camera, 'TriggerSoftware'):
259+
trigger = camera.TriggerSoftware
260+
if trigger.TryExecute():
261+
print("✓ Software trigger executed")
262+
```
263+
264+
### Error Handling Examples
265+
```python
266+
# Old API - vague error
267+
try:
268+
node = camera.GetNodeMap().GetNode("FakeParam")
269+
value = node.GetValue() # "Node not existing" - unhelpful!
270+
except:
271+
pass
272+
273+
# New API - helpful error with parameter name
274+
try:
275+
param = camera.FakeParam
276+
except AttributeError as e:
277+
print(e) # "Camera parameter access failed: Parameter 'FakeParam' does not exist on this camera/device"
278+
279+
# Safe methods - no exceptions
280+
if camera.HasParameter("FakeParam"): # Returns False
281+
value = camera.FakeParam.GetValue()
282+
else:
283+
print("Parameter 'FakeParam' not available")
284+
285+
# Try setting with helpful feedback
286+
if not camera.TrySetParameter("Width", 1000):
287+
print("Failed to set Width - parameter may not exist or not be writable")
288+
```
289+
290+
## Backward Compatibility
291+
292+
**Complete backward compatibility** is maintained:
293+
294+
```python
295+
# Old API still works
296+
width_node = camera.GetNodeMap().GetNode("Width")
297+
old_writable = geni.IsWritable(width_node)
298+
299+
# New API works alongside
300+
width_param = camera.Width
301+
new_writable = width_param.IsWritable()
302+
303+
# Mixed usage is fine
304+
assert old_writable == new_writable
305+
```
306+
307+
## Benefits Summary
308+
309+
**100% SDK Compatibility**: Exact interface matching with Pylon C++ SDK
310+
**Type Safety**: Automatic type-specific parameter wrappers
311+
**Error Safety**: Try* methods for safe operations
312+
**Range Operations**: Percentage, min/max, value correction
313+
**Full Backward Compatibility**: Existing code continues to work
314+
**Enhanced Error Handling**: Robust parameter validation with helpful messages
315+
**Modern API**: Clean, intuitive parameter access
316+
317+
## Implementation Files
318+
319+
1. **`src/pylon/NodeWrapper.i`** - Core extended parameter classes
320+
2. **`src/pylon/InstantCamera.i`** - Enhanced camera integration
321+
3. **`samples/modern_api_demo.py`** - Comprehensive demonstration
322+
4. **`samples/error_handling_demo.py`** - Focused error handling demonstration
323+
5. **`docs/MODERN_API_PROPOSAL.md`** - This documentation
324+
325+
This implementation provides **complete API parity** with the modern Pylon C++ SDK while maintaining full backward compatibility with existing pypylon code.

0 commit comments

Comments
 (0)