Exception during discovery

Hi all,

While testing connection events, etc., I got an exception with the following message:
‘External component has thrown an exception.’

The stacktrace is:
at Stemmer.Cvb.DriverLib.DODiscover(String seed, Int64 flags, Int64 reserved, IntPtr& list, IntPtr timeout)
at Cvb.Driver.DeviceDiscovery.Discover(String seed, Flags flags, Int64 reserved, Int32 timeoutMs, ATLIST&
list)
at Cvb.Driver.DeviceDiscovery.Discover(String seed, Flags flags, Int32 timeoutMs, ATLIST& list)
at…

More details on how I got this exception:
I disconnected a device, which was actively grabbing, waited a few seconds and reconnected it on another network card. Since then my discovery is broken even after application restarts. (I haven’t tried restarting my PC or disconnecting the device or anything else yet.)

The parameters of the failing discovery call are:

  • The seed of the network card on which the device was reconnected.
  • IgnoreVins|UpToLevelDevice|IgnoreAccessStatus
  • 0
  • An uninitialized ATLIST for the results

Any idea how I could resolve this issue?

Some extra information: the device is accessible with other SDKs while connected to the second card.
I’ve also reconnected it to the original network card and discovery works again without failure.

Hi @Tomes, which :cvb: version do you have installed? I assume 13.00.005?

Can you please provide your IP address setup for the NICs and camera? Also we might be able to get more details if you could enable native debugging in your app’s properties (this also shows the native call stack and exception).

This looks like something we need to fix.

@parsd, yes, I’m using 13.00.005.

The working NIC:
IP: 192.168.124.2
Mask: 255.255.255.0
Default gateway: 0.0.0.0

The failing NIC:
IP: 192.168.124.3
Mask: 255.255.255.0
Default gateway: 0.0.0.0

Camera:
IP: 192.168.124.1
Mask: 255.255.255.0
Default gateway: 0.0.0.0

I couldn’t retrieve the native call stack, even with native debugging enabled.
By the way, it fails for both the socket and the filter driver.

Do I understand correctly: You connect the camera directly to your NICs. So that if you disconnect the camera also the NIC becomes disconnected?

Does device configuration work with the GenICam Browser or the :cvb: Management Console?

Multiple NICs in the same subnet are considered non-standard by Windows.

This would happen if you attach a switch/router to both NICs and the camera to it. Then various problems occur like cameras not being able to be opened. If you attach the camera directly this won’t happen. If you attach two cameras to one NIC each you also would have the issue.

We will look into this after the Christmas Holidays (which last until 05 January 18).

Yes, the camera is directly connected. It is visible in the Management Console after discovery for both NICs, but on the failing NIC grabbing fails (‘Acquisition timeout’). Too be clear, I’m using only one camera to reproduce this. All other cameras and/or switches are disconnected.

Please configure the NICs with different subnets.
e.g.

  • NIC1: IP: 192.168.124.1
  • NIC2: IP: 192.168.125.1

Then Test the camera on both NICs one time with e.g. IP: 192.168.124.2
and one time with e.g. IP: 192.168.125.2

If it still fails on the second NIC but it works on the first NIC using the Management Console or the GenICam Browser I assume a hardware Issue on the second NIC or the network card performance settings of the second NIC have very bad settings.

1 Like

Until we fix this, you can use the different subnets proposed by @Sebastian as a workaround. If you do this, though, you need to do broadcast discovery (as the camera can only be in one subnet) and then programmatically change the camera’s IP.

This is how you can do it with the new CVB .Net API (it is less code to write than with the C-API):

The code below needs the Stemmer.Cvb.dll assembly and assumes
using Stemmer.Cvb;
using Stemmer.Cvb.Driver;
using Stemmer.Cvb.GenApi;
and
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;

1. Broadcast Discovery

First we need to enumerate all GEV interfaces in the system, that are active:

static DiscoveryInformation[] DiscoverGevInterfaces()
{
  const DiscoverFlags DiscoverInterfaces = DiscoverFlags.UpToLevelInterface | DiscoverFlags.IgnoreGevSD;
	
  return DeviceFactory.Discover(DiscoverInterfaces)
    .Where(info => IsGevInterface(info) && IsActive(info))
    .ToArray();
}

static bool IsGevInterface(DiscoveryInformation info)
{
  return info.TryGetProperty(DiscoveryProperties.InterfaceTLType, out var type) 
    && type.ToUpper() == "GEV";
}

static bool IsActive(DiscoveryInformation info)
{
  return info.TryGetProperty(DiscoveryProperties.InterfaceSubNetList, out var subnets)
    && !string.IsNullOrEmpty(subnets);
}

On each of the found interfaces we can do a broadcast discover:

static DiscoveryInformation[] BroadcastDiscoverOn(DiscoveryInformation iface)
{
  const DiscoverFlags DiscoverAll = DiscoverFlags.IgnoreVins | DiscoverFlags.IgnoreGevSD | DiscoverFlags.IncludeInaccessible;

  iface.SetGenApiFeature("TLInterface", "DisableSubnetMatch", "1");
  iface.SetGenApiFeature("TLInterface", "AllowBroadcastDiscoveryResponse", "1");
	
  return DeviceFactory.Discover(iface, DiscoverAll)
    .ToArray();
}

If we combine both we can get the list of all available GEV devices (reachable or not):

var allInterfaces = DiscoverGevInterfaces();
var allDevices = allInterfaces
  .SelectMany(iface => BroadcastDiscoverOn(iface));

2. Find Cameras outside the Interface’s Subnet

The discovered devices have the complete access path in the DiscoverInformation struct. So we can filter in a way that we only have the ones outside the interface’s subnet:

var unreachableDevices = allDevices
  .Where(device => !IsDeviceReachable(device));

Using the following implementation:

static bool IsDeviceReachable(DiscoveryInformation info)
{
  return GetDeviceSubnet(info)
    .Equals(GetInterfaceSubnet(info));
}

static IPAddress GetInterfaceSubnet(DiscoveryInformation info)
{
  var ipAndSubnet = GetInterfaceIPAndSubnets(info);
		
  // for simplicity we only check the first ip/mask
  return GetSubnet(ipAndSubnet[0], ipAndSubnet[1]);	
}

static IPAddress[] GetInterfaceIPAndSubnets(DiscoveryInformation info)
{
  return info[DiscoveryProperties.InterfaceSubNetList]
    .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
    .Select(addr => IPAddress.Parse(addr))
    .ToArray();
}

static IPAddress GetDeviceSubnet(DiscoveryInformation info)
{
  return GetSubnet
  (
    IPAddress.Parse(info[DiscoveryProperties.DeviceIP]),
    IPAddress.Parse(info[DiscoveryProperties.DeviceSubnetMask])
  );
}

static IPAddress GetSubnet(IPAddress address, IPAddress subnetMask)
{
  return new IPAddress
  (
    address.GetAddressBytes()
      .Zip(subnetMask.GetAddressBytes(), (addr, mask) => (byte)(addr & mask))
      .ToArray()
  );
}

3. Create new IP in the Subnet

We keep it simple and use the host address 2 (su.bn.et.2, e.g. 192.168.125.2) to assign to the camera:

var cam = unreachableDevices.First();
var newIP = CreateNewHostIP(cam, 2);

with:

// simplyfied version for subnet mask 255.255.255.0
static IPAddress CreateNewHostIP(DiscoveryInformation device, byte hostAddress)
{
  return new IPAddress
  (
    GetInterfaceSubnet(device)
      .GetAddressBytes()
      .Take(3)
      .Concat(new[] { hostAddress })
      .ToArray()
  );
}

4. Assign the IP

For that we need to open the interface device of the NIC the camera is attached to:

var ifaceToOpen = allInterfaces
  .First(iface => iface[DiscoveryProperties.InterfaceId] == cam[DiscoveryProperties.InterfaceId]);
		
using (var ifaceDevice = DeviceFactory.Open(ifaceToOpen))
{
  ForceIP(ifaceDevice.NodeMaps[NodeMapNames.Interface], cam, newIP);
}

The ForceIP is implemented as follows:
(Disclaimer: the code compiles, but I didn’t have an actual camera to test with.)

static void ForceIP(NodeMap iface, DiscoveryInformation camera, IPAddress newIP)
{
  var macAddress = PhysicalAddress.Parse(camera[DiscoveryProperties.DeviceMac]);
  var subnet = GetInterfaceSubnetMask(camera);
	
  var updateDeviceList = iface["DeviceUpdateList"] as CommandNode;
  updateDeviceList.Execute();
		
  var cfgMac = iface["IPCfg_MAC"] as IntegerNode;
  cfgMac.Value = MacToInt(macAddress);
  var cfgIP = iface["IPCfg_IP"] as IntegerNode;
  cfgIP.Value = IPToInt(newIP);
  var cfgSubnet = iface["IPCfg_Subnet"] as IntegerNode;
  cfgSubnet.Value = IPToInt(subnet);
  var cfgForceIP = iface["IPCfg_SetForceIP"] as CommandNode;
  cfgForceIP.Execute();
}

static IPAddress GetInterfaceSubnetMask(DiscoveryInformation info)
{
  return GetInterfaceIPAndSubnets(info)[1];
}

static long MacToInt(PhysicalAddress mac)
{
  long value = 0;
  foreach (var b in mac.GetAddressBytes().Reverse())
  {
    value <<= 8;
    value += b;
  }
  return value;
}

static uint IPToInt(IPAddress addr)
{
  uint value = 0;
  foreach (var b in addr.GetAddressBytes())
  {
    value <<= 8;
    value += b;
  }
  return value;
}
3 Likes

Thanks all. I can work with assigning the cameras to one subnet each. I’d just like to see the exception to be removed so the discovery doesn’t fail as a whole. I’ll add a try-catch for now.

It is good to hear that you can make progress with your app. I added this bug to our bug tracker, so that it will be fixed.

Hi, regarding the crash: this should alleviate the issue (although the discovery within the same subnet has not been fixed, yet):

https://forum.commonvisionblox.com/t/devicediscovery-discover-still-an-sehexception/352/2?u=parsd

I’ve just tested this, but unfortunately the exception still occurs. I don’t know whether it’s useful or not, but the bug could only be reproduced if the camera used a persistent IP.

@ parsd For your code above, on the C++ version you did here How to set a the IP-Address of GEV-Camera programmatically in CVB you set this node auto cfgStaticNode = interfaceNM->NodeCvb::CommandNode(CVB_LIT(“IPCfg_SetStatIP”)); and executes it, but you didnt use this node in your code above, I’m unsure why? Without it, the IP address doesn’t change for me, but when i try to add it in and execute the command, a writememory error occurs. I’m wondering why you left it out here and not before, and if you have any idea why when i run all of the above, the IP address on my camera doesnt change. thanks.

@Adam, not sure what your problem is:

  1. The example above does force IP which gives the device a tempory IP address. Temporary meaning until the next reset/power cycle of the camera
  2. Static IP (which is called persistent IP in GigE Vision) will set a new camera IP address which is applied after the next reset/power cycle of the camera

When you do force IP you need to rediscover all devices to see the update. And yes, the persistent IP field in the camera won’t be updated.

I was first wondering the difference between your C++ example and C#, but also I’ve run all this code and rediscovered the devices and it doesnt update the IP

@Adam I think I might be able to answer your questions.
Let’s jump right in:
How did you verify that the IP is not updated? As @parsd pointed out, ForceIP only updates the adress until the next power cycle.
After running the code above, I have used the GenICamBrowser to check the IP of the camera. With my test setup the program did successfully change the IP adress of the camera temporarily.

I was also able to reproduce the writememory error from your earlier post. After ForceIP the camera has to be rediscovered, because it has a new IP now and it cannot be accessed through the old one anymore.

The additional code needed looks almost identical to Step 4 in the tutorial above:

ifaceToOpen = allInterfaces
  .First(iface => iface[DiscoveryProperties.InterfaceId] == cam[DiscoveryProperties.InterfaceId]);
using (var ifaceDevice = DeviceFactory.Open(ifaceToOpen))
{
  StatIP(ifaceDevice.NodeMaps[NodeMapNames.Interface], cam, newIP);
}

StatIP is implemented as follows:

static void StatIP(NodeMap iface, DiscoveryInformation camera, IPAddress newIP)
{
  var macAddress = PhysicalAddress.Parse(camera[DiscoveryProperties.DeviceMac]);
  var subnet = GetInterfaceSubnetMask(camera);
  var updateDeviceList = iface["Std::DeviceUpdateList"] as CommandNode;
  updateDeviceList.Execute();

  var cfgMac = iface["Cust::IPCfg_MAC"] as IntegerNode;
  cfgMac.Value = MacToInt(macAddress);
  var cfgIP = iface["Cust::IPCfg_IP"] as IntegerNode;
  cfgIP.Value = IPToInt(newIP);
  var cfgSubnet = iface["Cust::IPCfg_Subnet"] as IntegerNode;
  cfgSubnet.Value = IPToInt(subnet);
  var cfgStatIP = iface["Cust::IPCfg_SetStatIP"] as CommandNode;
  cfgStatIP.Execute();
}
4 Likes