Eip开源主站EIPScanner在Linux上的调试记录(一 初步调通)
本文记录了EIP(Ethernet/IP)协议开发的探索过程。作者发现官方文档仅限注册厂商使用,转而参考国外开源社区资料。经过调研,排除了OpENer(仅支持从机方案)后,最终选用EIPScanner开源方案并成功移植。开发前通过在Windows平台使用EtherNET/IPScannerDemo软件与PLC进行通讯验证,配合WireShark抓包工具完成了协议调试。文章分享了相关参考链接,包括开
目录
一、引言
关于EIP的通讯协议,找了很多资料,没有比较全面完善的。大家都指向,去阅读官方文档,但官方文档只有注册EIP协议的厂家才可使用。本次开发的主要参考来源于国外开源社区。
二、方案调研
2.1 协议原文
原文链接,可自行参考下载查阅
https://ia802308.us.archive.org/4/items/ovda_cip_docs/
2.2 开源方案资料
从过来人角度。这个网址给出的EIP开源方案总结的还是比较可信的。
本文最终成功移植跑通的是上述网址所列的EIPScanner方案,开源地址为:
https://github.com/nimbuscontrols/EIPScanner
EIPScanner方案的帮助文档地址为:
https://eipscanner.readthedocs.io/en/latest/index.html
2.3 走的弯路
在采用EIPScanner方案之前,能找到的资料大多指向OpENer。于是从OpENer开始,按照CSDN的博客一步步摸索,最终复现了其中写的”运行起来没有任何输出,不知可否“。在出现问题后,又手动加了打印,调试了一番。
从现在来看,是由于对协议理解不够导致的。我们要找的是主机方案,而不是从机方案。OpENer是适用于EIP从机的开源实现.在cwyyprog的开头就讲到了。
另外,OpENer的支持文档几乎没有。
https://blog.csdn.net/yueni_zhao/article/details/126662297
三、初步调试
3.1、EIP在Win上适配
在移植开发之前,想在win上,通过EIP上位机软件与运控器通讯,用于验证此路径是否可用。(事实证明这个决定是对的,如果没这一步,开源方案很难调通)。
于是寻找”EIP调试助手“(类似于串口调试助手),最终通过B站某博主的视频找到了合适了经验。《Ethernet/IP 从站实战(信捷、基恩士)》。链接如下。
视频中用到的EtherNET/IP Scanner Demo 软件链接如下:
网络抓帧工具,WireShark链接如下:
https://www.wireshark.org/download.html
以上环境配置好后,在PLC上设置好生产者链接,及要交互的数据参数,同时在WIn端按相同配置完EtherNET/IP Scanner Demo的设置。通讯自动打通,可以通过上位机接收及下发数据指令。具体上位机EtherNET/IP Scanner Demo的配置过程参考:
使用EtherNET/IP Scanner Demo软件与PLC进行通讯的配置说明_eip scanner怎么添加-CSDN博客
3.2、EIP在Ubuntu上调试
正如2.1、2.2节所述,最终采用EIPScanner方案实现上位机的适配。在EIPScanner的帮助文档中,提到了少许关于EIP协议的事情,若后续深入使用EIP,需要补充相关知识。
这里调试主要是得到源码、编译、跑example。example跑通的情况下再进行适配开发。
得到源码参考上述开源地址、
编码参考官方帮助文档、过程中若无Libgtest LibGlog等库,自行安装即可
经调试经验总结,例程建议按DiscoveryManagerExample、ExplicitMessagingExample、ImplicitMessagingExample的顺序测试。

3.2.1 获取从站信息例程
其中DiscoveryManagerExample通过一般的通讯方式,请求PLC的相关信息,放到device.identityObject结构体中,可自行添加打印,观察得到的数据。经调试后的代码如下:
int main() {
Logger::setLogLevel(LogLevel::DEBUG);
#if OS_Windows
WSADATA wsaData;
int winsockStart = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (winsockStart != 0) {
Logger(LogLevel::ERROR) << "Failed to start WinSock - error code: " << winsockStart;
return EXIT_FAILURE;
}
#endif
DiscoveryManager discoveryManager("192.168.2.88", 0xAF12, std::chrono::seconds(1));
auto devices = discoveryManager.discover();
for (auto& device : devices) {
Logger(LogLevel::INFO) << "Discovered device: "
<< device.identityObject.getProductName()
<< " with address " << device.socketAddress.toString()<<"VendorID:"<<device.identityObject.getVendorId()\
<<"ClassID:"<<device.identityObject.getClassId()\
<<"Numb:"<<device.identityObject.getSerialNumber()\
<<"InsTance:"<<device.identityObject.getInstanceId();
}
#if OS_Windows
WSACleanup();
#endif
return EXIT_SUCCESS;
}
代码运行打印的log如下:
[DEBUG] Opened UDP socket fd=3
[DEBUG] Close UDP socket fd=3
[INFO] Discovered device: AP700 Series PLC EIP Adapter with address 192.168.2.88:44818VendorID:1660ClassID:1Numb:1309027550InsTance:0
3.2.2 显式连接
其中,ExplicitMessagingExample的例程通过Forward Open的形式,与下位机建立CIP的连接。显试连接的调通是隐试连接的基础,后续用到的隐试连接同样按此流程进行。
显试连接可以理解为 消息请求与回答的应用场景 使用,适合于单次的通讯。在自研机械臂中,可以按此方式完成 通断使能、故障复位、版本号获取等通讯场景。
//未调通,暂不贴信息
3.2.3 隐式连接
其中ImplicitMessagingExample,为隐试信息的收发,对应与PLC中为生产者连接方式。
调试之处,对照着PLC的设置,并翻看了导出的EDS文件,寻找关键字,做对应适配,代码如下:
int main() {
Logger::setLogLevel(LogLevel::DEBUG);
#if OS_Windows
WSADATA wsaData;
int winsockStart = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (winsockStart != 0) {
Logger(LogLevel::ERROR) << "Failed to start WinSock - error code: " << winsockStart;
return EXIT_FAILURE;
}
#endif
auto si = std::make_shared<SessionInfo>("192.168.2.88", 0xAF12);
// Implicit messaging
ConnectionManager connectionManager;
ConnectionParameters parameters;
parameters.connectionPath = {0x20, 0x04,0x24, 0x78, 0x2C, 0x64, 0x2C, 0x6E}; // config Assm151, output Assm150, intput Assm100
parameters.o2tRealTimeFormat = true;
parameters.originatorVendorId = 342;
parameters.originatorSerialNumber = 32423;
parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::P2P;
parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY;
parameters.t2oNetworkConnectionParams |= 4; //size of Assm100 =32
parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::P2P;
parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY;
parameters.o2tNetworkConnectionParams |= 4; //size of Assm150 = 32
parameters.originatorSerialNumber = 0x12345;
parameters.o2tRPI = 1000000;
parameters.t2oRPI = 1000000;
parameters.transportTypeTrigger |= NetworkConnectionParams::CLASS1;
auto io = connectionManager.forwardOpen(si, parameters);
if (auto ptr = io.lock()) {
ptr->setDataToSend(std::vector<uint8_t>(4));
ptr->setReceiveDataListener([](auto realTimeHeader, auto sequence, auto data) {
std::ostringstream ss;
ss << "secNum=" << sequence << " data=";
for (auto &byte : data) {
ss << "[" << std::hex << (int) byte << "]";
}
Logger(LogLevel::INFO) << "Received: " << ss.str();
});
ptr->setCloseListener([]() {
Logger(LogLevel::INFO) << "Closed";
});
}
int count = 200;
while (connectionManager.hasOpenConnections() && count-- > 0) {
connectionManager.handleConnections(std::chrono::milliseconds(100));
}
connectionManager.forwardClose(si, io);
#if OS_Windows
WSACleanup();
#endif
return EXIT_SUCCESS;
}
跑不通,运行log如下:
//省略
后修改了各种参数,做了大量尝试与验证,均未跑通。
后续,在开源社区的issues部分,看大家是否有相同问题。看到默认为WORD类型(16bit),而PLC中为USINT类型(8bit)、在MessageRouter.h中,将use_8_bit_path_segments=false改为true,解决了报错数据长度不对问题.
在issues中看到反馈,有在西门子PLC上跑不通的问题,Issues连接如下:
https://github.com/nimbuscontrols/EIPScanner/issues/48
得到,通过WireShark的方式,抓帧分析错误原因。并进一步,在WIN上跑通的配置,抓正确帧。再换到Ubuntu上,抓错误帧。通过调整ubuntu配置,得到正确的帧,从而跑通了IO连接.调整后的代码如下:
int main() {
Logger::setLogLevel(LogLevel::DEBUG);
#if OS_Windows
WSADATA wsaData;
int winsockStart = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (winsockStart != 0) {
Logger(LogLevel::ERROR) << "Failed to start WinSock - error code: " << winsockStart;
return EXIT_FAILURE;
}
#endif
//auto si = std::make_shared<SessionInfo>("172.28.1.3", 0xAF12);
auto si = std::make_shared<SessionInfo>("192.168.2.88", 0xAF12);
// Implicit messaging
ConnectionManager connectionManager;
ConnectionParameters parameters;
//parameters.connectionPath = {0x20, 0x04,0x24, 151, 0x2C, 150, 0x2C, 100}; // config Assm151, output Assm150, intput Assm100
parameters.connectionPath = {0x34,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20, 0x04,0x24, 0x01, 0x2C, 0x64, 0x2C, 0x6E};
parameters.o2tRealTimeFormat = true;
parameters.t2oRealTimeFormat = true;
parameters.originatorVendorId = 0xaa;
//parameters.connectionTimeoutMultiplier=64;
//parameters.o2tNetworkConnectionId=0x011764f7;
//parameters.t2oNetworkConnectionId=0x011764f6;
parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::P2P;
parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY;
parameters.t2oNetworkConnectionParams |= 0; //size of Assm100 =1
parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::P2P;
parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY;
parameters.o2tNetworkConnectionParams |= 4; //size of Assm100 =1
parameters.originatorSerialNumber = 0x12345;
parameters.o2tRPI = 50000;
parameters.t2oRPI = 50000;//50ms
parameters.transportTypeTrigger |= NetworkConnectionParams::CLASS1;
parameters.transportTypeTrigger |= NetworkConnectionParams::TRIG_CYCLIC;
auto io = connectionManager.forwardOpen(si, parameters);
if (auto ptr = io.lock()) {
ptr->setDataToSend(std::vector<uint8_t>(4));
ptr->setReceiveDataListener([](auto realTimeHeader, auto sequence, auto data) {
std::ostringstream ss;
ss << "secNum=" << sequence << " data=";
for (auto &byte : data) {
ss << "[" << std::hex << (int) byte << "]";
}
Logger(LogLevel::INFO) << "Received: " << ss.str();
});
ptr->setCloseListener([]() {
Logger(LogLevel::INFO) << "Closed";
});
}
int count = 20000;
while (connectionManager.hasOpenConnections() && count-- > 0) {
//connectionManager.handleConnections(std::chrono::milliseconds(100));
connectionManager.handleConnections(std::chrono::milliseconds(50));
}
connectionManager.forwardClose(si, io);
#if OS_Windows
WSACleanup();
#endif
return EXIT_SUCCESS;
}
系统输出的log如下:
//省略
此系列的b站视频见:
基于EIPScanner的Linux Ethernet/IP的多生产者连接及Tag读写实现分享(一 引言)_哔哩哔哩_bilibili
参考代码见:
咸鱼ID:tb764914262
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)