You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by hu...@apache.org on 2022/06/26 07:38:51 UTC
[plc4x] 01/03: Started to clean up
This is an automated email from the ASF dual-hosted git repository.
hutcheb pushed a commit to branch eip_update
in repository https://gitbox.apache.org/repos/asf/plc4x.git
commit 2f0d8c38eb7949663f64850f1f48a28753da7d4c
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Fri Jun 24 07:55:53 2022 +1000
Started to clean up
---
.../java/eip/base/protocol/EipProtocolLogic.java | 137 ++++++++++++++++++---
.../eip/src/main/resources/protocols/eip/eip.mspec | 16 +++
.../eip/ParserSerializerTestsuiteLittle.xml | 95 ++++++++++++++
3 files changed, 229 insertions(+), 19 deletions(-)
diff --git a/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/protocol/EipProtocolLogic.java b/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/protocol/EipProtocolLogic.java
index 1879704e9..aee0902f7 100644
--- a/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/protocol/EipProtocolLogic.java
+++ b/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/protocol/EipProtocolLogic.java
@@ -20,6 +20,7 @@ package org.apache.plc4x.java.eip.base.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
+import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
import org.apache.plc4x.java.api.messages.*;
import org.apache.plc4x.java.api.model.PlcField;
@@ -43,6 +44,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
+import java.time.temporal.TemporalUnit;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -72,6 +74,8 @@ public class EipProtocolLogic extends Plc4xProtocolBase<EipPacket> implements Ha
private boolean useConnectionManager = false;
+ private boolean cipEncapsulationAvailable = false;
+
@Override
public void setConfiguration(EIPConfiguration configuration) {
this.configuration = configuration;
@@ -80,9 +84,34 @@ public class EipProtocolLogic extends Plc4xProtocolBase<EipPacket> implements Ha
this.tm = new RequestTransactionManager(1);
}
- @Override
- public void onConnect(ConversationContext<EipPacket> context) {
+ public CompletableFuture<Boolean> detectEndianness(ConversationContext<EipPacket> context) {
logger.debug("Sending Register Session EIP Package");
+ CompletableFuture<Boolean> future = new CompletableFuture<>();
+ RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+
+ NullLittleCommand listServicesRequest = new NullLittleCommand(
+ EMPTY_SESSION_HANDLE,
+ CIPStatus.Success.getValue(),
+ DEFAULT_SENDER_CONTEXT,
+ 0L,
+ this.configuration.getByteOrder());
+ transaction.submit(() -> context.sendRequest(listServicesRequest)
+ .expectResponse(EipPacket.class, REQUEST_TIMEOUT).unwrap(p -> p)
+ .onError((p,e) -> logger.warn("No response for initial packet. Suspect device uses Big endian"))
+ .onTimeout(p -> logger.warn("No response for initial packet. Suspect device uses Big endian"))
+ .check(p -> p instanceof NullLittleCommand)
+ .handle(p -> {
+ logger.info("Device uses little endian");
+ future.complete(true);
+ })
+ );
+ return future;
+ }
+
+ private CompletableFuture<Boolean> listServices(ConversationContext<EipPacket> context) {
+ logger.debug("Sending List Services packet to confirm CIP Encapsulation is available");
+ CompletableFuture<Boolean> future = new CompletableFuture<>();
+ RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
ListServicesRequest listServicesRequest = new ListServicesRequest(
EMPTY_SESSION_HANDLE,
@@ -90,24 +119,96 @@ public class EipProtocolLogic extends Plc4xProtocolBase<EipPacket> implements Ha
DEFAULT_SENDER_CONTEXT,
0L,
this.configuration.getByteOrder());
- context.sendRequest(listServicesRequest)
+ transaction.submit(() -> context.sendRequest(listServicesRequest)
.expectResponse(EipPacket.class, REQUEST_TIMEOUT).unwrap(p -> p)
.check(p -> p instanceof ListServicesResponse)
.handle(p -> {
if (p.getStatus() == CIPStatus.Success.getValue()) {
ServicesResponse listServicesResponse = (ServicesResponse) ((ListServicesResponse) p).getTypeId().get(0);
- this.useConnectionManager = listServicesResponse.getSupportsCIPEncapsulation();
- logger.debug("Device is capable of CIP over EIP encapsulation");
+ if (listServicesResponse.getSupportsCIPEncapsulation()) {
+ logger.debug("Device is capable of CIP over EIP encapsulation");
+ }
+ future.complete(listServicesResponse.getSupportsCIPEncapsulation());
} else {
- logger.warn("Got status code while polling for supported services [{}]", p.getStatus());
+ logger.warn("Got status code while polling for supported EIP services [{}]", p.getStatus());
+ future.complete(false);
+ }
+ })
+ );
+ return future;
+ }
+
+ private CompletableFuture<Boolean> getAllAttributes(ConversationContext<EipPacket> context) {
+ logger.debug("Requesting list of supported attributes");
+ CompletableFuture<Boolean> future = new CompletableFuture<>();
+ RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+ ListServicesRequest listServicesRequest = new ListServicesRequest(
+ EMPTY_SESSION_HANDLE,
+ CIPStatus.Success.getValue(),
+ DEFAULT_SENDER_CONTEXT,
+ 0L,
+ this.configuration.getByteOrder());
+ transaction.submit(() -> context.sendRequest(listServicesRequest)
+ .expectResponse(EipPacket.class, REQUEST_TIMEOUT).unwrap(p -> p)
+ .check(p -> p instanceof ListServicesResponse)
+ .handle(p -> {
+ if (p.getStatus() == CIPStatus.Success.getValue()) {
+ ServicesResponse listServicesResponse = (ServicesResponse) ((ListServicesResponse) p).getTypeId().get(0);
+ if (listServicesResponse.getSupportsCIPEncapsulation()) {
+ logger.debug("Device is capable of CIP over EIP encapsulation");
+ }
+ future.complete(listServicesResponse.getSupportsCIPEncapsulation());
+ } else {
+ logger.warn("Got status code while polling for supported EIP services [{}]", p.getStatus());
+ future.complete(false);
}
- onConnectRegisterSession(context);
- });
+ })
+ );
+ return future;
}
- private void onConnectRegisterSession(ConversationContext<EipPacket> context) {
+ @Override
+ public void onConnect(ConversationContext<EipPacket> context) {
+
+ try {
+ detectEndianness(context).get(REQUEST_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ // This is where we should set the endianness, still not sure if this is correct though.
+ }
+
+ try {
+ this.cipEncapsulationAvailable = listServices(context).get(REQUEST_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
+ if (!this.cipEncapsulationAvailable) {
+ logger.error("Device doesn't support EIP with Encapsulated CIP");
+ context.fireDisconnected();
+ return;
+ }
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ // This is where we should set the endianness, still not sure if this is correct though.
+ }
+
+ try {
+ onConnectRegisterSession(context).get(REQUEST_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ // This is where we should set the endianness, still not sure if this is correct though.
+ }
+
+ try {
+ getAllAttributes(context).get(REQUEST_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ // This is where we should set the endianness, still not sure if this is correct though.
+ }
+
+
+
+ }
+
+ private CompletableFuture<Boolean> onConnectRegisterSession(ConversationContext<EipPacket> context) {
logger.debug("Sending Register Session EIP Package");
+ CompletableFuture<Boolean> future = new CompletableFuture<>();
+ RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+
EipConnectionRequest connectionRequest =
new EipConnectionRequest(
EMPTY_SESSION_HANDLE,
@@ -115,25 +216,23 @@ public class EipProtocolLogic extends Plc4xProtocolBase<EipPacket> implements Ha
DEFAULT_SENDER_CONTEXT,
0L,
this.configuration.getByteOrder());
- context.sendRequest(connectionRequest)
+
+ transaction.submit(() -> context.sendRequest(connectionRequest)
.expectResponse(EipPacket.class, REQUEST_TIMEOUT).unwrap(p -> p)
.check(p -> p instanceof EipConnectionRequest)
.handle(p -> {
if (p.getStatus() == CIPStatus.Success.getValue()) {
sessionHandle = p.getSessionHandle();
senderContext = p.getSenderContext();
- logger.debug("Got assigned with Session {}", sessionHandle);
- if (this.useConnectionManager) {
- onConnectOpenConnectionManager(context, p);
- } else {
- logger.debug("Using unconnected explicit messaging");
- // Send an event that connection setup is complete.
- context.fireConnected();
- }
+ logger.debug("Got assigned with Session handle {}", sessionHandle);
+ future.complete(true);
} else {
logger.warn("Got status code [{}]", p.getStatus());
+ future.completeExceptionally(new PlcConnectionException("Error while handling Register Session response"));
}
- });
+ })
+ );
+ return future;
}
public void onConnectOpenConnectionManager(ConversationContext<EipPacket> context, EipPacket response) {
diff --git a/protocols/eip/src/main/resources/protocols/eip/eip.mspec b/protocols/eip/src/main/resources/protocols/eip/eip.mspec
index e423d04be..b1bb73b5a 100644
--- a/protocols/eip/src/main/resources/protocols/eip/eip.mspec
+++ b/protocols/eip/src/main/resources/protocols/eip/eip.mspec
@@ -29,6 +29,8 @@
[array byte senderContext count '8']
[simple uint 32 options]
[typeSwitch command,response
+ ['0x0001' NullLittleCommand
+ ]
['0x0004','false' ListServicesRequest
]
['0x0004','true' ListServicesResponse
@@ -53,6 +55,8 @@
[simple uint 16 itemCount]
[array TypeId('order') typeId count 'itemCount']
]
+ ['0x0100' NullBigCommand
+ ]
]
]
@@ -91,6 +95,18 @@
[discriminator bit response]
[discriminator uint 7 service]
[typeSwitch service,response,connected
+ ['0x01','false' GetAttributeAllRequest
+ [implicit int 8 requestPathSize '(classSegment.lengthInBytes + instanceSegment.lengthInBytes)/2']
+ [simple PathSegment('order') classSegment]
+ [simple PathSegment('order') instanceSegment]
+ ]
+ ['0x01','true' GetAttributeAllResponse
+ [implicit int 8 requestPathSize '(classSegment.lengthInBytes + instanceSegment.lengthInBytes)/2']
+ [simple PathSegment('order') classSegment]
+ [simple PathSegment('order') instanceSegment]
+ [implicit uint 16 numberOfClasses 'COUNT(classId)']
+ [array uint 16 classId count 'numberOfClasses']
+ ]
['0x4C','false' CipReadRequest
[implicit int 8 requestPathSize 'COUNT(tag) / 2']
[array byte tag count '(requestPathSize * 2)']
diff --git a/protocols/eip/src/test/resources/protocols/eip/ParserSerializerTestsuiteLittle.xml b/protocols/eip/src/test/resources/protocols/eip/ParserSerializerTestsuiteLittle.xml
index df089e9d5..3483e13b2 100644
--- a/protocols/eip/src/test/resources/protocols/eip/ParserSerializerTestsuiteLittle.xml
+++ b/protocols/eip/src/test/resources/protocols/eip/ParserSerializerTestsuiteLittle.xml
@@ -1102,5 +1102,100 @@
</testcase>
+ <testcase>
+ <name>EIP Get Attribute List Request - Message Router</name>
+ <raw>6f00160045000040000000001400000030145b0300000000000000002000020000000000b2000600010220022401</raw>
+ <root-type>EipPacket</root-type>
+ <parser-arguments>
+ <byteOrder>LITTLE_ENDIAN</byteOrder>
+ <response>false</response>
+ </parser-arguments>
+ <xml>
+ <EipPacket>
+ <command dataType="uint" bitLength="16">111</command>
+ <packetLength dataType="uint" bitLength="16">22</packetLength>
+ <sessionHandle dataType="uint" bitLength="32">1073741893</sessionHandle>
+ <status dataType="uint" bitLength="32">0</status>
+ <senderContext dataType="byte" bitLength="64">0x1400000030145b03</senderContext>
+ <options dataType="uint" bitLength="32">0</options>
+ <CipRRData>
+ <interfaceHandle dataType="uint" bitLength="32">0</interfaceHandle>
+ <timeout dataType="uint" bitLength="16">32</timeout>
+ <itemCount dataType="uint" bitLength="16">2</itemCount>
+ <typeId isList="true">
+ <TypeId>
+ <id dataType="uint" bitLength="16">0</id>
+ <NullAddressItem>
+ <reserved dataType="uint" bitLength="16">0</reserved>
+ </NullAddressItem>
+ </TypeId>
+ <TypeId>
+ <id dataType="uint" bitLength="16">178</id>
+ <UnConnectedDataItem>
+ <packetSize dataType="uint" bitLength="16">6</packetSize>
+ <service>
+ <CipService>
+ <response dataType="bit" bitLength="1">false</response>
+ <service dataType="uint" bitLength="7">1</service>
+ <GetAttributeAllRequest>
+ <requestPathSize dataType="int" bitLength="8">2</requestPathSize>
+ <classSegment>
+ <PathSegment>
+ <pathSegment dataType="uint" bitLength="3">1</pathSegment>
+ <LogicalSegment>
+ <segmentType>
+ <LogicalSegmentType>
+ <logicalSegmentType dataType="uint" bitLength="3">0</logicalSegmentType>
+ <ClassID>
+ <format dataType="uint" bitLength="2">0</format>
+ <segmentClass dataType="uint" bitLength="8">2</segmentClass>
+ </ClassID>
+ </LogicalSegmentType>
+ </segmentType>
+ </LogicalSegment>
+ </PathSegment>
+ </classSegment>
+ <instanceSegment>
+ <PathSegment>
+ <pathSegment dataType="uint" bitLength="3">1</pathSegment>
+ <LogicalSegment>
+ <segmentType>
+ <LogicalSegmentType>
+ <logicalSegmentType dataType="uint" bitLength="3">1</logicalSegmentType>
+ <InstanceID>
+ <format dataType="uint" bitLength="2">0</format>
+ <instance dataType="uint" bitLength="8">1</instance>
+ </InstanceID>
+ </LogicalSegmentType>
+ </segmentType>
+ </LogicalSegment>
+ </PathSegment>
+ </instanceSegment>
+ </GetAttributeAllRequest>
+ </CipService>
+ </service>
+ </UnConnectedDataItem>
+ </TypeId>
+ </typeId>
+ </CipRRData>
+ </EipPacket>
+ </xml>
+ </testcase>
+
+ <testcase>
+ <name>EIP Get Attribute List Response - Message Router</name>
+ <raw>6f00b60045000040000000001400000030145b0300000000000000000000020000000000b200a600810000004e003a03770066004300f6003700f500ac035f005d005e000003ab033703a503040348004203a4038b002f031204b603b203b303b003b10330034f004e00aa03a803a703a6036e037003320331032d031703b20049033503710072007803ac00b0002b03b100730067006b0068007d038d008c006d006a0038031a0369004500f20074006e008e0070006c0002006a036400a100f4000100c100c000060000010500</raw>
+ <root-type>EipPacket</root-type>
+ <parser-arguments>
+ <byteOrder>LITTLE_ENDIAN</byteOrder>
+ <response>true</response>
+ </parser-arguments>
+ <xml>
+ <EipPacket>
+
+
+ </EipPacket>
+ </xml>
+ </testcase>
</test:testsuite>
\ No newline at end of file