Mech-Eye LNX-8300-GL: GVSP stream not received by GenTL producer

Context

We are integrating a Mech-Eye LNX-8300-GL laser profiler with CVB on Ubuntu 22.04 x86_64. The camera is advertised as standard GenICam/GigE Vision and works fine with the official Mech-Eye SDK and HALCON’s GigEVision2 producer. With CVB we get zero image data, even though the camera clearly transmits it.

We tested with CVB 14.01.007 — the camera opens, but no frames arrive.

Symptoms

  • Start() succeeds, AcquisitionStatus = true (AcquisitionTriggerWait), laser fires on software trigger.
  • WaitFor() always returns Timeout. Node map shows:
    • Cust::NumPacketsReceived = 0
    • Cust::NumBuffersDelivered = 0
    • Cust::DeviceRegistersValid = false
  • If StreamDestinationIP/StreamPort are not set before Start(), WaitFor() SIGSEGVs (double free in the acquisition engine).

Wireshark analysis (the important part)

We captured the interface while running the CVB app with StreamDestinationIP = 192.168.23.11 and StreamPort = 49152:

Time GVCP/GVSP Result
+0.000s WRITE SCDA0 (0xD18) = 192.168.23.11 ACK ok
+0.001s WRITE SCP0 (0xD00) = 49152 ACK ok
+0.002s WRITE TLParamsLocked, AcquisitionStart ACK ok
+0.044s–+5s 42,474 GVSP packets → host:49152 camera IS transmitting (~61 MB)
+0.044s ICMP Port Unreachable (host→camera) port 49152 not bound
+5.1s WRITE AcquisitionStop ACK ok

Findings:

  1. GVCP writes to the stream registers succeed — the device honors WRITE SCDA0/SCP0 and the camera does start sending GVSP.
  2. The camera transmits the data — confirmed 42k GVSP packets on the wire.
  3. No UDP socket is bound on the destination port. The host kernel sends ICMP Port Unreachable (Linux rate-limits it to ~1/s, so only the first few appear) and silently drops the rest. The GVSP data reaches the NIC but is discarded by the kernel.
  4. CVB’s GenTL producer never opens the socket it configures. It writes SCP0 = 49152 to the device telling it “send here”, but does not itself bind() a UDP socket on 49152. We proved this by binding our own socket on 49152 before Start(): ICMP disappeared, but CVB still returned Timeout (it uses its own internal receive path that never connected to that port).

Additional experiments:

  • MCLocal mode (StreamDestinationMode = MCLocal): CVB writes SCP0 = some port but SCDA0 = 0x00000000 (invalid IP) → no GVSP at all.
  • ImageStream vs CompositeStream: identical behavior.
  • Various PacketSize (1500/8192), TriggerMode = Off, DataAcquisitionMethod = Nonstop: no change.

Questions

Our reading is that the GenTL producer is not binding the GVSP receive socket / not completing the stream setup for this camera. Questions:

  1. Is there a known requirement for the LNX-8300-GL (or GigE Vision profilers in general) regarding stream setup that we are missing — e.g. a specific node to execute, GevSCPHostPort vs the Cust::StreamPort we set, flow-set / mult-part configuration, or GevDataStreamingMode?
  2. Why does MCLocal write SCDA0 = 0? Shouldn’t the producer populate the host IP automatically?

inimal main.cpp that reproduces the issue (hardcoded stream IP/port, WaitFor loop) is included below.


main.cpp (minimal reproducible)

#include <iostream>
#include <chrono>
#include <csignal>
#include <cvb/device_factory.hpp>
#include <cvb/driver/image_stream.hpp>
#include <cvb/driver/driver.hpp>
#include <cvb/driver/genicam_device.hpp>
#include <cvb/genapi/node_map.hpp>
#include <cvb/genapi/integer_node.hpp>
#include <cvb/genapi/command_node.hpp>

using namespace std;

static void signalHandler(int sig) {
    cerr << "\n=== SEGFAULT (signal " << sig << ") ===" << endl;
    exit(1);
}

int main() {
    signal(SIGSEGV, signalHandler);
    signal(SIGBUS, signalHandler);

    cout << "=== MechEye LNX-8300-GL / CVB GenTL test ===" << endl;

    // [1] Discover (GenTL only)
    auto cameras = Cvb::DeviceFactory::Discover(Cvb::DiscoverFlags::IgnoreVins);
    if (cameras.empty()) { cerr << "No devices\n"; return 1; }
    for (size_t i = 0; i < cameras.size(); ++i) {
        Cvb::String serial{"?"}, model{"?"};
        cameras[i].TryGetProperty(Cvb::Driver::DiscoveryProperties::DeviceSerialNumber, serial);
        cameras[i].TryGetProperty(Cvb::Driver::DiscoveryProperties::DeviceModel, model);
        cout << "  [" << i << "] " << model << " serial=" << serial << endl;
    }

    // [2] Open with GenTL stack
    Cvb::DevicePtr device;
    try {
        device = Cvb::DeviceFactory::Open<Cvb::GenICamDevice>(
            cameras[0].AccessToken(), Cvb::AcquisitionStack::PreferGenTL);
    } catch (const exception& e) { cerr << "Open error: " << e.what() << endl; return 1; }
    cout << "Opened OK" << endl;

    auto genDevice = dynamic_pointer_cast<Cvb::GenICamDevice>(device);
    auto imageStream = genDevice->Stream<Cvb::ImageStream>(0);
    if (!imageStream) { cerr << "No stream\n"; return 1; }

    // [3] Configure stream destination (host IP + port) — REQUIRED to avoid SIGSEGV
    auto ds = imageStream->NodeMap(Cvb::NodeMapID::DataStream);
    if (ds) {
        if (auto ip = ds->Node<Cvb::IntegerNode>("StreamDestinationIP"))
            ip->SetValue(3232241419);          // 192.168.23.11
        if (auto port = ds->Node<Cvb::IntegerNode>("StreamPort"))
            port->SetValue(49152);
        if (auto apply = ds->Node<Cvb::CommandNode>("StreamDestinationApply"))
            apply->Execute();
        cout << "StreamDestination set (192.168.23.11:49152)" << endl;
    }

    // [4] Pool + Start
    imageStream->RegisterManagedFlowSetPool(8);
    imageStream->Start();
    cout << "Start() OK, AcquisitionState=" << (int)imageStream->AcquisitionState() << endl;

    // [5] WaitFor loop (5 s)
    auto t0 = chrono::steady_clock::now();
    int frames = 0, timeouts = 0;
    while (chrono::duration_cast<chrono::seconds>(chrono::steady_clock::now() - t0).count() < 5) {
        try {
            Cvb::MultiPartImagePtr image;
            Cvb::WaitStatus status;
            Cvb::NodeMapEnumerator en;
            tie(image, status, en) = imageStream->WaitFor(chrono::milliseconds(500));
            if (status == Cvb::WaitStatus::Ok && image) { cout << "FRAME #" << ++frames << endl; }
            else if (status == Cvb::WaitStatus::Timeout) { if (++timeouts <= 3) cout << "Timeout #" << timeouts << endl; }
        } catch (const exception& e) { cerr << "WaitFor error: " << e.what() << endl; break; }
    }

    imageStream->Stop();
    cout << "Frames=" << frames << " Timeouts=" << timeouts << endl;
    cout << ">>> Check Cust::NumPacketsReceived / NumBuffersDelivered in TLDatastream node map (both 0 in our case)" << endl;
    return 0;
}

Hi @marioroos ,

Thanks for reaching out and for this already very detailed information. As we need to exchange more data, it would be good to do this via direct support contact. I will contact you with a first feedback via E-Mail for a further exchange.

We then can summarise the findings in the forum in the end after fixing the issue.