调试通过别人的代码,gige 500万工业相机海康。
如果你下载了open gige vision 代码,你可以参考我下面的代码(可以打开相机并取像显示):
/* if(argc != 3)
throw std::runtime_error(str(boost::format("Usage: %1% <ip> <port>") % argv[0]));*/
GigEVision vis;
//char ip[16];
// wcstombs(ip, _T(argv[1]), sizeof(ip));
//char port[16];
// wcstombs(port, _T(argv[2]), sizeof(port));
vis.Gvcp().Connect("192.168.20.54", "3956");//192.168.20.48本地客户端,链接相机192.168.20.54服务器端
// vis.Gvcp().Connect("169.254.0.2", "12221");//192.168.20.48本地,相机192.168.20.54服务器端
// vis.Gvcp().Connect(argv[1], argv[2]);
std::string hello=vis.Gvcp().FindCam().to_string();//返回的是远端设备gige相机地址202403081043//
// std::cout << "Cam Addr: " << vis.Gvcp().FindCam().to_string() << std::endl;
vis.GenICam().ReadXmlFile();
vis.GenICam().PrintNodes();
// format bool values as strings,通过字符串格式布尔值,这些字符串是通过读xml得到的,这里的字符串相当于命令,很像当年vc6.0打开关闭光驱的情形。202403111002
std::cout.setf (std::ios::boolalpha);
以上代码最关键是,标红的,其他不重要。再看接下来:
//读相机width
boost::array<uint8_t, 16> buff = {0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x00, 0x91, 0x00, 0x03, 0x03, 0x60, 0x00, 0x00, 0x00, 0x00};
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buff), *vis.Gvcp().m_it);
boost::array<uint8_t, 12> recv_buf;
boost::asio::ip::udp::endpoint sender_endpoint;
if(vis.Gvcp().m_sock.receive_from(boost::asio::buffer(recv_buf), sender_endpoint) == 12)
{
recv_buf[0] == 0x00;
recv_buf[1] == 0x00;
recv_buf[2] == 0x00;
recv_buf[3] == 0x81;
recv_buf[4] == 0x00;
recv_buf[5] == 0x04;
recv_buf[6] == 0x00;
recv_buf[7] == 0x91;
recv_buf[8] == 0x00;
recv_buf[9] == 0x00;
recv_buf[10] == 0x0a;
recv_buf[11] == 0x20;
//r如果返回如此,width=2592=registervalue=0x0a20
}
//读相机height
boost::array<uint8_t, 16>
buff1 = {0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x00, 0x92, 0x00, 0x03, 0x03, 0xa0, 0x00, 0x00, 0x00, 0x00};
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buff1), *vis.Gvcp().m_it);
boost::array<uint8_t, 12> recv_buf2;
// boost::asio::ip::udp::endpoint sender_endpoint;
if(vis.Gvcp().m_sock.receive_from(boost::asio::buffer(recv_buf2), sender_endpoint) == 12)
{
recv_buf2[10] == 0x07;
recv_buf2[11] == 0x98;
//如果返回如此,width=1944=registervalue=0x0798
}
//读相机canshu offsetx?
boost::array<uint8_t, 16>
buff3 = {0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x00, 0x93, 0x00, 0x03, 0x03, 0x0e0, 0x00, 0x00, 0x00, 0x00};
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buff3), *vis.Gvcp().m_it);
boost::array<uint8_t, 12> recv_buf3;
// boost::asio::ip::udp::endpoint sender_endpoint;
if(vis.Gvcp().m_sock.receive_from(boost::asio::buffer(recv_buf3), sender_endpoint) == 12)
{
recv_buf3[11] == 0;
recv_buf3[10] == 0;
//如果返回如此,offsetx=0=registervalue=0x00
}
//读相机canshu offsety?
boost::array<uint8_t, 16>
buff4 = {0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x00, 0x94, 0x00, 0x03, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00};
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buff4), *vis.Gvcp().m_it);
boost::array<uint8_t, 12> recv_buf4;
// boost::asio::ip::udp::endpoint sender_endpoint;
if(vis.Gvcp().m_sock.receive_from(boost::asio::buffer(recv_buf4), sender_endpoint) == 12)
{
recv_buf4[11] == 0;
recv_buf4[10] == 0;
//如果返回如此,offsety=0=registervalue=0x00
}
//读相机canshu pixformat?
boost::array<uint8_t, 16>
buff5 = {0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x00, 0x95, 0x00, 0x03, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00};
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buff5), *vis.Gvcp().m_it);
boost::array<uint8_t, 12> recv_buf5;
// boost::asio::ip::udp::endpoint sender_endpoint;
if(vis.Gvcp().m_sock.receive_from(boost::asio::buffer(recv_buf5), sender_endpoint) == 12)
{
recv_buf5[11] == 0x01;
recv_buf5[10] == 0x00;
recv_buf5[9] == 0x08;
recv_buf5[8] == 0x01;
//如果返回如此,pixformat=17301505=registervalue="mono8"=01080001
}
//初始化接受数组202403141044
// rawBytes = new byte[Width * Height * bytesPerPixel];
//xmlisloaded=true;IsStreaming=false;准备打开流通道,先要控制
//开始控制,向寄存器写入
boost::array<uint8_t, 16>
buff6= {0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x01, 0x28, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00};
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buff6), *vis.Gvcp().m_it);
boost::array<uint8_t, 12> recv_buf6;
// boost::asio::ip::udp::endpoint sender_endpoint;
if(vis.Gvcp().m_sock.receive_from(boost::asio::buffer(recv_buf6), sender_endpoint) == 12)
{
recv_buf6[11] == 0x00;
recv_buf6[10] == 0x00;
recv_buf6[9] == 0x00;
recv_buf6[8] == 0x00;
//如果返回如此,its free can be control
}
//开始控制,向寄存器写入
boost::array<uint8_t, 16>
buff7= {0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x01, 0x29, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x02};
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buff7), *vis.Gvcp().m_it);
boost::array<uint8_t, 12> recv_buf7;
// boost::asio::ip::udp::endpoint sender_endpoint;
if(vis.Gvcp().m_sock.receive_from(boost::asio::buffer(recv_buf7), sender_endpoint) == 12)
{
recv_buf7[11] == 0x01;
recv_buf7[10] == 0x00;
recv_buf7[9] == 0x00;
recv_buf7[8] == 0x00;
//如果返回如此, recv_buf7[11] == 0x01;开始设置心跳,循环5次,如果拒绝,进入心跳线程
}
for(int i=0;i<5;i++)//怎样判断gvcpReply.status==success?如果在5次内设置成功了,就要跳出来
{
uint8_t hello=0x2d;
boost::array<uint8_t, 16>
buff8= {0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x01, hello, 0x00, 0x00, 0x09, 0x38, 0x00, 0x00, 0x27, 0x10};
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buff8), *vis.Gvcp().m_it);
boost::array<uint8_t, 12> recv_buf8;
vis.Gvcp().m_sock.receive_from(boost::asio::buffer(recv_buf8), sender_endpoint);
hello++;
if(recv_buf8[11]==1)
{
//成功了?
i=5;
break;
}
}
vis.Gvcp().StartHeartbeat();
Sleep(10);
讲解:这些代码中和gige相机的互动最为关键,像极了一问一答。特别是心跳的正常运转(以前工程中与电控通信,老是觉得plc的心跳不重要,有怠慢,在此已经意识到,请包涵我的无知),继续代码:
int iFd;//套接字
//以下用winsock绑定套接字udp多播是ok的,暂时不用,注释
struct sockaddr_in Addr;
if ((iFd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
printf("socket fail\n");
//return -1;
}
memset(&Addr, 0, sizeof(struct sockaddr_in));
Addr.sin_family = AF_INET;
Addr.sin_addr.s_addr = inet_addr("192.168.20.48");//本地地址192.168.20.48
Addr.sin_port = htons(63378);//多播的端口,端口用63378
if (bind(iFd, (struct sockaddr *)&Addr, sizeof(Addr)) == -1)//绑定
{
printf("bind failed!\n");
}
else
{
printf("bind ok\n");
}
//开始控制,向寄存器写入GevSCPHostPort
boost::array<uint8_t, 16>
buff9= {0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x01, 0x2e, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f7, 0x92};//写入端口port=63378
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buff9), *vis.Gvcp().m_it);
boost::array<uint8_t, 12> recv_buf9;
// boost::asio::ip::udp::endpoint sender_endpoint;
if(vis.Gvcp().m_sock.receive_from(boost::asio::buffer(recv_buf9), sender_endpoint) == 12)
{
recv_buf9[11] == 0x01;
//如果返回如此, recv_buf9[11] == 0x01;GevSCPHostPort写入ok
}
//开始控制,向寄存器写入GevSCDA
boost::array<uint8_t, 16>
buff10= {0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
//0x01, 0x2f, 0x00, 0x00, 0x0d, 24, 239, 192, 11, 12};//使用多播地址
0x01, 0x2f, 0x00, 0x00, 0x0d, 24, 192, 168, 20, 48};//多播地址?
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buff10), *vis.Gvcp().m_it);
boost::array<uint8_t, 12> recv_buf10;
// boost::asio::ip::udp::endpoint sender_endpoint;
if(vis.Gvcp().m_sock.receive_from(boost::asio::buffer(recv_buf10), sender_endpoint) == 12)
{
recv_buf10[11] == 0x01;
//如果返回如此, recv_buf10[11] == 0x01;GevSCDA写入ok
}
/* <Integer Name="ExposureTime_RegAddr">
<Value>
0x00030b04
</Value>*/
boost::array<uint8_t, 16>
buffex= {0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x03, 0x2f, 0x00, 0x03, 0x0b, 0x04,0,0,0x61,0x0a8};
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buffex), *vis.Gvcp().m_it);
boost::array<uint8_t, 12> recv_bufex;
if(vis.Gvcp().m_sock.receive_from(boost::asio::buffer(recv_bufex), sender_endpoint) == 12)
{
recv_bufex[11] == 0x01;
// if( recv_bufex[11] ==136&& recv_bufex[10] == 19) //如果曝光值是5000,设置为25000=0x61,0x0a8
// {
//boost::array<uint8_t, 16> buffex25000= {0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
//0x01, 0x30, 0x00, 0x03, 0x0b, 0x04,64,0,0x61,0x0a8};
//vis.Gvcp().m_sock.send_to(boost::asio::buffer(buffex25000), *vis.Gvcp().m_it);
// }
}
bool mtu1500=false;
//开始控制,向寄存器写入 var gevSCPSPacketSize = (await Gvcp.GetRegister(nameof(GvcpRegister.GevSCPSPacketSize))).pValue;//地址0x0d04
boost::array<uint8_t, 16>
buff11= {0x42, 0x01, 0x00, 0x80, 0x00, 0x04,
0x01, 0x30, 0x00, 0x00, 0x0d, 0x04};
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buff11), *vis.Gvcp().m_it);
boost::array<uint8_t, 12> recv_buf11;
// boost::asio::ip::udp::endpoint sender_endpoint;
if(vis.Gvcp().m_sock.receive_from(boost::asio::buffer(recv_buf11), sender_endpoint) == 12)
{
recv_buf11[11] == 220;//0x0dc
recv_buf11[10] == 5;//代表mtu=1500,这是读到寄存器gevSCPSPacketSize的值
//如果返回如此, recv_buf11[10] == 0x1f(31); recv_buf11[11] == 0xe4(228);代表mtu=8164,gevSCPSPacketSize 这是读到寄存器gevSCPSPacketSize的值
if( recv_buf11[11] == 220&& recv_buf11[10] == 5)
{
mtu1500=true;
}
}
if(mtu1500)
{
boost::array<uint8_t, 16>
//buff8136= {0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
//0x01, 0x31, 0x00, 0x00, 0x0d, 0x04, 64, 0, 0x1f, 0x0c8};//8136
buff8136= {0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x01, 0x31, 0x00, 0x00, 0x0d, 0x04, 64, 0, 0x1f, 0x0e4};//8164
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buff8136), *vis.Gvcp().m_it);
}
//开始控制,向寄存器写入 acquisitionStart.SetValueAsync(1).ConfigureAwait(false)) as GvcpReply
boost::array<uint8_t, 16>
buff12= {0x42, 0x01, 0x00, 0x82, 0x00, 0x08,
0x01, 0x32, 0x00, 0x03, 0x08, 4, 0, 0, 0, 1};
vis.Gvcp().m_sock.send_to(boost::asio::buffer(buff12), *vis.Gvcp().m_it);
boost::array<uint8_t, 12> recv_buf12;
// boost::asio::ip::udp::endpoint sender_endpoint;
if(vis.Gvcp().m_sock.receive_from(boost::asio::buffer(recv_buf12), sender_endpoint) == 12)
{//这个在c#范例中化的时间很长,写寄存器开始acquisitionStart
recv_buf12[11] == 0x01;
//0 0 0 131 0 4 1 50 0 0 0 1
//如果返回如此, recv_buf12[11] == 0x01;acquisitionStart 写入ok
}
到这里,程序已经成功了,后面是读出图像并显示的过程,linux的第三方gil包和socket未达到预期,全部注释掉了。全部使用windows的东东,在通信和显示图像上。
// 接收数据
char buffer[9000];
int len = sizeof(buffer);
sockaddr_in from;
int fromlen = sizeof(from);
// recvfrom(iFd, buffer, len, 0, (SOCKADDR*)&from, &fromlen);//应该有接受异常保护机制,这里如果异常,要继续向下达成202403181431
// GvspInfo.IsDecodingAsVersion2 = ((buffer[4] & 0xF0) >> 4) == 8;
struct gvspinfo stu;//方式
/* if( ((buffer[4] & 0xF0) >> 4) != 8)
{*/
stu. BlockIDIndex = 2;
stu. BlockIDLength = 2;
stu. PacketIDIndex = 6;
stu. PayloadOffset = 8;
stu. TimeStampIndex = 12;
stu. DataIdentifier = 0x03;
stu. DataEndIdentifier = 0x02;
//}else
//{
// // GvspInfo.SetDecodingTypeParameter();
// stu. BlockIDIndex = 8 ;
// stu. BlockIDLength = 8 ;
// stu. PacketIDIndex = 18 ;
// stu. PayloadOffset = 20 ;
// stu. TimeStampIndex = 24 ;
// stu. DataIdentifier = 0x83 ;
// stu. DataEndIdentifier = 0x82 ;
//}
int packetID=-1;
int frame=1;
int jishu=-1;
uint16_t nPartOfFrame=0;//一帧有619个8136
// uint16_t ceshi=0;
COPYDATASTRUCT cds;
uint8_t** buffer2 = new uint8_t*[2];
/* buffer2[0] = new char[stu.RawImageSize];
buffer2[1] = new char[stu.RawImageSize];*/
buffer2[0] = new uint8_t[2592*1944];
buffer2[1] = new uint8_t[5038848];
int jiou=1;
vis.Gvsp(). m_img.recreate(2592, 1944);
recvfrom(iFd, buffer, 9000, 0, (SOCKADDR*)&Addr, &fromlen);
nPartOfFrame = ntohs(((uint16_t*)&buffer)[3]);
if(0==nPartOfFrame)
for(;;)
{
int changdu= recvfrom(iFd, buffer, 9000, 0, (SOCKADDR*)&Addr, &fromlen);//1472要不要也写一个分支处理程序?202403210919
if(changdu==8164)
{
nPartOfFrame = ntohs(((uint16_t*)&buffer)[3]);//不进来,说明虽然设置8164,但实质只有8136收到buffer20240323
}
if(changdu!=8136)
{
std::cout<< "\recvpacketlenth: " << changdu<< "\frame: " << nPartOfFrame <<std::endl;
//没有看到0包,说明0包没用202403311428
continue;
}
if (changdu==8136)
{
nPartOfFrame = ntohs(((uint16_t*)&buffer)[3]);
jiou= frame%2;
int zhenshidenPartOfFrame= ( nPartOfFrame-1)%619;
if(jiou==1)
{
/* std::copy(std::begin(buffer)+8, std::end(buffer), buffer2[1]+ zhenshidenPartOfFrame*8128);*/
std::copy(std::begin(buffer)+8, std::begin(buffer)+8136, buffer2[1]+ zhenshidenPartOfFrame*8128);
// std::copy(std::begin(buffer)+8, std::end(buffer)+8136, view(vis.Gvsp().m_img).begin()+ zhenshidenPartOfFrame*8128);//太慢,应该丢失太多
// std::copy(std::begin(buffer)+8, std::end(buffer)+8136, vis.Gvsp(). m_buffBIG.begin()+ zhenshidenPartOfFrame*8128);//速度可以
//还是copydata好202403201554
}
else
{
std::copy(std::begin(buffer)+8, std::begin(buffer)+8136, buffer2[0]+ zhenshidenPartOfFrame*8128);
/* std::copy(std::begin(buffer)+8, std::end(buffer), buffer2[0]+ zhenshidenPartOfFrame*8128);*/
}
if(618==zhenshidenPartOfFrame)
{
frame++;
cds.dwData=0;
cds.cbData=5038848;//一帧500万像素
//cds.cbData=4096;//一帧500万像素
cds.lpData= buffer2[1];//这样写可以吗?
if(jiou==0)
{
cds.lpData= buffer2[0];
}
// cds.lpData=(PVOID)hdr->lpData;
// ::SendMessage(hWnd,WM_COPYDATA,NULL,(LPARAM)&cds);
HWND hWnd= :: FindWindow(NULL,L"arrimg");//(LPCSTR),可以利用收声音的c#程序,现成的
//HWND hWnd= ::FindWindow(NULL,(LPCSTR)"arrimg");
::SendMessage(hWnd,WM_COPYDATA,NULL,(LPARAM)&cds);
}
}
}
最后加红的代码,是用sendmessage把图像丢出去,使用windows的WM_COPYDATA消息。
这个方法简单,如果你能存储数据流,像gil的png方式,存几幅图也是可以的,我试了,也是ok的。
要搞定,沉下心来,持之以恒。我可能讲的东西容量太大,你不要指望很快掌握,我吃不好,睡不好,花了整整一个月时间研究,针对这件事情,这还是在有图可希冀的情况下。