GetNode("NonExistingNodeName") throws Exception
Hi, can somebody confirm, that GetNode("NonExistingNodeName") actually throws an exception? I can't believe that, since it makes genicam.IsAvailable(), etc. completely useless!?
genicam.IsAvailable(camera.CenterX)
LogicalErrorException Traceback (most recent call last) Cell In[8], line 1 ----> 1 genicam.IsAvailable(camera.CenterX)
File ~\anaconda3\envs\pypylon\Lib\site-packages\pypylon\pylon.py:4352, in InstantCamera.getattr(self, attribute) 4350 return object.getattr(self, attribute) 4351 else: -> 4352 return self.GetNodeMap().GetNode(attribute)
File ~\anaconda3\envs\pypylon\Lib\site-packages\pypylon\genicam.py:1115, in INodeMap.GetNode(self, Name) 1114 def GetNode(self, Name): -> 1115 return _genicam.INodeMap_GetNode(self, Name)
LogicalErrorException: Node not existing (file 'genicam_wrap.cpp', line 16504)
I'm not sure I understand your question, but I think you have confused "IsAvailable" with "IsImplemented".
Some nodes, especially EnumEntries, change availability at runtime, like HighSpeedBurst Mode. And also "IsImpemented" returns False for some nodes if the camera is not open.
So the fact that "GetNode" does not throw an error is not necessarily the same as "IsImplemented" or "IsAvailable".
import pypylon.genicam as geni
import pypylon.pylon as py
camera = py.InstantCamera(py.TlFactory.GetInstance().CreateFirstDevice())
# you can call "GetNode" before open the camera,
# but you cant access them and some will be displayed as "notImplemented"
node_map: geni.INodeMap = camera.GetNodeMap()
node = node_map.GetNode("EventFrameStart")
assert not geni.IsImplemented(node)
camera.Open()
node = node_map.GetNode("EventFrameStart")
assert geni.IsImplemented(node)
# after the camera was opened, some nodes are only
# available with matching preconditions
# Set the Frame Burst Start to Trigger to off, so HighSpeed is not available
camera.TriggerSelector.Value = "FrameBurstStart"
camera.TriggerMode.Value = "Off"
# check the nodes
node = node_map.GetNode("EnumEntry_BslAcquisitionBurstMode_HighSpeed")
assert geni.IsImplemented(node)
assert not geni.IsAvailable(node)
print(f"List of Available Symbolics: {node_map.GetNode('BslAcquisitionBurstMode').Symbolics}")
# Set the Frame Burst Start to Trigger to off, so HighSpeed is available
camera.TriggerSelector.Value = "FrameBurstStart"
camera.TriggerMode.Value = "On"
# check the nodes again
node = node_map.GetNode("EnumEntry_BslAcquisitionBurstMode_HighSpeed")
assert geni.IsImplemented(node)
assert geni.IsAvailable(node)
print(f"List of Available Symbolics: {node_map.GetNode('BslAcquisitionBurstMode').Symbolics}")
# and access to an unknown node map will always raise an LogicalErrorException
try:
node_map.GetNode("This is not a Node!")
except py.LogicalErrorException as error:
print("Node not found, as expected!")
else:
raise AssertionError("An unknown Node Name should raise a Logical Error")
In C++ GetNode("nonexistant") returns a NULL pointer and IsAvailable(NULL) returns false.
This runs without any exception in C++:
INodeMap& nodemap = camera.GetNodeMap();
INode* p_node = nodemap.GetNode("This is not a Node!");
bool bAvailable = IsAvailable(p_node);
bAvailable = IsAvailable(NULL);`
I would expect the same behavior in python as well.
E.g., not every Basler camera model supports the feature "CenterX" and I simply want to check for the presence of that feature. So far, I have no other option as to put every GetNode() in a try/except block.
But maybe I'm missing something, that was the reason for posting this.
Ah okay, i think its an "Ask forgiveness, not permission" way to handle non existing nodes.
But if you want to avoid exception handling at all cost, may you can convert the node map in a dict and use .get() and check if the node is None afterwards. But i am not sure if there is a more efficient way.
nodemap_dict = {node.Node.Name: node for node in node_map.GetNodes()}
assert nodemap_dict.get("OffsetX", None) is not None
assert nodemap_dict.get("Not existing", None) is None
Thanks for understanding the point.
The one or the other way..., in sake of number of lines and in order to keep readability reasonable, it will end up in a helper function.
I will keep this issue open for another while, maybe the Basler guys agree and something will happen with the next release ;-)