工业相机
- Industrial camera
- 度申 do3think
GenICam 标准
GigE Vision 协议
- 设备发现机制:GVCP Device Discovery
- 使用UDP广播,不能跨路由,所以只限定同一局域网 a “limited broadcast” by RFC1122
- Definitions of broadcast types are provided in section 3.3.6 of RFC1122
- In GigE Vision 1.x, GVCP is only defined to listen on UDP port 3956.
- GigE Vision version 2.0 defines how devices can advertise and discover each other using a combination of multicast DNS and DNS-SD, called Zeroconf Discovery
- A GigE Vision device MUST have an XML device description file with the syntax described in the GenApi module of the GenICam standard
- The filename of the XML device description file MUST be different when the file content changes.
GVCP
- GVCP是一种基于UDP传输层协议的应用层协议,此控制协议不包括GigE视觉流媒体协议(GVSP)。
- 控制设备的连接是唯一的,读取的连接可以多个
- GVCP 的 IP头固定为20字节,不使用 IP 头可选项(linux socket 就是默认不使用)
- The first GVCP port of the device MUST use UDP port 3956.This standard GVCP port has been registered with IANA (http://www.iana.org/assignments/port-numbers).
- GVCP, 命令(command)和确认(ack)消息包含在同一个包中,这样能确保包不用碎片化分割,同理,GVCP的包大小不能超过 576 字节,因为可避免IP碎片化的最大传输单元(MTU)为576字节
- 一个GVCP包的结构大小:
Layer Size(in bytes) IP header (options not allowed) 20 UDP header 8 GVCP header 8 Max. GVCP payload 540 Total 576 - GVCP 包必须32位对齐,header已满足条件,即要求有效载荷payload部分必须4字节对齐
- 应用获取设备确认消息超时后,必须重发命令消息,重发命令消息时 req_id 保持不变
- 重传消息默认是 200 ms 超时,次数3,这两个值都应可设置
- req_id 初始值不能为0,由应用端按命令消息递增
- 设备端的 ack_id 返回与命令消息一一对应,即重复的命令也会重复返回(但可能不会重复执行),应用端需处理收到多次同一 ack_id 消息的情况
- 应用端必须忽略 ack_id 不匹配的消息,应用端必须收到前一条命令的ack确认消息后才能发送下一条命令(除了 DISCOVERY_CMD, PACKETRESEND_CMD)
- 应用端要确保心跳连接。设备提供一个Heartbeat Timeout启动寄存器。建议应用端在该设备心跳超时时间内发送三次心跳消息,避免连接被设备端自动关闭
- GVCP header 头结构: 第一字节值固定 0x42
COMMAND MESSAGE HEADER 0x42 8 bits Hard-coded key code value used for message validation. flag 8 bits Upper 4 bits (msb, bit 0 to bit 3) are specific to each GVCP commands. ^^ ^^ Lower 4 bits (lsb, bit 4 to bit 7) are common to all GVCP commands. ^^ ^^ All unused bits must be set to 0. ^^ ^^ bit 0 to 3 – Specific to each command. Set to 0 if the command does not use them. ^^ ^^ bit 4 – Reserved, set to 0 on transmission, ignore on reception. ^^ ^^ bit 5 – Reserved, set to 0 on transmission, ignore on reception. ^^ ^^ bit 6 – Reserved, set to 0 on transmission, ignore on reception. ^^ ^^ bit 7 – ACKNOWLEDGE: ^^ ^^ SET = 要求接收者必须发送 ack 确认消息. ^^ ^^ CLEAR = 接收者不应发送 ack 确认消息. ^^ ^^ ACKNOWLEDGE 的优先级比命令本身是否需要回复的优先级要高 command 16 bits Command message value (see dictionary section). ^^ ^^ Commands from 0 to 32765 are reserved for GigE Vision. Others are available for customization (device-specific). length 16 bits 负载 payload 的字节数,不包括头长度 req_id 16 bits 递增的 req_id, 非0,应用端提供,设备端回复 ack_id 时拷贝相同值 ACK 消息头结构:
ACKNOWLEDGE MESSAGE HEADER | ||
---|---|---|
status | 16 bits | Status of the requested operation (see status code section) |
acknowledge | 16 bits | Acknowledge message value (see dictionary section). |
length | 16 bits | 头之后的负载的字节数 |
ack_id | 16 bits | 值与所响应的命令消息的req_id 相同 |
- GVCP必须使用网络字节顺序,即大端序 big-endian
DISCOVERY
- DISCOVERY_CMD 值 0x0002
- 命令消息头 flag 的 bit3 指示设备是否广播 DISCOVER_ACK 消息
- 命令消息头 flag 的 bit7 必定设置为1,即设备端必须回复 ack 消息
DISCOVERY_ACK 值 0x0003 , 回复消息的头与负载结构(每行4字节,除了标明长度的):
0 1 2 3 status answer = DISCOVERY_ACK length ack_id spec_version_major spec_version_minor device_mode reserved device_MAC_address (high) device_MAC_address (low) IP_config_options IP_config_current reserved reserved reserved current_IP reserved reserved reserved current_subnet_mask reserved reserved reserved default_gateway manufacturer_name [32 bytes] model_name [32 bytes] device_version [32 bytes] manufacturer_specific_information [48 bytes] serial_number [16 bytes] user_defined_name [16 bytes] 测试代码:
#include <iostream> #include <cstring> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <iomanip> #define BROADCAST_PORT 3956 // GigE Vision discovery port #define BUFFER_SIZE 1024 int main() { int sock; struct sockaddr_in broadcastAddr; char broadcastBuffer[BUFFER_SIZE]; int broadcastPermission; unsigned int sendLen; // 创建UDP套接字 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { std::cerr << "Socket creation failed." << std::endl; return -1; } // 设置广播权限 broadcastPermission = 1; if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (void *) &broadcastPermission, sizeof(broadcastPermission)) < 0) { std::cerr << "Setting socket option to SO_BROADCAST failed." << std::endl; close(sock); return -1; } // 设置广播地址 memset(&broadcastAddr, 0, sizeof(broadcastAddr)); broadcastAddr.sin_family = AF_INET; broadcastAddr.sin_addr.s_addr = inet_addr("255.255.255.255"); // 广播地址 broadcastAddr.sin_port = htons(BROADCAST_PORT); // 构造广播消息(这里简化了,实际应该按照GigE Vision协议构造) uint8_t discovery_msg[8] = {0}; discovery_msg[0] = 0x42; discovery_msg[1] = 0x80; discovery_msg[2] = 0; discovery_msg[3] = 0x02; // DISCOVERY_CMD: 0x0002 discovery_msg[4] = 0; discovery_msg[5] = 0; // length 0 discovery_msg[6] = 0; discovery_msg[7] = 1; // req_id 1 const size_t send_len = 8; // 发送广播消息 if (sendto(sock, discovery_msg, send_len, 0, (struct sockaddr *) &broadcastAddr, sizeof(broadcastAddr)) != send_len) { std::cerr << "Broadcast sendto() failed." << std::endl; close(sock); return -1; } std::cout << "Broadcast message sent. Waiting for responses..." << std::endl; // 接收响应(这里简化了,实际应该处理多个响应,并解析GigE Vision协议数据) struct sockaddr_in recvAddr; socklen_t addrLen = sizeof(recvAddr); int recvLen = recvfrom(sock, broadcastBuffer, BUFFER_SIZE, 0, (struct sockaddr *) &recvAddr, &addrLen); if (recvLen > 0) { broadcastBuffer[recvLen] = '\0'; std::cout << "Received a response from: " << inet_ntoa(recvAddr.sin_addr) << std::endl; std::cout << "Size: " << recvLen << std::endl; std::cout << "Response: " << std::hex << std::setfill('0') << std::setw(2) << std::showbase; for (int i = 0; i < recvLen; ++i) { if (i >= 80 && broadcastBuffer[i] > 32 && broadcastBuffer[i] < 127) { std::cout << broadcastBuffer[i] << " "; } else { std::cout << (int)(broadcastBuffer[i]) << " "; } if (i > 0 && ((i+1) % 16 == 0)) { std::cout << std::endl; } } std::cout << std::endl; char mac[7] = {0}; memcpy(mac, broadcastBuffer + 18, 6); char ip[4] = {0}; memcpy(ip, broadcastBuffer + 44, 4); char manufacturer_name[33] = {0}; memcpy(manufacturer_name, broadcastBuffer + 80, 32); char model_name[33] = {0}; memcpy(model_name, broadcastBuffer + 80 + 32, 32); char device_version[33] = {0}; memcpy(device_version, broadcastBuffer + 80 + 64, 32); char manufacturer_spec_info[49] = {0}; memcpy(manufacturer_spec_info, broadcastBuffer + 80 + 96, 48); char serial_number[17] = {0}; memcpy(serial_number, broadcastBuffer + 80 + 96 + 48, 16); char user_defined_name[17] = {0}; memcpy(user_defined_name, broadcastBuffer + 80 + 96 + 48 + 16, 16); std::cout << std::noshowbase << "mac: " << (int)(mac[0]) << ":" << (int)(mac[1]) << ":" << (int)(mac[2]) << ":" << (int)(mac[3]) << ":" << (int)(mac[4]) << ":" << (int)(mac[5]) << std::endl; std::cout << std::dec << std::setw(0) << "ip: " << (int)(ip[0]) << "." << (int)(ip[1]) << "." << (int)(ip[2]) << "." << (int)(ip[3]) << std::endl; std::cout << "manufacturer_name : " << manufacturer_name << std::endl; std::cout << "model_name : " << model_name << std::endl; std::cout << "device_version : " << device_version << std::endl; std::cout << "manufacturer_spec_info : " << manufacturer_spec_info << std::endl; std::cout << "serial_number : " << serial_number << std::endl; std::cout << "user_defined_name : " << user_defined_name << std::endl; } else { std::cerr << "No response received, or recvfrom() failed." << std::endl; } // 关闭套接字 close(sock); return 0; }
FORCEIP
- FORCEIP_CMD (0x0004)可以强制修改静态IP,
- 前提1:知道相机的MAC地址
- 前提2:相机当前没有控制应用端连接
channel 概念
- 通道(channel)概念:A channel is a virtual link used to transfer information between GigE Vision entities. GigE Vision supports three types of channels:
- Control channel (there is always 1 control channel for the primary application)
- Stream channel (from 0 to 512 stream channels)
- Message channel (0 or 1 message channel)
- 除了 3965 用于控制通道端口,其它通道可用任何端口
- GVCP 定义了4级权限
- Exclusive access: 独占权限,当应用程序具有独占访问权限时,设备不能允许第二个应用创建控制通道
- Control access:控制权限,当应用程序具有控制权限时,设备可以允许第二个应用端具有读取权限
- Control access with switchover enabled:可切换的控制权限,设备允许第二个具有正确凭据的应用端拥有控制权限
- Monitor access: 监视权限,如果设备当前没有独占权限的应用端连接就可以使用
- 一个设备只支持独占访问是完全合法的
- 备忘:14.4节 讲述怎么让额外的应用端控制设备
- 应用可在不关闭控制通道的情况下切换权限
- PENDING_ACK:重置等待 ack 超时时间
GVSP
- A stream channel enables data transfer from a GVSP transmitter to a GVSP receiver. This transfer uses the GigE Vision Streaming Protocol (GVSP).
- 允许 1-512 个连接
- 也是基于 UDP IPv4, IP头也是固定20字节不使用IP头可选项
- GVSP 的包大小不要求32位对齐,包大小取决于链路之间的MTU大小 -SCPSx 只决定数据负载(data payload)的包的大小, 数据头包(data leader)与尾包(data trailer)还是限定576字节大小的包。
- 标准传输模式(Standard Transmission mode),把数据分为 data leader, data payload, data trailer 三部分package
- 全入传输模式(All-in Transmission Mode),一个package全包含 data leader, payload, trailer. 可选,不一定支持