目录
一、背景
在一般场景下,只需一路IO连接,但稍微复杂的场景,就需要不同通讯周期的连接,这就需要有多组IO连接。
而大于一组的连接调试方法是一样的,因此主要解决2组连接的代码开发。
二、可行性验证
在Eip Scanner Demo(主站)、PLC(从站)软件上分别配置两个生产者连接,验证其数据收发可行性及稳定性。得到视频及报文如下:
1、Enip
2、ForwardOpen 1
3、ForwardOpen 2
4、ForwardCloes 2
5、ForwardCloes 1
可见,进行了两次ForwardOpen,建立了两个生产者连接。
由上文可见,两个生产者连接的ForwardOpen的不同点在于:
1、Connection Path 由PLC自动生成,直接配置到Ubuntu中
2、Connecte Serial Number 自由选择,只要是两个连接不同即可,见协议原文
3、Connection Parameter (可选,如果两个收发字节数相同,则此项也相同)
因此,便可满足上述要求情况下,将多生产者连接集成到一个SDK中。
但需要注意多线程下的单一物理网口的数据抢占问题。
三、开发调试
发现在_connectionMap中管理了所有的IO连接。
在forwardopen阶段注册_connectionMap,每开一个IO连接都会注册一个_connectionMap进去
在handleConnections中,若IO连接不存在,则删除掉
而在代码中,无需在两个连接的情况下守护两个connectionManager,于是更改代码如下:
int main() {
。。。。
parameters.o2tRealTimeFormat = true;
parameters.t2oRealTimeFormat = true;
parameters.originatorVendorId = 0xaa;
parameters.connectionTimeoutMultiplier=2;
parameters.priorityTimeTick=10;
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 = 0x012345;
parameters.o2tRPI = 50000;
parameters.t2oRPI = 50000;//50ms
parameters.transportTypeTrigger |= NetworkConnectionParams::CLASS1;
parameters.transportTypeTrigger |= NetworkConnectionParams::TRIG_CYCLIC;
std::chrono::milliseconds timeout(5000);
//connect one
auto si_1 = std::make_shared<SessionInfo>("192.168.2.88", 0xAF12,timeout,50000);
ConnectionManager connectionManager_1;
parameters.connectionPath = {0x20, 0x04,0x24, 0x01, 0x2C, 0x65, 0x2C, 0xFF};
parameters.connectionSerialNumber = 0xbebc;
auto io_1 = connectionManager_1.forwardOpen(si_1, parameters);
if (auto ptr = io_1.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";
});
}
//connect two
auto si_2 = std::make_shared<SessionInfo>("192.168.2.88", 0xAF12,timeout,50050);
//ConnectionManager connectionManager_2;
parameters.connectionPath = {0x20, 0x04,0x24, 0x01, 0x2C, 0x64, 0x2C, 0x6E};
parameters.connectionSerialNumber = 0x275D;
//auto io_2 = connectionManager_2.forwardOpen(si_2, parameters);
auto io_2 = connectionManager_1.forwardOpen(si_2, parameters);
if (auto ptr = io_2.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";
});
}
//handle connect
while (connectionManager_1.hasOpenConnections()) {
connectionManager_1.handleConnections(std::chrono::milliseconds(50));
// connectionManager_2.handleConnections(std::chrono::milliseconds(50));
}
connectionManager_1.forwardClose(si_1, io_1);
connectionManager_1.forwardClose(si_2, io_2);
。。。。
return EXIT_SUCCESS;
}
显示Log如下,能正常维持两个连接
wireshark报文如下:
此系列的b站视频见:
基于EIPScanner的Linux Ethernet/IP的多生产者连接及Tag读写实现分享(一 引言)_哔哩哔哩_bilibili
参考代码见:
咸鱼ID:tb764914262