Exception during discovery

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