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 = 0Cust::NumBuffersDelivered = 0Cust::DeviceRegistersValid = false
- If
StreamDestinationIP/StreamPortare not set beforeStart(),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:
- GVCP writes to the stream registers succeed — the device honors WRITE
SCDA0/SCP0and the camera does start sending GVSP. - The camera transmits the data — confirmed 42k GVSP packets on the wire.
- 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.
- CVB’s GenTL producer never opens the socket it configures. It writes
SCP0 = 49152to the device telling it “send here”, but does not itselfbind()a UDP socket on 49152. We proved this by binding our own socket on 49152 beforeStart(): 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 writesSCP0= some port butSCDA0 = 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:
- 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,
GevSCPHostPortvs theCust::StreamPortwe set, flow-set / mult-part configuration, orGevDataStreamingMode? - Why does
MCLocalwriteSCDA0 = 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;
}