Skip to content

Commit

Permalink
fix: Gave the initial connection request a bit more time ... also tri…
Browse files Browse the repository at this point in the history
…ed to find the reason for the reconnects.
  • Loading branch information
chrisdutz committed Jan 26, 2024
1 parent 6142f24 commit 54888ea
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 11 deletions.
18 changes: 12 additions & 6 deletions plc4j/drivers/profinet/pom.xml
Expand Up @@ -141,6 +141,15 @@
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>

<dependency>
<groupId>org.apache.plc4x</groupId>
<artifactId>plc4j-utils-test-utils</artifactId>
Expand All @@ -155,12 +164,9 @@
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<groupId>org.pcap4j</groupId>
<artifactId>pcap4j-packetfactory-static</artifactId>
<scope>test</scope>
</dependency>

<dependency>
Expand Down
Expand Up @@ -192,13 +192,14 @@ public boolean onConnect() throws ExecutionException, InterruptedException, Time
*/
public void start() {
final long timeout = (long) deviceContext.getConfiguration().getReductionRatio() * deviceContext.getConfiguration().getSendClockFactor() * deviceContext.getConfiguration().getWatchdogFactor() * MIN_CYCLE_NANO_SEC;
final int cycleTime = (int) ((deviceContext.getConfiguration().getSendClockFactor() * deviceContext.getConfiguration().getReductionRatio() * MIN_CYCLE_NANO_SEC) / 1000000);
final int cycleTime = (deviceContext.getConfiguration().getSendClockFactor() * deviceContext.getConfiguration().getReductionRatio() * MIN_CYCLE_NANO_SEC) / 1000000;
Function<Object, Boolean> subscription =
message -> {
long startTime = System.nanoTime();
ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);
ses.scheduleAtFixedRate(() -> {
try {
// System.out.println("State: " + deviceContext.getState().name() + " queue length: " + deviceContext.getQueue().size());
switch (deviceContext.getState()) {
// If an ipAddress is specified in the device config, we use PN DCP to set the IP
// address of the PN device identified by the name to that given IP address.
Expand All @@ -214,8 +215,9 @@ public void start() {
// Send the packet and process the response ...
recordIdAndSend(createConnection, deviceContext.getSourcePort(), deviceContext.getDestinationPort());

// Wait for it to be finished processing ...
createConnection.getResponseHandled().get(timeout, TimeUnit.NANOSECONDS);
// For some reason the first response quite often came in too late,
// so we're extending the wait time here.
createConnection.getResponseHandled().get(8 * timeout, TimeUnit.NANOSECONDS);
break;
// TODO: It seems this state is never used?
// It seems that in this step we would be setting parameters in the PN device (hereby configuring it)
Expand All @@ -231,21 +233,31 @@ public void start() {
recordIdAndSend(writeParametersEnd, deviceContext.getSourcePort(), deviceContext.getDestinationPort());
writeParametersEnd.getResponseHandled().get(timeout, TimeUnit.NANOSECONDS);
break;
// Here we're waiting for an incoming application-ready request from the device.
case WAITAPPLRDY:
break;
// Here we've received the application-ready request from the device and simply acknowledge
// it, which finishes the connection setup.
case APPLRDY:
ApplicationReadyResponse applicationReadyResponse = new ApplicationReadyResponse(deviceContext.getActivityUuid(), deviceContext.getSequenceNumber());
send(applicationReadyResponse, ProfinetDeviceContext.DEFAULT_UDP_PORT, deviceContext.getApplicationResponseDestinationPort());
deviceContext.getContext().fireConnected();
deviceContext.setState(ProfinetDeviceState.CYCLICDATA);
break;
// In this state we're receiving data from the remote device and in this part of the
// code, we're sending back our data in every cycle.
// TODO: Possibly check if, depending on the reduction ratio, we only have to send back data every few cycles.
case CYCLICDATA:
CyclicData cyclicData = new CyclicData(startTime);
messageWrapper.sendPnioMessage(cyclicData, deviceContext);
// TODO: Check if we're getting data every cycle ... if not, react.
break;
case ABORT:
// TODO: Handle this
}
} catch (InterruptedException | ExecutionException | TimeoutException e) {
deviceContext.setState(ProfinetDeviceState.ABORT);
logger.warn("Got exception", e);
}
}, 0, cycleTime, TimeUnit.MILLISECONDS);
return null;
Expand Down
Expand Up @@ -36,11 +36,14 @@ public class ManualProfinetIoTest {
public static void main(String[] args) throws Exception {
// eth.addr == 88:3f:99:03:ef:b0

// Zylk device name = simocodexbpn156e
// Chris device name = cdxb195b3

// In this example 192.168.54.2 is the local IP of the computer running PLC4J and 192.168.54.23 is the IP of the PN device.
//final PlcConnection connection = new DefaultPlcDriverManager().getConnection("profinet://192.168.54.2?gsddirectory=~/.gsd&devices=[[simocodexbpn156e,DAP%201,(1,),192.168.54.23]]&reductionratio=16&sendclockfactor=32&dataholdfactor=3&watchdogfactor=3");
// REMARK: The driver would use the local network device with the given IP address and to an auto-discovery, trying to find any devices returned with the matching name.
// If this device is then found and an IP address is provided, it would use PN-DCP to set the IP address of that device to the given value.
final PlcConnection connection = new DefaultPlcDriverManager().getConnection("profinet://192.168.54.220?gsddirectory=~/.gsd&devices=[[simocodexbpn156e,DAP%201,(1,)]]&reductionratio=16&sendclockfactor=32&dataholdfactor=3&watchdogfactor=3");
final PlcConnection connection = new DefaultPlcDriverManager().getConnection("profinet://192.168.54.220?gsddirectory=~/.gsd&devices=[[simocodexbpn156e,DAP%201,(1,)]]&reductionratio=128&sendclockfactor=128&dataholdfactor=3&watchdogfactor=3");

PlcBrowseRequest browseRequest = connection.browseRequestBuilder().addQuery("all", "*").build();
PlcBrowseResponse plcBrowseResponse = browseRequest.execute().get(4000, TimeUnit.MILLISECONDS);
Expand All @@ -55,7 +58,7 @@ public static void main(String[] args) throws Exception {
// - Adam Analog Input: eth.addr == 74fe4863f6c2
// - Adam Digital I/O: eth.addr == 74fe48824a7c
PlcSubscriptionRequest.Builder builder = connection.subscriptionRequestBuilder();
builder.addChangeOfStateTag("Input 4", ProfinetTag.of("cdxb195b3.1.1.Inputs.2:BOOL"));
builder.addChangeOfStateTag("Input 4", ProfinetTag.of("simocodexbpn156e.1.1.Inputs.2:BOOL"));
PlcSubscriptionRequest request = builder.build();

final PlcSubscriptionResponse response = request.execute().get();
Expand Down
@@ -0,0 +1,96 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.plc4x.java.profinet;

import org.apache.plc4x.java.profinet.readwrite.PnDcp_Pdu;
import org.apache.plc4x.java.profinet.readwrite.PnDcp_Pdu_RealTimeCyclic;
import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
import org.pcap4j.core.PcapHandle;
import org.pcap4j.core.Pcaps;
import org.pcap4j.packet.Dot1qVlanTagPacket;
import org.pcap4j.packet.EthernetPacket;
import org.pcap4j.packet.Packet;
import org.pcap4j.packet.UnknownPacket;

import java.io.EOFException;
import java.util.Arrays;
import java.util.concurrent.TimeoutException;

public class ManualProfinetPcapTest {

public static void main(String[] args) throws Exception {
try (PcapHandle handle = Pcaps.openOffline("/Users/cdutz/Projects/Apache/PLC4X/profinet-slow.pcapng", PcapHandle.TimestampPrecision.NANO);){
int lastIncomingCycleTime = 0;
int lastOutgoingCycleTime = 0;
int minDelay = 65000;
int maxDelay = 0;
while (true) {
try {
Packet packet = handle.getNextPacketEx();
EthernetPacket.EthernetHeader packetHeader = (EthernetPacket.EthernetHeader) packet.getHeader();
boolean fromDevice = Arrays.equals(new byte[]{(byte) 0xF8, (byte) 0xE4, (byte) 0x3B, (byte) 0xB6, (byte) 0x9B, (byte) 0xBF}, packetHeader.getSrcAddr().getAddress());

// All packets from the device have VLan wrapped around the packet.
if(fromDevice) {
if(packet.getPayload() instanceof Dot1qVlanTagPacket) {
Dot1qVlanTagPacket vlanTagPacket = (Dot1qVlanTagPacket) packet.getPayload();
UnknownPacket payload = (UnknownPacket) vlanTagPacket.getPayload();
ReadBufferByteBased readBuffer = new ReadBufferByteBased(payload.getRawData());
PnDcp_Pdu pnDcpPdu = PnDcp_Pdu.staticParse(readBuffer);
if(pnDcpPdu instanceof PnDcp_Pdu_RealTimeCyclic) {
PnDcp_Pdu_RealTimeCyclic pnDcpPdu1 = (PnDcp_Pdu_RealTimeCyclic) pnDcpPdu;
lastIncomingCycleTime = pnDcpPdu1.getCycleCounter();
//System.out.printf("--> %d\n", pnDcpPdu1.getCycleCounter());
}
} else {
System.out.println("Other packet");
}
} else if (packet.getPayload() instanceof UnknownPacket) {
UnknownPacket payload = (UnknownPacket) packet.getPayload();
ReadBufferByteBased readBuffer = new ReadBufferByteBased(payload.getRawData());
PnDcp_Pdu pnDcpPdu = PnDcp_Pdu.staticParse(readBuffer);
if(pnDcpPdu instanceof PnDcp_Pdu_RealTimeCyclic) {
PnDcp_Pdu_RealTimeCyclic pnDcpPdu1 = (PnDcp_Pdu_RealTimeCyclic) pnDcpPdu;
int difference = (pnDcpPdu1.getCycleCounter() < lastIncomingCycleTime) ? lastIncomingCycleTime - pnDcpPdu1.getCycleCounter() : pnDcpPdu1.getCycleCounter() - lastIncomingCycleTime;
if(difference < 60000 && difference > 100) {
if (difference < minDelay) {
minDelay = difference;
}
if (difference > maxDelay) {
maxDelay = difference;
}
System.out.printf("<-- %10d %10d %10d-%10d\n", difference, pnDcpPdu1.getCycleCounter() - lastOutgoingCycleTime, minDelay, maxDelay);
}
lastOutgoingCycleTime = pnDcpPdu1.getCycleCounter();
}
} else {
System.out.println("Other packet");
}
} catch (TimeoutException e) {
System.out.println("TIMEOUT");
} catch (EOFException e) {
System.out.println("EOF");
break;
}
}
}
}

}

0 comments on commit 54888ea

Please sign in to comment.